From a6d56d7dd0ba121b787d302cb3786faae902f544 Mon Sep 17 00:00:00 2001 From: wwlleo0730 Date: Thu, 14 Feb 2019 12:24:49 +0800 Subject: [PATCH] feat: get menus from server & add iframe view --- src/api/menu.js | 11 + src/mock/index.js | 4 + src/mock/menu.js | 104 +++++ src/permission.js | 10 +- src/router/index.js | 43 ++- src/store/modules/permission.js | 133 ++++++- src/store/modules/tagsView.js | 2 +- src/utils/validate.js | 15 + src/views/console/dept/index.vue | 9 + src/views/console/dict/index.vue | 5 + src/views/console/menu/index.vue | 19 + src/views/console/role/index.vue | 5 + src/views/console/user/index.vue | 355 ++++++++++++++++++ src/views/iframe/main.vue | 124 ++++++ .../layout/components/Sidebar/SidebarItem.vue | 13 +- 15 files changed, 826 insertions(+), 26 deletions(-) create mode 100644 src/api/menu.js create mode 100644 src/mock/menu.js create mode 100644 src/views/console/dept/index.vue create mode 100644 src/views/console/dict/index.vue create mode 100644 src/views/console/menu/index.vue create mode 100644 src/views/console/role/index.vue create mode 100644 src/views/console/user/index.vue create mode 100644 src/views/iframe/main.vue diff --git a/src/api/menu.js b/src/api/menu.js new file mode 100644 index 00000000..a79ec419 --- /dev/null +++ b/src/api/menu.js @@ -0,0 +1,11 @@ +import request from '@/utils/request' +/** + *根据角色获得可见菜单 + */ +export function getMenuByRole(query) { + return request({ + url: '/menus/role', + method: 'get', + params: query + }) +} diff --git a/src/mock/index.js b/src/mock/index.js index 3e00e918..9f876f18 100644 --- a/src/mock/index.js +++ b/src/mock/index.js @@ -3,6 +3,7 @@ import loginAPI from './login' import articleAPI from './article' import remoteSearchAPI from './remoteSearch' import transactionAPI from './transaction' +import menuAPI from './menu' // 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题 // https://github.com/nuysoft/Mock/issues/300 @@ -23,6 +24,9 @@ Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername) Mock.mock(/\/login\/logout/, 'post', loginAPI.logout) Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo) +// 角色相关 +Mock.mock(/\/menus\/role/, 'get', menuAPI.getMenuByRole) + // 文章相关 Mock.mock(/\/article\/list/, 'get', articleAPI.getList) Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle) diff --git a/src/mock/menu.js b/src/mock/menu.js new file mode 100644 index 00000000..3335010e --- /dev/null +++ b/src/mock/menu.js @@ -0,0 +1,104 @@ +const menuData = +[ + { + 'id': 1, + 'path': '/console', + 'redirect': 'noredirect', + 'component': 'Layout', + 'name': 'console', + 'title': '系统管理', + 'icon': 'component', + 'parentId': -1, + 'children': [ + { + 'children': [], + 'name': 'user', + 'component': 'console/user/index', + 'id': 7, + 'title': '用户管理', + 'icon': 'user', + 'parentId': 1, + 'path': 'user' + }, + { + 'children': [], + 'name': 'menu', + 'component': 'console/menu/index', + 'id': 8, + 'title': '菜单管理', + 'icon': 'documentation', + 'parentId': 1, + 'path': 'menu' + }, + { + 'children': [], + 'name': 'role', + 'component': 'console/role/index', + 'id': 9, + 'title': '角色管理', + 'icon': 'documentation', + 'parentId': 1, + 'path': 'role' + }, + { + 'children': [], + 'name': 'dict', + 'component': 'console/dict/index', + 'id': 10, + 'title': '字典管理', + 'icon': 'documentation', + 'parentId': 1, + 'path': 'dict' + }, + { + 'children': [], + 'name': 'dept', + 'component': 'console/dept/index', + 'id': 11, + 'title': '部门管理', + 'icon': 'documentation', + 'parentId': 1, + 'path': 'dept' + } + ] + }, + { + 'id': 22, + 'name': 'myiframe', + 'component': 'Iframe', + 'redirect': 'noredirect', + 'title': '第三方系统', + 'icon': 'link', + 'path': '/myiframe', + 'parentId': -1, + 'children': [ + { + 'id': 23, + 'children': [], + 'name': 'wechat', + 'component': 'Iframe', + 'title': '微信', + 'icon': 'wechat', + 'parentId': 22, + 'path': 'wechatUrl?src=https://pc.weixin.qq.com/&name=微信' + }, + { + 'id': 24, + 'children': [], + 'name': 'qq', + 'component': 'Iframe', + 'title': '腾讯QQ', + 'icon': 'qq', + 'parentId': 22, + 'path': 'qqUrl?src=https://im.qq.com/index.shtml&name=腾讯QQ' + } + ] + } + +] + +export default { + getMenuByRole: config => { + return menuData + } +} diff --git a/src/permission.js b/src/permission.js index e556cb00..6465b472 100644 --- a/src/permission.js +++ b/src/permission.js @@ -4,7 +4,7 @@ import { Message } from 'element-ui' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css'// progress bar style import { getToken } from '@/utils/auth' // getToken from cookie - +import { validatenull } from '@/utils/validate' NProgress.configure({ showSpinner: false })// NProgress Configuration // permission judge function @@ -27,8 +27,12 @@ router.beforeEach((to, from, next) => { if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 store.dispatch('GetUserInfo').then(res => { // 拉取user_info const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop'] - store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表 - router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 + store.dispatch('GenerateRoutes', { roles }).then((accessedRouters) => { // 根据roles权限生成可访问的路由表 + if (!validatenull(accessedRouters)) { + router.addRoutes(accessedRouters) + } else { + next({ path: '/401', replace: true, query: { noGoBack: true }}) + } // 动态添加可访问路由表 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record }) }).catch((err) => { diff --git a/src/router/index.js b/src/router/index.js index dda18156..ca3a209a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -76,6 +76,27 @@ export const constantRouterMap = [ } ] }, + { + path: '/myiframe', + component: Layout, + redirect: '/myiframe', + hidden: true, + children: [{ + path: ':routerPath', + name: 'myiframe', + component: () => import('@/views/iframe/main') + }] + } +] + +export default new Router({ + // mode: 'history', // require service support + scrollBehavior: () => ({ y: 0 }), + routes: constantRouterMap +}) + +let localRoutermap = [ + { path: '/documentation', component: Layout, @@ -101,16 +122,7 @@ export const constantRouterMap = [ meta: { title: 'guide', icon: 'guide', noCache: true } } ] - } -] - -export default new Router({ - // mode: 'history', // require service support - scrollBehavior: () => ({ y: 0 }), - routes: constantRouterMap -}) - -export const asyncRouterMap = [ + }, { path: '/permission', component: Layout, @@ -366,3 +378,14 @@ export const asyncRouterMap = [ { path: '*', redirect: '/404', hidden: true } ] + +const routerGetType = 'server' + +if (routerGetType === 'server') { + localRoutermap = [ + { path: '*', redirect: '/404', hidden: true } + ] +} + +export const routerMode = routerGetType +export const asyncRouterMap = localRoutermap diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index 13f60efb..1f41fc2f 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -1,4 +1,9 @@ -import { asyncRouterMap, constantRouterMap } from '@/router' +import { asyncRouterMap, constantRouterMap, routerMode } from '@/router' + +import { validatenull } from '@/utils/validate' +// for get menus from server +import { getMenuByRole } from '@/api/menu' +import Layout from '@/views/layout/Layout' /** * 通过meta.role判断是否与当前用户权限匹配 @@ -43,23 +48,129 @@ const permission = { SET_ROUTERS: (state, routers) => { state.addRouters = routers state.routers = constantRouterMap.concat(routers) + }, + ADD_MENU_ROUTERS: (state, routers) => { + state.addRouters = state.addRouters.concat(routers) + state.routers = state.routers.concat(routers) } }, actions: { GenerateRoutes({ commit }, data) { - return new Promise(resolve => { + if (routerMode === 'local') { + return new Promise(resolve => { + const { roles } = data + let accessedRouters + if (roles.includes('admin')) { + accessedRouters = asyncRouterMap + } else { + accessedRouters = filterAsyncRouter(asyncRouterMap, roles) + } + commit('SET_ROUTERS', accessedRouters) + // return for add to router + resolve(accessedRouters) + }) + } else { const { roles } = data - let accessedRouters - if (roles.includes('admin')) { - accessedRouters = asyncRouterMap - } else { - accessedRouters = filterAsyncRouter(asyncRouterMap, roles) - } - commit('SET_ROUTERS', accessedRouters) - resolve() - }) + + return new Promise((resolve, reject) => { + getMenuByRole(roles[0]).then(response => { + if (!response.data) { + reject('GenerateRoutesFromServer failed, please try later again.') + } + const menus = response.data + + if (menus.length === 0) { + reject('menus data is null') + } + + const accessedRouters = buildRouter(menus) + const menuRouters = buildMenuRouter(menus) + // final add 404 + accessedRouters.push({ path: '*', redirect: '/404', hidden: true }) + // commit to stores + commit('SET_ROUTERS', accessedRouters) + commit('ADD_MENU_ROUTERS', menuRouters) + + // return for add to router + resolve(accessedRouters) + }) + }) + } } } } +/** ************************************ + * build Router by menu api + * add 20190213 +***************************************/ + +function buildMenuRouter(aMenu) { + const aRouter = [] + + aMenu.forEach(item => { + if (!validatenull(item.component)) { + const oRouter = { + meta: { 'title': '', 'icon': '' }, + children: [] + } + + if (item.component === 'Iframe') { + oRouter.path = item.path + oRouter.name = item.name + oRouter.id = item.id || null + oRouter.redirect = item.redirect || null + oRouter.meta.icon = item.icon + oRouter.meta.title = item.title + oRouter.meta.noCache = item.noCache || false + oRouter.meta.breadcrumb = item.breadcrumb || true + oRouter.children = validatenull(item.children) ? [] : buildMenuRouter(item.children) + + aRouter.push(oRouter) + } + } + }) + + return aRouter +} + +function buildRouter(aMenu) { + const aRouter = [] + + aMenu.forEach(item => { + if (!validatenull(item.component)) { + const oRouter = { + meta: { 'title': '', 'icon': '' }, + children: [] + } + + if (item.component !== 'Iframe') { + if (item.component === 'Layout') { + oRouter.component = Layout + } else { + oRouter.component = require('@/views/' + item.component + '.vue').default + } + + oRouter.path = item.path + oRouter.name = item.name + oRouter.id = item.id || null + oRouter.redirect = item.redirect || null + oRouter.meta.icon = item.icon + oRouter.meta.title = item.title + oRouter.meta.noCache = item.noCache || false + oRouter.meta.breadcrumb = item.breadcrumb || true + oRouter.children = validatenull(item.children) ? [] : buildRouter(item.children) + + aRouter.push(oRouter) + } + } + }) + + return aRouter +} + +/** ************************************ + * build Router by menu api end +***************************************/ + export default permission diff --git a/src/store/modules/tagsView.js b/src/store/modules/tagsView.js index cbf9eeb7..b9d7d5ba 100644 --- a/src/store/modules/tagsView.js +++ b/src/store/modules/tagsView.js @@ -8,7 +8,7 @@ const tagsView = { if (state.visitedViews.some(v => v.path === view.path)) return state.visitedViews.push( Object.assign({}, view, { - title: view.meta.title || 'no-name' + title: view.meta.title || view.query.name || 'no-name' }) ) }, diff --git a/src/utils/validate.js b/src/utils/validate.js index ada0e7e2..6b964c6a 100644 --- a/src/utils/validate.js +++ b/src/utils/validate.js @@ -40,3 +40,18 @@ export function validateEmail(email) { const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ return re.test(email) } + +/** + * 是否为空判断 + */ +export function validatenull(val) { + if (val instanceof Array) { + if (val.length === 0) return true + } else if (val instanceof Object) { + if (JSON.stringify(val) === '{}') return true + } else { + if (val === 'null' || val === null || val === 'undefined' || val === undefined || val === '') return true + return false + } + return false +} diff --git a/src/views/console/dept/index.vue b/src/views/console/dept/index.vue new file mode 100644 index 00000000..2add1eec --- /dev/null +++ b/src/views/console/dept/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/views/console/dict/index.vue b/src/views/console/dict/index.vue new file mode 100644 index 00000000..796fce64 --- /dev/null +++ b/src/views/console/dict/index.vue @@ -0,0 +1,5 @@ + diff --git a/src/views/console/menu/index.vue b/src/views/console/menu/index.vue new file mode 100644 index 00000000..0ce24abf --- /dev/null +++ b/src/views/console/menu/index.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/views/console/role/index.vue b/src/views/console/role/index.vue new file mode 100644 index 00000000..445e4f03 --- /dev/null +++ b/src/views/console/role/index.vue @@ -0,0 +1,5 @@ + diff --git a/src/views/console/user/index.vue b/src/views/console/user/index.vue new file mode 100644 index 00000000..cac935e4 --- /dev/null +++ b/src/views/console/user/index.vue @@ -0,0 +1,355 @@ + + + diff --git a/src/views/iframe/main.vue b/src/views/iframe/main.vue new file mode 100644 index 00000000..2b189773 --- /dev/null +++ b/src/views/iframe/main.vue @@ -0,0 +1,124 @@ +