From 1e2fc52ac964c8418fd0607f072726c05bc1427a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8A=B1=E8=A3=A4=E8=A1=A9?= Date: Sat, 9 Mar 2019 11:06:48 +0800 Subject: [PATCH 01/12] remove empty file --- src/mock/table.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/mock/table.js diff --git a/src/mock/table.js b/src/mock/table.js deleted file mode 100644 index e69de29b..00000000 From 698df4942db8c9d65353dbefb75b383e67f33bb5 Mon Sep 17 00:00:00 2001 From: Pan Date: Mon, 11 Mar 2019 11:43:07 +0800 Subject: [PATCH 02/12] docs: add link --- src/styles/variables.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 50d9b3ef..22689568 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -22,6 +22,7 @@ $subMenuHover:#001528; $sideBarWidth: 180px; // the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass :export { menuText: $menuText; menuActiveText: $menuActiveText; From 8edf2094986130c57de00f936e242e2c34ad26d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8A=B1=E8=A3=A4=E8=A1=A9?= Date: Tue, 12 Mar 2019 11:12:28 +0800 Subject: [PATCH 03/12] fix[Sidebar]: fixed collapse animation problem (#1690) --- src/styles/sidebar.scss | 20 ++++++++++++++----- src/styles/variables.scss | 2 +- src/views/layout/components/Sidebar/index.vue | 1 + 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss index 96e89be7..03449706 100644 --- a/src/styles/sidebar.scss +++ b/src/styles/sidebar.scss @@ -83,19 +83,26 @@ .hideSidebar { .sidebar-container { - width: 36px !important; + width: 54px !important; } .main-container { - margin-left: 36px; + margin-left: 54px; + } + + .svg-icon { + margin-right: 0px; } .submenu-title-noDropdown { - padding-left: 10px !important; + padding: 0 !important; position: relative; .el-tooltip { - padding: 0 10px !important; + padding: 0 !important; + .svg-icon { + margin-left: 20px; + } } } @@ -103,7 +110,10 @@ overflow: hidden; &>.el-submenu__title { - padding-left: 10px !important; + padding: 0 !important; + .svg-icon { + margin-left: 20px; + } .el-submenu__icon-arrow { display: none; diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 22689568..98d7b672 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -19,7 +19,7 @@ $menuHover:#263445; $subMenuBg:#1f2d3d; $subMenuHover:#001528; -$sideBarWidth: 180px; +$sideBarWidth: 210px; // the :export directive is the magic sauce for webpack // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass diff --git a/src/views/layout/components/Sidebar/index.vue b/src/views/layout/components/Sidebar/index.vue index 11ef4c02..27da29b5 100644 --- a/src/views/layout/components/Sidebar/index.vue +++ b/src/views/layout/components/Sidebar/index.vue @@ -6,6 +6,7 @@ :background-color="variables.menuBg" :text-color="variables.menuText" :active-text-color="variables.menuActiveText" + :collapse-transition="false" mode="vertical" > From 9574643e927788826082c05db59a2f0ca458f4c3 Mon Sep 17 00:00:00 2001 From: Jesonhu Date: Tue, 12 Mar 2019 13:23:59 +0800 Subject: [PATCH 04/12] fix[Tree-Table]: fixed update item data bug (#1692) --- src/components/TreeTable/index.vue | 13 +++++++++++++ src/views/tree-table/custom/index.vue | 13 ++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/TreeTable/index.vue b/src/components/TreeTable/index.vue index cbe1eb60..9ce7a503 100644 --- a/src/components/TreeTable/index.vue +++ b/src/components/TreeTable/index.vue @@ -146,6 +146,19 @@ export default { this.selcetRecursion(child, select, children) }) } + }, + updateTreeNode(item) { + return new Promise(resolve => { + const { _id, _parent } = item + const index = _id.split('-').slice(-1)[0] // get last index + if (_parent) { + _parent.children.splice(index, 1, item) + resolve(this.data) + } else { + this.data.splice(index, 1, item) + resolve(this.data) + } + }) } } } diff --git a/src/views/tree-table/custom/index.vue b/src/views/tree-table/custom/index.vue index 6f9142a6..11e47912 100644 --- a/src/views/tree-table/custom/index.vue +++ b/src/views/tree-table/custom/index.vue @@ -127,17 +127,8 @@ export default { this.tempItem = Object.assign({}, row) this.dialogFormVisible = true }, - updateItem() { - const data = this.$refs.TreeTable.getData() - const { _id } = this.tempItem - - for (let i = 0; i < data.length; i++) { - if (data[i]._id === _id) { - data.splice(i, 1, Object.assign({}, this.tempItem)) - break - } - } - + async updateItem() { + await this.$refs.TreeTable.updateTreeNode(this.tempItem) this.dialogFormVisible = false }, addMenuItem(row, type) { From cf48ed218bf2ae6a843a11245de33952e4ddc173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A3=A4=E8=A3=86=E4=B8=89=E9=87=8D=E5=A5=8F?= Date: Wed, 13 Mar 2019 15:26:09 +0800 Subject: [PATCH 05/12] fix[Waves-Directive]: fixed v-waves does not support update (#1705) --- src/directive/waves/waves.js | 97 ++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/src/directive/waves/waves.js b/src/directive/waves/waves.js index a77f876e..38e07f88 100644 --- a/src/directive/waves/waves.js +++ b/src/directive/waves/waves.js @@ -1,42 +1,73 @@ import './waves.css' -export default{ - bind(el, binding) { - el.addEventListener('click', e => { - const customOpts = Object.assign({}, binding.value) - const opts = Object.assign({ +const context = '@@wavesContext' + +function handleClick(el, binding) { + function handle(e) { + const customOpts = Object.assign({}, binding.value) + const opts = Object.assign( + { ele: el, // 波纹作用元素 type: 'hit', // hit 点击位置扩散 center中心点扩展 color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 - }, customOpts) - const target = opts.ele - if (target) { - target.style.position = 'relative' - target.style.overflow = 'hidden' - const rect = target.getBoundingClientRect() - let ripple = target.querySelector('.waves-ripple') - if (!ripple) { - ripple = document.createElement('span') - ripple.className = 'waves-ripple' - ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' - target.appendChild(ripple) - } else { - ripple.className = 'waves-ripple' - } - switch (opts.type) { - case 'center': - ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' - ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' - break - default: - ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px' - ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px' - } - ripple.style.backgroundColor = opts.color - ripple.className = 'waves-ripple z-active' - return false + }, + customOpts + ) + const target = opts.ele + if (target) { + target.style.position = 'relative' + target.style.overflow = 'hidden' + const rect = target.getBoundingClientRect() + let ripple = target.querySelector('.waves-ripple') + if (!ripple) { + ripple = document.createElement('span') + ripple.className = 'waves-ripple' + ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' + target.appendChild(ripple) + } else { + ripple.className = 'waves-ripple' } - }, false) + switch (opts.type) { + case 'center': + ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px' + ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px' + break + default: + ripple.style.top = + (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || + document.body.scrollTop) + 'px' + ripple.style.left = + (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || + document.body.scrollLeft) + 'px' + } + ripple.style.backgroundColor = opts.color + ripple.className = 'waves-ripple z-active' + return false + } } + + if (!el[context]) { + el[context] = { + removeHandle: handle + } + } else { + el[context].removeHandle = handle + } + + return handle } +export default { + bind(el, binding) { + el.addEventListener('click', handleClick(el, binding), false) + }, + update(el, binding) { + el.removeEventListener('click', el[context].removeHandle, false) + el.addEventListener('click', handleClick(el, binding), false) + }, + unbind(el) { + el.removeEventListener('click', el[context].removeHandle, false) + el[context] = null + delete el[context] + } +} From f38d5810d91101c1ec38f1270c57302bfdf92a0f Mon Sep 17 00:00:00 2001 From: Yunfei <687418+OtaconBYF@users.noreply.github.com> Date: Thu, 14 Mar 2019 16:12:47 +0800 Subject: [PATCH 06/12] fix[utils]: fixed param2Obj not decoding plus sign (#1712) --- src/utils/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/index.js b/src/utils/index.js index fbcb4602..ebe58ff8 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -136,7 +136,8 @@ export function param2Obj(url) { decodeURIComponent(search) .replace(/"/g, '\\"') .replace(/&/g, '","') - .replace(/=/g, '":"') + + .replace(/=/g, '":"') + .replace(/\+/g, ' ') + '"}' ) } From 17f0d84b35188fdfe0d39a9740a472df8ea58aaf Mon Sep 17 00:00:00 2001 From: yuntao1997 <510835147@qq.com> Date: Fri, 15 Mar 2019 16:00:58 +0800 Subject: [PATCH 07/12] feature[Directive]: add auto-height table directive (#1702) --- src/directive/el-table/adaptive.js | 42 ++++++++++++++++++++++++++++++ src/directive/el-table/index.js | 14 ++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/directive/el-table/adaptive.js create mode 100644 src/directive/el-table/index.js diff --git a/src/directive/el-table/adaptive.js b/src/directive/el-table/adaptive.js new file mode 100644 index 00000000..3fa29c91 --- /dev/null +++ b/src/directive/el-table/adaptive.js @@ -0,0 +1,42 @@ + +import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' + +/** + * How to use + * ... + * el-table height is must be set + * bottomOffset: 30(default) // The height of the table from the bottom of the page. + */ + +const doResize = (el, binding, vnode) => { + const { componentInstance: $table } = vnode + + const { value } = binding + + if (!$table.height) { + throw new Error(`el-$table must set the height. Such as height='100px'`) + } + const bottomOffset = (value && value.bottomOffset) || 30 + + if (!$table) return + + const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset + $table.layout.setHeight(height) + $table.doLayout() +} + +export default { + bind(el, binding, vnode) { + el.resizeListener = () => { + doResize(el, binding, vnode) + } + + addResizeListener(el, el.resizeListener) + }, + inserted(el, binding, vnode) { + doResize(el, binding, vnode) + }, + unbind(el) { + removeResizeListener(el, el.resizeListener) + } +} diff --git a/src/directive/el-table/index.js b/src/directive/el-table/index.js new file mode 100644 index 00000000..d4cf406d --- /dev/null +++ b/src/directive/el-table/index.js @@ -0,0 +1,14 @@ + +import adaptive from './adaptive' + +const install = function(Vue) { + Vue.directive('el-height-adaptive-table', adaptive) +} + +if (window.Vue) { + window['el-height-adaptive-table'] = adaptive + Vue.use(install); // eslint-disable-line +} + +adaptive.install = install +export default adaptive From c963f56686b9731a517a17c4d562bc3da0fa3771 Mon Sep 17 00:00:00 2001 From: Serge Date: Fri, 15 Mar 2019 18:08:12 +0800 Subject: [PATCH 08/12] feature[Permission]: add role permission management page (#1605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加角色权限管理菜单 --- src/api/role.js | 38 +++ src/components/HeaderSearch/index.vue | 24 +- src/lang/en.js | 8 +- src/lang/es.js | 8 +- src/lang/zh.js | 8 +- src/mock/index.js | 8 + src/mock/role.js | 61 ++++ src/permission.js | 45 +-- src/router/index.js | 16 +- src/store/getters.js | 4 +- src/store/modules/permission.js | 28 +- src/views/layout/components/Sidebar/index.vue | 4 +- .../layout/components/TagsView/index.vue | 6 +- src/views/permission/role.vue | 267 ++++++++++++++++++ 14 files changed, 467 insertions(+), 58 deletions(-) create mode 100644 src/api/role.js create mode 100644 src/mock/role.js create mode 100644 src/views/permission/role.vue diff --git a/src/api/role.js b/src/api/role.js new file mode 100644 index 00000000..c81405a8 --- /dev/null +++ b/src/api/role.js @@ -0,0 +1,38 @@ +import request from '@/utils/request' + +export function getRoutes() { + return request({ + url: '/routes', + method: 'get' + }) +} + +export function getRoles() { + return request({ + url: '/roles', + method: 'get' + }) +} + +export function deleteRole(id) { + return request({ + url: `/roles/${id}`, + method: 'delete' + }) +} + +export function addRole(data) { + return request({ + url: '/roles', + method: 'post', + data + }) +} + +export function updateRole(key, data) { + return request({ + url: `/roles/${key}`, + method: 'put', + data + }) +} diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue index ab0d556a..678a218c 100644 --- a/src/components/HeaderSearch/index.vue +++ b/src/components/HeaderSearch/index.vue @@ -33,8 +33,8 @@ export default { } }, computed: { - routers() { - return this.$store.getters.permission_routers + routes() { + return this.$store.getters.permission_routes }, lang() { return this.$store.getters.language @@ -42,10 +42,10 @@ export default { }, watch: { lang() { - this.searchPool = this.generateRouters(this.routers) + this.searchPool = this.generateRoutes(this.routes) }, - routers() { - this.searchPool = this.generateRouters(this.routers) + routes() { + this.searchPool = this.generateRoutes(this.routes) }, searchPool(list) { this.initFuse(list) @@ -59,7 +59,7 @@ export default { } }, mounted() { - this.searchPool = this.generateRouters(this.routers) + this.searchPool = this.generateRoutes(this.routes) }, methods: { click() { @@ -100,10 +100,10 @@ export default { }, // Filter out the routes that can be displayed in the sidebar // And generate the internationalized title - generateRouters(routers, basePath = '/', prefixTitle = []) { + generateRoutes(routes, basePath = '/', prefixTitle = []) { let res = [] - for (const router of routers) { + for (const router of routes) { // skip hidden router if (router.hidden) { continue } @@ -125,11 +125,11 @@ export default { } } - // recursive child routers + // recursive child routes if (router.children) { - const tempRouters = this.generateRouters(router.children, data.path, data.title) - if (tempRouters.length >= 1) { - res = [...res, ...tempRouters] + const tempRoutes = this.generateRoutes(router.children, data.path, data.title) + if (tempRoutes.length >= 1) { + res = [...res, ...tempRoutes] } } } diff --git a/src/lang/en.js b/src/lang/en.js index 05b34598..caed5c5b 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -6,6 +6,7 @@ export default { guide: 'Guide', permission: 'Permission', pagePermission: 'Page Permission', + rolePermission: 'Role Permission', directivePermission: 'Directive Permission', icons: 'Icons', components: 'Components', @@ -86,9 +87,14 @@ export default { github: 'Github Repository' }, permission: { + addRole: 'New Role', + editPermission: 'Edit Permission', roles: 'Your roles', switchRoles: 'Switch roles', - tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.' + tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.', + delete: 'Delete', + confirm: 'Confirm', + cancel: 'Cancel' }, guide: { description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', diff --git a/src/lang/es.js b/src/lang/es.js index 8575d382..f0d7de41 100755 --- a/src/lang/es.js +++ b/src/lang/es.js @@ -5,6 +5,7 @@ export default { documentation: 'Documentación', guide: 'Guía', permission: 'Permisos', + rolePermission: 'Permisos de rol', pagePermission: 'Permisos de la página', directivePermission: 'Permisos de la directiva', icons: 'Iconos', @@ -86,9 +87,14 @@ export default { github: 'Repositorio Github' }, permission: { + addRole: 'Nuevo rol', + editPermission: 'Permiso de edición', roles: 'Tus permisos', switchRoles: 'Cambiar permisos', - tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.' + tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.', + delete: 'Borrar', + confirm: 'Confirmar', + cancel: 'Cancelar' }, guide: { description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', diff --git a/src/lang/zh.js b/src/lang/zh.js index 1fd18355..9bea838e 100644 --- a/src/lang/zh.js +++ b/src/lang/zh.js @@ -5,6 +5,7 @@ export default { documentation: '文档', guide: '引导页', permission: '权限测试页', + rolePermission: '角色权限', pagePermission: '页面权限', directivePermission: '指令权限', icons: '图标', @@ -86,9 +87,14 @@ export default { github: 'Github 地址' }, permission: { + addRole: '新增角色', + editPermission: '编辑权限', roles: '你的权限', switchRoles: '切换权限', - tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。' + tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。', + delete: '删除', + confirm: '确定', + cancel: '取消' }, guide: { description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于', diff --git a/src/mock/index.js b/src/mock/index.js index 3e00e918..f22c762f 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 roleAPI from './role' // 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题 // https://github.com/nuysoft/Mock/issues/300 @@ -23,6 +24,13 @@ Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername) Mock.mock(/\/login\/logout/, 'post', loginAPI.logout) Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo) +// 角色相关 +Mock.mock(/\/routes/, 'get', roleAPI.getRoutes) +Mock.mock(/\/roles/, 'get', roleAPI.getRoles) +Mock.mock(/\/roles$/, 'post', roleAPI.addRole) +Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole) +Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'delete', roleAPI.deleteRole) + // 文章相关 Mock.mock(/\/article\/list/, 'get', articleAPI.getList) Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle) diff --git a/src/mock/role.js b/src/mock/role.js new file mode 100644 index 00000000..ae4afeb8 --- /dev/null +++ b/src/mock/role.js @@ -0,0 +1,61 @@ +import Mock from 'mockjs' +import { deepClone } from '@/utils' +import { filterAsyncRoutes } from '@/store/modules/permission' +import { asyncRoutes, constantRoutes } from '@/router' + +const routes = deepClone([...constantRoutes, ...asyncRoutes]) + +const roles = [ + { + key: 'admin', + name: 'admin', + description: 'Super Administrator. Have access to view all pages.', + routes: routes + }, + { + key: 'editor', + name: 'editor', + description: 'Normal Editor. Can see all pages except permission page', + routes: filterAsyncRoutes(routes, ['editor']) + }, + { + key: 'visitor', + name: 'visitor', + description: 'Just a visitor. Can only see the home page and the document page', + routes: [{ + path: '', + redirect: 'dashboard', + children: [ + { + path: 'dashboard', + name: 'Dashboard', + meta: { title: 'dashboard', icon: 'dashboard' } + } + ] + }] + } +] + +export default { + getRoutes() { + return routes + }, + getRoles() { + return roles + }, + addRole() { + return Mock.mock('@integer(300, 5000)') + }, + updateRole() { + const res = { + data: 'success' + } + return res + }, + deleteRole() { + const res = { + data: 'success' + } + return res + } +} diff --git a/src/permission.js b/src/permission.js index e556cb00..25b6812f 100644 --- a/src/permission.js +++ b/src/permission.js @@ -2,41 +2,49 @@ import router from './router' import store from './store' 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 'nprogress/nprogress.css' // progress bar style +import { getToken } from '@/utils/auth' // get token from cookie -NProgress.configure({ showSpinner: false })// NProgress Configuration +NProgress.configure({ showSpinner: false }) // NProgress Configuration // permission judge function function hasPermission(roles, permissionRoles) { - if (roles.indexOf('admin') >= 0) return true // admin permission passed directly + if (roles.includes('admin')) return true // admin permission passed directly if (!permissionRoles) return true return roles.some(role => permissionRoles.indexOf(role) >= 0) } -const whiteList = ['/login', '/auth-redirect']// no redirect whitelist +const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist router.beforeEach((to, from, next) => { NProgress.start() // start progress bar - if (getToken()) { // determine if there has token + if (getToken()) { + // determine if there has token + /* has token*/ if (to.path === '/login') { next({ path: '/' }) NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it } else { - 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) // 动态添加可访问路由表 - next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record + 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 object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}] + store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record + }) }) - }).catch((err) => { - store.dispatch('FedLogOut').then(() => { - Message.error(err) - next({ path: '/' }) + .catch(err => { + store.dispatch('FedLogOut').then(() => { + Message.error(err) + next({ path: '/' }) + }) }) - }) } else { // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ if (hasPermission(store.getters.roles, to.meta.roles)) { @@ -49,7 +57,8 @@ router.beforeEach((to, from, next) => { } } else { /* has no token*/ - if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 + if (whiteList.indexOf(to.path) !== -1) { + // 在免登录白名单,直接进入 next() } else { next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 diff --git a/src/router/index.js b/src/router/index.js index a4a71e46..54305b26 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -33,7 +33,7 @@ import nestedRouter from './modules/nested' affix: true if true, the tag will affix in the tags-view } **/ -export const constantRouterMap = [ +export const constantRoutes = [ { path: '/redirect', component: Layout, @@ -81,7 +81,6 @@ export const constantRouterMap = [ { path: '/documentation', component: Layout, - redirect: '/documentation/index', children: [ { path: 'index', @@ -109,10 +108,10 @@ export const constantRouterMap = [ export default new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), - routes: constantRouterMap + routes: constantRoutes }) -export const asyncRouterMap = [ +export const asyncRoutes = [ { path: '/permission', component: Layout, @@ -141,6 +140,15 @@ export const asyncRouterMap = [ title: 'directivePermission' // if do not set roles, means: this page does not require permission } + }, + { + path: 'role', + component: () => import('@/views/permission/role'), + name: 'RolePermission', + meta: { + title: 'rolePermission', + roles: ['admin'] + } } ] }, diff --git a/src/store/getters.js b/src/store/getters.js index cf314f5c..797573f7 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -12,8 +12,8 @@ const getters = { status: state => state.user.status, roles: state => state.user.roles, setting: state => state.user.setting, - permission_routers: state => state.permission.routers, - addRouters: state => state.permission.addRouters, + permission_routes: state => state.permission.routes, + addRoutes: state => state.permission.addRoutes, errorLogs: state => state.errorLog.logs } export default getters diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index 13f60efb..fba03bc3 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -1,4 +1,4 @@ -import { asyncRouterMap, constantRouterMap } from '@/router' +import { asyncRoutes, constantRoutes } from '@/router' /** * 通过meta.role判断是否与当前用户权限匹配 @@ -15,17 +15,17 @@ function hasPermission(roles, route) { /** * 递归过滤异步路由表,返回符合用户角色权限的路由表 - * @param routes asyncRouterMap + * @param routes asyncRoutes * @param roles */ -function filterAsyncRouter(routes, roles) { +export function filterAsyncRoutes(routes, roles) { const res = [] routes.forEach(route => { const tmp = { ...route } if (hasPermission(roles, tmp)) { if (tmp.children) { - tmp.children = filterAsyncRouter(tmp.children, roles) + tmp.children = filterAsyncRoutes(tmp.children, roles) } res.push(tmp) } @@ -36,27 +36,27 @@ function filterAsyncRouter(routes, roles) { const permission = { state: { - routers: [], - addRouters: [] + routes: [], + addRoutes: [] }, mutations: { - SET_ROUTERS: (state, routers) => { - state.addRouters = routers - state.routers = constantRouterMap.concat(routers) + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) } }, actions: { GenerateRoutes({ commit }, data) { return new Promise(resolve => { const { roles } = data - let accessedRouters + let accessedRoutes if (roles.includes('admin')) { - accessedRouters = asyncRouterMap + accessedRoutes = asyncRoutes } else { - accessedRouters = filterAsyncRouter(asyncRouterMap, roles) + accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) } - commit('SET_ROUTERS', accessedRouters) - resolve() + commit('SET_ROUTES', accessedRoutes) + resolve(accessedRoutes) }) } } diff --git a/src/views/layout/components/Sidebar/index.vue b/src/views/layout/components/Sidebar/index.vue index 27da29b5..5a81e84f 100644 --- a/src/views/layout/components/Sidebar/index.vue +++ b/src/views/layout/components/Sidebar/index.vue @@ -9,7 +9,7 @@ :collapse-transition="false" mode="vertical" > - + @@ -23,7 +23,7 @@ export default { components: { SidebarItem }, computed: { ...mapGetters([ - 'permission_routers', + 'permission_routes', 'sidebar' ]), variables() { diff --git a/src/views/layout/components/TagsView/index.vue b/src/views/layout/components/TagsView/index.vue index 53d592bc..72e127f2 100644 --- a/src/views/layout/components/TagsView/index.vue +++ b/src/views/layout/components/TagsView/index.vue @@ -45,8 +45,8 @@ export default { visitedViews() { return this.$store.state.tagsView.visitedViews }, - routers() { - return this.$store.state.permission.routers + routes() { + return this.$store.state.permission.routes } }, watch: { @@ -93,7 +93,7 @@ export default { return tags }, initTags() { - const affixTags = this.affixTags = this.filterAffixTags(this.routers) + const affixTags = this.affixTags = this.filterAffixTags(this.routes) for (const tag of affixTags) { // Must have tag name if (tag.name) { diff --git a/src/views/permission/role.vue b/src/views/permission/role.vue new file mode 100644 index 00000000..df7befa6 --- /dev/null +++ b/src/views/permission/role.vue @@ -0,0 +1,267 @@ + + + + + From 763b31d915be67aecc8f24283d4898ccded8b55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=96=B0?= Date: Sun, 17 Mar 2019 16:36:09 +0800 Subject: [PATCH 09/12] feature[Excel]: support export merged header export (#1718) --- src/vendor/Export2Excel.js | 14 +++++++++++++ src/views/excel/exportExcel.vue | 36 +++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/vendor/Export2Excel.js b/src/vendor/Export2Excel.js index ba956dc1..3fda4465 100644 --- a/src/vendor/Export2Excel.js +++ b/src/vendor/Export2Excel.js @@ -145,9 +145,11 @@ export function export_table_to_excel(id) { } export function export_json_to_excel({ + multiHeader, header, data, filename, + merges, autoWidth = true, bookType= 'xlsx' } = {}) { @@ -155,10 +157,22 @@ export function export_json_to_excel({ filename = filename || 'excel-list' data = [...data] data.unshift(header); + + for (let header of multiHeader) { + data.unshift(header) + } + var ws_name = "SheetJS"; var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); + if (merges.length > 0) { + if (!ws['!merges']) ws['!merges'] = []; + merges.forEach(item => { + ws['!merges'].push(XLSX.utils.decode_range(item)) + }) + } + if (autoWidth) { /*设置worksheet每列的最大宽度*/ const colWidth = data.map(row => row.map(val => { diff --git a/src/views/excel/exportExcel.vue b/src/views/excel/exportExcel.vue index 551b89f8..1702d978 100644 --- a/src/views/excel/exportExcel.vue +++ b/src/views/excel/exportExcel.vue @@ -18,20 +18,22 @@ {{ scope.$index }} - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + +