swagger文档树展示开发

This commit is contained in:
暮光:城中城
2021-10-26 22:32:42 +08:00
parent 1d999710bb
commit 395090e958
18 changed files with 424 additions and 71 deletions

View File

@@ -27,6 +27,7 @@ public class SwaggerDocServiceImpl extends ServiceImpl<SwaggerDocMapper, Swagger
queryWrapper.eq(swaggerDoc.getDocType() != null, "doc_type", swaggerDoc.getDocType());
queryWrapper.eq(swaggerDoc.getOpenVisit() != null, "open_visit", swaggerDoc.getOpenVisit());
queryWrapper.eq(swaggerDoc.getDocStatus() != null, "doc_status", swaggerDoc.getDocStatus());
queryWrapper.orderByAsc("id");
return this.list(queryWrapper);
}
}

View File

@@ -80,15 +80,20 @@ public class SwaggerDocumentController {
if (resourceList == null || resourceList.isEmpty()) {
return DocResponseJson.warn("该地址未找到文档");
}
// 删除原有文档
if (swaggerDoc.getId() != null) {
swaggerDocService.removeById(swaggerDoc.getId());
}
// 存明细地址
String swaggerDomain = SwaggerDocUtil.getSwaggerResourceDomain(docUrl);
for (SwaggerResource resource : resourceList) {
swaggerDoc.setId(null);
swaggerDoc.setDocUrl(resource.getUrl());
swaggerDoc.setDocUrl(swaggerDomain + resource.getUrl());
swaggerDoc.setName(resource.getName());
swaggerDocService.save(swaggerDoc);
}
} else if (SwaggerDocUtil.isSwaggerLocation(docUrl)) {
swaggerDocService.save(swaggerDoc);
swaggerDocService.saveOrUpdate(swaggerDoc);
} else {
return DocResponseJson.warn("不支持的地址:" + docUrl);
}

View File

