api文档支持开放访问

This commit is contained in:
暮光:城中城
2021-12-05 22:58:48 +08:00
parent 928c79b747
commit 189e96ff42
70 changed files with 5578 additions and 2983 deletions

View File

@@ -32,10 +32,19 @@
:scroll="{ x: 1400, y: 'calc(100vh - 300px)' }">
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'operation'">
<a-button type="link" @click="editDoc(record)">编辑</a-button>
<a-button size="small" type="link" @click="editDoc(record)">编辑</a-button>
<a-popconfirm title="确定要删除吗?" @confirm="deleteDoc(record)">
<a-button type="link" danger>删除</a-button>
<a-button size="small" type="link" danger>删除</a-button>
</a-popconfirm>
<a-dropdown :trigger="['click']">
<template #overlay>
<a-menu @click="handleActionMenuClick($event, record)">
<a-menu-item key="shareView"><link-outlined /> 查看开放文档</a-menu-item>
<a-menu-item key="shareInstruction"><edit-outlined /> 编辑开放文档说明</a-menu-item>
</a-menu>
</template>
<a-button type="link" size="small">更多<DownOutlined /></a-button>
</a-dropdown>
</template>
<template v-if="column.dataIndex === 'docType'">
<a-tag color="red" v-if="text === 1">Swagger URL</a-tag>
@@ -81,7 +90,9 @@
</template>
</a-form-item>
<a-form-item label="文档内容" required name="jsonContent" v-else-if="docEdit.docType === 2">
<a-textarea placeholder="请输入JSON格式的swagger文档内容" v-model:value="docEdit.jsonContent"></a-textarea>
<!-- 尝试使用ace编辑器但感觉除了卡顿意义不大不会在这里编辑json内容暂时用textarea代替-->
<!-- <ace-editor v-model:value="docEdit.jsonContent" lang="json" theme="monokai" width="100%" height="100" :options="aceEditorConfig"></ace-editor>-->
<a-textarea placeholder="请输入JSON格式的Swagger文档内容" v-model:value="docEdit.jsonContent" :auto-size="{ minRows: 5, maxRows: 10 }"></a-textarea>
<template #extra>
查看文档内容
<a-popover title="文档内容说明">
@@ -108,7 +119,8 @@
</template>
</a-form-item>
<a-form-item label="文档内容" required name="jsonContent" v-else-if="docEdit.docType === 4">
<a-textarea placeholder="请输入JSON格式的openapi文档内容" v-model:value="docEdit.jsonContent"></a-textarea>
<!-- <ace-editor v-model:value="docEdit.jsonContent" lang="json" theme="monokai" width="100%" height="100" :options="aceEditorConfig"></ace-editor>-->
<a-textarea placeholder="请输入JSON格式的OpenApi文档内容" v-model:value="docEdit.jsonContent" :auto-size="{ minRows: 5, maxRows: 10 }"></a-textarea>
<template #extra>
查看文档内容
<a-popover title="文档内容说明">
@@ -135,12 +147,15 @@
</a-popover>
</template>
</a-form-item>
<!-- <a-form-item label="开放访问" required name="openVisit">-->
<!-- <a-radio-group v-model:value="docEdit.openVisit">-->
<!-- <a-radio :value="0"></a-radio>-->
<!-- <a-radio :value="1">开放</a-radio>-->
<!-- </a-radio-group>-->
<!-- </a-form-item>-->
<a-form-item label="开放访问" required name="openVisit">
<a-radio-group v-model:value="docEdit.openVisit">
<a-radio :value="0"></a-radio>
<a-radio :value="1">开放</a-radio>
</a-radio-group>
<template #extra>
开放访问后无需登录即可通过<a @click="openShareViewWindow(docEdit)">开放文档URL</a>访问改文档信息
</template>
</a-form-item>
<a-form-item label="状态" required name="docStatus">
<a-radio-group v-model:value="docEdit.docStatus">
<a-radio :value="1">启用</a-radio>
@@ -149,14 +164,21 @@
</a-form-item>
</a-form>
</a-modal>
<EditShareInstruction ref="instruction"></EditShareInstruction>
</template>
<script>
import { toRefs, ref, reactive, onMounted } from 'vue';
import {zyplayerApi} from '../../api';
import {useStore} from 'vuex';
import aceEditor from "../../assets/ace-editor";
import EditShareInstruction from "./components/EditShareInstruction.vue";
import {getZyplayerApiBaseUrl} from "../../api/request/utils";
import {DownOutlined, LinkOutlined, EditOutlined} from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
export default {
components: {aceEditor, EditShareInstruction, DownOutlined, LinkOutlined, EditOutlined},
setup() {
const store = useStore();
let docList = ref([]);
@@ -218,6 +240,25 @@
});
};
const deleteDoc = async (row) => updateDoc(row.id, null, 0);
const openShareViewWindow = record => {
if (!record.shareUuid) {
message.warning('请先保存文档后再试');
} else if (record.openVisit === 1) {
window.open(getZyplayerApiBaseUrl() + '/doc-api#/share/home?uuid=' + record.shareUuid);
} else {
message.warning('改文档尚未开启开放访问功能,请在编辑页选择开放后再试');
}
};
const handleActionMenuClick = (item, record) => {
if (item.key === 'shareView') {
openShareViewWindow(record);
} else if (item.key === 'shareInstruction') {
instruction.value.editDoc(record.id);
}
}
let instruction = ref();
onMounted(() => {
searchDocList();
});
@@ -234,7 +275,10 @@
deleteDoc,
editDoc,
handleTableChange,
openShareViewWindow,
handleActionMenuClick,
pagination,
instruction,
newDocRules: {
name: [{required: true, message: '请输入文档名称', trigger: 'change'}],
docUrl: [{required: true, message: '请输入文档地址', trigger: 'change'}],
@@ -247,12 +291,21 @@
{title: 'ID', dataIndex: 'id', width: 70},
{title: '文档名称', dataIndex: 'name', width: 250},
{title: '文档类型', dataIndex: 'docType', width: 120},
// {title: '开放访问', dataIndex: 'openVisit', width: 90},
{title: '开放访问', dataIndex: 'openVisit', width: 90},
{title: '状态', dataIndex: 'docStatus', width: 90},
{title: '文档地址', dataIndex: 'docUrl'},
{title: '目标域名', dataIndex: 'rewriteDomain', width: 250},
{title: '操作', dataIndex: 'operation', fixed: 'right', width: 170},
{title: '操作', dataIndex: 'operation', fixed: 'right', width: 200},
],
aceEditorConfig: {
wrap: true,
autoScrollEditorIntoView: true,
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
minLines: 10,
maxLines: 15,
},
swaggerDocDemo:
'{\n'
+ ' "swagger": "2.0",\n'

