From c9e4451974b4d85c9e4a14e500dc4c815c0ffe1d Mon Sep 17 00:00:00 2001
From: iczer <1126263215@qq.com>
Date: Tue, 28 Jul 2020 20:48:02 +0800
Subject: [PATCH 1/4] chore: optimize the code of async router; :star2:
---
.env | 4 +
.env.development | 4 +
src/bootstrap.js | 19 ++--
src/components/exception/ExceptionPage.vue | 12 ++-
src/layouts/tabs/index.js | 2 +
src/mock/user/login.js | 2 +
src/mock/user/routes.js | 14 ++-
src/pages/exception/403.vue | 7 +-
src/pages/exception/404.vue | 2 +-
src/pages/exception/500.vue | 7 +-
src/pages/login/Login.vue | 13 ++-
src/pages/login/index.js | 2 +
src/router/config.async.js | 38 ++++++++
src/router/config.js | 100 ++++++++++++++++++---
src/router/index.js | 17 +---
src/router/router.map.js | 5 +-
src/services/user.js | 4 +-
src/store/modules/account.js | 50 +++++++++++
src/utils/routerUtil.js | 97 +++++++++++++++++---
19 files changed, 331 insertions(+), 68 deletions(-)
create mode 100644 .env
create mode 100644 .env.development
create mode 100644 src/layouts/tabs/index.js
create mode 100644 src/pages/login/index.js
create mode 100644 src/router/config.async.js
diff --git a/.env b/.env
new file mode 100644
index 0000000..58cfd7b
--- /dev/null
+++ b/.env
@@ -0,0 +1,4 @@
+VUE_APP_NAME=admin
+VUE_APP_ROUTES_KEY=admin.routes
+VUE_APP_PERMISSIONS_KEY=admin.permissions
+VUE_APP_ROLES_KEY=admin.roles
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..58cfd7b
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,4 @@
+VUE_APP_NAME=admin
+VUE_APP_ROUTES_KEY=admin.routes
+VUE_APP_PERMISSIONS_KEY=admin.permissions
+VUE_APP_ROLES_KEY=admin.roles
diff --git a/src/bootstrap.js b/src/bootstrap.js
index 7b1ae4b..b3f0226 100644
--- a/src/bootstrap.js
+++ b/src/bootstrap.js
@@ -1,23 +1,18 @@
-// 应用启动时需要执行的操作放在这里
-import {loadRoutes} from '@/utils/routerUtil'
+import {loadRoutes, loginGuard, authorityGuard} from '@/utils/routerUtil'
/**
* 启动引导方法
+ * 应用启动时需要执行的操作放在这里
* @param router 应用的路由实例
* @param store 应用的 vuex.store 实例
* @param i18n 应用的 vue-i18n 实例
*/
function bootstrap({router, store, i18n}) {
- // 加载本地存储的异步路由
- const localRoutes = localStorage.getItem('routes')
- if (localRoutes) {
- try {
- const routesConfig = JSON.parse(localRoutes)
- loadRoutes(routesConfig, router, store, i18n)
- } catch (e) {
- console.error(e.message)
- }
- }
+ // 加载路由
+ loadRoutes({router, store, i18n})
+ // 添加路由守卫
+ loginGuard(router)
+ authorityGuard(router, store)
}
export default bootstrap
diff --git a/src/components/exception/ExceptionPage.vue b/src/components/exception/ExceptionPage.vue
index 5c5316b..f9fb84a 100644
--- a/src/components/exception/ExceptionPage.vue
+++ b/src/components/exception/ExceptionPage.vue
@@ -7,7 +7,7 @@
{{config[type].title}}
{{config[type].desc}}
@@ -18,11 +18,19 @@ import Config from './typeConfig'
export default {
name: 'ExceptionPage',
- props: ['type'],
+ props: ['type', 'homeRoute'],
data () {
return {
config: Config
}
+ },
+ methods: {
+ backHome() {
+ if (this.homeRoute) {
+ this.$router.push(this.homeRoute)
+ }
+ this.$emit('backHome', this.type)
+ }
}
}
diff --git a/src/layouts/tabs/index.js b/src/layouts/tabs/index.js
new file mode 100644
index 0000000..60a363f
--- /dev/null
+++ b/src/layouts/tabs/index.js
@@ -0,0 +1,2 @@
+import TabsView from './TabsView'
+export default TabsView
diff --git a/src/mock/user/login.js b/src/mock/user/login.js
index 4bf77f5..abeb0cc 100644
--- a/src/mock/user/login.js
+++ b/src/mock/user/login.js
@@ -22,6 +22,8 @@ Mock.mock('/login', 'post', ({body}) => {
result.data.user = user
result.data.token = 'Authorization:' + Math.random()
result.data.expireAt = new Date(new Date().getTime() + 30 * 60 * 1000)
+ result.data.permissions = [{id: 'demo', extra: ['add', 'edit', 'delete']}]
+ result.data.roles = [{id: 'admin', extra: ['add', 'edit', 'delete']}]
}
return result
})
diff --git a/src/mock/user/routes.js b/src/mock/user/routes.js
index 78f4316..8b4b96b 100644
--- a/src/mock/user/routes.js
+++ b/src/mock/user/routes.js
@@ -8,11 +8,21 @@ Mock.mock('/routes', 'get', () => {
children: ['demo',
{
router: 'parent1',
- children: ['demo'],
+ children: [{
+ router: 'demo',
+ name: 'demo1',
+ authority: {
+ permission: 'demo',
+ role: 'admin'
+ }
+ }],
},
{
router: 'parent2',
- children: ['demo'],
+ children: [{
+ router: 'demo',
+ name: 'demo2'
+ }],
},
{
router: 'exception',
diff --git a/src/pages/exception/403.vue b/src/pages/exception/403.vue
index ca933ea..37f1041 100644
--- a/src/pages/exception/403.vue
+++ b/src/pages/exception/403.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/src/pages/exception/404.vue b/src/pages/exception/404.vue
index fa4edfa..7bbca9b 100644
--- a/src/pages/exception/404.vue
+++ b/src/pages/exception/404.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/src/pages/login/Login.vue b/src/pages/login/Login.vue
index 460fb79..3ed0a22 100644
--- a/src/pages/login/Login.vue
+++ b/src/pages/login/Login.vue
@@ -78,6 +78,7 @@ import CommonLayout from '@/layouts/CommonLayout'
import {login, getRoutesConfig} from '@/services'
import {setAuthorization} from '@/utils/request'
import {loadRoutes} from '@/utils/routerUtil'
+import {mapMutations} from 'vuex'
export default {
name: 'Login',
@@ -95,6 +96,7 @@ export default {
}
},
methods: {
+ ...mapMutations('account', ['setUser', 'setPermissions', 'setRoles']),
onSubmit (e) {
e.preventDefault()
this.form.validateFields((err) => {
@@ -111,14 +113,17 @@ export default {
const loginRes = res.data
if (loginRes.code >= 0) {
const user = loginRes.data.user
+ const permissions = loginRes.data.permissions
+ const roles = loginRes.data.roles
+ this.setUser(user)
+ this.setPermissions(permissions)
+ this.setRoles(roles)
setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
// 获取路由配置
getRoutesConfig().then(result => {
const routesConfig = result.data.data
- localStorage.setItem('routes', JSON.stringify(routesConfig))
- loadRoutes(routesConfig, this.$router, this.$store, this.$i18n)
- this.$router.push('/parent1/demo')
- this.$store.commit('account/setUser', user)
+ loadRoutes({router: this.$router, store: this.$store, i18n: this.$i18n}, routesConfig)
+ this.$router.push('/demo')
this.$message.success(loginRes.message, 3)
})
} else {
diff --git a/src/pages/login/index.js b/src/pages/login/index.js
new file mode 100644
index 0000000..8b44975
--- /dev/null
+++ b/src/pages/login/index.js
@@ -0,0 +1,2 @@
+import Login from './Login'
+export default Login
diff --git a/src/router/config.async.js b/src/router/config.async.js
new file mode 100644
index 0000000..4fc21b0
--- /dev/null
+++ b/src/router/config.async.js
@@ -0,0 +1,38 @@
+import routerMap from './router.map'
+import {parseRoutes} from '@/utils/routerUtil'
+
+// 异步路由配置
+const routesConfig = [
+ 'login',
+ 'root',
+ {
+ router: 'exp404',
+ path: '*',
+ name: '404'
+ },
+ {
+ router: 'exp403',
+ path: '/403',
+ name: '403'
+ }
+]
+
+const options = {
+ routes: parseRoutes(routesConfig, routerMap)
+}
+
+// 不需要登录拦截的路由配置
+const loginIgnore = {
+ names: ['404'], //根据路由名称匹配
+ paths: ['/login'], //根据路由fullPath匹配
+ /**
+ * 判断路由是否包含在该配置中
+ * @param route vue-router 的 route 对象
+ * @returns {boolean}
+ */
+ includes(route) {
+ return this.names.includes(route.name) || this.paths.includes(route.path)
+ }
+}
+
+export {options, loginIgnore}
diff --git a/src/router/config.js b/src/router/config.js
index 61fe1f3..d882e52 100644
--- a/src/router/config.js
+++ b/src/router/config.js
@@ -1,19 +1,93 @@
-import routerMap from './router.map'
-import {parseRoutes} from '@/utils/routerUtil'
+import Login from '@/pages/login/Login'
+import TabsView from '@/layouts/tabs/TabsView'
+import BlankView from '@/layouts/BlankView'
+import PageView from '@/layouts/PageView'
// 路由配置
-const routesConfig = [
- 'login',
- 'root',
- {
- router: 'exp404',
- path: '*',
- name: '404'
- }
-]
-
const options = {
- routes: parseRoutes(routesConfig, routerMap)
+ routes: [
+ {
+ path: '/login',
+ name: '登录页',
+ component: Login
+ },
+ {
+ path: '*',
+ name: '404',
+ component: () => import('@/pages/exception/404'),
+ },
+ {
+ path: '/',
+ name: '首页',
+ component: TabsView,
+ redirect: '/login',
+ children: [
+ {
+ path: 'demo',
+ name: '演示页0',
+ meta: {
+ icon: 'file-ppt'
+ },
+ component: () => import('@/pages/demo')
+ },
+ {
+ path: 'parent1',
+ name: '父级路由1',
+ meta: {
+ icon: 'dashboard'
+ },
+ component: BlankView,
+ children: [
+ {
+ path: 'demo1',
+ name: '演示页面1',
+ component: () => import('@/pages/demo'),
+ }
+ ]
+ },
+ {
+ path: 'parent2',
+ name: '父级路由2',
+ meta: {
+ icon: 'form'
+ },
+ component: PageView,
+ children: [
+ {
+ path: 'demo2',
+ name: '演示页面2',
+ component: () => import('@/pages/demo'),
+ }
+ ]
+ },
+ {
+ path: 'exception',
+ name: '异常页',
+ meta: {
+ icon: 'warning',
+ },
+ component: BlankView,
+ children: [
+ {
+ path: '404',
+ name: 'Exp404',
+ component: () => import('@/pages/exception/404')
+ },
+ {
+ path: '403',
+ name: 'Exp403',
+ component: () => import('@/pages/exception/403')
+ },
+ {
+ path: '500',
+ name: 'Exp500',
+ component: () => import('@/pages/exception/500')
+ }
+ ]
+ },
+ ]
+ }
+ ]
}
// 不需要登录拦截的路由配置
diff --git a/src/router/index.js b/src/router/index.js
index fa07945..5a4a2a5 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,19 +1,10 @@
import Vue from 'vue'
import Router from 'vue-router'
-import {checkAuthorization} from '@/utils/request'
-import {options, loginIgnore} from './config'
+// import {options, loginIgnore} from './config' //本地路由配置
+import {options, loginIgnore} from './config.async' //异步路由配置
Vue.use(Router)
+const router = new Router({...options})
-const router = new Router(options)
-
-// 登录拦截
-router.beforeEach((to, from, next) => {
- if (!loginIgnore.includes(to) && !checkAuthorization()) {
- next({path: '/login'})
- } else {
- next()
- }
-})
-
+export {loginIgnore}
export default router
diff --git a/src/router/router.map.js b/src/router/router.map.js
index b486dfd..1132ff2 100644
--- a/src/router/router.map.js
+++ b/src/router/router.map.js
@@ -13,25 +13,22 @@ const routerMap = {
component: () => import('@/pages/login')
},
demo: {
- authority: 'admin',
name: '演示页',
renderMenu: false,
component: () => import('@/pages/demo')
},
exp403: {
- authority: 'admin',
+ authority: '*',
name: 'exp403',
path: '403',
component: () => import('@/pages/exception/403')
},
exp404: {
- authority: '*',
name: 'exp404',
path: '404',
component: () => import('@/pages/exception/404')
},
exp500: {
- authority: 'admin',
name: 'exp500',
path: '500',
component: () => import('@/pages/exception/500')
diff --git a/src/services/user.js b/src/services/user.js
index 7046124..04c91c8 100644
--- a/src/services/user.js
+++ b/src/services/user.js
@@ -22,7 +22,9 @@ async function getRoutesConfig() {
* 退出登录
*/
function logout() {
- localStorage.removeItem('routes')
+ localStorage.removeItem(process.env.VUE_APP_ROUTES_KEY)
+ localStorage.removeItem(process.env.VUE_APP_PERMISSIONS_KEY)
+ localStorage.removeItem(process.env.VUE_APP_ROLES_KEY)
removeAuthorization()
}
diff --git a/src/store/modules/account.js b/src/store/modules/account.js
index c02aea1..2e27883 100644
--- a/src/store/modules/account.js
+++ b/src/store/modules/account.js
@@ -10,6 +10,44 @@ export default {
avatar: '',
position: '',
address: ''
+ },
+ permissions: [],
+ roles: [],
+ routesConfig: []
+ },
+ getters: {
+ permissions: state => {
+ if (!state.permissions || state.permissions.length === 0) {
+ try {
+ const permissions = localStorage.getItem(process.env.VUE_APP_PERMISSIONS_KEY)
+ state.permissions = eval(permissions) ? JSON.parse(permissions) : state.permissions
+ } catch (e) {
+ console.error(e.message)
+ }
+ }
+ return state.permissions
+ },
+ roles: state => {
+ if (!state.roles || state.roles.length === 0) {
+ try {
+ const roles = localStorage.getItem(process.env.VUE_APP_ROLES_KEY)
+ state.roles = eval(roles) ? JSON.parse(roles) : state.roles
+ } catch (e) {
+ console.error(e.message)
+ }
+ }
+ return state.roles
+ },
+ routesConfig: state => {
+ if (!state.routesConfig || state.routesConfig.length === 0) {
+ try {
+ const routesConfig = localStorage.getItem(process.env.VUE_APP_ROUTES_KEY)
+ state.routesConfig = eval(routesConfig) ? JSON.parse(routesConfig) : state.routesConfig
+ } catch (e) {
+ console.error(e.message)
+ }
+ }
+ return state.routesConfig
}
},
mutations: {
@@ -31,6 +69,18 @@ export default {
throw e
}
})
+ },
+ setPermissions(state, permissions) {
+ state.permissions = permissions
+ localStorage.setItem(process.env.VUE_APP_PERMISSIONS_KEY, JSON.stringify(permissions))
+ },
+ setRoles(state, roles) {
+ state.roles = roles
+ localStorage.setItem(process.env.VUE_APP_ROLES_KEY, JSON.stringify(roles))
+ },
+ setRoutesConfig(state, routesConfig) {
+ state.routesConfig = routesConfig
+ localStorage.setItem(process.env.VUE_APP_ROUTES_KEY, JSON.stringify(routesConfig))
}
}
}
diff --git a/src/utils/routerUtil.js b/src/utils/routerUtil.js
index 5792b30..57f0e06 100644
--- a/src/utils/routerUtil.js
+++ b/src/utils/routerUtil.js
@@ -1,6 +1,8 @@
import routerMap from '@/router/router.map'
import {mergeI18nFromRoutes} from '@/utils/i18n'
import Router from 'vue-router'
+import {loginIgnore} from '@/router'
+import {checkAuthorization} from '@/utils/request'
/**
* 根据 路由配置 和 路由组件注册 解析路由
@@ -10,7 +12,7 @@ import Router from 'vue-router'
function parseRoutes(routesConfig, routerMap) {
let routes = []
routesConfig.forEach(item => {
- // 读取 router,初始化 routeCfg
+ // 获取注册在 routerMap 中的 router,初始化 routeCfg
let router = undefined, routeCfg = {}
if (typeof item === 'string') {
router = routerMap[item]
@@ -19,7 +21,7 @@ function parseRoutes(routesConfig, routerMap) {
router = routerMap[item.router]
routeCfg = item
}
- // 从 register 和 routeCfg 解析路由
+ // 从 router 和 routeCfg 解析路由
if (!router) {
console.warn(`can't find register for router ${routeCfg.router}, please register it in advance.`)
} else {
@@ -45,17 +47,88 @@ function parseRoutes(routesConfig, routerMap) {
/**
* 加载路由
+ * @param router 应用路由实例
+ * @param store 应用的 vuex.store 实例
+ * @param i18n 应用的 vue-i18n 实例
* @param routesConfig
- * @param router
- * @param store
*/
-function loadRoutes(routesConfig, router, store, i18n) {
- const routes = parseRoutes(routesConfig, routerMap)
- router.matcher = new Router(router.options).matcher
- router.addRoutes(routes)
- const menuRoutes = routes.find(item => item.path === '/').children
- mergeI18nFromRoutes(i18n, menuRoutes)
- store.commit('setting/setMenuData', menuRoutes)
+function loadRoutes({router, store, i18n}, routesConfig) {
+ // 如果 routesConfig 有值,则更新到本地localStorage,否则从本地localStorage获取
+ if (routesConfig) {
+ store.commit('account/setRoutesConfig', routesConfig)
+ } else {
+ routesConfig = store.getters['account/routesConfig']
+ }
+ if (routesConfig && routesConfig.length > 0) {
+ const routes = parseRoutes(routesConfig, routerMap)
+ router.matcher = new Router(router.options).matcher
+ router.addRoutes(routes)
+ const menuRoutes = routes.find(item => item.path === '/').children
+ mergeI18nFromRoutes(i18n, menuRoutes)
+ store.commit('setting/setMenuData', menuRoutes)
+ }
}
-export {parseRoutes, loadRoutes}
+/**
+ * 登录守卫
+ * @param router 应用路由实例
+ */
+function loginGuard(router) {
+ router.beforeEach((to, from, next) => {
+ if (!loginIgnore.includes(to) && !checkAuthorization()) {
+ next({path: '/login'})
+ } else {
+ next()
+ }
+ })
+}
+
+/**
+ * 权限守卫
+ * @param router 应用路由实例
+ * @param store 应用的 vuex.store 实例
+ */
+function authorityGuard(router, store) {
+ router.beforeEach((to, form, next) => {
+ const permissions = store.getters['account/permissions']
+ const roles = store.getters['account/roles']
+ if (!hasPermission(to, permissions) && !hasRole(to, roles)) {
+ next({path: '/403'})
+ } else {
+ next()
+ }
+ })
+}
+
+/**
+ * 判断是否有路由的权限
+ * @param route 路由
+ * @param permissions 用户权限集合
+ * @returns {boolean|*}
+ */
+function hasPermission(route, permissions) {
+ const authority = route.meta.authority || '*'
+ let required = '*'
+ if (typeof authority === 'string') {
+ required = authority
+ } else if (typeof authority === 'object') {
+ required = authority.permission
+ }
+ return required === '*' || (permissions && permissions.findIndex(item => item === required || item.id === required) !== -1)
+}
+
+/**
+ * 判断是否有路由需要的角色
+ * @param route 路由
+ * @param roles 用户角色集合
+ */
+function hasRole(route, roles) {
+ const authority = route.meta.authority || '*'
+ let required = undefined
+ if (typeof authority === 'object') {
+ required = authority.role
+ }
+ return authority === '*' || (required && roles.findIndex(item => item === required || item.id === required) !== -1)
+}
+
+export {parseRoutes, loadRoutes, loginGuard, authorityGuard}
From bceb33996b9616c158aedb7ca34934cd9aedb6f2 Mon Sep 17 00:00:00 2001
From: iczer <1126263215@qq.com>
Date: Tue, 28 Jul 2020 21:33:40 +0800
Subject: [PATCH 2/4] chore:replace pouchdb with localStorage; :star2:
---
.env | 1 +
.env.development | 1 +
package.json | 1 -
src/layouts/header/HeaderlAvatar.vue | 4 +-
src/store/index.js | 9 +-
src/store/modules/account.js | 43 ++--
yarn.lock | 341 +--------------------------
7 files changed, 29 insertions(+), 371 deletions(-)
diff --git a/.env b/.env
index 58cfd7b..a776eb8 100644
--- a/.env
+++ b/.env
@@ -2,3 +2,4 @@ VUE_APP_NAME=admin
VUE_APP_ROUTES_KEY=admin.routes
VUE_APP_PERMISSIONS_KEY=admin.permissions
VUE_APP_ROLES_KEY=admin.roles
+VUE_APP_USER_KEY=admin.user
diff --git a/.env.development b/.env.development
index 58cfd7b..a776eb8 100644
--- a/.env.development
+++ b/.env.development
@@ -2,3 +2,4 @@ VUE_APP_NAME=admin
VUE_APP_ROUTES_KEY=admin.routes
VUE_APP_PERMISSIONS_KEY=admin.permissions
VUE_APP_ROLES_KEY=admin.roles
+VUE_APP_USER_KEY=admin.user
diff --git a/package.json b/package.json
index 8aa0a05..e0a99b4 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,6 @@
"enquire.js": "^2.1.6",
"js-cookie": "^2.2.1",
"mockjs": "^1.1.0",
- "pouchdb": "^7.2.1",
"viser-vue": "^2.4.8",
"vue": "^2.6.11",
"vue-i18n": "^8.18.2",
diff --git a/src/layouts/header/HeaderlAvatar.vue b/src/layouts/header/HeaderlAvatar.vue
index 256580a..5884727 100644
--- a/src/layouts/header/HeaderlAvatar.vue
+++ b/src/layouts/header/HeaderlAvatar.vue
@@ -25,13 +25,13 @@