@@ -1,23 +1,33 @@
package com.zyplayer.doc.swaggerplus.controller;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.zyplayer.doc.core.annotation.AuthMan;
import com.zyplayer.doc.core.exception.ConfirmException;
import com.zyplayer.doc.core.json.DocResponseJson;
import com.zyplayer.doc.core.json.ResponseJson;
import com.zyplayer.doc.data.repository.manage.entity.SwaggerDoc;
import com.zyplayer.doc.data.service.manage.SwaggerDocService;
import com.zyplayer.doc.swaggerplus.service.SwaggerHttpRequestService;
import io.swagger.models.Swagger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.swagger.web.ApiKeyVehicle;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.UiConfiguration;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
/**
* 承接了所有的ApiResourceController的接口
@@ -29,21 +39,39 @@ import java.util.Set;
@RestController
public class SwaggerProxyController {
private static final String HAL_MEDIA_TYPE = "application/hal+json";
@Resource
private SwaggerDocService swaggerDocService;
@Resource
private SwaggerHttpRequestService swaggerHttpRequestService;
@RequestMapping("/swagger-resources")
public List<SwaggerResource> swaggerResources() {
Set<SwaggerResource> resourceList = new HashSet<>();
List<SwaggerResource> resourceList = new LinkedList<>();
List<SwaggerDoc> docList = swaggerDocService.getSwaggerDocList(new SwaggerDoc());
for (SwaggerDoc swaggerDoc : docList) {
SwaggerResource resource = new SwaggerResource();
resource.setUrl("/doc-swagger/doc/content?id=" + swaggerDoc.getId());
resource.setUrl("/v2/api-docs?id=" + swaggerDoc.getId());
resource.setName(swaggerDoc.getName());
resource.setSwaggerVersion("2.0");
resourceList.add(resource);
}
return new LinkedList<>(resourceList);
return resourceList;
}
@ResponseBody
@RequestMapping(value = "/v2/api-docs", produces = {MimeTypeUtils.APPLICATION_JSON_VALUE, HAL_MEDIA_TYPE})
public ResponseEntity<Json> content(HttpServletRequest request, Long id) {
SwaggerDoc swaggerDoc = swaggerDocService.getById(id);
if (swaggerDoc == null) {
throw new ConfirmException("文档不存在");
}
if (Objects.equals(swaggerDoc.getDocType(), 1)) {
String contentStr = swaggerHttpRequestService.requestUrl(request, swaggerDoc.getDocUrl());
return new ResponseEntity<>(new Json(contentStr), HttpStatus.OK);
}
return new ResponseEntity<>(new Json(swaggerDoc.getJsonContent()), HttpStatus.OK);
}
@ResponseBody

View File

@@ -14,6 +14,14 @@ public class SwaggerDocUtil {
return docUrl.contains("/swagger-resources");
}
public static String getSwaggerResourceDomain(String docUrl) {
int index = docUrl.indexOf("/swagger-resources");
if (index >= 0) {
return docUrl.substring(0, index);
}
return "";
}
public static boolean isSwaggerLocation(String docUrl) {
return docUrl.contains("/v2/api-docs");
}

View File

@@ -1,6 +1,7 @@
import apiClient from './request/custom.js'
export const customApi = {
get: (url, data) => apiClient({url: url, method: 'get', data: data}),
post: (url, data) => apiClient({url: url, method: 'post', data: data}),
};

View File

@@ -1,5 +1,5 @@
import Axios from 'axios'
import interceptors from './interceptors'
import interceptors from './interceptorsCustom'
import {getCustomApiBaseUrl} from "./utils";
const apiClient = Axios.create({

View File

@@ -5,7 +5,7 @@ import {getZyplayerApiBaseUrl} from "./utils";
// 增加不需要验证结果的标记
const noValidate = {
"./swagger-resources": true,
"/zyplayer-doc-db/datasource/test": true,
"/v2/api-docs": true,
};
export default function (axios) {

View File

@@ -0,0 +1,41 @@
import qs from 'qs'
import { message } from 'ant-design-vue';
export default function (axios) {
axios.interceptors.request.use(
config => {
if (config.method === 'get') {
config.params = config.params || {};
config.params = {...config.params, _: (new Date()).getTime()}
} else if (config.method === 'post') {
config.data = config.data || {};
if (config.data instanceof FormData) {
// 表单,无需特殊处理
} else if (config.data instanceof Object) {
config.data = qs.stringify(config.data);
}
}
return config;
},
error => {
console.log(error);
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
if (!!response.message) {
vue.$message.error('请求错误:' + response.message);
} else {
return response.data;
}
return Promise.reject('请求错误');
},
error => {
console.log('err' + error);
message.error('请求错误:' + error.message);
return Promise.reject(error)
}
);
}

View File

@@ -3,7 +3,7 @@
<a-layout-sider theme="light" :trigger="null" collapsible v-model:collapsed="appMenuCollapsed" :width="rightAsideWidth" style="height: 100vh;overflow: auto;">
<div class="logo">
<img src="../../assets/logo.png">
<h1>swagger文档管理</h1>
<h1>Swagger文档管理</h1>
</div>
<menu-layout :collapsed="appMenuCollapsed"></menu-layout>
</a-layout-sider>
@@ -13,13 +13,10 @@
<a-layout>
<a-layout-header style="border-bottom: 2px solid #eee;background: #fff; padding: 0; box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);-webkit-box-shadow:0 1px 4px rgba(0, 21, 41, 0.08);">
<a-row type="flex">
<a-col flex="60px">
<a-col flex="auto">
<MenuUnfoldOutlined class="trigger" v-if="appMenuCollapsed" @click="appMenuCollapsed = !appMenuCollapsed"/>
<MenuFoldOutlined class="trigger" v-else @click="appMenuCollapsed = !appMenuCollapsed"/>
</a-col>
<a-col flex="auto" style="text-align: center;">
<span v-if="initialEnv === 'newGray'" class="initial-env">当前环境灰度</span>
</a-col>
<a-col flex="400px" style="text-align: right;padding-right: 20px;">
<header-avatar></header-avatar>
</a-col>
@@ -46,14 +43,10 @@
return {
minHeight: minHeight + 'px',
appMenuCollapsed: false,
rightAsideWidth: 250
}
},
computed: {
initialEnv () {
return this.$store.state.initialEnv;
rightAsideWidth: 300
}
},
computed: {},
mounted() {
this.dragChangeRightAsideWidth();
},

View File

@@ -17,6 +17,7 @@
<template v-if="menuItem.meta">
<DashboardOutlined v-if="menuItem.meta.icon === 'DashboardOutlined'"/>
<FileTextOutlined v-if="menuItem.meta.icon === 'FileTextOutlined'"/>
<InfoCircleOutlined v-if="menuItem.meta.icon === 'InfoCircleOutlined'"/>
</template>
<span>{{menuItem.name}}</span>
</router-link>
@@ -30,7 +31,8 @@
SettingOutlined,
CarryOutOutlined,
FileTextOutlined,
DashboardOutlined
DashboardOutlined,
InfoCircleOutlined,
} from '@ant-design/icons-vue';
export default {
@@ -43,7 +45,7 @@
},
components: {
StarOutlined, SettingOutlined, CarryOutOutlined, FileTextOutlined,
DashboardOutlined
DashboardOutlined, InfoCircleOutlined
},
methods: {
haveShowChildren(children) {

View File

@@ -4,13 +4,27 @@
<menu-children-layout :menuItem="menuItem" v-for="menuItem in menuData"></menu-children-layout>
</a-menu>
<a-divider style="margin: 6px 0;"/>
<div v-show="!collapsed">
<div v-show="!collapsed" class="doc-tree">
<div style="padding: 10px 5px;">
<a-select placeholder="请选择分组" v-model:value="swaggerDocChoice" style="width: 100%;">
<a-select placeholder="请选择分组" v-model:value="swaggerDocChoice" @change="swaggerDocChoiceChange" style="width: 100%;">
<a-select-option :value="item.url" v-for="item in swaggerResourceList">{{item.name}}</a-select-option>
</a-select>
</div>
<a-directory-tree :tree-data="treeData" v-model:expandedKeys="expandedKeys" @select="docChecked"></a-directory-tree>
<a-directory-tree :showIcon="false" :tree-data="treeData" v-model:expandedKeys="expandedKeys" @select="docChecked">
<template #title="{ title, isLeaf, method }">
<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>
<a-tag color="orange" v-else-if="method === 'put'">put</a-tag>
<a-tag color="green" v-else-if="method === 'head'">head</a-tag>
<a-tag color="cyan" v-else-if="method === 'patch'">patch</a-tag>
<a-tag color="blue" v-else-if="method === 'delete'">delete</a-tag>
<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}}
</template>
</a-directory-tree>
</div>
</div>
</template>
@@ -18,6 +32,7 @@
<script>
import MenuChildrenLayout from './MenuChildrenLayout.vue'
import {customApi} from '../../api'
import {createTreeViewByTag, getTreeDataForTag} from '../../store/SwaggerDocUtil'
export default {
name: 'MenuLayout',
@@ -42,9 +57,9 @@
title: '用户信息管理',
key: '0-0-0',
children: [
{title: '/getUserInfo', key: '0-0-0-0', isLeaf: true, path: '/doc/view', query: {path: '/getUserInfo'}},
{title: '/deleteUserInfo', key: '0-0-0-1', isLeaf: true, path: '/doc/view', query: {path: '/deleteUserInfo'}},
{title: '/updateUserInfo', key: '0-0-0-2', isLeaf: true, path: '/doc/view', query: {path: '/updateUserInfo'}},
{title: '/getUserInfo', key: '0-0-0-0', isLeaf: true, query: {path: '/getUserInfo'}},
{title: '/deleteUserInfo', key: '0-0-0-1', isLeaf: true, query: {path: '/deleteUserInfo'}},
{title: '/updateUserInfo', key: '0-0-0-2', isLeaf: true, query: {path: '/updateUserInfo'}},
],
},
],
@@ -81,22 +96,59 @@
docChecked(val, node) {
if (node.node.isLeaf) {
let dataRef = node.node.dataRef;
this.$router.push({path: dataRef.path, query: dataRef.query});
this.$router.push({path: '/doc/view', query: dataRef.query});
}
},
getSwaggerResourceList() {
customApi.post('./swagger-resources').then(res => {
customApi.get('./swagger-resources').then(res => {
if (res instanceof Array) {
this.swaggerResourceList = res || [];
if (this.swaggerResourceList.length > 0) {
this.swaggerDocChoice = this.swaggerResourceList[0].url;
this.swaggerDocChoiceChange();
}
} else {
this.$message.error('获取文档列表请求失败');
}
});
},
swaggerDocChoiceChange() {
this.loadV2Doc(this.swaggerDocChoice);
},
loadV2Doc(url) {
customApi.get(url).then(res => {
let v2Doc = this.toJsonObj(res);
if (typeof v2Doc !== 'object') {
this.$message.error('获取文档数据请求失败');
}
this.$store.commit('setSwaggerDoc', v2Doc);
let metaInfo = {url};
let treeData = createTreeViewByTag(v2Doc);
this.treeData = getTreeDataForTag(v2Doc, treeData.pathIndex, metaInfo);
this.$store.commit('setSwaggerTreePathMap', treeData.treePathDataMap);
});
},
toJsonObj(value) {
if (typeof value !== 'string') {
return value;
}
try {
return JSON.parse(value);
} catch (e) {
try {
return eval('(' + value + ')');// 处理变态的单双引号共存字符串
} catch (e) {
return value || undefined;
}
}
}
}
}
</script>
<style>
.doc-tree{padding: 10px 4px;}
.doc-tree .ant-tree-switcher{width: 15px;}
.doc-tree .ant-tree-switcher-noop{width: 0;}
.doc-tree .ant-tag{margin-right: 0;}
</style>

View File

@@ -21,6 +21,14 @@ let routers = [
},
component: () => import('./views/common/Console.vue')
},
{
path: '/doc/info',
name: '文档信息',
meta: {
icon: 'InfoCircleOutlined',
},
component: () => import('./views/doc/DocInfo.vue')
},
{
path: '/doc/manage',
name: '文档管理',

View File

@@ -0,0 +1,144 @@
export function getDefinitions(definitions) {
if (!definitions) {
return {};
}
let swaggerDefinitions = {};
Object.keys(definitions).forEach((key) => {
swaggerDefinitions["#/definitions/" + key] = definitions[key];
});
return swaggerDefinitions;
}
export function createTreeViewByTag(swagger, keywords) {
let pathIndex = {}, treePathDataMap = {};
let paths = swagger.paths;
let domain = swagger.domainUrl;// 服务器代理会返回此属性
let rewriteDomainUrl = swagger.rewriteDomainUrl;// 服务器代理会返回此属性
if (!paths) {
return;
}
if (!domain) {
domain = "http://" + swagger.host + swagger.basePath;
}
if (domain.substring(domain.length - 1) === "/") {
domain = domain.substring(0, domain.length - 1);
}
//console.log(paths);
Object.keys(paths).forEach(key => {
//console.log(key, paths[key]);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "get", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "head", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "post", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "put", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "patch", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "delete", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "options", treePathDataMap);
setRequestMethodForTag(rewriteDomainUrl, domain, paths[key], pathIndex, key, "trace", treePathDataMap);
});
// console.log(pathIndex);
// console.log(treePathDataMap);
// console.log(treeData);
return {pathIndex: pathIndex, treePathDataMap: treePathDataMap};
}
/**
* 设置对象的各种请求方式,存在则复制
* @param source 资源原始json的paths的指定对象
* @param pathObj 当前的待赋值对象
* @param url url绝对路径
* @param method 请求方式post、get...
* @returns
*/
function setRequestMethodForTag(rewriteDomainUrl, domain, source, pathObj, url, method, treePathDataMap) {
if (!source[method] || !source[method].tags) {
return;
}
source[method].tags.forEach(function(val, index) {
let tempObj = pathObj[val];
if(!tempObj) {
tempObj = pathObj[val] = {};
}
let tempUrlObj = tempObj[url];
if(!tempUrlObj) {
tempUrlObj = tempObj[url] = {};
}
let tempPath = url + "." + method;
tempUrlObj[method] = source[method];
tempUrlObj[method].path = tempPath;
tempUrlObj[method].url = url;
tempUrlObj[method].method = method;
tempUrlObj[method].domain = domain;
tempUrlObj[method].rewriteDomainUrl = rewriteDomainUrl;
treePathDataMap[tempPath] = source[method];
});
}
export function getTreeDataForTag(swagger, pathData, metaInfo) {
return [
{
title: swagger.title || 'Swagger接口文档',
key: '0-0',
children: getTreeHtmlForTag(pathData, 0, metaInfo)
}
];
}
function getTreeHtmlForTag(pathData, treeId, metaInfo) {
let treeData = [];
let indexNow = 1;
// get, head, post, put, patch, delete, options, trace
let actionArrays = ["get", "head", "post", "put", "patch", "delete", "options", "trace"];
Object.keys(pathData).forEach(key => {
let tempNode = pathData[key];
let tempTreeId = treeId + "_" + indexNow;
// 只有一个子元素而且有method元素说明是只有一个节点
let nodeSub = getObjectFirstAttributeIfOnly(tempNode);
if (nodeSub && nodeSub.method) {
nodeSub.treeId = tempTreeId;
let title = nodeSub.summary || nodeSub.path;
treeData.push({
title: title,
key: tempTreeId,
isLeaf: true,
method: nodeSub.method,
query: {
path: nodeSub.path,
...metaInfo
}
});
} else if (actionArrays.indexOf(key) >= 0) {
tempNode.treeId = tempTreeId;
let title = tempNode.summary || tempNode.path;
treeData.push({
title: title,
key: tempTreeId,
isLeaf: true,
method: tempNode.method,
query: {
path: tempNode.path,
...metaInfo
}
});
} else {
treeData.push({title: key, key: key, children: getTreeHtmlForTag(tempNode, tempTreeId, metaInfo)});
}
indexNow++;
});
return treeData;
}
/**
* 如果对象只有一个属性则返回第一个属性否则返回null
* @param data
* @returns
*/
function getObjectFirstAttributeIfOnly(data) {
let len = 0, value = "";
for (let key in data) {
if (++len > 1) {
return undefined;
}
value = data[key];
}
return value;
}