View File

@@ -0,0 +1,95 @@
<template>
<a-modal v-model:visible="editShareInstructionVisible" @ok="handleNewDocOk" width="90%"
:bodyStyle="{height: 'calc(100vh - 300px)'}">
<template #title>
编辑开放文档说明
<a-tooltip placement="bottom">
<template #title>此说明文档将展示在开放文档的首页展示可点击查看开放文档查看效果</template>
<info-circle-outlined />
</a-tooltip>
</template>
<mavon-editor ref="mavonEditor" v-model="shareInstruction" :toolbars="toolbars"
:externalLink="false"
style="height: 100%;"
placeholder="请录入文档内容"/>
</a-modal>
</template>
<script>
import { toRefs, ref, reactive, onMounted, nextTick } from 'vue';
import {zyplayerApi} from '../../../api';
import {useStore} from 'vuex';
import aceEditor from "../../../assets/ace-editor";
import {getZyplayerApiBaseUrl} from "../../../api/request/utils";
import {BranchesOutlined, InfoCircleOutlined} from '@ant-design/icons-vue';
import {mavonEditor, markdownIt} from 'mavon-editor'
import { message } from 'ant-design-vue';
export default {
components: {aceEditor, BranchesOutlined, mavonEditor, InfoCircleOutlined},
setup() {
const store = useStore();
let docEdit = ref({});
let shareInstruction = ref('');
let editShareInstructionVisible = ref(false);
const handleNewDocOk = async () => {
if (!shareInstruction.value) {
message.error('请输入开放文档的说明');
return;
}
zyplayerApi.apiDocUpdate({id: docEdit.value.id, shareInstruction: shareInstruction.value}).then(res => {
editShareInstructionVisible.value = false;
});
};
const editDoc = async (id) => {
editShareInstructionVisible.value = true;
zyplayerApi.apiDocDetail({id: id}).then(res => {
docEdit.value = res.data;
shareInstruction.value = res.data.shareInstruction;
});
};
onMounted(() => {
});
return {
editShareInstructionVisible,
docEdit,
shareInstruction,
handleNewDocOk,
editDoc,
toolbars: {
bold: true, // 粗体
italic: true, // 斜体
header: true, // 标题
underline: true, // 下划线
strikethrough: true, // 中划线
mark: true, // 标记
superscript: true, // 上角标
subscript: true, // 下角标
quote: true, // 引用
ol: true, // 有序列表
ul: true, // 无序列表
link: true, // 链接
imagelink: true, // 图片链接
code: true, // code
table: true, // 表格
fullscreen: true, // 全屏编辑
readmodel: true, // 沉浸式阅读
/* 1.3.5 */
undo: true, // 上一步
redo: true, // 下一步
trash: true, // 清空
save: true, // 保存触发events中的save事件
/* 1.4.2 */
navigation: true, // 导航目录
/* 2.1.8 */
alignleft: true, // 左对齐
aligncenter: true, // 居中
alignright: true, // 右对齐
/* 2.2.1 */
subfield: true, // 单双栏模式
preview: true, // 预览
},
};
},
};
</script>

