api文档支持开放访问
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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>
|
||||
101
zyplayer-doc-ui/api-ui/src/views/openapi/share/DocView.vue
Normal file
101
zyplayer-doc-ui/api-ui/src/views/openapi/share/DocView.vue
Normal 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>
|
||||
34
zyplayer-doc-ui/api-ui/src/views/share/ShareHome.vue
Normal file
34
zyplayer-doc-ui/api-ui/src/views/share/ShareHome.vue
Normal 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>
|
||||
101
zyplayer-doc-ui/api-ui/src/views/swagger/share/DocView.vue
Normal file
101
zyplayer-doc-ui/api-ui/src/views/swagger/share/DocView.vue
Normal 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>
|
||||
Reference in New Issue
Block a user