• Vue 新手学习笔记:vue-element-admin 之登陆及目录权限控制


    登陆
    万事开头难,做什么事都要有个起点,后面才能更好的进行下去,因此我选择的起点就是最为直观的登陆页面 /login/index.vue

    /src/views/login/index
    去除那些无关的东西,比如什么 rules 校验啊,默认的账号密码之类的东西,直接看核心登陆方法 handleLogin

    handleLogin() {
    this.$refs.loginForm.validate(valid => {
    if (valid) {
    this.loading = true
    # 请求 store 中的方法
    this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
    this.loading = false
    this.$router.push({ path: this.redirect || '/' })
    }).catch((errorMsg) => {
    this.loading = false
    this.$message({
    type: 'error',
    message: errorMsg
    })
    })
    } else {
    console.log('error submit!!')
    return false
    }
    })
    },
    store 主要事做一些缓存之类的处理,这里调用了 store 中的 LoginByUsername 把表单内容传过去,这里我就改了点 catch 把我自己需要的错误信息进行提示,其他东西不需要改动

    /src/store/modules/user
    上面登陆中请求的 LoginByUsername 正是在这

    // 用户名登录
    LoginByUsername({ commit }, userInfo) {
    const username = userInfo.username.trim()
    return new Promise((resolve, reject) => {
    // 请求后台登陆
    loginByUsername(username, userInfo.password).then(response => {
    const data = response.data
    if (response.data.errorCode !== 200) {
    // 登陆失败,回传提示信息
    reject(data.errorMsg)
    }
    // 设置 token,作为用户已登陆的前端标识,存在 cookie 中
    commit('SET_TOKEN', data.retData)
    setToken(data.retData)
    resolve()
    }).catch(error => {
    reject(error)
    })
    })
    },
    setToken() 方法会把 token 保存到 cookie 里,很重要

    下面有个 GetUserInfo 方法,在你登陆的时候会去获取你的权限数据

    // 获取用户信息
    GetUserInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
    // 请求获取权限
    getUserInfo(state.token).then(response => {
    if (response.status !== 200) { // 由于mockjs 不支持自定义状态码只能这样hack
    reject('error')
    }
    const data = response.data

    if (data.retData.module && data.retData.module.length > 0) { // 验证返回的roles是否是一个非空数组
    commit('SET_ROLES', data.retData.module)
    } else {
    // 当用户无任何权限时设置
    commit('SET_ROLES', ['普通用户'])
    }

    commit('SET_NAME', data.retData.username)
    commit('SET_AVATAR', 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif')
    commit('SET_INTRODUCTION', data.retData.username)
    resolve(response)
    }).catch(error => {
    reject(error)
    })
    })
    },
    不需要知道那个 new Promise 啥的干啥用,反正我不知道,只要知道 getUserInfo 这个方法就行了,这个方法会以上面之前保存的 token 为参数去请求获取你的用户权限,原逻辑是没有权限就跳登陆页面,我这系统需要,没权限也有个首页可看,所以
    SET_ROLES 参数给了个“普通用户”,反正什么值无所谓有值,没权限就行。
    下面 commit 里的参数,分别为,用户名,头像,介绍,保留就好,也可自定义。
    PS:在每次页面跳转时候页面都会走一个 GetUserInfo ,以此来判断用户有没有失效,因此,我的做法是直接把用户信息包括权限保存在 redis 里取,不要每次都从库里查

    /src/api/login
    接口的请求都会从这里去请求后台,就不多赘述,基本只需要改动下请求路径就ok了。
    登出 logout 也是,请求保证后台注销,前端处理部分也是在 store/modules/user 里的 LogOut 方法,基本不需要改动。

    权限
    大多数系统都有根据用户权限,或者说角色,展示相应页面的要求

    /src/router/index
    结合框架来实现目录的控制,如果是传统项目,一般是把目录结构整个存在后台数据库里,然后前端循环展示出来,但是这个在这里不需要这么麻烦,只需要把每个页面对应的权限保存起来就行了

    目录在分为两个部分 constantRouterMap 与 asyncRouterMap
    constantRouterMap:主要是通用部分,每个用户都有的页面
    asyncRouterMap:需要进行权限过滤的页面

    所以像首页这种都丢在 constantRouterMap 里,而像权限管理页面这种放在 asyncRouterMap 里
    在目录上加上 permission 标识,这个是我自定义的唯一标识,用于区分每个页面的权限,我这里直接以页面的路径为值,因为路径肯定唯一

    {
    path: '',
    component: Layout,
    alwaysShow: true,
    redirect: '/xxx/xxx',
    name: 'xxx',
    meta: {
    title: 'xxx',
    icon: 'xxx'
    },
    children: [
    {
    path: '/xxx/xxx',
    component: () => import('@/views/xxx/xxx'),
    name: 'xxx',
    meta: {
    title: 'xxx',
    icon: 'message',
    # 权限标识,每个目录唯一
    permission: '/xxx/xxx',
    noCache: false }
    }
    ]
    },
    然后就只需要在加载目录时对目录进行判断就可以了

    /src/permission
    这个文件就是整个路由逻辑在这

    从这里的逻辑可以看到,登陆后,现在判断了用户权限,如果没权限就会进入之前说到的 GetUserInfo 方法去获取权限,由于要对目录进行控制,所以在 GetUserInfo 里我们也需要获取到目录的权限列表,只需要获取到有的就行了,没有权限的目录就不需要获取。
    在 GetUserInfo 的最后通过 resolve 方法把返回值返回这个页面,截图中  module 就是我获取到的有权限的目录列表,然后通过
    GenerateRoutes 来生成要加载的目录,接下来就是改这了

    /src/store/modules/permission
    找到 GenerateRoutes

    const permission = {
    state: {
    routers: constantRouterMap,
    addRouters: []
    },
    mutations: {
    SET_ROUTERS: (state, routers) => {
    state.addRouters = routers
    state.routers = constantRouterMap.concat(routers)
    }
    },
    actions: {
    GenerateRoutes({ commit }, data) {
    return new Promise(resolve => {
    const { roles } = data
    // 权限对列表进行过滤
    const accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
    commit('SET_ROUTERS', accessedRouters)
    resolve(http://www.my516.com)
    })
    }
    }
    }
    对于通用目录我们不需要管,在原逻辑中,这里通过判断如果用户是管理员就直接把整个权限目录都加载,如果不是再进行过滤,因为我们这完全就通过后台控制,包括管理员所以就把判断去掉了,直接对目录进行过滤

    找到 filterAsyncRouter 方法

    /**
    * 递归过滤异步路由表,返回符合用户角色权限的路由表
    * @param routes asyncRouterMap
    * @param roles
    */
    function filterAsyncRouter(routes, roles) {
    const res = []
    routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
    // 是否存在子节点,存在子节点说明当前节点为展开栏,并非页面
    if (tmp.children) {
    tmp.children = filterAsyncRouter(tmp.children, roles)
    // forCheck() 方法递归判断该节点下是否存在页面,不存在则隐藏
    // true:不存在,false:存在
    tmp.hidden = forCheck(tmp.children)
    }
    res.push(tmp)
    }
    })

    return res
    }

    /**
    * 递归子节点,判断是否存在展示页面,存在返回 false,不存在返回 true
    * @param routes
    */
    function forCheck(routes) {
    // 设置默认为隐藏
    let isHidden = true
    // 判断是否存页面,不存在说明该节点下不存在页面
    if (routes.length > 0) {
    // 循环子目录,如果子目录中不存在需要权限页面
    // 说明子页面全是展开栏,隐藏
    for (const route of routes) {
    // 存在 permission 说明为页面,不存在说明为展开栏,将子页面列表继续递归
    if (route.meta && route.meta.permission) {
    isHidden = false
    return
    } else {
    isHidden = forCheck(route.children)
    }
    }
    }
    return isHidden
    }
    通过循环权限目录通过 hasPermission 方法进行判断
    forCheck 是我自己封装的方法,因为项目中不只存在二级目录,所以,通过递归的方式,判断子节点下是否有展示页,也就是带 permission 的页面,如果有,返回 false,没有返回 true

    /**
    * 通过meta.role判断是否与当前用户权限匹配
    * @param roles
    * @param route
    */
    function hasPermission(roles, route) {
    if (route.meta && route.meta.permission) {
    return roles.some(role => route.meta.permission.includes(role.permission))
    } else {
    return true
    }
    }
    hasPermission 方法,这里,我们之前加的 permission 参数就起到作用了
    如果有 permission 就进行判断, roles 参数就是 /src/permission 里传过来的 module 目录。
    通过 roles.some 循环 roles 别名 role,如果目录里 includes(包含)这个目录权限,就返回true,否则 false,为 true 的会被显示,当然如果直接没有 permission 就说明不需要后台管控,就直接 true,这样目录就被加载出来了。
    ---------------------

  • 相关阅读:
    __doPostBack的使用
    【转】function,new,constructor and prototye
    谈谈一些网页游戏失败的原因到底有哪些?(转)
    全面剖析页游巨头发家史(转)
    2013.02.20开通博客
    老子喜欢的女人
    如何成为强大的程序员?(转)
    注重健康
    学习方法总结
    数据库知识点滴积累
  • 原文地址:https://www.cnblogs.com/hyhy904/p/11106077.html
Copyright © 2020-2023  润新知