View File

@@ -0,0 +1,101 @@
<template>
<template v-if="isLoadSuccess">
<DocContent :docInfoShow="docInfoShow" :requestParamList="requestParamList" :responseParamList="responseParamList"></DocContent>
</template>
<a-spin v-else tip="文档数据加载中...">
<div style="padding: 20px 0;height: 100px;"></div>
</a-spin>
</template>
<script>
import {toRefs, ref, reactive, onMounted, watch} from 'vue';
import { useRouter, useRoute } from "vue-router";
import {useStore} from 'vuex';
import { message } from 'ant-design-vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import openApiAnalysis from '../../../assets/core/OpenApiAnalysis.js'
import DocContent from '../docView/DocContent.vue'
import {markdownIt} from 'mavon-editor'
import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css'
export default {
components: { DocContent },
setup() {
const route = useRoute();
const store = useStore();
let activePage = ref('doc');
let requestParamList = ref([]);
let responseParamList = ref([]);
let docInfoShow = ref({
url: '',
description: '',
method: '',
consumes: '',
produces: '',
});
let isLoadSuccess = ref(false);
let intervalNum = 0;
let intervalTimer = undefined;
const initLoadDocument = () => {
let path = route.query.path + '.' + route.query.method;
if (Object.keys(store.state.openApiUrlMethodMap).length <= 0) {
console.log('文档尚未加载,等待加载完成');
if (!intervalTimer) {
intervalTimer = setInterval(() => {
if (isLoadSuccess.value || intervalNum++ > 50) {
clearInterval(intervalTimer);
return;
}
if (Object.keys(store.state.openApiUrlMethodMap).length > 0) {
console.log('文档内容改变,重新加载文档');
initLoadDocument();
}
}, 1000);
}
return;
}
let docInfo = store.state.openApiUrlMethodMap[path];
if (!docInfo) {
message.error('没有找到对应的文档');
return;
}
isLoadSuccess.value = true;
store.commit('addTableName', {key: route.fullPath, val: docInfo.summary});
// 解析接口说明
let consumes = '', produces = '';
if (docInfo.consumes && docInfo.consumes.length > 0) {
consumes = docInfo.consumes.join(' ');
}
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: description,
method: docInfo.method || '',
consumes: consumes,
produces: produces,
};
// 解析请求参数
let definitionsDataMap = store.state.openApiDefinitions;
requestParamList.value = openApiAnalysis.getRequestParamList(docInfo.parameters, definitionsDataMap);
responseParamList.value = openApiAnalysis.getResponseParamList(docInfo.responses, definitionsDataMap);
}
onMounted(() => {
initLoadDocument();
});
const changePage = () => {
}
return {
docInfoShow,
activePage,
changePage,
isLoadSuccess,
requestParamList,
responseParamList,
};
},
};
</script>

