技术栈:vue2、vue-router、vuex、webpack、git UI组件库:ant-design-vue 项目模板:ant-design-pro-vue

一、用户登录

1、登录步骤

  1. 用户登录验证,将密码通过md5加密
  2. 请求Login验证用户,成功后,返回用户的token
  3. 将用户token存到vuex中,并存到localstorage中
  4. 跳转路由,通过路由守卫去获取用户信息和拥有权限(权限、菜单、用户信息)
    • 若没有则请求action中的GetAuth获取用户相关信息
    • 有则通过userInfo.menu动态生成菜单和权限
  5. 通过addRoutes动态挂载到 router上
  6. 跳转路由,重定向到对应地址中

    这里可以看出把 登录获取用户信息 分成了两个接口,主要目的在于当用户刷新页面时,可以根据登录时获取到的身份令牌(cookie/token)等,去获取用户信息,从而避免刷新需要调用登录接口

2、动态路由

:::info 💡前端路由方案:后端提供权限和路由信息结构接口,前端通过返回的权限动态生成权限和菜单 ::: 步骤如下:

  1. 判断是否有token,没有则跳转到登录页面
  2. 获取用户信息和拥有权限store.dispatch('GetAuth', { token })
  3. 用户信息获取成功后,调用generatorMenu动态生成菜单路由表
    • 调用listToTree将用户菜单转为树形结构
    • 调用generator格式化树形结构数据生成vue-router层级路由表
  4. 将构建的路由结构信息利用vue-router提供的动态增加路由方法router.addRoutes加入到路由表中
  5. 加入路由表后将页面跳转到用户原始要访问的页面,如果没有redirect则进入默认页面(/dashboard/workplace)

    3、按钮权限控制

    步骤如下:

  6. 获取完用户信息和拥有权限后,遍历用户权限

  7. 根据每个菜单的permission中的actionEntitySet属性获取该菜单下用户拥有的按钮权限
  8. 将每个菜单的权限放到role.permission中,并将role存到vuex中
    • role.permission.actionList包含该用户在该菜单所拥有的权限
    • role.permissionList则包含用户所拥有的菜单
  9. 通过自定义指令v-action来控制用户的按钮权限

    二、角色管理:Action权限指令

    指令用法:
    在需要控制action级别权限的组件上使用v-action:[method], 如下:
    • 添加用户
    • 删除用户
    • 修改

当前用户没有权限时,组件上使用了该指令则会被隐藏

实现原理:

  1. 通过Vue.directive()自定义指令
  2. 使用inserted钩子函数,在被绑定元素插入父节点时进行操作
  3. 通过vuex获取roles.permission判断是否拥有任意按钮权限
    • 若无任何权限,则卸载按钮
    • 若拥有按钮权限,则根据菜单名称进行匹配,找到当前菜单下的按钮权限
  4. 根据binding.arg的值进行匹配,判断是否有该按钮的权限
    • 有则显示,没有则隐藏或卸载

      三、角色管理:分配权限树

      :::info 💡功能介绍:通过树形结构展示角色的权限资源包括菜单、按钮,可对角色权限进行操控(用树形控件来做) :::

      🌻业务需求:选中父节点,子节点全部选中;选中任意一个子节点,其父节点要被选中,如果是取消按钮节点,则不影响父菜单节点,若取消父菜单节点则取消所有子节点

😍实现效果:
image.png

💪具体实现原理::::tips

  1. 使用树形控件来做,并完全操控每个节点
  2. 获取所有权限资源树
  3. 获取角色拥有的权限 -> 递归生成角色权限树
  4. 初始化角色权限树
  • 递归判断父节点类型,并判断子节点是否全被选中,赋予不同的选中状态(全选/半选)
  1. 默认展开角色拥有的权限
  2. 编写两个函数用于查找资源
  • findNode 查找权限资源树中符合的节点(栈)
  • getChildrenIds 查找节点的所有子节点Id(递归)
  1. 当管理员选中某一个子节点时,所有相关联的父节点(根据parentId循环查找父节点)都要被选中(按情况赋予不同状态 全选/半选);当选中父节点时,子节点全部选中
  2. 当管理员取消按钮类型的节点时,不影响父菜单节点(全选时要变为半选);若取消菜单类型节点时,子按钮节点应全部取消,当然取消时所有相关联的父节点要判断此时的状态,如果子节点都是按钮则变为半选,若是菜单则父节点得判断是取消还是半选 :::

四、项目优化

  1. UI组件库按需引入
  2. 路由懒加载
  3. 图片懒加载 v-lazyload
  4. 尽量选择提供ES模块格式的依赖,因为对tree-shaking和代码分割更友好

    1、包体积与Tree-shaking优化

    尽量减少打包后的文件体积,可以提高页面加载速度;tree-shaking可以用来剔除不必要的代码或者没有用到的组件,减少打包后的文件体积

    2、代码分割

    通过webpack等构建工具将打包后的JavaScript包进行分包,可以按需或并行加载的文件,通过代码分割,页面加载时需要的功能就加载,额外的功能在需要时才加载,从而提高性能
  • vue-router路由懒加载,使用import()
  • 通过webpack分析ESM动态导入语法来自动进行代码分割
  • 配置webpack.externals属性排除一些第三方库的代码,通过CDN的方式去加载目标数据,减小打包体积