├── static
└── .gitkeep
├── .eslintignore
├── src
├── views
│ └── pages
│ │ ├── layout
│ │ ├── empty.layout.vue
│ │ ├── index.vue
│ │ ├── app-main.vue
│ │ ├── sidebar
│ │ │ ├── item.vue
│ │ │ ├── link.vue
│ │ │ ├── index.vue
│ │ │ └── sidebar-item.vue
│ │ ├── swaggerInfo.vue
│ │ ├── commonSetting.vue
│ │ └── topbar.vue
│ │ ├── simpleMain
│ │ └── index.vue
│ │ ├── simpleSwagger
│ │ └── index.vue
│ │ ├── main
│ │ └── index.vue
│ │ ├── swagger
│ │ ├── reqJsonView.vue
│ │ ├── mdShow.vue
│ │ ├── jsonedit.vue
│ │ ├── assist
│ │ │ ├── buildmd.js
│ │ │ └── swagger.helper.js
│ │ └── index.vue
│ │ ├── simpleLayout
│ │ ├── app-main.vue
│ │ └── index.vue
│ │ └── login
│ │ └── index.vue
├── assets
│ ├── logo.png
│ └── vue.png
├── styles
│ ├── index.scss
│ ├── tennetcn-ui
│ │ └── index.scss
│ └── main
│ │ ├── sidebar-simple.scss
│ │ └── sidebar.scss
├── store
│ ├── getters.js
│ ├── main.js
│ ├── index.js
│ └── swagger.js
├── router
│ ├── route.load.js
│ ├── index.js
│ ├── route.config.js
│ └── menu.load.js
├── main.js
├── components
│ └── util
│ │ ├── file
│ │ └── down.js
│ │ └── http
│ │ └── request.js
├── App.vue
└── api
│ └── swagger.js
├── config
├── prod.env.js
├── dev.env.js
└── index.js
├── .editorconfig
├── .gitignore
├── .babelrc
├── index.html
├── .eslintrc
├── README.md
└── package.json
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 | /test/
6 |
--------------------------------------------------------------------------------
/src/views/pages/layout/empty.layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chfree/think-swagger-ui-vuele/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/vue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chfree/think-swagger-ui-vuele/HEAD/src/assets/vue.png
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/src/views/pages/simpleMain/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
swagger ui
4 |
5 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
16 | publish_local.sh
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import './main/sidebar.scss';
2 | @import './main/sidebar-simple.scss';
3 | @import './tennetcn-ui//index.scss';
4 |
5 | a,
6 | a:focus,
7 | a:hover {
8 | cursor: pointer;
9 | color: inherit;
10 | outline: none;
11 | text-decoration: none;
12 | }
13 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/src/views/pages/simpleSwagger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | const getters = {
2 | swaggerPath: state => state.swagger.path,
3 | swaggerInfo: state => state.swagger.info,
4 | menus: state => state.swagger.menus,
5 | headers: state => state.swagger.headers,
6 | theme: state => state.main.theme
7 |
8 | }
9 | export default getters
10 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | swagger-ui
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/store/main.js:
--------------------------------------------------------------------------------
1 | const main = {
2 | state: {
3 | theme: null
4 | },
5 | mutations: {
6 | theme: function(state, result) {
7 | state.theme = result
8 | }
9 | },
10 | actions: {
11 | path({ commit }, param) {
12 | commit('theme', param)
13 | }
14 | }
15 | }
16 |
17 | export default main
18 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import swagger from './swagger'
4 | import getters from './getters'
5 | import main from './main'
6 |
7 | Vue.use(Vuex)
8 |
9 | const store = new Vuex.Store({
10 | modules: {
11 | swagger,
12 | main
13 | },
14 | getters
15 | })
16 |
17 | export default store
18 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "ga": true,
4 | "Promise": true,
5 | "cc": true
6 | },
7 | "plugins": ["html", "json"],
8 | "extends": "elemefe",
9 | "rules": {
10 | "no-restricted-globals": ["error", "event", "fdescribe"],
11 | "semi": [2, "never"]
12 | },
13 | "parserOptions": {
14 | "ecmaVersion": 6,
15 | "ecmaFeatures": {
16 | "jsx": true
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/views/pages/main/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
index1
4 |
5 |
6 |
7 |
28 |
29 |
--------------------------------------------------------------------------------
/src/router/route.load.js:
--------------------------------------------------------------------------------
1 | // function LOAD_PAGES_MAP(name) {
2 | // return require.ensure([], () => require(`../views/pages/${name}.vue`))
3 | // }
4 |
5 | // export const loadPage = function(path) {
6 | // return LOAD_PAGES_MAP(path)
7 | // }
8 |
9 | const LOAD_PAGES_MAP = {
10 | 'zh-CN': name => {
11 | return r => require.ensure([], () =>
12 | r(require(`../views/pages/${name}.vue`)),
13 | 'zh-CN')
14 | }
15 | }
16 |
17 | export const loadPage = function(path) {
18 | return LOAD_PAGES_MAP['zh-CN'](path)
19 | }
20 |
--------------------------------------------------------------------------------
/src/views/pages/layout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
23 |
24 |
--------------------------------------------------------------------------------
/src/views/pages/swagger/reqJsonView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
--------------------------------------------------------------------------------
/src/views/pages/simpleLayout/app-main.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/views/pages/layout/app-main.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import router from './router'
6 | import TennetcnUI from 'tennetcn-ui'
7 | import 'element-ui/lib/theme-chalk/index.css'
8 | import 'tennetcn-ui/lib/styles/index.css'
9 | import '@/styles/index.scss'
10 | Vue.config.productionTip = false
11 |
12 | Vue.use(TennetcnUI)
13 |
14 | import store from './store'
15 |
16 | /* eslint-disable no-new */
17 | new Vue({
18 | el: '#app',
19 | store,
20 | router,
21 | render: h => h(App)
22 | })
23 |
--------------------------------------------------------------------------------
/src/views/pages/swagger/mdShow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
29 |
30 |
--------------------------------------------------------------------------------
/src/components/util/file/down.js:
--------------------------------------------------------------------------------
1 | export function writeFileDown(content, fileName, option = {}) {
2 | var a = document.createElement('a')
3 | var url = window.URL.createObjectURL(new Blob([content],
4 | { type: (option.type || mimeType.txt) + ';charset=' + (option.encoding || 'utf-8') }))
5 | a.href = url
6 | a.target = '_blank'
7 | a.download = fileName || 'file.txt'
8 | a.click()
9 | window.URL.revokeObjectURL(url)
10 | }
11 |
12 | export const mimeType = {
13 | txt: 'text/plain',
14 | sql: 'text/plain',
15 | md: 'text/markdown',
16 | pdf: 'application/pdf',
17 | xls: 'application/vnd.ms-excel',
18 | js: 'application/x-javascript',
19 | html: 'text/html'
20 | }
21 |
--------------------------------------------------------------------------------
/src/views/pages/layout/sidebar/item.vue:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
23 |
24 |
36 |
--------------------------------------------------------------------------------
/src/views/pages/simpleLayout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
21 |
22 |
--------------------------------------------------------------------------------
/src/views/pages/layout/sidebar/link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
36 |
--------------------------------------------------------------------------------
/src/store/swagger.js:
--------------------------------------------------------------------------------
1 | const swagger = {
2 | state: {
3 | info: null,
4 | path: null,
5 | menus: null,
6 | headers: null
7 | },
8 | mutations: {
9 | swaggerPath: function(state, result) {
10 | state.path = result
11 | },
12 | swaggerInfo: function(state, result) {
13 | state.info = result
14 | },
15 | menus: function(state, result) {
16 | state.menus = result
17 | },
18 | headers: function(state, result) {
19 | state.headers = result
20 | }
21 | },
22 | actions: {
23 | path({ commit }, param) {
24 | commit('swaggerPath', param)
25 | },
26 | info({ commit }, param) {
27 | commit('swaggerInfo', param)
28 | },
29 | menus({ commit }, param) {
30 | commit('menus', param)
31 | },
32 | headers({ commit }, param) {
33 | commit('headers', param)
34 | }
35 | }
36 | }
37 |
38 | export default swagger
39 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import routeConfig from './route.config'
4 | import { isEmpty } from 'tennetcn-ui/lib/utils'
5 | import swaggerService from '@/api/swagger'
6 | import store from '@/store'
7 |
8 | Vue.use(Router)
9 |
10 | const createRouter = () => new Router({
11 | scrollBehavior: () => ({ y: 0 }),
12 | routes: routeConfig
13 | })
14 |
15 | const router = createRouter()
16 |
17 | var isFirstLoad = true
18 |
19 | router.afterEach((to, from) => {
20 | if (isFirstLoad) {
21 | firstLoad()
22 | isFirstLoad = false
23 | }
24 | })
25 |
26 | function firstLoad() {
27 | if (router.currentRoute.path !== '/') {
28 | const swaggerPath = window.sessionStorage.swaggerPath
29 | const theme = window.sessionStorage.theme
30 | store.commit('theme', theme)
31 | if (isEmpty(swaggerPath)) {
32 | router.push({ path: '/' })
33 | } else {
34 | swaggerService.reqAndResolveSwagger(swaggerPath).then(result => {
35 | })
36 | }
37 | }
38 | }
39 |
40 | export default router
41 |
--------------------------------------------------------------------------------
/src/views/pages/swagger/jsonedit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 保存
6 | 关闭
7 |
8 |
9 |
10 |
11 |
48 |
49 |
--------------------------------------------------------------------------------
/src/router/route.config.js:
--------------------------------------------------------------------------------
1 | import login from '@/views/pages/login'
2 | import layoutMain from '@/views/pages/layout'
3 | import simpleLayout from '@/views/pages/simpleLayout'
4 | import main from '@/views/pages/main'
5 | import simpleMain from '@/views/pages/simpleMain'
6 | import swagger from '@/views/pages/swagger'
7 | import simpleSwagger from '@/views/pages/simpleSwagger'
8 |
9 | const mainRoute = [
10 | {
11 | path: '/',
12 | name: 'login',
13 | component: login
14 | },
15 | {
16 | path: '/main',
17 | name: 'main',
18 | component: layoutMain,
19 | children: [
20 | {
21 | path: 'index',
22 | name: 'mainIndex',
23 | component: main
24 | }
25 | ]
26 | },
27 | {
28 | path: '/simpleMain',
29 | name: 'simpleMain',
30 | component: simpleLayout,
31 | children: [
32 | {
33 | path: 'index',
34 | name: 'simpleMainIndex',
35 | component: simpleMain
36 | }
37 | ]
38 | },
39 | {
40 | path: '/swagger',
41 | name: 'swagger',
42 | component: layoutMain,
43 | children: [
44 | {
45 | path: 'index',
46 | name: 'swaggerIndex',
47 | component: swagger
48 | }
49 | ]
50 | },
51 | {
52 | path: '/simpleSwagger',
53 | name: 'simpleSwagger',
54 | component: simpleLayout,
55 | children: [
56 | {
57 | path: 'index',
58 | name: 'simpleSwaggerIndex',
59 | component: simpleSwagger
60 | }
61 | ]
62 | }
63 | ]
64 |
65 | const megreRoute = function() {
66 | let route = []
67 | route = route.concat(mainRoute)
68 | return route
69 | }
70 |
71 | export default megreRoute()
72 |
--------------------------------------------------------------------------------
/src/api/swagger.js:
--------------------------------------------------------------------------------
1 | import request from '@/components/util/http/request'
2 | import store from '@/store'
3 | import { resolveMenu } from '@/router/menu.load'
4 |
5 | function reqSwagger(url) {
6 | return new Promise(function(resolve, reject) {
7 | request.get(url, {}, response => {
8 | const result = response.data
9 | resolve(result)
10 | })
11 | })
12 | }
13 |
14 | function reqAndResolveSwagger(url) {
15 | return new Promise(function(resolve, reject) {
16 | reqSwagger(url).then(result => {
17 | if (result.swagger !== undefined) {
18 | window.sessionStorage.swaggerPath = url
19 | store.commit('swaggerPath', url)
20 | store.commit('swaggerInfo', result)
21 |
22 | const menus = resolveMenu()
23 | result['menus'] = menus
24 | }
25 | resolve(result)
26 | })
27 | })
28 | }
29 |
30 | function resolveSwagger(swaggerJson) {
31 | store.commit('swaggerPath', null)
32 | window.sessionStorage.swaggerPath = null
33 | const result = JSON.parse(swaggerJson)
34 | return new Promise(function(resolve, reject) {
35 | if (result.swagger !== undefined) {
36 | store.commit('swaggerInfo', result)
37 |
38 | const menus = resolveMenu()
39 | result['menus'] = menus
40 | }
41 | resolve(result)
42 | })
43 | }
44 |
45 | function sendRequest(method, url, requestData) {
46 | return new Promise(function(resolve, reject) {
47 | request[method](url, requestData, response => {
48 | resolve(response)
49 | })
50 | })
51 | }
52 |
53 | const swaggerService = {
54 | reqSwagger,
55 | resolveSwagger,
56 | reqAndResolveSwagger,
57 | sendRequest
58 | }
59 |
60 | export default swaggerService
61 |
--------------------------------------------------------------------------------
/src/styles/tennetcn-ui/index.scss:
--------------------------------------------------------------------------------
1 | .tc-table{
2 | .el-table__body tr.current-row{
3 | background-color: #bad4ee !important;
4 | }
5 | .el-table__body tr.current-row:hover{
6 | background-color: #bad4ee !important;
7 | }
8 | .el-table--striped .el-table__body tr.el-table__row--striped.current-row td,
9 | .el-table__body tr.current-row>td{
10 | background-color: #bad4ee !important;
11 | }
12 | .el-table__empty-block {
13 | min-height: 35px;
14 | }
15 | td {
16 | padding: 6px 0px !important;
17 | }
18 | thead{
19 | th{
20 | background-color: #E4E7ED;
21 | color: #303133;
22 | padding: 5px 0px !important;
23 | }
24 | }
25 | }
26 |
27 | .el-button--think{
28 | background-color: #394A5f;
29 | border-color: #394A5f;
30 | color: #fff;
31 | }
32 | .el-button--think:hover{
33 | background-color: #465B76;
34 | border-color: #6482a9;
35 | color: #fff;
36 | }
37 | .el-button--think:focus{
38 | background-color: #465B76;
39 | border-color: #6482a9;
40 | color:#fff;
41 | }
42 |
43 | .el-row{
44 | margin-left: 0px !important;
45 | margin-right: 0px !important;
46 | }
47 | /*******
48 | * 日期选择变小一点
49 | ********/
50 | .el-date-picker__header{
51 | margin: 6px;
52 | }
53 | .el-picker-panel__content{
54 | margin: 8px;
55 | width: auto !important;
56 | }
57 | .el-date-table{
58 | th {
59 | padding: 3px !important;
60 | }
61 | td {
62 | padding: 3px 0px !important;
63 | }
64 | }
65 |
66 | /**
67 | ** input-tag 修复样式 0.0.14版本修复后,去掉此样式
68 | **/
69 | .vue-input-tag-wrapper{
70 | padding-top: 1px;
71 | .input-tag{
72 | height: 26px;
73 | line-height: 26px;
74 | padding: 0px 4px;
75 | margin-bottom: 1px;
76 | }
77 | }
--------------------------------------------------------------------------------
/src/router/menu.load.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 | import { isEmpty } from 'tennetcn-ui/lib/utils'
3 | /*
4 | *{name:name,desc:desc,children:[{path:'/login',reqMethod:[]}]}
5 | */
6 |
7 | const resolveMenu = function() {
8 | var menus = []
9 | const swaggerInfo = store.getters.swaggerInfo
10 | console.log(swaggerInfo, 'swaggerInfo')
11 | const rootPath = getRootPath()
12 | const paths = swaggerInfo.paths
13 | var tagMap = {}
14 | Array.from(Object.keys(paths)).forEach(path => {
15 | const reqMethod = paths[path]
16 | const firstMethod = reqMethod[Object.keys(reqMethod)[0]]
17 | const firstTag = firstMethod.tags[0]
18 | const title = isEmpty(firstMethod.summary) ? path : firstMethod.summary
19 | var children = []
20 | children.push({path: path.substr(1, path.length - 1), key: path, reqMethod: reqMethod, meta: {icon: '', title: title}, routeParam: {firstTag: firstTag, path: path}})
21 |
22 | tagMap[firstTag] = (tagMap[firstTag] || []).concat(children)
23 | })
24 |
25 | Array.from(swaggerInfo.tags).forEach((tag, pindex) => {
26 | const hidden = tag.name === 'basic-error-controller'
27 | const children = tagMap[tag.name]
28 | children.forEach((child, index) => {
29 | child.routeParam.pindex = pindex
30 | child.routeParam.index = index
31 | })
32 | console.log(rootPath, 'rootPath')
33 | const menu = Object.assign({meta: {icon: '', title: tag.name }, path: rootPath, key: tag.name, hidden: hidden}, tag, {children: children, routeParam: {}})
34 | menus.push(menu)
35 | })
36 | store.commit('menus', menus)
37 |
38 | return menus
39 | }
40 |
41 | function getRootPath() {
42 | const theme = store.getters.theme
43 | if (theme === 'admin') {
44 | return '/swagger'
45 | } else if (theme === 'simple') {
46 | return '/simpleSwagger'
47 | }
48 | }
49 |
50 | export { resolveMenu }
51 |
--------------------------------------------------------------------------------
/src/views/pages/layout/sidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
56 |
74 |
--------------------------------------------------------------------------------
/src/views/pages/layout/sidebar/sidebar-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
32 |
58 |
--------------------------------------------------------------------------------
/src/views/pages/layout/swaggerInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{swaggerPath}}
6 |
7 |
8 |
9 |
10 |
11 | 标题{{swaggerInfo.info.title}}
12 |
13 |
14 | 版本{{swaggerInfo.info.version}}
15 |
16 |
17 |
18 |
19 | 描述{{swaggerInfo.info.description}}
20 |
21 |
22 |
23 |
24 |
25 |
26 | 作者{{swaggerInfo.info.contact.name}}
27 |
28 |
29 | email{{swaggerInfo.info.contact.email}}
30 |
31 |
32 |
33 |
34 | 主页{{swaggerInfo.info.contact.url}}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
57 |
58 |
--------------------------------------------------------------------------------
/src/views/pages/swagger/assist/buildmd.js:
--------------------------------------------------------------------------------
1 | import { isEmptyObject } from 'tennetcn-ui/lib/utils'
2 | export default {
3 | data() {
4 | return {
5 | }
6 | },
7 | methods: {
8 | buildMd: function() {
9 | let arrMds = []
10 | const basePath = this.swaggerInfo.basePath === '/' ? '' : this.swaggerInfo.basePath
11 | const requestUrl = this.methodForm.requestProtocol + this.swaggerInfo.host + basePath
12 | const reqMethod = this.menuInfo.reqMethod[this.activeName] || {}
13 | arrMds.push('# ' + reqMethod.summary)
14 | arrMds.push('## 请求头')
15 | arrMds.push('| 名称 | 描述 |')
16 | arrMds.push('| --- | --- |')
17 | arrMds.push('| Host | `' + requestUrl + '` |')
18 | arrMds.push('| 请求地址 | `' + this.methodForm.requestPath + '` |')
19 | arrMds.push('| 请求方式 | `' + this.activeName + '` |')
20 | arrMds.push('| 响应Content-Type | `' + this.methodForm.contentType + '` |\r\n')
21 | arrMds.push('## 请求参数')
22 | if (this.isPostJson) {
23 | arrMds.push('```')
24 | console.log(this.parameters[0], 'xxxx')
25 | arrMds.push(JSON.stringify(this.parameters[0].schemaDescription, null, '\t'))
26 | arrMds.push('```')
27 | } else {
28 | arrMds.push('| 名称 | 描述 | 是否必填 | 参数类型 | 数据类型 |')
29 | arrMds.push('| --- | --- | --- | --- | --- |')
30 | this.parameters.forEach(item => {
31 | arrMds.push('| ' + item.name + ' | ' + (item.description || '') + ' | ' + item.required + ' | ' + item.in + ' | ' + item.type + ' |')
32 | })
33 | }
34 |
35 | arrMds.push('\r\n## 响应数据')
36 | const respJson = this.calcComplexParamResp()
37 | if (isEmptyObject(respJson)) {
38 | try {
39 | arrMds.push('```')
40 | arrMds.push(JSON.stringify(this.responseResult, null, ' '))
41 | arrMds.push('```')
42 | } catch (err) {
43 | console.log(err)
44 | }
45 | } else {
46 | try {
47 | arrMds.push('```')
48 | arrMds.push(JSON.stringify(respJson, null, ' '))
49 | arrMds.push('```')
50 | } catch (err) {
51 | console.log(err)
52 | }
53 | }
54 | return arrMds.join('\r\n')
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {
14 | },
15 |
16 | // Various Dev Server settings
17 | host: 'localhost', // can be overwritten by process.env.HOST
18 | port: 8092, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
19 | autoOpenBrowser: false,
20 | errorOverlay: true,
21 | notifyOnErrors: true,
22 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
23 |
24 | // Use Eslint Loader?
25 | // If true, your code will be linted during bundling and
26 | // linting errors and warnings will be shown in the console.
27 | useEslint: true,
28 | // If true, eslint errors and warnings will also be shown in the error overlay
29 | // in the browser.
30 | showEslintErrorsInOverlay: false,
31 |
32 | /**
33 | * Source Maps
34 | */
35 |
36 | // https://webpack.js.org/configuration/devtool/#development
37 | devtool: 'cheap-module-eval-source-map',
38 |
39 | // If you have problems debugging vue-files in devtools,
40 | // set this to false - it *may* help
41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
42 | cacheBusting: true,
43 |
44 | cssSourceMap: true
45 | },
46 |
47 | build: {
48 | // Template for index.html
49 | index: path.resolve(__dirname, '../dist/index.html'),
50 |
51 | // Paths
52 | assetsRoot: path.resolve(__dirname, '../dist'),
53 | assetsSubDirectory: 'static',
54 | assetsPublicPath: './',
55 |
56 | /**
57 | * Source Maps
58 | */
59 |
60 | productionSourceMap: true,
61 | // https://webpack.js.org/configuration/devtool/#production
62 | devtool: '#source-map',
63 |
64 | // Gzip off by default as many popular static hosts such as
65 | // Surge or Netlify already gzip all static assets for you.
66 | // Before setting to `true`, make sure to:
67 | // npm install --save-dev compression-webpack-plugin
68 | productionGzip: false,
69 | productionGzipExtensions: ['js', 'css'],
70 |
71 | // Run the build command with an extra argument to
72 | // View the bundle analyzer report after build finishes:
73 | // `npm run build --report`
74 | // Set to `true` or `false` to always turn it on or off
75 | bundleAnalyzerReport: process.env.npm_config_report
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/views/pages/layout/commonSetting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 添加
6 |
7 |
8 |
9 | 启用
10 |
11 |
12 | 头名称
13 |
14 |
15 | 头信息
16 |
17 |
18 | 操作
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 删除
33 |
34 |
35 |
36 |
37 |
38 |
39 | 保存
40 | 关闭
41 |
42 |
43 |
44 |
45 |
77 |
78 |
--------------------------------------------------------------------------------
/src/components/util/http/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import store from '@/store'
3 | import { isEmpty } from 'tennetcn-ui/lib/utils'
4 |
5 | axios.defaults.baseURL = ''
6 | axios.defaults.headers.common['Authorization'] = window.sessionStorage.getItem('token')
7 | axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*'
8 | axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'
9 | axios.defaults.withCredentials = false
10 |
11 | const http = axios.create({
12 | transformRequest: [function(data) {
13 | let newData = ''
14 | for (const k in data) {
15 | if (data.hasOwnProperty(k) === true && data[k] !== null && data[k] !== undefined) {
16 | newData += encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) + '&'
17 | }
18 | }
19 | return newData
20 | }]
21 | })
22 | http.interceptors.request.use(setConfig)
23 | const httpJson = axios.create({
24 | headers: {
25 | 'Content-Type': 'application/json;charset=utf-8'
26 | }
27 | })
28 |
29 | httpJson.interceptors.request.use(setConfig)
30 |
31 | function setConfig(config) {
32 | const headers = [].concat(store.state.swagger.headers || [])
33 | headers.forEach(item => {
34 | if (item.use && !isEmpty(item.headerInfo)) {
35 | config.headers[item.headerName] = item.headerInfo
36 | }
37 | })
38 | return config
39 | }
40 |
41 | function apiAxios(method, url, params, success, error) {
42 | execRequest(http({
43 | method: method,
44 | url: url,
45 | data: method === 'POST' || method === 'PUT' ? params : null,
46 | params: method === 'GET' || method === 'DELETE' ? params : null
47 | }), success, error)
48 | }
49 |
50 | function apiJsonAxios(method, url, params, success, error) {
51 | execRequest(httpJson({
52 | method: method,
53 | url: url,
54 | data: params
55 | }), success, error)
56 | }
57 |
58 | function execRequest(httpRequest, success, error) {
59 | httpRequest.then(function(res) {
60 | success(res)
61 | }).catch(function(err) {
62 | console.log(err, 'err')
63 | if (error || error === undefined) {
64 | success(err.response)
65 | } else {
66 | error(err.response)
67 | }
68 | })
69 | }
70 |
71 | export default {
72 | get: function(url, params, response) {
73 | return apiAxios('GET', url, params, response)
74 | },
75 | post: function(url, params, response) {
76 | return apiAxios('POST', url, params, response)
77 | },
78 | put: function(url, params, response) {
79 | return apiAxios('PUT', url, params, response)
80 | },
81 | delete: function(url, params, response) {
82 | return apiAxios('DELETE', url, params, response)
83 | },
84 | postJson: function(url, params, response) {
85 | return apiJsonAxios('POST', url, params, response)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/views/pages/layout/topbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | SwaggerUI
9 |
10 |
11 | 通用设置
12 | swagger信息
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
60 |
61 |
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # think-swagger-ui-vuele
2 | `swagger-ui`有非常多的版本,觉得不太好用,用`postman`,每个接口都要自己进行录入。所以在基于`think-vuele`进行了`swagger`格式`json`的解析,自己实现了一套swaggerui界面。
3 |
4 | swagger分为后端数据提供方方和前端页面展示请求方。从一定角度来看,swagger是一种标准的数据格式的定义,对于不同语言进行实现一些注解API式的东西,能快速生成这种描述`restful`格式的`api`信息的`json`串.
5 |
6 | 此项目模块依赖于[`think-vuele`](http://vuele.tennetcn.com)
7 |
8 | demo:[http://sw.tennetcn.com](http://sw.tennetcn.com)
9 |
10 | github:[https://github.com/chfree/think-swagger-ui-vuele](https://github.com/chfree/think-swagger-ui-vuele)
11 |
12 | ## 使用方式
13 | ### 自行下载编译
14 | ```shell
15 | // 下载代码
16 | git clone https://github.com/chfree/think-swagger-ui-vuele
17 |
18 | // 安装依赖
19 | npm install
20 |
21 | // 直接运行
22 | npm run dev
23 |
24 | // 打包
25 | npm run build
26 | ```
27 |
28 | ### java项目 maven直接依赖
29 | ```xml
30 |
31 | com.tennetcn.free
32 | think-swagger-ui-starter
33 | 0.0.5
34 |
35 | ```
36 | 此jar包的开源项目为[`think-free-base`](https://github.com/chfree/think-free-base/tree/master/think-swagger-ui-starter)中的子项目模块
37 |
38 | # V1.1.1
39 | ## 更新功能点
40 | - 显示`application/json`模式请求的参数描述
41 | - 显示了响应示例描述
42 |
43 | # V1.1.0
44 | ## 更新功能点
45 | - 加入了请求响应的时间差
46 | - 可以将某个api的请求响应信息以md的模式查看和导出
47 | - 优化了部分显示内容
48 |
49 |
50 |
51 | # V1.0.0
52 |
53 | ## 登陆
54 | 登陆界面分为`json`模式和`swagger`请求地址访问,没多大区别,只有拿到标准的`swagger`的`json`数据即可。
55 |
56 | 支持两种主题,一种是后端管理系统模式的主题。另外一种也是类似,中间1024px进行居中,两边留白。
57 |
58 | 
59 |
60 | ## 主页
61 | 对于我使用过的一个版本的`swagger`来说,当接口数量在`1000+`以上,会等的时间非常长,原因是他一次将所有接口数据进行解析渲染,这个就是慢的原因。
62 |
63 | 所以我将此进行优化,改为先解析出`api`摘要信息,然后在点击摘要的时候进行请求头、请求体的渲染;基本可以做到秒开
64 |
65 | 可以自动填充非`json`请求体的数据,采用的是`mock.Random`。
66 |
67 | 对于json请求体的数据,可以进行`json`格式化编辑,也是非常方便。`json`在线格式化编辑使用的是`josdejong`大神的[`jsoneditor`](https://github.com/josdejong/jsoneditor)
68 |
69 | 对于响应数据直接采用`json`格式化组件进行格式化展示,支持展开层级。再也不用将返回的数据在去找相关的`json`格式化工具进行格式化了。格式化控件采用的是`chenfengjw163`大神的[`vue-json-viewer`](https://github.com/chenfengjw163/vue-json-viewer)
70 |
71 | 
72 |
73 | 
74 |
75 | 
76 |
77 | 
78 |
79 | ## 设置
80 | 在后端api请求的时候,一般都会在请求头中带一些token的验证,来进行用户标识,所以在设置中,进行了自定义请求头的设置,可以方便的设置相关的请求头,在任何一个请求都会自动带上设置的请求信息。
81 |
82 | 
83 |
84 | ## swagger 信息展示
85 | 来源于后端swagger配置的相关信息在此处进行展示
86 | 
87 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "think-swagger-ui",
3 | "version": "1.1.0",
4 | "description": "A Vue.js project",
5 | "author": "zfree <79763939@qq.com>",
6 | "private": true,
7 | "scripts": {
8 | "bootstrap": "yarn || npm i",
9 | "ndev": "npm run bootstrap && webpack-dev-server --mode development --inline --progress --config build/webpack.dev.conf.js",
10 | "dev": "webpack-dev-server --mode development --inline --progress --config build/webpack.dev.conf.js",
11 | "start": "npm run dev",
12 | "lint": "eslint --ext .js,.vue src",
13 | "nbuild": "rimraf dist && npm run bootstrap && node build/build.js",
14 | "build": "rimraf dist && node build/build.js"
15 | },
16 | "dependencies": {
17 | "axios": "0.18.0",
18 | "tennetcn-ui": "0.0.70",
19 | "vue": "2.6.10",
20 | "vue-json-viewer": "^2.2.1",
21 | "vue-meditor": "^2.1.1",
22 | "vue-router": "^3.0.1",
23 | "vuex": "^3.1.1"
24 | },
25 | "devDependencies": {
26 | "autoprefixer": "8.5.0",
27 | "babel-core": "6.26.0",
28 | "babel-eslint": "^10.0.1",
29 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
30 | "babel-loader": "7.1.5",
31 | "babel-plugin-syntax-jsx": "^6.18.0",
32 | "babel-plugin-transform-runtime": "6.23.0",
33 | "babel-plugin-transform-vue-jsx": "3.7.0",
34 | "babel-preset-env": "1.7.0",
35 | "babel-preset-stage-2": "6.24.1",
36 | "chalk": "2.4.1",
37 | "copy-webpack-plugin": "4.5.2",
38 | "css-loader": "^2.1.0",
39 | "echarts": "^4.2.1",
40 | "eslint": "4.19.1",
41 | "eslint-config-elemefe": "0.1.1",
42 | "eslint-friendly-formatter": "4.0.1",
43 | "eslint-loader": "2.0.0",
44 | "eslint-plugin-html": "^4.0.1",
45 | "eslint-plugin-json": "^1.2.0",
46 | "eslint-plugin-vue": "4.7.1",
47 | "extract-text-webpack-plugin": "^3.0.2",
48 | "file-loader": "1.1.11",
49 | "friendly-errors-webpack-plugin": "1.7.0",
50 | "html-webpack-plugin": "4.0.0-alpha",
51 | "jsoneditor": "^7.0.4",
52 | "mini-css-extract-plugin": "0.4.1",
53 | "mockjs": "^1.0.1-beta3",
54 | "node-notifier": "5.2.1",
55 | "node-sass": "^4.14.1",
56 | "optimize-css-assets-webpack-plugin": "5.0.0",
57 | "ora": "3.0.0",
58 | "portfinder": "1.0.16",
59 | "rimraf": "2.6.2",
60 | "sass-loader": "7.0.3",
61 | "semver": "5.5.0",
62 | "shelljs": "0.8.2",
63 | "style-loader": "^0.23.1",
64 | "transliteration": "^1.1.11",
65 | "uglifyjs-webpack-plugin": "^1.1.1",
66 | "url-loader": "1.0.1",
67 | "vue-loader": "15.4.2",
68 | "vue-template-compiler": "2.6.10",
69 | "webpack": "4.16.5",
70 | "webpack-cli": "3.1.0",
71 | "webpack-dev-server": "^3.1.10",
72 | "webpack-merge": "^4.2.1",
73 | "webpack-node-externals": "^1.7.2"
74 | },
75 | "engines": {
76 | "node": ">= 6.0.0",
77 | "npm": ">= 3.0.0"
78 | },
79 | "browserslist": [
80 | "> 1%",
81 | "last 2 versions",
82 | "not ie <= 8"
83 | ]
84 | }
85 |
--------------------------------------------------------------------------------
/src/styles/main/sidebar-simple.scss:
--------------------------------------------------------------------------------
1 | #app.simple {
2 | $top-title-bar-height: 51px;
3 | $left-container-width: 200px;
4 | $menuBg:#fff;
5 | $subMenuBg:#fff;
6 | $menuHover:#ecf5ff;
7 |
8 | // 主体区域
9 | .main-container {
10 | min-height: calc(100% - var(80px));
11 | transition: margin-left .28s;
12 | margin-left: $left-container-width;
13 | position: relative;
14 | }
15 | // 侧边栏
16 | .sidebar-container {
17 | transition: width 0.28s;
18 | width: $left-container-width !important;
19 | height: calc(100% - var($top-title-bar-height));
20 | position: fixed;
21 | font-size: 0px;
22 | top: $top-title-bar-height;
23 | bottom: 0;
24 | left: 0;
25 | z-index: 1001;
26 | overflow: hidden;
27 | //reset element-ui css
28 | .horizontal-collapse-transition {
29 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
30 | }
31 | .el-scrollbar__bar.is-vertical{
32 | right: 0px;
33 | }
34 | .scrollbar-wrapper {
35 | overflow-x: hidden!important;
36 | .el-scrollbar__view {
37 | height: 100%;
38 | }
39 | }
40 | .is-horizontal {
41 | display: none;
42 | }
43 | a {
44 | display: inline-block;
45 | width: 100%;
46 | overflow: hidden;
47 | }
48 | .svg-icon {
49 | margin-right: 16px;
50 | }
51 | .el-menu {
52 | border: none;
53 | height: 100%;
54 | width: 100% !important;
55 | }
56 | }
57 | .hideSidebar {
58 | .sidebar-container {
59 | width: 36px !important;
60 | }
61 | .main-container {
62 | margin-left: 36px;
63 | }
64 | .submenu-title-noDropdown {
65 | padding-left: 10px !important;
66 | position: relative;
67 | .el-tooltip {
68 | padding: 0 10px !important;
69 | }
70 | }
71 | .el-submenu {
72 | overflow: hidden;
73 | &>.el-submenu__title {
74 | padding-left: 10px !important;
75 | .el-submenu__icon-arrow {
76 | display: none;
77 | }
78 | }
79 | }
80 | .el-menu--collapse {
81 | .el-submenu {
82 | &>.el-submenu__title {
83 | &>span {
84 | height: 0;
85 | width: 0;
86 | overflow: hidden;
87 | visibility: hidden;
88 | display: inline-block;
89 | }
90 | }
91 | }
92 | }
93 | }
94 | .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
95 | .sidebar-container .el-submenu .el-menu-item:not(.is-active) {
96 | min-width: $left-container-width !important;
97 | background-color: $subMenuBg !important;
98 | &:hover {
99 | background-color: $menuHover !important;
100 | }
101 | }
102 | .el-menu--collapse .el-menu .el-submenu {
103 | min-width: $left-container-width !important;
104 | }
105 | .sidebar-container {
106 | .el-submenu__title,.el-menu-item{
107 | height: 50px !important;
108 | line-height: 50px !important;
109 | font-size: 14px;
110 | font-weight: 600;
111 | }
112 | .el-submenu .el-menu-item{
113 | height: 48px !important;
114 | line-height: 48px !important;
115 | font-size: 14px;
116 | font-weight: 600;
117 | }
118 | }
119 |
120 | //适配移动端
121 | .mobile {
122 | .main-container {
123 | margin-left: 0px;
124 | }
125 | .sidebar-container {
126 | transition: transform .28s;
127 | width: 180px !important;
128 | }
129 | &.hideSidebar {
130 | .sidebar-container {
131 | transition-duration: 0.3s;
132 | transform: translate3d(-180px, 0, 0);
133 | }
134 | }
135 | .logo-img{
136 | display: none;
137 | }
138 | }
139 | .withoutAnimation {
140 | .main-container,
141 | .sidebar-container {
142 | transition: none;
143 | }
144 | }
145 | }
146 |
147 | .el-menu--vertical{
148 | & >.el-menu{
149 | .svg-icon{
150 | margin-right: 16px;
151 | }
152 | }
153 |
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/styles/main/sidebar.scss:
--------------------------------------------------------------------------------
1 | #app.main {
2 | $top-title-bar-height: 50px;
3 | $left-container-width: 200px;
4 | $menuBg:#394A60;
5 | $subMenuBg:#394A60;
6 | $menuHover:#1d61a0;
7 |
8 | // 主体区域
9 | .main-container {
10 | min-height: calc(100% - var(80px));
11 | transition: margin-left .28s;
12 | margin-left: $left-container-width;
13 | position: relative;
14 | }
15 | // 侧边栏
16 | .sidebar-container {
17 | transition: width 0.28s;
18 | width: $left-container-width !important;
19 | height: calc(100% - var($top-title-bar-height));
20 | position: fixed;
21 | font-size: 0px;
22 | top: $top-title-bar-height;
23 | bottom: 0;
24 | left: 0;
25 | z-index: 1001;
26 | overflow: hidden;
27 | //reset element-ui css
28 | .horizontal-collapse-transition {
29 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
30 | }
31 | .el-scrollbar__bar.is-vertical{
32 | right: 0px;
33 | }
34 | .scrollbar-wrapper {
35 | overflow-x: hidden!important;
36 | .el-scrollbar__view {
37 | height: 100%;
38 | }
39 | }
40 | .is-horizontal {
41 | display: none;
42 | }
43 | a {
44 | display: inline-block;
45 | width: 100%;
46 | overflow: hidden;
47 | }
48 | .svg-icon {
49 | margin-right: 16px;
50 | }
51 | .el-menu {
52 | border: none;
53 | height: 100%;
54 | width: 100% !important;
55 | }
56 | }
57 | .hideSidebar {
58 | .sidebar-container {
59 | width: 36px !important;
60 | }
61 | .main-container {
62 | margin-left: 36px;
63 | }
64 | .submenu-title-noDropdown {
65 | padding-left: 10px !important;
66 | position: relative;
67 | .el-tooltip {
68 | padding: 0 10px !important;
69 | }
70 | }
71 | .el-submenu {
72 | overflow: hidden;
73 | &>.el-submenu__title {
74 | padding-left: 10px !important;
75 | .el-submenu__icon-arrow {
76 | display: none;
77 | }
78 | }
79 | }
80 | .el-menu--collapse {
81 | .el-submenu {
82 | &>.el-submenu__title {
83 | &>span {
84 | height: 0;
85 | width: 0;
86 | overflow: hidden;
87 | visibility: hidden;
88 | display: inline-block;
89 | }
90 | }
91 | }
92 | }
93 | }
94 | .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
95 | .sidebar-container .el-submenu .el-menu-item:not(.is-active) {
96 | min-width: $left-container-width !important;
97 | background-color: $subMenuBg !important;
98 | &:hover {
99 | background-color: $menuHover !important;
100 | }
101 | }
102 | .el-menu--collapse .el-menu .el-submenu {
103 | min-width: $left-container-width !important;
104 | }
105 | .sidebar-container {
106 | .el-submenu__title,.el-menu-item{
107 | height: 50px !important;
108 | line-height: 50px !important;
109 | font-size: 14px;
110 | font-weight: 600;
111 | }
112 | .el-submenu .el-menu-item{
113 | height: 48px !important;
114 | line-height: 48px !important;
115 | font-size: 14px;
116 | font-weight: 600;
117 | }
118 | }
119 |
120 | //适配移动端
121 | .mobile {
122 | .main-container {
123 | margin-left: 0px;
124 | }
125 | .sidebar-container {
126 | transition: transform .28s;
127 | width: 180px !important;
128 | }
129 | &.hideSidebar {
130 | .sidebar-container {
131 | transition-duration: 0.3s;
132 | transform: translate3d(-180px, 0, 0);
133 | }
134 | }
135 | .logo-img{
136 | display: none;
137 | }
138 | }
139 | .withoutAnimation {
140 | .main-container,
141 | .sidebar-container {
142 | transition: none;
143 | }
144 | }
145 | }
146 |
147 | .el-menu--vertical{
148 | & >.el-menu{
149 | .svg-icon{
150 | margin-right: 16px;
151 | }
152 | }
153 |
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/views/pages/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
swagger-ui
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 访问
20 | {{modeText}}
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
102 |
103 |
--------------------------------------------------------------------------------
/src/views/pages/swagger/assist/swagger.helper.js:
--------------------------------------------------------------------------------
1 | import { isEmpty, isEmptyObject } from 'tennetcn-ui/lib/utils'
2 | export default {
3 | data() {
4 | return {
5 | methodForm: {
6 | requestProtocol: 'http://',
7 | contentType: null,
8 | requestPath: this.$route.query.path,
9 | requestMethod: this.activeName
10 | },
11 | responseResult: {},
12 | paramColumn: [
13 | { text: '启用', name: 'open', width: '60', editable: true, type: 'checkbox' },
14 | { text: '参数', name: 'name', width: '180', editable: true },
15 | { text: '值', name: 'value', editable: true, type: 'input' },
16 | { text: '描述', name: 'description', width: '180' },
17 | { text: '是否必填', name: 'required', width: '80' },
18 | { text: '参数类型', name: 'in', width: '120' },
19 | { text: '数据类型', name: 'type', width: '120' }
20 | ],
21 | simpleParamColumn: [
22 | { text: '启用', name: 'open', width: '50', editable: true, type: 'checkbox' },
23 | { text: '参数', name: 'name', width: '120', editable: true },
24 | { text: '值', name: 'value', editable: true, type: 'input' },
25 | { text: '描述', name: 'description', width: '120' },
26 | { text: '必填', name: 'required', width: '50' },
27 | { text: '参数类型', name: 'in', width: '80' },
28 | { text: '数据类型', name: 'type', width: '80' }
29 | ],
30 | customParamItem: {
31 | category: 'custom',
32 | disabled: null,
33 | in: 'query',
34 | name: '',
35 | editable: true,
36 | open: true,
37 | required: false,
38 | type: 'string'
39 | },
40 | customParam: [],
41 | responseTimeInfo: {
42 | requestTime: null,
43 | responseTime: null,
44 | diffTime: null
45 | }
46 | }
47 | },
48 | computed: {
49 | columns: function() {
50 | if (this.theme === 'admin') {
51 | return this.paramColumn
52 | } else if (this.theme === 'simple') {
53 | return this.simpleParamColumn
54 | }
55 | },
56 | menuInfo() {
57 | if (this.menus === null) {
58 | return null
59 | }
60 | const parentMenu = this.menus[this.$route.query.pindex]
61 | if (parentMenu === null) {
62 | return null
63 | }
64 | return parentMenu.children[this.$route.query.index]
65 | },
66 | tabs() {
67 | if (this.menuInfo === null) {
68 | return []
69 | }
70 | const reqMethod = this.menuInfo.reqMethod
71 | const tabs = Object.keys(reqMethod)
72 |
73 | this.activeName = tabs[0]
74 | return tabs
75 | },
76 | reqMethod() {
77 | if (this.menuInfo === null) {
78 | return {}
79 | }
80 | return this.menuInfo.reqMethod[this.activeName] || {}
81 | },
82 | producesProviders() {
83 | const produces = this.reqMethod.produces
84 | if (isEmpty(produces)) {
85 | return []
86 | }
87 |
88 | return produces.map((item, index) => {
89 | if (index === 0) {
90 | this.methodForm.contentType = item
91 | }
92 | return { text: item, value: item, id: index }
93 | })
94 | },
95 | parameters() {
96 | let params = (this.reqMethod.parameters || [])
97 | this.isPostJson = false
98 | params.forEach(item => {
99 | this.$set(item, 'open', true)
100 | this.$set(item, 'editable', false)
101 | if (!isEmpty(item.schema) && !isEmpty(item.schema.$ref)) {
102 | item.type = 'json'
103 | this.isPostJson = true
104 | const value = JSON.stringify(this.calcComplexParam(item), null, ' ')
105 | this.$set(item, 'value', value)
106 | this.$set(item, 'defaultValue', value)
107 | // schema description
108 | item.schemaDescription = this.calcComplexParamDescription(item)
109 | }
110 | })
111 | return params.concat(this.customParam)
112 | }
113 | },
114 | methods: {
115 | calcComplexParamDescription(item) {
116 | return this.calcComplexParam(item, true)
117 | },
118 | calcComplexParamResp() {
119 | const resps = this.menuInfo.reqMethod[this.activeName].responses
120 | return this.calcComplexParam(resps['200'], true)
121 | },
122 | calcComplexParam(item, isDesc) {
123 | var result = {}
124 | // 不是复杂属性
125 | if (isEmpty(item.schema) || isEmpty(item.schema.$ref)) {
126 | return result
127 | }
128 | this.loopCalcComplexParam(item.schema, result, isDesc)
129 | return result
130 | },
131 | loopCalcComplexParam(parentRefProperty, parentObj, isDesc) {
132 | if (!isEmpty(parentRefProperty.$ref)) {
133 | const ref = this.getDefinName(parentRefProperty.$ref)
134 | const refDefin = this.swaggerInfo.definitions[ref]
135 |
136 | for (var key in refDefin.properties) {
137 | const refProperty = refDefin.properties[key]
138 | if (refProperty.type === 'array') {
139 | var childArr = []
140 | this.loopCalcComplexParamArr(refProperty, childArr, isDesc)
141 | if (isDesc) {
142 | if (isEmpty(refProperty.items.$ref)) {
143 | parentObj[key] = [refProperty.description + '(' + refProperty.items.type + ')']
144 | } else {
145 | parentObj[key] = childArr
146 | }
147 | } else {
148 | parentObj[key] = childArr
149 | }
150 | } else {
151 | var childObj = {}
152 | // 继续计算子级
153 | this.loopCalcComplexParam(refProperty, childObj, isDesc)
154 | if (isDesc) {
155 | if (isEmpty(refProperty.type)) {
156 | parentObj[key] = childObj
157 | } else {
158 | parentObj[key] = refProperty.description + '(' + refProperty.type + ')'
159 | }
160 | } else {
161 | parentObj[key] = isEmptyObject(childObj) ? (refProperty.type === 'integer' ? 0 : '') : childObj
162 | }
163 |
164 | }
165 | }
166 | }
167 | },
168 | loopCalcComplexParamArr(parentRefProperty, parentArr, isDesc) {
169 | if (isEmpty(parentRefProperty.items.$ref)) {
170 | if (parentRefProperty.items.type === 'string') {
171 | parentArr.push('')
172 | } else {
173 | parentArr.push([])
174 | }
175 | } else {
176 | var childObj = {}
177 | // 继续计算子级
178 | this.loopCalcComplexParam(parentRefProperty.items, childObj, isDesc)
179 |
180 | parentArr.push(childObj)
181 | }
182 | },
183 | getDefinName(refFull) {
184 | return refFull.replace('#/definitions/', '')
185 | },
186 | tabName(name) {
187 | const reqMethod = this.menuInfo.reqMethod[name] || {}
188 | return reqMethod.summary + '-' + name
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/views/pages/swagger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 请求
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 新增
33 | 查看md
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | 格式化编辑
42 |
43 |
44 |
45 |
46 |
47 |
48 | {{value}}
49 |
50 |
51 |
52 |
53 |
54 |
55 | {{value}}
56 |
57 |
58 |
59 | {{value}}
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 试一试
69 | 填充数据
70 | 重置
71 |
72 |
73 |
74 | 响应
75 |
76 |
77 |
78 | 请求时间
79 |
80 |
81 | {{responseTimeInfo.requestTime}}
82 |
83 |
84 | 响应时间
85 |
86 |
87 | {{responseTimeInfo.responseTime}}
88 |
89 |
90 | 相差毫秒
91 |
92 |
93 | {{responseTimeInfo.diffTime}}
94 |
95 |
96 |
97 |
98 |
99 |
105 |
106 |
107 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
279 |
280 |
--------------------------------------------------------------------------------