diff --git a/mock/index.js b/mock/index.js index 6907e861..1b3b6173 100644 --- a/mock/index.js +++ b/mock/index.js @@ -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 diff --git a/mock/role/index.js b/mock/role.js similarity index 80% rename from mock/role/index.js rename to mock/role.js index 39148076..0e382993 100644 --- a/mock/role/index.js +++ b/mock/role.js @@ -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', diff --git a/mock/role/routes.js b/mock/role/routes.js deleted file mode 100644 index d718919c..00000000 --- a/mock/role/routes.js +++ /dev/null @@ -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 } -] diff --git a/mock/route.js b/mock/route.js new file mode 100644 index 00000000..1934ec47 --- /dev/null +++ b/mock/route.js @@ -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: {} + } + } + } +] diff --git a/src/api/route.js b/src/api/route.js new file mode 100644 index 00000000..9891b64d --- /dev/null +++ b/src/api/route.js @@ -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 + }) +} diff --git a/src/main.js b/src/main.js index ef07f389..6ad980e6 100644 --- a/src/main.js +++ b/src/main.js @@ -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 diff --git a/src/router/index.js b/src/router/index.js index fbe8ad25..ed9929e4 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -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() diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index aeb5ee53..b01435cc 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -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) + }) }) } } diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss index 3dad4c39..4d6f2a0a 100644 --- a/src/styles/sidebar.scss +++ b/src/styles/sidebar.scss @@ -1,3 +1,4 @@ +@import "./variables.scss"; #app { .main-container { diff --git a/src/views/permission/route.vue b/src/views/permission/route.vue new file mode 100644 index 00000000..577df344 --- /dev/null +++ b/src/views/permission/route.vue @@ -0,0 +1,50 @@ + + + + diff --git a/vue.config.js b/vue.config.js index 7b8f3a5d..dc0e360e 100644 --- a/vue.config.js +++ b/vue.config.js @@ -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()