swagger查看页面的各种优化,替换首页原有文档入口

This commit is contained in:
暮光:城中城
2021-10-30 19:52:28 +08:00
parent 59ecaf4477
commit c8f570fa6a
46 changed files with 392 additions and 105 deletions

View File

@@ -1,6 +1,6 @@
import {message} from 'ant-design-vue';
// 无需特殊处理的参数类型
let notNeedHandleTypeArr = ['file', 'string', 'integer', 'long', 'double', 'object', 'number', 'boolean'];
let notNeedHandleTypeArr = ['file', 'string', 'integer', 'long', 'double', 'object', 'number', 'boolean', 'ref'];
/**
* 参数解析
* @author 暮光:城中城
@@ -15,21 +15,23 @@ export default {
let requestParamList = [];
for (let i = 0; i < parameters.length; i++) {
let parameter = parameters[i];
let description = parameter.description || '';
let type = parameter.type;
let format = parameter.format;
let subType = undefined;
let children = undefined;
if (!type) {
if (parameter.schema && parameter.schema.type) {
type = parameter.schema.type;
}
}
let additional = undefined;
if (type === 'array') {
// 解析parameter.items.$ref 或 parameter.items.originalRef {$ref: "#/definitions/Model", originalRef: "Model"}
// 解析parameter.items.type {type: 'file'}
if (parameter.items && parameter.items.originalRef) {
children = this.getParamDefinitions(parameter.items.originalRef, definitionsDataMap, indexKey, {}, 0);
} else if (parameter.schema && parameter.schema.items && parameter.schema.items.originalRef) {
children = this.getParamDefinitions(parameter.schema.items.originalRef, definitionsDataMap, indexKey, {}, 0);
} else if (parameter.schema) {
if (parameter.schema.items && parameter.schema.items.originalRef) {
children = this.getParamDefinitions(parameter.schema.items.originalRef, definitionsDataMap, indexKey, {}, 0);
} else if (parameter.schema.type) {
subType = parameter.schema.type;
}
} else if (parameter.items && parameter.items.type) {
subType = parameter.items.type;
} else {
@@ -37,10 +39,43 @@ export default {
message.error('001-遇到未处理的类型,请联系开发人员修改:' + type);
}
} else if (!type) {
if (parameter.schema && parameter.schema.originalRef) {
// 解析parameter.schema {originalRef: "Model", $ref: "#/definitions/Model"}
type = parameter.schema.originalRef;
children = this.getParamDefinitions(type, definitionsDataMap, indexKey, {}, 0);
if (parameter.schema) {
if (parameter.schema.originalRef) {
// 解析parameter.schema {originalRef: "Model", $ref: "#/definitions/Model"}
type = parameter.schema.originalRef;
children = this.getParamDefinitions(type, definitionsDataMap, indexKey, {}, 0);
} else if (parameter.schema.type) {
type = parameter.schema.type;
if (parameter.schema.additionalProperties) {
additional = {};
children = this.getAdditionalProperties(parameter.schema.additionalProperties, additional, definitionsDataMap, indexKey, {}, 0);
format = additional.type;
} else if (parameter.schema.items) {
if (parameter.schema.items.originalRef) {
children = this.getParamDefinitions(parameter.schema.items.originalRef, definitionsDataMap, indexKey, {}, 0);
} else if (parameter.schema.items.type) {
subType = parameter.schema.items.type;
} else {
console.log('0014-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
}
} else {
console.log('0011-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
}
} else {
console.log('0013-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
message.error('0013-遇到未处理的类型,请联系开发人员修改:' + type);
}
} else if (parameter.items && parameter.items.type) {
// 解析parameter.items {type: "object", $ref: "#/definitions/Model"}
type = parameter.items.type;
if (parameter.items.additionalProperties) {
additional = {};
children = this.getAdditionalProperties(parameter.items.additionalProperties, additional, definitionsDataMap, indexKey, {}, 0);
format = additional.type;
} else {
console.log('0012-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
message.error('0012-遇到未处理的类型,请联系开发人员修改:' + type);
}
} else {
console.log('002-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
message.error('002-遇到未处理的类型,请联系开发人员修改:' + type);
@@ -53,13 +88,22 @@ export default {
message.error('003-遇到未处理的类型,请联系开发人员修改:' + type);
}
}
if (parameter.enum && parameter.enum.length > 0) {
description = description || '枚举类型';
description += ',可选值:' + parameter.enum.join('、');
}
requestParamList.push({
type: type,
key: indexKey,
in: parameter.in,
name: parameter.name,
subType: subType,
required: parameter.required ? '是' : '否',
description: parameter.description,
format: format,
enum: parameter.enum,
collectionFormat: parameter.collectionFormat,// 枚举多选时=multi
description: description,
additional: additional,
children: children,
});
indexKey++;
@@ -90,29 +134,54 @@ export default {
},
getParamDefinitions(ref, definitionsDataMap, indexKey, parentRef, deep) {
let definition = definitionsDataMap[ref];
// 层级大于5层 或 父节点已经解析过此类型了 或者 没有类型定义
if (deep >= 5 || parentRef[ref] || !definition) {
// 层级大于5层 或者 没有类型定义
if (deep >= 10 || !definition) {
return undefined;
}
// 允许重复递归一次
parentRef[ref] = (parentRef[ref] || 0) + 1;
if (parentRef[ref] > 2) {
return undefined;
}
parentRef[ref] = 1;
let paramList = [];
let type = definition.type;
let properties = definition.properties;
let indexSub = 1;
if (type === 'object') {
let currentLevelTypes = {};
Object.keys(properties).forEach(key => {
let parameter = properties[key];
let type = parameter.type;
let format = parameter.format;
let description = parameter.description || '';
let subType = undefined;
let additional = undefined;
let enums = undefined;
let keySub = indexKey + '_' + indexSub;
let children = undefined;
// 把当前层级用过的类型清除,防止多个同类型在一层,后面的不能解析
Object.keys(currentLevelTypes).forEach(currentLevelType => {
parentRef[currentLevelType] = undefined;
});
if (type === 'array') {
// 解析parameter.items {originalRef: "Model", $ref: "#/definitions/Model"}
if (parameter.items && parameter.items.originalRef) {
subType = parameter.items.originalRef;
children = this.getParamDefinitions(parameter.items.originalRef, definitionsDataMap, keySub, parentRef, deep + 1);
} else if (parameter.items && parameter.items.type) {
subType = parameter.items.type;
} else {
console.log('004-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
message.error('004-遇到未处理的类型,请联系开发人员修改:' + type);
}
} else if (type === 'object') {
if (parameter.additionalProperties) {
additional = {};
children = this.getAdditionalProperties(parameter.additionalProperties, additional, definitionsDataMap, keySub, parentRef, deep + 1);
format = additional.type;
} else {
console.log('0041-遇到未处理的类型,请联系开发人员修改:' + type, parameter);
}
} else if (!type) {
if (parameter.originalRef) {
type = parameter.originalRef;
@@ -129,18 +198,60 @@ export default {
message.error('006-遇到未处理的类型,请联系开发人员修改:' + type);
}
}
if (parameter.items && parameter.items.enum && parameter.items.enum.length > 0) {
enums = parameter.items.enum;
description = description || '枚举类型';
description += ',可选值:' + parameter.items.enum.join('、');
}
paramList.push({
type: type,
name: key,
key: keySub,
description: parameter.description,
subType: subType,
format: format,
description: description,
enum: enums,
additional: additional,
children: children,
});
indexSub++;
currentLevelTypes[type] = 1;
});
}
return paramList.length > 0 ? paramList : undefined;
},
getAdditionalProperties(additionalProperties, additional, definitionsDataMap, keySub, parentRef, deep) {
if (additionalProperties.originalRef) {
additional.type = additionalProperties.originalRef;
additional.children = this.getParamDefinitions(additionalProperties.originalRef, definitionsDataMap, keySub, parentRef, deep + 1);
return additional.additional;
} else if (additionalProperties.additionalProperties) {
additional.type = additionalProperties.type;
additional.additional = {};
return this.getAdditionalProperties(additionalProperties.additionalProperties, additional.additional, definitionsDataMap, keySub, parentRef, deep + 1);
} else if (additionalProperties.type === 'array') {
additional.type = additionalProperties.type;
if (additionalProperties.items && additionalProperties.items.originalRef) {
let children = this.getParamDefinitions(additionalProperties.items.originalRef, definitionsDataMap, keySub, parentRef, deep + 1);
additional.additional = {
type: additionalProperties.items.originalRef,
children: children
};
return children;
} else {
console.log('007-遇到未处理的类型,请联系开发人员修改:', additionalProperties);
message.error('007-遇到未处理的类型,请联系开发人员修改');
}
} else {
additional.type = additionalProperties.type;
if (notNeedHandleTypeArr.indexOf(additional.type) >= 0) {
// 无需特殊处理的类型
} else {
console.log('008-遇到未处理的类型,请联系开发人员修改:', additionalProperties);
message.error('008-遇到未处理的类型,请联系开发人员修改');
}
}
return undefined;
},
}

View File

@@ -12,7 +12,7 @@
</a-select>
</div>
<a-directory-tree :showIcon="false" :tree-data="treeData" v-model:expandedKeys="expandedKeys" @select="docChecked">
<template #title="{ title, isLeaf, method }">
<template #title="{ title, isLeaf, method, children, key }">
<template v-if="isLeaf">
<a-tag color="pink" v-if="method === 'get'">get</a-tag>
<a-tag color="red" v-else-if="method === 'post'">post</a-tag>
@@ -23,7 +23,8 @@
<a-tag color="purple" v-else-if="method === 'options'">options</a-tag>
<a-tag color="purple" v-else-if="method === 'trace'">trace</a-tag>
</template>
{{title}}
<span style="margin: 0 6px 0 3px;">{{title}}</span>
<a-badge v-if="children" :count="children.length" :number-style="{backgroundColor: '#fff', color: '#999', boxShadow: '0 0 0 1px #d9d9d9 inset'}"/>
</template>
</a-directory-tree>
</a-spin>
@@ -106,6 +107,7 @@
this.loadV2Doc(this.swaggerDocChoice);
},
loadV2Doc(url) {
this.expandedKeys = [];
this.treeDataLoading = true;
customApi.get(url).then(res => {
let v2Doc = this.toJsonObj(res);
@@ -119,6 +121,7 @@
let treeData = createTreeViewByTag(v2Doc, '');
this.treeData = getTreeDataForTag(v2Doc, treeData.pathData, metaInfo);
this.$store.commit('setSwaggerTreePathMap', treeData.pathDataMap);
this.$store.commit('setMethodStatistic', treeData.methodStatistic);
setTimeout(() => this.treeDataLoading = false, 100);
});
},
@@ -145,4 +148,7 @@
.doc-tree .ant-tree-switcher{width: 15px;}
.doc-tree .ant-tree-switcher-noop{width: 0;}
.doc-tree .ant-tag{margin-right: 0;}
.ant-badge-not-a-wrapper:not(.ant-badge-status) {
vertical-align: text-top;
}
</style>

View File

@@ -22,3 +22,17 @@ app.component(ElCascader.name, ElCascader);
app.component(ElCascaderPanel.name, ElCascaderPanel);
app.component(ElConfigProvider.name, ElConfigProvider);
app.mount('#app');
// 注册一个全局自定义指令
import hljs from 'highlight.js'
import 'highlight.js/styles/googlecode.css'
app.directive('highlight', {
updated(el) {
let blocks = el.querySelectorAll('pre code')
blocks.forEach((block) => {
hljs.highlightBlock(block);
});
}
});

View File

@@ -7,10 +7,10 @@ const methodArray = ["get", "head", "post", "put", "patch", "delete", "options",
* @returns {{pathDataMap: {}, pathData: {}}}
*/
export function createTreeViewByTag(swagger, keywords) {
let pathData = {}, pathDataMap = {};
let pathData = {}, pathDataMap = {}, methodStatistic = {};
let swaggerPaths = swagger.paths;
if (!swaggerPaths) {
return {pathDataMap, pathData};
return {pathDataMap, pathData, methodStatistic};
}
//console.log(swaggerPaths);
Object.keys(swaggerPaths).forEach(url => {
@@ -20,6 +20,9 @@ export function createTreeViewByTag(swagger, keywords) {
if (!pathMethods[method] || !pathMethods[method].tags) {
continue;
}
let methodUp = method.toUpperCase();
methodStatistic[methodUp] = (methodStatistic[methodUp] || 0) + 1;
methodStatistic['TOTAL'] = (methodStatistic['TOTAL'] || 0) + 1;
pathMethods[method].tags.forEach(tag => {
let pathTag = pathData[tag];
if (!pathTag) {
@@ -39,7 +42,7 @@ export function createTreeViewByTag(swagger, keywords) {
});
}
});
return {pathData, pathDataMap};
return {pathData, pathDataMap, methodStatistic};
}
export function getTreeDataForTag(swagger, pathData, metaInfo) {

View File

@@ -10,6 +10,7 @@ export default createStore({
swaggerDoc: {},
swaggerDefinitions: {},
swaggerTreePathMap: {},
methodStatistic: {},
}
},
mutations: {
@@ -26,6 +27,9 @@ export default createStore({
setSwaggerTreePathMap(state, swaggerTreePathMap) {
state.swaggerTreePathMap = swaggerTreePathMap;
},
setMethodStatistic(state, methodStatistic) {
state.methodStatistic = methodStatistic;
},
addTableName(state, item) {
let sameObj = Object.assign({}, state.pageTabNameMap);
sameObj[item.key] = item.val;

View File

@@ -35,7 +35,7 @@
<a target="_blank" href="http://www.eclipse.org/jgit">JGit</a>...
</div>
<a-divider content-position="left">前端</a-divider>
Vueelement-uiwangeditormavon-editorqrcodejs2vantvue-routeraxiosvue-hljsbraceechartssql-formattervue-clipboard2...
Vue3element-uiant-design-vuewangeditormavon-editorqrcodejs2vantvue-routeraxiosvue-hljsbraceechartssql-formattervue-clipboard2...
<div>
</div>
</div>

View File

@@ -14,6 +14,17 @@
<a-form-item label="文档说明">
<span v-html="swaggerDocInfo.description"></span>
</a-form-item>
<a-form-item label="接口统计">
<a-row :gutter="[16, 16]">
<template v-for="method in ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH', 'OPTIONS', 'TRACE', 'TOTAL']">
<a-col :span="6" v-if="methodStatistic[method]">
<a-card size="small">
<a-statistic :title="method === 'TOTAL'?'总计':method + '方法'" :value="methodStatistic[method]" suffix="个"></a-statistic>
</a-card>
</a-col>
</template>
</a-row>
</a-form-item>
</a-form>
<div v-else style="text-align: center;">暂无文档信息请先选择文档</div>
</a-card>
@@ -32,7 +43,10 @@
},
swaggerDocInfo () {
return this.$store.state.swaggerDoc.info;
}
},
methodStatistic () {
return this.$store.state.methodStatistic;
},
},
mounted() {
},

View File

@@ -159,7 +159,7 @@
},
docListColumns: [
{title: 'ID', dataIndex: 'id', width: 70},
{title: '文档名称', dataIndex: 'name', width: 150},
{title: '文档名称', dataIndex: 'name', width: 250},
{title: '文档类型', dataIndex: 'docType', width: 90},
{title: '开放访问', dataIndex: 'openVisit', width: 90},
{title: '状态', dataIndex: 'docStatus', width: 90},

View File

@@ -3,15 +3,32 @@
<a-tab-pane tab="接口说明" key="doc">
<a-form :label-col="{span: 4}" :wrapper-col="{span: 20}">
<a-form-item label="接口地址">{{docInfoShow.url}}</a-form-item>
<a-form-item label="说明">{{docInfoShow.description}}</a-form-item>
<a-form-item label="说明">
<a-card size=small><div class="markdown-body" v-html="docInfoShow.description" v-highlight></div></a-card>
</a-form-item>
<a-form-item label="请求方式">{{docInfoShow.method}}</a-form-item>
<a-form-item label="请求数据类型">{{docInfoShow.consumes}}</a-form-item>
<a-form-item label="响应数据类型">{{docInfoShow.produces}}</a-form-item>
<a-form-item label="请求参数">
<a-table :dataSource="requestParamList" :columns="requestParamListColumns" size="small" :pagination="false" defaultExpandAllRows>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'htmlStr'">
<div v-html="text"></div>
<template v-if="column.dataIndex === 'type'">
{{text}}
<template v-if="record.subType">[{{record.subType}}]</template>
<template v-if="record.format">({{record.format}})</template>
</template>
<template v-if="column.dataIndex === 'in'">
<a-tag color="pink" v-if="text === 'header'">header</a-tag>
<a-tag color="red" v-else-if="text === 'body'">body</a-tag>
<a-tag color="orange" v-else-if="text === 'query'">query</a-tag>
<a-tag color="green" v-else-if="text === 'formData'">formData</a-tag>
<template v-else-if="!text">-</template>
<a-tag color="purple" v-else>{{text}}</a-tag>
</template>
<template v-if="column.dataIndex === 'required'">
<span v-if="text === '是'" style="color: #f00;"></span>
<template v-else-if="text === '否'"></template>
<template v-else>-</template>
</template>
</template>
</a-table>
@@ -26,6 +43,13 @@
<template #expandedRowRender="{ record }">
<template v-if="record.schemas">
<a-table :dataSource="record.schemas" :columns="responseParamListColumns" size="small" :pagination="false" defaultExpandAllRows>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'type'">
{{text}}
<template v-if="record.subType">[{{record.subType}}]</template>
<template v-if="record.format">({{record.format}})</template>
</template>
</template>
</a-table>
</template>
<div v-else style="text-align: center;padding: 10px 0;">无参数说明</div>
@@ -35,7 +59,7 @@
</a-form>
</a-tab-pane>
<a-tab-pane tab="在线调试" key="debug">
{{activePage}}
暂未开放
</a-tab-pane>
</a-tabs>
</template>
@@ -47,6 +71,9 @@
import { message } from 'ant-design-vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import swaggerAnalysis from '../../assets/utils/SwaggerAnalysisV2'
import {markdownIt} from 'mavon-editor'
import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css'
export default {
setup() {
@@ -98,9 +125,10 @@
if (docInfo.produces && docInfo.produces.length > 0) {
produces = docInfo.produces.join(' ');
}
let description = markdownIt.render(docInfo.description || docInfo.summary || '');
docInfoShow.value = {
url: docInfo.url,
description: (docInfo.description || docInfo.summary),
description: description,
method: docInfo.method || '',
consumes: consumes,
produces: produces,
@@ -122,41 +150,23 @@
requestParamList,
requestParamListColumns: [
{title: '参数名', dataIndex: 'name', width: 200},
{title: '类型', dataIndex: 'type', width: 150},
{title: '参数位置', dataIndex: 'in', width: 100},
{title: '必填', dataIndex: 'required', width: 60},
{title: '说明', dataIndex: 'description'},
{title: '类型', dataIndex: 'type'},
{title: '参数位置', dataIndex: 'in'},
{title: '是否必填', dataIndex: 'required'},
],
responseParamList,
responseCodeListColumns: [
{title: '状态码', dataIndex: 'code', width: 100},
{title: '类型', dataIndex: 'type', width: 250},
{title: '说明', dataIndex: 'desc'},
{title: '类型', dataIndex: 'type'},
],
responseParamListColumns: [
{title: '参数名', dataIndex: 'name', width: 200},
{title: '参数名', dataIndex: 'name', width: 250},
{title: '类型', dataIndex: 'type', width: 250},
{title: '说明', dataIndex: 'description'},
{title: '类型', dataIndex: 'type'},
],
};
},
};
</script>
<style>
/* S-JSON展示的样式 */
pre.json{margin-top:0px;margin-bottom:0px;}
pre.json .canvas{font:10pt georgia;background-color:#ececec;color:#000000;border:1px solid #cecece;}
pre.json .object-brace{color:#00aa00;font-weight:bold;}
pre.json .array-brace{color:#0033ff;font-weight:bold;}
pre.json .property-name{color:#cc0000;font-weight:bold;}
pre.json .string{color:#007777;}
pre.json .number{color:#aa00aa;}
pre.json .boolean{color:#0000ff;}
pre.json .function{color:#aa6633;text-decoration:italic;}
pre.json .null{color:#0000ff;}
pre.json .comma{color:#000000;font-weight:bold;}
pre.json .annotation{color:#aaa;}
pre img{cursor: pointer;}
/* E-JSON展示的样式 */
</style>