View File

@@ -0,0 +1,34 @@
<template>
<div v-if="apiDoc.shareInstruction">
<div class="markdown-body" v-html="markdownToHtml(apiDoc.shareInstruction)" style="margin: 0 auto;max-width: 1000px;"></div>
</div>
<div v-else style="text-align: center;">欢迎访问开放API文档</div>
</template>
<script>
import {toRefs, ref, reactive, onMounted, watch, computed} from 'vue';
import { useRouter, useRoute } from "vue-router";
import {useStore} from 'vuex';
import { message } from 'ant-design-vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import {markdownIt} from 'mavon-editor'
import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css'
import swaggerAnalysis from "../../assets/core/SwaggerAnalysis";
export default {
setup() {
const store = useStore();
const apiDoc = computed(() => store.state.apiDoc);
onMounted(() => {
});
const markdownToHtml = desc => {
return markdownIt.render(desc || '');
}
return {
apiDoc,
markdownToHtml,
};
},
};
</script>

View File

@@ -0,0 +1,101 @@
<template>
<template v-if="isLoadSuccess">
<DocContent :docInfoShow="docInfoShow" :requestParamList="requestParamList" :responseParamList="responseParamList"></DocContent>
</template>
<a-spin v-else tip="文档数据加载中...">
<div style="padding: 20px 0;height: 100px;"></div>
</a-spin>
</template>
<script>
import {toRefs, ref, reactive, onMounted, watch} from 'vue';
import { useRouter, useRoute } from "vue-router";
import {useStore} from 'vuex';
import { message } from 'ant-design-vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import swaggerAnalysis from '../../../assets/core/SwaggerAnalysis.js'
import DocContent from '../docView/DocContent.vue'
import {markdownIt} from 'mavon-editor'
import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css'
export default {
components: { DocContent },
setup() {
const route = useRoute();
const store = useStore();
let activePage = ref('doc');
let requestParamList = ref([]);
let responseParamList = ref([]);
let docInfoShow = ref({
url: '',
description: '',
method: '',
consumes: '',
produces: '',
});
let isLoadSuccess = ref(false);
let intervalNum = 0;
let intervalTimer = undefined;
const initLoadDocument = () => {
let path = route.query.path + '.' + route.query.method;
if (Object.keys(store.state.swaggerUrlMethodMap).length <= 0) {
console.log('文档尚未加载,等待加载完成');
if (!intervalTimer) {
intervalTimer = setInterval(() => {
if (isLoadSuccess.value || intervalNum++ > 50) {
clearInterval(intervalTimer);
return;
}
if (Object.keys(store.state.swaggerUrlMethodMap).length > 0) {
console.log('文档内容改变,重新加载文档');
initLoadDocument();
}
}, 1000);
}
return;
}
let docInfo = store.state.swaggerUrlMethodMap[path];
if (!docInfo) {
message.error('没有找到对应的文档');
return;
}
isLoadSuccess.value = true;
store.commit('addTableName', {key: route.fullPath, val: docInfo.summary});
// 解析接口说明
let consumes = '', produces = '';
if (docInfo.consumes && docInfo.consumes.length > 0) {
consumes = docInfo.consumes.join(' ');
}
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: description,
method: docInfo.method || '',
consumes: consumes,
produces: produces,
};
// 解析请求参数
let definitionsDataMap = store.state.swaggerDefinitions;
requestParamList.value = swaggerAnalysis.getRequestParamList(docInfo.parameters, definitionsDataMap);
responseParamList.value = swaggerAnalysis.getResponseParamList(docInfo.responses, definitionsDataMap);
}
onMounted(() => {
initLoadDocument();
});
const changePage = () => {
}
return {
docInfoShow,
activePage,
changePage,
isLoadSuccess,
requestParamList,
responseParamList,
};
},
};
</script>