在线预览
https://github.com/PanJiaChen/vue-element-admin/blob/master/README.zh-CN.md (总连接)
使用文档
一、细节注意点及代码分析
1.settings.js 文件
1.showSettings: false,//右边设置 是否关闭
2.tagsView: false,导航栏是否关闭
3.fixedHeader: false,
4.sidebarLogo: true,左边菜单顶部是否显示标题和logo
2.登录逻辑 权限控制 代码分析
A. permission.js 是权限控制的 (其实就是路由拦截器)登录逻辑
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' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration 是否有转圈效果
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist 没有重定向白名单
router.beforeEach(async (to, from, next) => {
// 开始进度条
NProgress.start()
// 设置页面标题
document.title = getPageTitle(to.meta.title)
// 确定用户是否已登录
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// 如果已登录,则重定向到主页
next({ path: '/' })
NProgress.done()
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
//有用户信息
next()
} else {
//无用户信息
try {
// 获得用户信息 实际是请求用户信息后返回,这里是模拟数据,直接从store中取
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
// generate accessible routes map based on roles
//方法generateRoutes在store/modules/permission.js
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)//生成可访问的路由表
// dynamically add accessible routes
router.addRoutes(accessRoutes) //动态添加可访问路由表
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true }) //hack方法 确保addRoutes已完成
} catch (error) {
// 删除token,进入登录页面重新登录
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly 在免费登录白名单,直接去
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
//没有访问权限的其他页面被重定向到登录页面。
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar完成进度条
NProgress.done()
})
/*
有token:再看看是不是去登录页的,登录页肯定不能拦截的,如果是登录页就直接放行。如果不是登录页,就要看看本地有没有用户信息,看看cookie中有没有用户信息(不一定是token,也可能是localstorage)。如果有用户信息,放行。如果没有用户信息,就调用接口去获取登录信息,然后后面还有一点代码,涉及到了动态添加路由(这里先说到这,后面具体说动态添加权限路由的事)。获取到用户信息后放行。如果在获取用户信息的过程中报错,则回到登录页
无token:先看看用户要进入的页面是不是在白名单内,一般登录、注册、忘记密码都是在白名单内的,这些页面,在无token的情况下也是直接放行。如果不在白名单内,滚回登录页。
*/
2.router
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* 这是框 */
import Layout from '@/layout'
/*添加的路由模块 */
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
/**
* Note: sub-menu only appear when route children.length >= 1
* 注意: 子菜单只在路由子菜单时长度> = 1的时候出现
* 参考网址: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true 如果设置为true,项目将不会显示在侧栏中(默认为false)
* alwaysShow: true 如果设置为true,将始终显示根菜单
* 如果不设置alwaysShow, 当项目有多个子路由时,它将成为嵌套模式,否则不显示根菜单
* redirect: noRedirect 如果设置noRedirect,则不会在面包屑中重定向
* name:'router-name' the name is used by (must set!!!)
* meta : {
roles: ['admin','editor'] 控制页面角色(可以设置多个角色)'admin','editor'
title: 'title' 名称显示在侧边栏和面包屑(推荐集)
icon: 'svg-name' 图标显示在侧栏中
breadcrumb: false 如果设置为false,则该项将隐藏在breadcrumb中(默认为true)
activeMenu: '/example/list' 如果设置路径,侧栏将突出显示您设置的路径
}
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
* 没有权限要求的基本页
* 所有角色都可以访问
* 不需要动态判断权限的路由
*/
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
}
]
},
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/auth-redirect',
component: () => import('@/views/login/auth-redirect'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/error-page/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error-page/401'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
}
]
},
{
path: '/documentation',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/documentation/index'),
name: 'Documentation',
meta: { title: 'Documentation', icon: 'documentation', affix: true }
}
]
}
...
]
/**
* asyncRoutes
* the routes that need to be dynamically loaded based on user roles
* 异步挂载的路由
* 动态需要根据权限加载的路由表
*/
export const asyncRoutes = [
{
path: '/permission',
component: Layout,
redirect: '/permission/page',
alwaysShow: true, // will always show the root menu
name: 'Permission',
meta: {
title: 'Permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: 'Page Permission',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
meta: {
title: 'Directive Permission'
// if do not set roles, means: this page does not require permission
}
},
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: 'Role Permission',
roles: ['admin']
}
}
]
},
...
// 404页面必须放在最后
{ path: '*', redirect: '/404', hidden: true }
]
//创建路由
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// 重置路由
//参考网址: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
/***
*
* constantRoutes 和 asyncRoutes这2个是routes中分两块路由配置,一块是固定的,无权限的路由配置,也就是不管是管理员身份,还是超级管理员身份,都会显示的路由配置。
第二块是,带权限的路由配置,根据用户权限来显示侧边栏。注意,带权限的配置里的meta中都有role项,代表是权限
*/
参考材料
二、业务操作
1.主菜单的字体图标更改
首先可以去阿里下载svg图片,放在src/icons/svg的文件里,然后在路由菜单中icon设置svg的名称(icon: 'reconciliation')
2.基于vue-admin-template-master框架开发 :权限控制
一般说来,权限管理可以分为两种情况:第一种为页面级访问权限(菜单权限),第二种为数据级操作权限(按钮权限)。第一种情况是非常常见的,即用户是否能够看到页面;第二种情况是用户能否对数据进行增删改查等操作。
前端的权限控制实质上就是用于展示,让操作变得更加友好,真正的安全实际上是由后端控制的。
登录拦截,动态路由
A.router 路由配置: constantRoutes 和 asyncRoutes设置
//公共路由
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
...
{
path: 'external-link',
component: Layout,
children: [
{
path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
meta: { title: 'External Link', icon: 'link' }
}
]
},
]
//动态路由
export const asyncRoutes = [
{
path: '/permission',
component: Layout,
redirect: '/permission/page',
alwaysShow: true, // will always show the root menu
name: 'Permission',
meta: {
title: '权限测试页',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: '角色权限',
roles: ['admin']
}
},
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: '页面权限',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
meta: {
title: '指令权限'
// if do not set roles, means: this page does not require permission
}
}
]
},
// 404 一定要放最后面
{ path: '*', redirect: '/404', hidden: true }
]
B.permission.js 访问 获取动态的路由
在store请求接口 获取路由数据 :store/modules/permission.js
import { asyncRoutes, constantRoutes } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
/*
roles是用户所带的权限
这个方法就是把动态路由配置里符合用户权限的配置筛选出来,组成一个数组,然后,和固定路由合并到了一起,存到了vuex中
*/
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)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
把值存在state里面
store/index.js
import permission from './modules/permission'
const store = new Vuex.Store({
modules: {
permission
},
getters
})
store/getters.js
const getters = {
roles: state => state.user.roles,//角色
permission_routes: state => state.permission.routes,//动态路由
}
export default getters
C.渲染页面components /sidebar /index.vue
...mapGetters(["permission_routes"]),
(1).页面权限(菜单权限):通过roles: ['admin']这个来区别不同角色访问不同页面,
(2).按钮控制权限: 通过permission: ['add', 'edit']这个来判断不同角色可以操作不同的按钮
在路由里面配置
export const asyncRoutes = [
{
path: '/btnPermission',
component: Layout,
children: [
{
path: 'index',
name: 'btnPermission',
component: () => import('@/views/btnPermission/index'),
meta: { title: '按钮控制页面显示', icon: 'form', permission: ['add', 'edit'] }
}
]
},
]
directive/hasPermission/index.js封装一个指令的函数
const hasPermissionBtn = {
inserted(el, binding, vnode) {
const { value } = binding;
let permissionList = vnode.context.$route.meta.permission;
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value;
const hasPermission = permissionList.some(role => {
return permissionRoles.includes(role);
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el);
}
}
}
}
const install = function (Vue) {
Vue.directive('hasPermissionBtn', hasPermissionBtn)
}
if (window.Vue) {
window['hasPermissionBtn'] = hasPermissionBtn
Vue.use(install); // eslint-disable-line
}
hasPermissionBtn.install = install;
export default hasPermissionBtn
views\btnPermission/index.vue:页面操作
新增
删除
修改
3.省市区 选择
git逛网 案例
安装:npm install element-china-area-data -S
绑定值:{{selectedOptions2}}
区域码转汉字:{{CodeToText[selectedOptions2[0]]}},{{CodeToText[selectedOptions2[1]]}},{{CodeToText[selectedOptions2[2]]}}
汉字转区域码:{{convertTextToCode(CodeToText[selectedOptions2[0]], CodeToText[selectedOptions2[1]], CodeToText[selectedOptions2[2]])}}
三、问题
1.element-ui的el-form验证和el-dialog关闭时清除验证
使用清除验证this.$refs.formName.clearValidate();