加入帮助文档

This commit is contained in:
marvzhang
2020-03-01 20:55:52 +08:00
parent 3803342b6c
commit 2712d7207d
14 changed files with 323 additions and 3 deletions

8
backend/entity/doc.go Normal file
View File

@@ -0,0 +1,8 @@
package entity
type DocItem struct {
Title string `json:"title"`
Url string `json:"url"`
Path string `json:"path"`
Children []DocItem `json:"children"`
}

View File

@@ -135,6 +135,8 @@ func main() {
// release版本
anonymousGroup.GET("/version", routes.GetVersion) // 获取发布的版本
anonymousGroup.GET("/releases/latest", routes.GetLatestRelease) // 获取最近发布的版本
// 文档
anonymousGroup.GET("/docs", routes.GetDocs) // 获取文档数据
}
authGroup := app.Group("/", middlewares.AuthorizationMiddleware())
{
@@ -259,7 +261,6 @@ func main() {
authGroup.GET("/git/branches", routes.GetGitBranches) // 获取 Git 分支
authGroup.GET("/git/public-key", routes.GetGitSshPublicKey) // 获取 SSH 公钥
}
}
// 路由ping

25
backend/routes/doc.go Normal file
View File

@@ -0,0 +1,25 @@
package routes
import (
"crawlab/services"
"github.com/apex/log"
"github.com/gin-gonic/gin"
"net/http"
"runtime/debug"
)
func GetDocs(c *gin.Context) {
type ResData struct {
String string `json:"string"`
}
data, err := services.GetDocs()
if err != nil {
log.Errorf(err.Error())
debug.PrintStack()
}
c.JSON(http.StatusOK, Response{
Status: "ok",
Message: "success",
Data: ResData{String:data},
})
}

27
backend/services/doc.go Normal file
View File

@@ -0,0 +1,27 @@
package services
import (
"github.com/apex/log"
"github.com/imroc/req"
"runtime/debug"
)
func GetDocs() (data string, err error) {
// 获取远端数据
res, err := req.Get("https://docs.crawlab.cn/search_plus_index.json")
if err != nil {
log.Errorf(err.Error())
debug.PrintStack()
return data, err
}
// 反序列化
data, err = res.ToString()
if err != nil {
log.Errorf(err.Error())
debug.PrintStack()
return data, err
}
return data, nil
}

View File

@@ -1,3 +1,4 @@
NODE_ENV='development'
VUE_APP_BASE_URL=http://localhost:8000
VUE_APP_CRAWLAB_BASE_URL=https://api.crawlab.cn
VUE_APP_CRAWLAB_BASE_URL=https://api.crawlab.cn
VUE_APP_DOC_URL=http://docs.crawlab.cn

View File

@@ -1,3 +1,4 @@
NODE_ENV='production'
VUE_APP_BASE_URL=/api
VUE_APP_CRAWLAB_BASE_URL=https://api.crawlab.cn
VUE_APP_DOC_URL=http://docs.crawlab.cn

View File

@@ -1,3 +1,4 @@
NODE_ENV='test'
VUE_APP_BASE_URL='http://localhost:8000'
VUE_APP_CRAWLAB_BASE_URL=https://api.crawlab.cn
VUE_APP_DOC_URL=http://docs.crawlab.cn

View File

@@ -0,0 +1,84 @@
<template>
<el-tree
:data="docData"
ref="documentation-tree"
node-key="path"
>
<span class="custom-tree-node" :class="data.active ? 'active' : ''" slot-scope="{ node, data }">
<template v-if="data.level === 1 && data.children && data.children.length">
<span>{{node.label}}</span>
</template>
<template v-else>
<span>
<a :href="data.url" target="_blank" style="display: block">
{{node.label}}
</a>
</span>
</template>
</span>
</el-tree>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
name: 'Documentation',
data () {
return {
data: []
}
},
computed: {
...mapState('doc', [
'docData'
]),
pathLv1 () {
if (this.$route.path === '/') return '/'
const m = this.$route.path.match(/(^\/\w+)/)
return m[1]
}
},
watch: {
pathLv1 () {
this.update()
}
},
methods: {
update () {
// expand related documentation list
setTimeout(() => {
this.docData.forEach(d => {
const node = this.$refs['documentation-tree'].getNode(d)
let isActive = false
for (let i = 0; i < this.$utils.doc.docs.length; i++) {
const pattern = this.$utils.doc.docs[i]
if (d.url.match(pattern)) {
isActive = true
break
}
}
node.expanded = isActive
this.$set(d, 'active', isActive)
})
}, 100)
},
async getDocumentationData () {
// fetch api data
await this.$store.dispatch('doc/getDocData')
}
},
async created () {
},
mounted () {
this.update()
}
}
</script>
<style scoped>
.el-tree >>> .custom-tree-node.active {
color: #409eff;
}
</style>

View File