View File

@@ -1,4 +1,5 @@
import {createStore} from 'vuex'
import {getDefinitions, createTreeViewByTag} from './SwaggerDocUtil'
export default createStore({
state() {
@@ -16,12 +17,22 @@ export default createStore({
name: '修改用户信息'
},
},
swaggerDoc: {},
swaggerDefinitions: {},
swaggerTreePathMap: [],
}
},
mutations: {
setUserInfo(state, userInfo) {
state.userInfo = userInfo;
},
setSwaggerDoc(state, swaggerDoc) {
state.swaggerDoc = swaggerDoc;
state.swaggerDefinitions = getDefinitions(swaggerDoc.definitions);
},
setSwaggerTreePathMap(state, swaggerTreePathMap) {
state.swaggerTreePathMap = swaggerTreePathMap;
},
addTableName(state, item) {
let sameObj = Object.assign({}, state.pageTabNameMap);
sameObj[item.key] = item.val;

View File

@@ -1,6 +1,6 @@
<template>
<div>
控制台
控制台{{swaggerDoc.info}}
</div>
</template>
@@ -11,7 +11,11 @@
data() {
return {}
},
computed: {},
computed: {
swaggerDoc () {
return this.$store.state.swaggerDoc;
}
},
mounted() {
},
methods: {}

View File

@@ -0,0 +1,40 @@
<template>
<a-card style="width: 600px">
<a-form :label-col="{span: 4}" :wrapper-col="{span: 20}">
<a-form-item label="标题">{{swaggerDocInfo.title}}</a-form-item>
<a-form-item label="版本">{{swaggerDocInfo.version}}</a-form-item>
<a-form-item label="作者" v-if="swaggerDocInfo.contact">
{{swaggerDocInfo.contact.name}} {{swaggerDocInfo.contact.email}}
<a :href="swaggerDocInfo.contact.url" target="_blank" v-if="swaggerDocInfo.contact.url">{{swaggerDocInfo.contact.url}}</a>
</a-form-item>
<a-form-item label="host">{{swaggerDoc.host}}</a-form-item>
<a-form-item label="许可证" v-if="swaggerDocInfo.license">
<a :href="swaggerDocInfo.license.url" target="_blank">{{swaggerDocInfo.license.name}}</a>
</a-form-item>
<a-form-item label="文档说明">
<span v-html="swaggerDocInfo.description"></span>
</a-form-item>
</a-form>
</a-card>
</template>
<script>
export default {
name: 'docInfo',
components: {},
data() {
return {}
},
computed: {
swaggerDoc () {
return this.$store.state.swaggerDoc || {};
},
swaggerDocInfo () {
return this.$store.state.swaggerDoc.info || {};
}
},
mounted() {
},
methods: {}
}
</script>

View File

@@ -29,11 +29,13 @@
</a-form>
<a-table :dataSource="docList" :columns="docListColumns" size="middle"
:loading="docListLoading"
:scroll="{ x: 1200, y: 'calc(100vh - 340px)' }">
:scroll="{ x: 1400, y: 'calc(100vh - 340px)' }">
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'operation'">
<a-button type="link">编辑</a-button>
<a-button type="link" danger @click="deleteDoc(record)">删除</a-button>
<a-button type="link" @click="editDoc(record)">编辑</a-button>
<a-popconfirm title="确定要删除吗?" @confirm="deleteDoc(record)">
<a-button type="link" danger>删除</a-button>
</a-popconfirm>
</template>
<template v-if="column.dataIndex === 'docType'">
<a-tag color="red" v-if="text === 1">URL添加</a-tag>
@@ -84,7 +86,7 @@
</template>
<script>
import { toRefs, ref, onMounted } from 'vue';
import { toRefs, ref, reactive, onMounted } from 'vue';
import {zyplayerApi} from '../../api';
export default {
@@ -118,6 +120,10 @@
docType: 1, openVisit: 0, docStatus: 1,
};
};
const editDoc = (record) => {
docEdit.value = {...record};
newDocVisible.value = true;
};
const updateDoc = async (id, docStatus, yn) => {
zyplayerApi.swaggerDocUpdate({id, docStatus, yn}).then(res => {
searchDocList();
@@ -138,6 +144,7 @@
openNewDoc,
handleNewDocOk,
deleteDoc,
editDoc,
newDocRules: {
name: [{required: true, message: '请输入文档名称', trigger: 'change'}],
docUrl: [{required: true, message: '请输入文档地址', trigger: 'change'}],
@@ -154,16 +161,11 @@
}, {
title: '文档名称',
dataIndex: 'name',
width: 150,
}, {
title: '文档类型',
dataIndex: 'docType',
width: 90,
}, {
title: '文档地址',
dataIndex: 'docUrl',
}, {
title: '目标域名',
dataIndex: 'rewriteDomain',
}, {
title: '开放访问',
dataIndex: 'openVisit',
@@ -172,6 +174,13 @@
title: '状态',
dataIndex: 'docStatus',
width: 90,
}, {
title: '文档地址',
dataIndex: 'docUrl',
}, {
title: '目标域名',
dataIndex: 'rewriteDomain',
width: 250,
}, {
title: '操作',
dataIndex: 'operation',

View File

@@ -1,33 +1,39 @@
<template>
<a-tabs v-model:activeKey="activePage" closable @tab-click="changePage" style="padding: 5px 10px 0;">
<a-tab-pane tab="接口说明" key="doc"/>
<a-tab-pane tab="在线调试" key="debug"/>
</a-tabs>
<a-tabs v-model:activeKey="activePage" closable @tab-click="changePage" style="padding: 5px 10px 0;">
<a-tab-pane tab="接口说明" key="doc"/>
<a-tab-pane tab="在线调试" key="debug"/>
</a-tabs>
</template>
<script>
export default {
name: 'About',
components: {},
data() {
return {
activePage: 'doc'
}
},
computed: {},
mounted() {
let path = this.$route.query.path;
let docInfo = this.$store.state.docMap[path];
if(!docInfo) {
this.$message.error('没有找到对应的文档');
return;
}
this.$store.commit('addTableName', {key: this.$route.fullPath, val: docInfo.name});
},
methods: {
changePage(){
import {toRefs, ref, reactive, onMounted} from 'vue';
import { useRouter } from "vue-router";
import {useStore} from 'vuex';
import { message } from 'ant-design-vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
}
}
}
export default {
setup() {
const router = useRouter();
const store = useStore()
let activePage = ref('doc');
onBeforeRouteUpdate(async (to, from) => {
let path = to.query.path;
let docInfo = store.state.swaggerTreePathMap[path];
if (!docInfo) {
message.error('没有找到对应的文档');
return;
}
store.commit('addTableName', {key: to.fullPath, val: docInfo.summary});
});
const changePage = () => {
}
return {
activePage,
changePage,
};
},
};
</script>