This commit is contained in:
Serge 2020-01-02 19:09:53 +02:00 committed by GitHub
commit 249f8adb28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 182 additions and 557 deletions

View File

@ -3,6 +3,7 @@ import { param2Obj } from '../src/utils'
import user from './user'
import role from './role'
import route from './route'
import article from './article'
import search from './remote-search'
@ -10,7 +11,8 @@ const mocks = [
...user,
...role,
...article,
...search
...search,
...route
]
// for front mock

View File

@ -1,8 +1,5 @@
import Mock from 'mockjs'
import { deepClone } from '../../src/utils/index.js'
import { asyncRoutes, constantRoutes } from './routes.js'
const routes = deepClone([...constantRoutes, ...asyncRoutes])
import { routes } from './route'
const roles = [
{
@ -36,18 +33,6 @@ const roles = [
]
export default [
// mock get all routes form server
{
url: '/routes',
type: 'get',
response: _ => {
return {
code: 20000,
data: routes
}
}
},
// mock get all roles form server
{
url: '/roles',

View File

@ -1,525 +0,0 @@
// Just a mock data
export const constantRoutes = [
{
path: '/redirect',
component: 'layout/Layout',
hidden: true,
children: [
{
path: '/redirect/:path*',
component: 'views/redirect/index'
}
]
},
{
path: '/login',
component: 'views/login/index',
hidden: true
},
{
path: '/auth-redirect',
component: 'views/login/auth-redirect',
hidden: true
},
{
path: '/404',
component: 'views/error-page/404',
hidden: true
},
{
path: '/401',
component: 'views/error-page/401',
hidden: true
},
{
path: '',
component: 'layout/Layout',
redirect: 'dashboard',
children: [
{
path: 'dashboard',
component: 'views/dashboard/index',
name: 'Dashboard',
meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
}
]
},
{
path: '/documentation',
component: 'layout/Layout',
children: [
{
path: 'index',
component: 'views/documentation/index',
name: 'Documentation',
meta: { title: 'Documentation', icon: 'documentation', affix: true }
}
]
},
{
path: '/guide',
component: 'layout/Layout',
redirect: '/guide/index',
children: [
{
path: 'index',
component: 'views/guide/index',
name: 'Guide',
meta: { title: 'Guide', icon: 'guide', noCache: true }
}
]
}
]
export const asyncRoutes = [
{
path: '/permission',
component: 'layout/Layout',
redirect: '/permission/index',
alwaysShow: true,
meta: {
title: 'Permission',
icon: 'lock',
roles: ['admin', 'editor']
},
children: [
{
path: 'page',
component: 'views/permission/page',
name: 'PagePermission',
meta: {
title: 'Page Permission',
roles: ['admin']
}
},
{
path: 'directive',
component: 'views/permission/directive',
name: 'DirectivePermission',
meta: {
title: 'Directive Permission'
}
},
{
path: 'role',
component: 'views/permission/role',
name: 'RolePermission',
meta: {
title: 'Role Permission',
roles: ['admin']
}
}
]
},
{
path: '/icon',
component: 'layout/Layout',
children: [
{
path: 'index',
component: 'views/icons/index',
name: 'Icons',
meta: { title: 'Icons', icon: 'icon', noCache: true }
}
]
},
{
path: '/components',
component: 'layout/Layout',
redirect: 'noRedirect',
name: 'ComponentDemo',
meta: {
title: 'Components',
icon: 'component'
},
children: [
{
path: 'tinymce',
component: 'views/components-demo/tinymce',
name: 'TinymceDemo',
meta: { title: 'Tinymce' }
},
{
path: 'markdown',
component: 'views/components-demo/markdown',
name: 'MarkdownDemo',
meta: { title: 'Markdown' }
},
{
path: 'json-editor',
component: 'views/components-demo/json-editor',
name: 'JsonEditorDemo',
meta: { title: 'Json Editor' }
},
{
path: 'split-pane',
component: 'views/components-demo/split-pane',
name: 'SplitpaneDemo',
meta: { title: 'SplitPane' }
},
{
path: 'avatar-upload',
component: 'views/components-demo/avatar-upload',
name: 'AvatarUploadDemo',
meta: { title: 'Avatar Upload' }
},
{
path: 'dropzone',
component: 'views/components-demo/dropzone',
name: 'DropzoneDemo',
meta: { title: 'Dropzone' }
},
{
path: 'sticky',
component: 'views/components-demo/sticky',
name: 'StickyDemo',
meta: { title: 'Sticky' }
},
{
path: 'count-to',
component: 'views/components-demo/count-to',
name: 'CountToDemo',
meta: { title: 'Count To' }
},
{
path: 'mixin',
component: 'views/components-demo/mixin',
name: 'ComponentMixinDemo',
meta: { title: 'componentMixin' }
},
{
path: 'back-to-top',
component: 'views/components-demo/back-to-top',
name: 'BackToTopDemo',
meta: { title: 'Back To Top' }
},
{
path: 'drag-dialog',
component: 'views/components-demo/drag-dialog',
name: 'DragDialogDemo',
meta: { title: 'Drag Dialog' }
},
{
path: 'drag-select',
component: 'views/components-demo/drag-select',
name: 'DragSelectDemo',
meta: { title: 'Drag Select' }
},
{
path: 'dnd-list',
component: 'views/components-demo/dnd-list',
name: 'DndListDemo',
meta: { title: 'Dnd List' }
},
{
path: 'drag-kanban',
component: 'views/components-demo/drag-kanban',
name: 'DragKanbanDemo',
meta: { title: 'Drag Kanban' }
}
]
},
{
path: '/charts',
component: 'layout/Layout',
redirect: 'noRedirect',
name: 'Charts',
meta: {
title: 'Charts',
icon: 'chart'
},
children: [
{
path: 'keyboard',
component: 'views/charts/keyboard',
name: 'KeyboardChart',
meta: { title: 'Keyboard Chart', noCache: true }
},
{
path: 'line',
component: 'views/charts/line',
name: 'LineChart',
meta: { title: 'Line Chart', noCache: true }
},
{
path: 'mixchart',
component: 'views/charts/mixChart',
name: 'MixChart',
meta: { title: 'Mix Chart', noCache: true }
}
]
},
{
path: '/nested',
component: 'layout/Layout',
redirect: '/nested/menu1/menu1-1',
name: 'Nested',
meta: {
title: 'Nested',
icon: 'nested'
},
children: [
{
path: 'menu1',
component: 'views/nested/menu1/index',
name: 'Menu1',
meta: { title: 'Menu1' },
redirect: '/nested/menu1/menu1-1',
children: [
{
path: 'menu1-1',
component: 'views/nested/menu1/menu1-1',
name: 'Menu1-1',
meta: { title: 'Menu1-1' }
},
{
path: 'menu1-2',
component: 'views/nested/menu1/menu1-2',
name: 'Menu1-2',
redirect: '/nested/menu1/menu1-2/menu1-2-1',
meta: { title: 'Menu1-2' },
children: [
{
path: 'menu1-2-1',
component: 'views/nested/menu1/menu1-2/menu1-2-1',
name: 'Menu1-2-1',
meta: { title: 'Menu1-2-1' }
},
{
path: 'menu1-2-2',
component: 'views/nested/menu1/menu1-2/menu1-2-2',
name: 'Menu1-2-2',
meta: { title: 'Menu1-2-2' }
}
]
},
{
path: 'menu1-3',
component: 'views/nested/menu1/menu1-3',
name: 'Menu1-3',
meta: { title: 'Menu1-3' }
}
]
},
{
path: 'menu2',
name: 'Menu2',
component: 'views/nested/menu2/index',
meta: { title: 'Menu2' }
}
]
},
{
path: '/example',
component: 'layout/Layout',
redirect: '/example/list',
name: 'Example',
meta: {
title: 'Example',
icon: 'example'
},
children: [
{
path: 'create',
component: 'views/example/create',
name: 'CreateArticle',
meta: { title: 'Create Article', icon: 'edit' }
},
{
path: 'edit/:id(\\d+)',
component: 'views/example/edit',
name: 'EditArticle',
meta: { title: 'Edit Article', noCache: true },
hidden: true
},
{
path: 'list',
component: 'views/example/list',
name: 'ArticleList',
meta: { title: 'Article List', icon: 'list' }
}
]
},
{
path: '/tab',
component: 'layout/Layout',
children: [
{
path: 'index',
component: 'views/tab/index',
name: 'Tab',
meta: { title: 'Tab', icon: 'tab' }
}
]
},
{
path: '/error',
component: 'layout/Layout',
redirect: 'noRedirect',
name: 'ErrorPages',
meta: {
title: 'Error Pages',
icon: '404'
},
children: [
{
path: '401',
component: 'views/error-page/401',
name: 'Page401',
meta: { title: 'Page 401', noCache: true }
},
{
path: '404',
component: 'views/error-page/404',
name: 'Page404',
meta: { title: 'Page 404', noCache: true }
}
]
},
{
path: '/error-log',
component: 'layout/Layout',
redirect: 'noRedirect',
children: [
{
path: 'log',
component: 'views/error-log/index',
name: 'ErrorLog',
meta: { title: 'Error Log', icon: 'bug' }
}
]
},
{
path: '/excel',
component: 'layout/Layout',
redirect: '/excel/export-excel',
name: 'Excel',
meta: {
title: 'Excel',
icon: 'excel'
},
children: [
{
path: 'export-excel',
component: 'views/excel/export-excel',
name: 'ExportExcel',
meta: { title: 'Export Excel' }
},
{
path: 'export-selected-excel',
component: 'views/excel/select-excel',
name: 'SelectExcel',
meta: { title: 'Select Excel' }
},
{
path: 'export-merge-header',
component: 'views/excel/merge-header',
name: 'MergeHeader',
meta: { title: 'Merge Header' }
},
{
path: 'upload-excel',
component: 'views/excel/upload-excel',
name: 'UploadExcel',
meta: { title: 'Upload Excel' }
}
]
},
{
path: '/zip',
component: 'layout/Layout',
redirect: '/zip/download',
alwaysShow: true,
meta: { title: 'Zip', icon: 'zip' },
children: [
{
path: 'download',
component: 'views/zip/index',
name: 'ExportZip',
meta: { title: 'Export Zip' }
}
]
},
{
path: '/pdf',
component: 'layout/Layout',
redirect: '/pdf/index',
children: [
{
path: 'index',
component: 'views/pdf/index',
name: 'PDF',
meta: { title: 'PDF', icon: 'pdf' }
}
]
},
{
path: '/pdf/download',
component: 'views/pdf/download',
hidden: true
},
{
path: '/theme',
component: 'layout/Layout',
redirect: 'noRedirect',
children: [
{
path: 'index',
component: 'views/theme/index',
name: 'Theme',
meta: { title: 'Theme', icon: 'theme' }
}
]
},
{
path: '/clipboard',
component: 'layout/Layout',
redirect: 'noRedirect',
children: [
{
path: 'index',
component: 'views/clipboard/index',
name: 'ClipboardDemo',
meta: { title: 'Clipboard Demo', icon: 'clipboard' }
}
]
},
{
path: '/i18n',
component: 'layout/Layout',
children: [
{
path: 'index',
component: 'views/i18n-demo/index',
name: 'I18n',
meta: { title: 'I18n', icon: 'international' }
}
]
},
{
path: 'external-link',
component: 'layout/Layout',
children: [
{
path: 'https://github.com/PanJiaChen/vue-element-admin',
meta: { title: 'External Link', icon: 'link' }
}
]
},
{ path: '*', redirect: '/404', hidden: true }
]

55
mock/route.js Normal file
View File

@ -0,0 +1,55 @@
// Just a mock data
import { asyncRoutes } from '../src/router'
// 将asyncRoutes的componet转换为路径字符串,以便导入数据库
const mapAsyncRoutes = asyncRoutes => {
return asyncRoutes.map(route => {
const tmp = { ...route }
if (tmp.component) {
if (tmp.component.__file) {
tmp.component = tmp.component.__file.slice(4)
} else if (tmp.component instanceof Function) {
tmp.component = /\/\*\!.*\*\//.exec(String(tmp.component))[0].split(' ')[1].slice(2)
}
console.log(tmp.component)
}
if (tmp.children && tmp.children.length) {
tmp.children = mapAsyncRoutes(tmp.children)
}
return tmp
})
}
const mapRouteId = routes => {
return routes.map(route => {
const tmp = { ...route }
tmp.id = Math.random()
tmp.children && tmp.children.length && (tmp.children = mapRouteId(tmp.children))
return tmp
})
}
const asyncRoutesMap = mapAsyncRoutes(asyncRoutes)
export { asyncRoutesMap as routes }
export default [
// mock get all routes form server
{
url: '/routes',
type: 'get',
response: _ => {
return {
code: 20000,
data: mapRouteId(asyncRoutesMap)
}
}
},
{
url: '/routes',
type: 'put',
response: _ => {
return {
code: 20000,
data: {}
}
}
}
]

16
src/api/route.js Normal file
View File

@ -0,0 +1,16 @@
import request from '@/utils/request'
export function getRoutes() {
return request({
url: '/routes',
method: 'get'
})
}
export function importRoutes(data) {
return request({
url: '/routes',
method: 'put',
data
})
}

View File

@ -28,9 +28,9 @@ import * as filters from './filters' // global filters
* please remove it before going online! ! !
*/
import { mockXHR } from '../mock'
if (process.env.NODE_ENV === 'production') {
mockXHR()
}
// if (process.env.NODE_ENV === 'production') {
mockXHR()
// }
Vue.use(Element, {
size: Cookies.get('size') || 'medium' // set element-ui default size

View File

@ -395,6 +395,26 @@ const createRouter = () => new Router({
const router = createRouter()
if (process.env.NODE_ENV === 'development') {
router.addRoutes([
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'route',
component: () => import('@/views/permission/route'),
name: 'Route',
meta: {
title: 'Route',
roles: ['admin']
}
}
]
}])
}
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()

View File

@ -1,5 +1,6 @@
import { asyncRoutes, constantRoutes } from '@/router'
import { constantRoutes } from '@/router'
import { getRoutes } from '@/api/route'
// const _import = path => import('@/views/' + path)
/**
* Use meta.role to determine if the current user has permission
* @param roles
@ -18,6 +19,21 @@ function hasPermission(roles, route) {
* @param routes asyncRoutes
* @param roles
*/
export function mapAsyncRoutes(routes) {
return routes.map(route => {
const tmp = { ...route }
if (typeof tmp.component === 'string') {
const path = tmp.component
tmp.component = () => import(`@/${path}`)
}
if (tmp.children && tmp.children.length) {
tmp.children = mapAsyncRoutes(tmp.children)
}
return tmp
})
}
export function filterAsyncRoutes(routes, roles) {
const res = []
@ -50,13 +66,17 @@ const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
getRoutes().then(res => {
const asyncRoutes = res.data
if (roles.includes('admin')) {
accessedRoutes = mapAsyncRoutes(asyncRoutes || [])
} else {
accessedRoutes = mapAsyncRoutes(filterAsyncRoutes(asyncRoutes, roles))
debugger
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
})
}
}

View File

@ -1,3 +1,4 @@
@import "./variables.scss";
#app {
.main-container {

View File

@ -0,0 +1,50 @@
<template>
<div class="app-container">
<el-button type="primary" style="margin:0 0 20px 0;" @click="handleImport">Import Routes</el-button>
<el-table :data="routes" style="width: 100%;margin-bottom: 20px;" border row-key="id">
<el-table-column v-for="key in keys" :key="key" :prop="key" :label="key" sortable>
<template slot-scope="scope">
<span>{{ scope.row[key] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import * as Route from '@/api/route'
import { routes } from '../../../mock/route'
export default {
name: 'Route',
data() {
return {
keys: ['name', 'path', 'redirect', 'hidden', 'component', 'meta'],
routes: []
}
},
created() {
this.getRoutes()
},
methods: {
getRoutes() {
Route.getRoutes().then(res => {
const { data: routes } = res
this.routes = routes
})
},
handleImport() {
console.log(JSON.stringify(routes))
Route.importRoutes({ routes }).then(res => {
this.$message({
type: 'success',
message: 'Import Success'
})
this.getRoutes()
})
}
}
}
</script>

View File

@ -46,8 +46,9 @@ module.exports = {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
after: require('./mock/mock-server.js')
}
// 如果启用mock-server需要为node配置一下webpack中@的alias
// after: require('./mock/mock-server.js')
},
configureWebpack: {
// provide the app's title in webpack's name field, so that
@ -104,7 +105,7 @@ module.exports = {
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html')
.use('script-ext-html-webpack-plugin', [{
// `runtime` must same as runtimeChunk name. default is `runtime`
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}])
.end()