@@ -450,6 +450,10 @@ export default {
'General': '通用',
'Enable Tutorial': '启用教程',
// 全局
'Related Documentation': '相关文档',
'Click to view related Documentation': '点击查看相关文档',
// 其他
tagsView: {
closeOthers: '关闭其他',

View File

@@ -17,6 +17,7 @@ import setting from './modules/setting'
import version from './modules/version'
import tour from './modules/tour'
import project from './modules/project'
import doc from './modules/doc'
import getters from './getters'
Vue.use(Vuex)
@@ -39,6 +40,7 @@ const store = new Vuex.Store({
version,
tour,
project,
doc,
// 统计
stats
},

View File

@@ -0,0 +1,61 @@
import request from '../../api/request'
const state = {
docData: []
}
const getters = {}
const mutations = {
SET_DOC_DATA (state, value) {
state.docData = value
}
}
const actions = {
async getDocData ({ commit }) {
const res = await request.get('/docs')
const data = JSON.parse(res.data.data.string)
// init cache
const cache = {}
// iterate paths
for (let path in data) {
if (data.hasOwnProperty(path)) {
const d = data[path]
if (path.match(/\/$/)) {
cache[path] = d
cache[path].children = []
} else if (path.match(/\.html$/)) {
const parentPath = path.split('/')[0] + '/'
cache[parentPath].children.push(d)
}
}
}
commit('SET_DOC_DATA', Object.values(cache).map(d => {
d.level = 1
d.label = d.title
d.url = process.env.VUE_APP_DOC_URL + '/' + d.url
if (d.children) {
d.children = d.children.map(c => {
c.level = 2
c.label = c.title
c.url = process.env.VUE_APP_DOC_URL + '/' + c.url
return c
})
}
return d
}))
}
}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}

20
frontend/src/utils/doc.js Normal file
View File

@@ -0,0 +1,20 @@
export default {
docs: [
{
path: '/projects',
pattern: '^Project'
},
{
path: '/spiders',
pattern: '^Spider'
},
{
path: '/tasks',
pattern: '^Task'
},
{
path: '/schedules',
pattern: '^Schedule'
}
]
}

View File

@@ -3,11 +3,13 @@ import encrypt from './encrypt'
import tour from './tour'
import log from './log'
import scrapy from './scrapy'
import doc from './doc'
export default {
stats,
encrypt,
tour,
log,
scrapy
scrapy,
doc
}

View File

@@ -1,12 +1,36 @@
<template>
<div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"></div>
<!--sidebar-->
<sidebar class="sidebar-container"/>
<!--./sidebar-->
<!--main container-->
<div class="main-container">
<navbar/>
<tags-view/>
<app-main/>
</div>
<!--./main container-->
<!--documentation-->
<div class="documentation">
<el-tooltip
:content="$t('Click to view related Documentation')"
>
<i class="el-icon-question" @click="onClickDocumentation"></i>
</el-tooltip>
<el-drawer
:title="$t('Related Documentation')"
:visible.sync="isShowDocumentation"
:before-close="onCloseDocumentation"
size="360px"
>
<documentation/>
</el-drawer>
</div>
<!--./documentation-->
</div>
</template>
@@ -18,16 +42,23 @@ import {
TagsView
} from './components'
import ResizeMixin from './mixin/ResizeHandler'
import Documentation from '../../components/Documentation/Documentation'
export default {
name: 'Layout',
components: {
Documentation,
Navbar,
Sidebar,
TagsView,
AppMain
},
mixins: [ResizeMixin],
data () {
return {
isShowDocumentation: false
}
},
computed: {
sidebar () {
return this.$store.state.app.sidebar
@@ -47,7 +78,18 @@ export default {
methods: {
handleClickOutside () {
this.$store.dispatch('CloseSideBar', { withoutAnimation: false })
},
onClickDocumentation () {
this.isShowDocumentation = true
this.$st.sendEv('全局', '点击页面文档')
},
onCloseDocumentation () {
this.isShowDocumentation = false
}
},
async created () {
await this.$store.dispatch('doc/getDocData')
}
}
</script>
@@ -77,4 +119,45 @@ export default {
position: absolute;
z-index: 999;
}
.documentation {
z-index: 9999;
position: fixed;
right: 25px;
bottom: 20px;
font-size: 24px;
cursor: pointer;
color: #909399;
}
</style>
<style scoped>
.documentation .el-tree {
margin-left: 20px;
}
.documentation >>> span[role="heading"]:focus {
outline: none;
}
.documentation >>> .el-tree-node__content {
height: 40px;
line-height: 40px;
}
.documentation >>> .custom-tree-node {
display: block;
width: 100%;
height: 40px;
line-height: 40px;
font-size: 14px;
}
.documentation >>> .custom-tree-node a {
display: block;
}
.documentation >>> .custom-tree-node:hover a {
text-decoration: underline;
}
</style>