美文网首页
用户,角色,权限,路由拦截,使用vuex 保存状态

用户,角色,权限,路由拦截,使用vuex 保存状态

作者: 冰落寞成 | 来源:发表于2021-07-02 11:25 被阅读0次

需求分析

系统需要权限管理不同的功能模块,采用的是是RBAC设计模式,权限划分主要分为4种情况

1、登录之前:模块1 -》不需要登录校验,没有权限校验;
2、需要登录之后才能看到:
  2-1,模块2 -》需要登录校验,有权限校验;
  2-2, 模块3 -》需要登录校验,没有权限校验;
3,某些模块的按钮需要权限校验。

RBAC用户角色权限设计

RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。


image.png

实现过程

一、实现思路
1、权限模块设计:
权限的增删改,主要字段有权限名称(pname),权限值(pval),权限分组(pgroup)
2, 角色模块设计:
角色增删改,绑定权限,主要字段角色名称,角色绑定的权限,备注
3、用户模块设计:
用户模块除了可以 增删改查等基本功能外,需要绑定自定义的角色,主要字段,用户信息,绑定的角色,相关联的权限
4,路由设计:
本次路由设计未采用动态路由,采用的是在路由表里的meta 对象里建一个pval 字段和权限表里的pval 字段保持一致,经过路由前置钩子过滤,进行控制的
5,侧边导航设计:
由于权限的划分,导航是动态改变的,所以添加了一个静态路由表来过滤有权限要求的导航
6,登录获设计
设计思路是:登录后,后端只返回登录校验的token,前端将token存放在localstora 里面,使用token去获取用户的权限,数据保存在vuex里面
二、路由表
1,路由表单如下:router.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import TheMain from '@/layout/main/TheMain'
import TheManage from '@/layout/manage/TheManageMain'

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
  return originalPush.call(this, location).catch(err => err)
}
const originalReplace = VueRouter.prototype.replace
VueRouter.prototype.replace = function replace (location) {
  return originalReplace.call(this, location).catch(err => err)
}
Vue.use(VueRouter)
/**
 * isCheck -> false 不参与权限校验的路由,true 参与权限校验的路由
 * pval -> 权限标识,
 * isLoginCheck -> false ,页面接口不需要登录就能看,true,页面接口需要登录后才能看
 */
const routes = [
  {
    path: '/',
    redirect: '/verification'
  },
  {
    path: '/login',
    component: () => import('@/views/login'),
    name: 'login',
    meta: {
      pval: 'login',
      isCheck: false,
      isLoginCheck: false
    }
  },
  {
    path: '/error',
    component: () => import('@/views/error'),
    name: 'error',
    meta: {
      pval: 'error',
      isCheck: false,
      isLoginCheck: false
    }
  },
  {
    path: '/verification',
    component: () => import('@/views/verification'),
    name: 'verification',
    meta: {
      pval: 'verification',
      isCheck: false,
      isLoginCheck: false
    }
  },
  {
    path: '/home',
    component: () => import('@/pages/home'),
    name: 'home',
    meta: {
      pval: 'home',
      isCheck: false,
      isLoginCheck: true
    }
  },
  {
    path: '/',
    name: 'TheMain',
    component: TheMain,
    children: [
      {
        path: '/report',
        component: () => import('@/pages/report'),
        name: 'report',
        meta: {
          pval: 'report',
          isCheck: true,
          isLoginCheck: true
        }
      },
      {
        path: '/sale',
        component: () => import('@/pages/saleManage'),
        name: 'sale',
        meta: {
          pval: 'sale',
          isCheck: true,
          isLoginCheck: true
        }
      },
      {
        path: '/contract',
        component: () => import('@/pages/contractManage'),
        name: 'contract',
        meta: {
          pval: 'contract',
          isCheck: true,
          isLoginCheck: true
        }
      },
      {
        path: '/finance',
        component: () => import('@/pages/finance'),
        name: 'finance',
        meta: {
          pval: 'finance',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/customer',
        component: () => import('@/pages/customerManage'),
        name: 'customer',
        meta: {
          pval: 'customer',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/factory',
        component: () => import('@/pages/factory'),
        name: 'factory',
        meta: {
          pval: 'factory',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/goods',
        component: () => import('@/pages/goods'),
        name: 'goods',
        meta: {
          pval: 'goods',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/purchase',
        component: () => import('@/pages/purchaseManage'),
        name: 'purchase',
        meta: {
          pval: 'purchase',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/access',
        component: () => import('@/pages/access'),
        name: 'access',
        meta: {
          pval: 'access',
          isCheck: true,
          isLoginCheck: true
        }
      },
      {
        path: '/weigh',
        component: () => import('@/pages/weigh'),
        name: 'weigh',
        meta: {
          pval: 'weigh',
          isCheck: true,
          isLoginCheck: true
        }
      },
      {
        path: '/weighbridge',
        component: () => import('@/pages/weighbridge'),
        name: 'weighbridge',
        meta: {
          pval: 'weighbridge',
          isCheck: false,
          isLoginCheck: true
        }
      },
      {
        path: '/smallScreen',
        component: () => import('@/pages/smallScreen'),
        name: 'smallScreen',
        meta: {
          pval: 'smallScreen',
          isCheck: false,
          isLoginCheck: true
        }
      },
      {
        path: '/camera',
        component: () => import('@/pages/cameraManage'),
        name: 'camera',
        meta: {
          pval: 'camera',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/video',
        component: () => import('@/pages/video'),
        name: 'video',
        meta: {
          pval: 'video',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/analysisArea',
        component: () => import('@/pages/analysisArea'),
        name: 'analysisArea',
        meta: {
          pval: 'analysisArea',
          isCheck: false,
          isLoginCheck: true
        }
      },
      {
        path: '/event',
        component: () => import('@/pages/eventAlarm'),
        name: 'event',
        meta: {
          pval: 'event',
          isCheck: false,
          isLoginCheck: true
        }
      },
      {
        path: '/equipment',
        component: () => import('@/pages/equipmentAlarm'),
        name: 'equipment',
        meta: {
          pval: 'equipment',
          isCheck: false,
          isLoginCheck: true
        }
      },
      {
        path: '/device',
        component: () => import('@/pages/deviceManage'),
        name: 'device',
        meta: {
          pval: 'device',
          isCheck: false,
          isLoginCheck: true
        }

      }, {
        path: '/deviceType',
        component: () => import('@/pages/deviceType'),
        name: 'deviceType',
        meta: {
          pval: 'deviceType',
          isCheck: false,
          isLoginCheck: true
        }

      }, {
        path: '/dispatch',
        component: () => import('@/pages/dispatchManage'),
        name: 'dispatch',
        meta: {
          pval: 'dispatch',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/inventory',
        component: () => import('@/pages/inventory'),
        name: 'inventory',
        meta: {
          pval: 'inventory',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/plc',
        component: () => import('@/pages/plcManage'),
        name: 'plc',
        meta: {
          pval: 'plc',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/carType',
        component: () => import('@/pages/carType'),
        name: 'carType',
        meta: {
          pval: 'carType',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/vobc',
        component: () => import('@/pages/vobcManage'),
        name: 'vobc',
        meta: {
          pval: 'vobc',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/car',
        component: () => import('@/pages/carInfo'),
        name: 'car',
        meta: {
          pval: 'car',
          isCheck: false,
          isLoginCheck: true
        }
      }, {
        path: '/wear',
        component: () => import('@/pages/wearManage'),
        name: 'wear',
        meta: {
          pval: 'wear',
          isCheck: false,
          isLoginCheck: true
        }
      }]
  },
  {
    path: '/manage',
    name: 'TheManage',
    component: TheManage,
    meta: {
      pval: 'manage',
      isCheck: true,
      isLoginCheck: true
    },
    children: [
      {
        path: '/manage/user',
        component: () => import('@/pages/manageGroup/userManage'),
        name: 'user',
        meta: {
          pval: 'user',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/manage/role',
        component: () => import('@/pages/manageGroup/roleManage'),
        name: 'role',
        meta: {
          pval: 'role',
          isCheck: true,
          isLoginCheck: true
        }
      },
      // {
      //   path: '/manage/perm',
      //   component: () => import('@/pages/manageGroup/permManage'),
      //   name: 'perm',
      //   meta: {
      //     pval: 'perm',
      //     isCheck: false,
      //     isLoginCheck: true
      //   }
      // },
      {
        path: '/manage/goodsType',
        component: () => import('@/pages/manageGroup/goodsType'),
        name: 'goodsType',
        meta: {
          pval: 'goodsType',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/manage/customerType',
        component: () => import('@/pages/manageGroup/customerType'),
        name: 'customerType',
        meta: {
          pval: 'customerType',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/manage/cameraType',
        component: () => import('@/pages/manageGroup/cameraType'),
        name: 'cameraType',
        meta: {
          pval: 'cameraType',
          isCheck: true,
          isLoginCheck: true
        }
      }, {
        path: '/manage/logManage',
        component: () => import('@/pages/manageGroup/logManage'),
        name: 'logManage',
        meta: {
          pval: 'logManage',
          isCheck: true,
          isLoginCheck: true
        }
      }
    ]
  }

]

const router = new VueRouter({
  routes
})

export default router

2、静态路由json表,主要用于导航数据过滤

/**
 * pval,isCheck 必须和路由表里的pval,isCheck保持一致
 */
const mainNav = [{
  pname: '报表管理',
  pval: 'report',
  isCheck: true
}, {
  pname: '销售管理',
  pval: 'sale',
  isCheck: true
}, {
  pname: '合同管理',
  pval: 'contract',
  isCheck: true
}, {
  pname: '财务结算',
  pval: 'finance',
  isCheck: true
}, {
  pname: '客户管理',
  pval: 'customer',
  isCheck: true
}, {
  pname: '工厂管理',
  pval: 'factory',
  isCheck: true
}, {
  pname: '货物管理',
  pval: 'goods',
  isCheck: true
}, {
  pname: '进货管理',
  pval: 'purchase',
  isCheck: true
}, {
  pname: '门禁管理',
  pval: 'access',
  isCheck: true
}, {
  pname: '称重管理',
  pval: 'weigh',
  isCheck: true
}, {
  pname: '地磅管理',
  pval: 'weighbridge',
  isCheck: false
}, {
  pname: '小屏管理',
  pval: 'smallScreen',
  isCheck: false
}, {
  pname: '摄像头管理',
  pval: 'camera',
  isCheck: false
}, {
  pname: '区域分析',
  pval: 'analysisArea',
  isCheck: false
}, {
  pname: '视频监控',
  pval: 'video',
  isCheck: false
}, {
  pname: '生产设备管理',
  pval: 'plc',
  isCheck: false
}, {
  pname: '人员穿戴设备',
  pval: 'wear',
  isCheck: false
}, {
  pname: '仓储管理',
  pval: 'storehouse',
  isCheck: false,
  children: [

    {
      pname: '设备入库',
      pval: 'device',
      isCheck: false
    },
    {
      pname: '库存量管理',
      pval: 'inventory',
      isCheck: false
    },
    {
      pname: '出库记录',
      pval: 'dispatch',
      isCheck: false
    },

    {
      pname: '设备类型',
      pval: 'deviceType',
      isCheck: false
    }
  ]
}, {
  pname: '车辆管理',
  pval: 'vehicle',
  isCheck: false,
  children: [
    {
      pname: '车辆类型',
      pval: 'carType',
      isCheck: false
    },
    {
      pname: '车载设备',
      pval: 'vobc',
      isCheck: false
    },
    {
      pname: '车辆信息',
      pval: 'car',
      isCheck: false
    }
  ]
}, {
  pname: '事件报警',
  pval: 'event',
  isCheck: false
}]
const manageNav = [{
  pname: '用户管理',
  pval: 'user',
  isCheck: true
}, {
  pname: '角色管理',
  pval: 'role',
  isCheck: true
},
// {
//   pname: '权限管理',
//   pval: 'perm',
//   isCheck: false
// },
{
  pname: '货物类型管理',
  pval: 'goodsType',
  isCheck: true
}, {
  pname: '客户类型管理',
  pval: 'customerType',
  isCheck: true
}, {
  pname: '摄像头类型管理',
  pval: 'cameraType',
  isCheck: true
}, {
  pname: '日志管理',
  pval: 'logManage',
  isCheck: true
}]
const Nav = {
  mainNav: mainNav,
  manageNav: manageNav
}
export default Nav

3, 路由过滤,使用的是beforeEach

import router from '@/router'
import store from '@/store'
import { loginInterception } from '@/config'
import Nav from './json.js'
/**
 * 跳转判断函数
 * @param {*} perms
 * @param {*} to
 * @param {*} next
 * @returns
 */
let mainNav = Nav.mainNav; let manageNav = Nav.manageNav
let mainNavIndex = mainNav.findIndex(n => (n.isCheck === false))
let manageNavIndex = manageNav.findIndex(n => (n.isCheck === false))

/**
 * 需要路由拦截
 * @param {*} perms
 * @param {*} to
 * @param {*} next
 * @returns
 */
let gotoFilter = (perms, to, next) => {
  if (to.path === '/manage') { // 跳转到后台管理系统的特殊
    let index = perms.findIndex(n => (n.ptype > 0))
    if (index > -1) { // 说明有后台管理系统的权限
      router.push('/manage/' + perms[index].pval)
    } else if (manageNavIndex > -1) { // 说明后台管理系统有不需要校验权限的模块存在
      router.push('/manage/' + manageNav[manageNavIndex].pval)
    } else { // 无权限换账号登录
      store.dispatch('signOut')
      router.push('/error')
    }
  } else { //
    let bool = perms.find((n) => n.pval === to.meta.pval)
    if (bool) { // 当路由在后端返回的权限表里面时
      return next()
    } else { // 当路由 不在后端返回的权限表里时(可能会在静态路由配置表里)
      let path = to.path
      let reg = RegExp(/manage/)
      if (reg.test(path)) { // 跳转到后台管理系统
        if (manageNavIndex > -1) { // 静态路由配置表里存在不需要校验的后台管理模块
          router.push('/manage/' + manageNav[manageNavIndex].pval)
        } else { // 无权限换账号登录
          store.dispatch('signOut')
          router.push('/error')
        }
      } else { // 前台系统
        if (mainNavIndex > -1) { // 静态路由配置表里存在 不需要校验权限的管理系统模块
          router.replace('/' + mainNav[mainNavIndex].pval)
        } else { // 无权限换账号登录
          store.dispatch('signOut')
          router.push('/error')
        }
      }
    }
  }
}
/**
 * 超级管理员或者不开启校验时
 * @param {*} to
 * @param {*} next
 */
let goto = (to, next) => {
  if (to.path === '/manage') {
    router.push('/manage/' + manageNav[0].pval)
  } else {
    next()
  }
}
router.beforeEach(async (to, from, next) => {
  if (window.ipcRenderer) { // exe 环境下
    if (to.name === 'verification') {
      window.ipcRenderer.send('hideMenu')
    } else {
      window.ipcRenderer.send('showMenu')
    }
  }
  if (loginInterception) { // 系统是启路由拦截
    let perms = store.state.perms; let factoryflag = store.state.factoryflag
    if (to.meta.isLoginCheck) { // 有些静态路由,不需要权限校验,但是需要登录 校验,比如 权限管理页
      if (!perms) { // 权限为空时,要重新获取权限
        await store.dispatch('saveUserInfoAsync')
        perms = store.state.perms
        factoryflag = store.state.factoryflag
      }
    }
    if (!to.meta.isCheck) { // 不参与路由拦截的部分路由
      goto(to, next)
    } else {
      if (factoryflag === 2 || factoryflag === 3) { // 系统超级管理员或者工厂超级管理员
        goto(to, next)
      } else { // 不是超级管理员,开启路由拦截
        gotoFilter(perms, to, next)
      }
    }
  } else { // 系统不开启路由拦截
    goto(to, next)
  }
})

二、vuex 设计
vuex 主要存储的是权限,用户信息,工厂id等
1.store 文件夹主要包含的文件如下:


image.png

2.index.js

import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './action'
import getters from './getters'
import state from './state'
Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})

3,state.js


import {
  getStore
} from '@/utils/mUtils.js'
const state = {
  userpic: '', // 当前用户logo
  logopic: '', // 当前工厂的logo
  factoryid: null, // 工厂id
  factoryflag: '', // 1->工厂员工,2->工厂的超级管理员,3->明天科技超级管理员,4-> 明天科技员工
  userId: '', // 当前用户id
  account: '', // 用户账号
  name: '', // 用户真实姓名
  perms: null, // 权限路径
  roles: null, // 权限路径
  token: getStore('token') // 系统token

}
export default state

4,multations-type.js

export const SAVE_USER_ID = 'SAVE_USER_ID'
export const SAVE_PERMS = 'SAVE_PERMS'
export const SAVE_ROLES = 'SAVE_ROLES'
export const SAVE_TOKEN = 'SAVE_TOKEN'
export const SAVE_ACCOUNT = 'SAVE_ACCOUNT'
export const SAVE_NAME = 'SAVE_NAME'
export const REMOVE_TOKEN = 'REMOVE_TOKEN'
export const SAVE_USER_PIC = 'SAVE_USER_PIC'
export const SAVE_LOGO_PIC = 'SAVE_LOGO_PIC'
export const SAVE_FACTORY_ID = 'FACTORY_ID'
export const SAVE_FACTORY_FLAG = 'SAVE_FACTORY_FLAG'

5, multations.js

import {
  SAVE_USER_ID,
  SAVE_PERMS,
  SAVE_ROLES,
  SAVE_TOKEN,
  SAVE_ACCOUNT,
  SAVE_NAME,
  REMOVE_TOKEN,
  SAVE_USER_PIC,
  SAVE_LOGO_PIC,
  SAVE_FACTORY_ID,
  SAVE_FACTORY_FLAG

} from './mutations-type.js'
import { setStore, getStore, removeStore, clearStore } from '@/utils/mUtils.js'
export default {
  [SAVE_USER_ID] (state, userId) {
    state.userId = userId
  },
  [SAVE_PERMS] (state, perms) {
    state.perms = perms
  },
  [SAVE_ROLES] (state, roles) {
    state.roles = roles
  },
  [SAVE_TOKEN] (state, token) {
    state.token = token
    setStore('token', state.token)
  },
  [SAVE_ACCOUNT] (state, account) {
    state.account = account
  },
  [SAVE_NAME] (state, name) {
    state.name = name
  },
  [REMOVE_TOKEN] (state) {
    removeStore('token')
  },
  [SAVE_USER_PIC] (state, userpic) {
    state.userpic = userpic
  },
  [SAVE_LOGO_PIC] (state, logopic) {
    state.logopic = logopic
  },
  [SAVE_FACTORY_ID] (state, factoryid) {
    state.factoryid = factoryid
  },
  [SAVE_FACTORY_FLAG] (state, factoryflag) {
    state.factoryflag = factoryflag
  }
  
}

6, action.js

import { Message } from 'element-ui'
import {
  SAVE_USER_ID,
  SAVE_PERMS,
  SAVE_ROLES,
  SAVE_TOKEN,
  SAVE_ACCOUNT,
  SAVE_NAME,
  REMOVE_TOKEN,
  SAVE_USER_PIC,
  SAVE_LOGO_PIC,
  SAVE_FACTORY_ID,
  SAVE_FACTORY_FLAG
} from './mutations-type.js'
import { getInfo } from '@/api'
export default {
  /**
   * 保存登录信息
   * @param {*} param0 
   * @param {*} info 
   */
  saveToken ({ commit }, info) {
    commit(SAVE_TOKEN, info)
  },
  /**
   * 保存用户头像
   * @param {*} param0 
   * @param {*} info 
   */
  saveUserPic ({ commit }, info) {
    commit(SAVE_USER_PIC, info)
  },
  /**
   * 异步保存用户信息
   * @param {*} param0 
   * @returns 
   */
  async saveUserInfoAsync ({ commit, state }) {
    let res = await getInfo().catch(err => {
      if (err.toString() === 'Error: timeout of 3000ms exceeded') { // 请求超时
        Message({
          message: '请求超时,请重试!',
          type: 'error',
          duration: 1000
        })
      }
      return Promise.reject(err)
    })
    if (res) {
      if (res.code === 200) {
        const { id, account, name, roles, perms, userpic, logopic, factoryid, factoryflag } = res.data
        commit(SAVE_USER_ID, id)
        commit(SAVE_ACCOUNT, account)
        commit(SAVE_NAME, name)
        commit(SAVE_PERMS, perms)
        commit(SAVE_ROLES, roles)
        commit(SAVE_USER_PIC, userpic)
        commit(SAVE_LOGO_PIC, logopic)
        commit(SAVE_FACTORY_ID, factoryid)
        commit(SAVE_FACTORY_FLAG, factoryflag)
        return perms
      } else {
        return Promise.resolve([])
      }
    } else {
      return Promise.resolve([])
    }

  },
  /**
   * 同步保存用户数据
   * @param {*} param0 
   * @param {*} info 
   */
  saveUserInfo ({ commit }, info) {
    commit(SAVE_USER_ID, info.id)
    commit(SAVE_ACCOUNT, info.account)
    commit(SAVE_NAME, info.name)
    commit(SAVE_PERMS, info.perms)
    commit(SAVE_ROLES, info.roles)
    commit(SAVE_USER_PIC, info.userpic)
    commit(SAVE_LOGO_PIC, info.logopic)
    commit(SAVE_FACTORY_ID, info.factoryid)
    commit(SAVE_FACTORY_FLAG, info.factoryflag)
  },
  /**
   * 退出
   * @param {*} param0 
   */
  signOut ({ commit }) {
    commit(SAVE_USER_ID, '')
    commit(SAVE_ACCOUNT, '')
    commit(SAVE_NAME, '')
    commit(SAVE_PERMS, '')
    commit(SAVE_ROLES, '')
    commit(REMOVE_TOKEN)
    commit(SAVE_USER_PIC, '')
    commit(SAVE_LOGO_PIC, '')
    commit(SAVE_FACTORY_ID, '')
    commit(SAVE_FACTORY_FLAG, '')
  }

}

7,getters.js

// import icon from '@/utils/router/icon.js'

export default {
  getMainNavs: (state) => {
    let data = state.perms
    let arr = []
    if (data.length > 0) {
      arr = data.filter(item => {
        return item.ptype === 0
      })
    }

    return arr
  },
  getManageNavs: (state) => {
    let data = state.perms
    let arr = []
    if (data.length > 0) {
      arr = data.filter(item => {
        return item.ptype > 0
      })
    }

    return arr
  }
}

8,本地存储mutils.js


/**
 * 存储localStorage
 */
export const setStore = (name, val) => {
  if (!name) return
  if (typeof val !== 'string') {
    val = JSON.stringify(val)
  }
  window.localStorage.setItem(name, val)
}

/**
 * 获取localStorage
 */
export const getStore = name => {
  if (!name) return

  return window.localStorage.getItem(name)
}

/**
 * 删除localStorage
 */
export const removeStore = name => {
  if (!name) return
  window.localStorage.removeItem(name)
}

/**
 * 清空localStorage
 */
export const clearStore = () => {
  window.localStorage.clear()
}

三、导航模块

<template>
  <el-menu
    class="menu-vertical-aside-nav"
    :default-active="this.$route.path"
    router
  >
    <template v-for="(item, index) in nav">
      <template v-if="item.children">
        <el-submenu :index="`/${item.pval}`" :key="index">
          <template slot="title">{{ item.pname }}</template>
          <template v-for="(citem, cindex) in item.children">
            <el-menu-item :index="`/${citem.pval}`" :key="cindex">{{
              citem.pname
            }}</el-menu-item>
          </template>
        </el-submenu>
      </template>
      <template v-else>
        <el-menu-item :index="`/${item.pval}`" :key="index">
          <span slot="title">{{ item.pname }}</span>
        </el-menu-item>
      </template>
    </template>
  </el-menu>
</template>
<script>
import Nav from '@/utils/router/json.js'
import { mapGetters, mapState } from 'vuex'
import { loginInterception } from '@/config'
export default {
  name: 'mainNav',
  computed: {
    ...mapState(['factoryid', 'factoryflag']),
    ...mapGetters(['getMainNavs'])
  },
  data () {
    return {
      nav: null
    }
  },
  mounted () {
    if (loginInterception) { // 系统开启权限校验
      if (this.factoryflag === 3) { // 系统超级管理员
        this.nav = Nav.mainNav
      } else if (this.factoryflag === 2) { // 工厂超级管理员,要剔除工厂管理模块
        this.nav = Nav.mainNav.filter(item => {
          return item.pval !== 'factory'
        })
      } else if (this.factoryflag === 1) { // 工厂员工
        this.nav = this.getNav(true)
      } else { // 明天科技员工
        this.nav = this.getNav(false)
      }
    } else { // 系统不开启登录校验
      this.nav = Nav.mainNav
    }
  },
  methods: {
    //  过滤
    getNav (bool) {
      let navArr = []
      let navs
      if (bool) { // 工厂员工,要过滤掉工厂管理
        navs = this.getMainNavs.filter(item => {
          return item.pval !== 'factory'
        })
      } else { // 明天科技员工
        navs = this.getMainNavs
      }
      let navRouter = Nav.mainNav
      for (let i in navRouter) {
        if (navRouter[i].isCheck) { // 参与权限校验
          for (let j in navs) {
            if (navRouter[i].pval === navs[j].pval) {
              navArr.push(navRouter[i])
            }
          }
        } else { // 不参与校验的直接写入
          navArr.push(navRouter[i])
        }
      }
      return navArr
    }
  }
}
</script>

四、登录设计
第一次登录强制用户更改密码

<template>
  <div class="login-page">
    <div class="login-content">
      <div class="system-info">
        <div class="system-info_title">智慧矿山综合管控平台</div>
        <div class="system-info_subtitle">全方位覆盖,一站式数据管理</div>
      </div>
      <div class="login-info">
        <div class="login-info_header vue-flex">
          <img :src="logoSrc" class="logoSrc" />
          <div class="login-info_text">登录/Login</div>
        </div>
        <el-form
          class="loginForm"
          :model="loginForm"
          :rules="rules"
          ref="loginForm"
        >
          <el-form-item prop="username">
            <el-input
              class="login-input"
              v-model="loginForm.account"
              placeholder="请输入账号"
              prefix-icon="el-icon-s-custom"
              @keyup.enter.native="loginBtn('loginForm')"
            ></el-input>
          </el-form-item>
          <el-form-item prop="password">
            <el-input
              type="password"
              v-model="loginForm.password"
              autocomplete="off"
              placeholder="请输入密码"
              prefix-icon="el-icon-lock"
              @keyup.enter.native="loginBtn('loginForm')"
            ></el-input>
          </el-form-item>
          <!-- <el-form-item label="确认密码" prop="checkPass">
            <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input>
          </el-form-item> -->
          <el-button
            class="login-btn primaryBtn"
            type="primary"
            @click="loginBtn('loginForm')"
            >登 录</el-button
          >
        </el-form>
      </div>
      <div class="login-pws">
        <el-dialog
          class="login-uploadPws-dialog miw400"
          width="30%"
          :visible.sync="isUpPws"
          :show-close="false"
          :close-on-click-modal="false"
          :close-on-press-escape="false"
        >
          <template v-if="isBeforeUpdate">
            <div slot="title" class="dialog-title-wrap">
              <i class="el-icon-lock"></i>
              <span class="dialog-title">更改密码</span>
            </div>
            <div class="pws-content" v-if="isBeforeUpdate">
              <div class="uplod-info">
                您是第一次登录系统,为了安全请您修改自己的密码!请牢记您的新密码。密码应由本人妥善保管,不得泄露给他人。否则,引起的后果将由您本人承担。
              </div>
              <el-form
                :model="pwsForm"
                status-icon
                :rules="pwsRules"
                ref="pwsForm"
                class="pwsForm"
                label-width="100px"
              >
                <el-form-item label="密码" prop="passwd">
                  <el-input
                    type="password"
                    v-model="pwsForm.passwd"
                    autocomplete="off "
                    placeholder="请输入密码"
                  ></el-input>
                </el-form-item>
                <el-form-item label="确认密码" prop="checkPass">
                  <el-input
                    type="password"
                    v-model="pwsForm.checkPass"
                    autocomplete="off"
                    placeholder="请输入确认密码"
                  ></el-input>
                </el-form-item>
              </el-form>
            </div>
          </template>
          <div class="upload-success vue-flex" v-else>
            <img :src="successIcon" class="success-icon" />
            密码修改成功,请重新登录!
          </div>
          <div slot="footer" class="dialog-footer vue-flex">
            <el-button
              type="primary"
              class="primaryBtn"
              @click="upPwdConfirm"
              v-if="isBeforeUpdate"
              >确认</el-button
            >
            <el-button
              class="primaryBtn"
              type="primary"
              @click="goToLogin"
              v-else
              >重新登录</el-button
            >
          </div>
        </el-dialog>
      </div>
    </div>
  </div>
</template>
<script>

import { mapActions } from 'vuex'
import { login, getInfo, updatePassword } from '@/api'
export default {
  name: '',
  components: {
  },
  data () {
    var validatePass = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请输入密码'))
      } else {
        if (this.pwsForm.checkPass !== '') {
          this.$refs.pwsForm.validateField('checkPass')
        }
        callback()
      }
    }
    var validatePass2 = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请再次输入密码'))
      } else if (value !== this.pwsForm.passwd) {
        callback(new Error('两次输入密码不一致!'))
      } else {
        callback()
      }
    }
    return {
      logoSrc: require('@/assets/login-logo.png'),
      successIcon: require('@/assets/success-icon.png'),
      isUpPws: false,
      loginForm: {
        account: null,
        password: null

      },
      rules: {
        account: [
          { required: true, message: '账号不能为空', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '密码不能为空', trigger: 'blur' }
        ]

      },
      pwsForm: {
        passwd: null,
        checkPass: null
      },
      userId: null,
      pwsRules: {
        passwd: [
          { required: true, message: '密码不能为空', trigger: 'blur' },
          { validator: validatePass, trigger: 'blur' }
        ],
        checkPass: [
          { required: true, message: '请再次输入密码', trigger: 'blur' },
          { validator: validatePass2, trigger: 'blur' }
        ]

      },
      isBeforeUpdate: true, //  true=> 弹框里面,提示:更改密码,false->提示: 更改成功,重新登录
      num: 0,
      isClick: true
    }
  },
  mounted () {
    this.signOut()
  },
  methods: {
    ...mapActions(['signOut', 'saveToken', 'saveUserInfo', 'save']),

    loginBtn () {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          if (this.isClick) {
            console.log('--')
            this.isClick = false
            this.loginHandler()
          }
        } else {
          this.$message({
            type: 'warning',
            message: '格式错误'
          })
          return false
        }
      })
    },
    /**
     * 登录
     */
    loginHandler () {
      let wd = this.$md5(this.loginForm.password)
      login({
        username: this.loginForm.account,
        password: wd
      }).then(res => {
        if (res.code === 200) {
          this.saveToken(res.data.tokenValue)
          getInfo().then(res => {
            if (res.code === 200) {
              if (res.data.loginflag) { // 首次登录
                this.isUpPws = true // 更改密码弹框
                this.isBeforeUpdate = true // 显示更改密码
                this.userId = res.data.id
                this.isClick = true
              } else {
                this.saveUserInfo(res.data)
                this.$message({
                  type: 'success',
                  message: '登录成功!',
                  duration: 1000,
                  onClose: () => {
                    // this.signOut() // 清空store

                    // this.$router.push('/report')
                    this.isClick = true
                    this.$router.push('/home')
                  }
                })
              }
            }
          }).catch(() => {
            this.isClick = true
          })
        } else {
          this.$message({
            type: 'warning',
            message: res.msg
          })
          this.isClick = true
        }
      }).catch(() => {
        this.isClick = true
      })
    },
    /**
     * 修改密码
     */
    upPwdConfirm () {
      this.$refs.pwsForm.validate((valid) => {
        if (valid) {
          updatePassword({
            passwd: this.$md5(this.pwsForm.passwd),
            userId: this.userId
          }).then(res => { // 新增
            if (res.code === 200) {
              this.$message({
                message: '恭喜你,修改成功',
                type: 'success'
              })
              this.isBeforeUpdate = false
            }
          })
        } else {
          return false
        }
      })
    },
    /**
     * 去登录
     */
    goToLogin () {
      this.isUpPws = false
      this.signOut()
    }

  }
}
</script>
<style lang="scss" scoped>
.login-page {
  min-width: 1360px;
  height: 100%;
  background: url("../../assets/bg.png") no-repeat center center;
  background-size: 100% 100%;
  display: flex;
  align-content: center;
  align-items: center;
  justify-content: center;
  justify-items: center;
  .login-content {
    display: flex;
    align-content: center;
    align-items: center;
    .system-info {
      // width: 770px;
      color: #fff;
      margin-right: 160px;
      text-align: right;
      padding: 64px;
      position: relative;
      &::before {
        content: "";
        width: 62px;
        height: 62px;
        right: 0;
        bottom: 0;
        border-right: 1px solid #f3f4f6;
        border-bottom: 1px solid #f3f4f6;
        position: absolute;
      }
      &::after {
        content: "";
        width: 62px;
        height: 62px;
        top: 0;
        left: 0;
        border-left: 1px solid #f3f4f6;
        border-top: 1px solid #f3f4f6;
        position: absolute;
      }
      .system-info_title {
        font-weight: 500;
        color: #ffffff;
        letter-spacing: 24px;
        font-size: 52px;
      }
      .system-info_subtitle {
        margin-top: 65px;
        opacity: 0.98;
        font-size: 40px;
        font-family: Alibaba PuHuiTi, Alibaba PuHuiTi-Regular;
        font-weight: 400;
        color: #fefefe;
        position: relative;
        letter-spacing: 7px;
        &::after {
          content: "";
          width: 150px;
          height: 1px;
          background: #f3f4f6;
          position: absolute;
          left: 0;
          top: 50%;
        }
      }
    }
    .login-info {
      width: 360px;
      height: 360px;
      padding: 20px;
      background: url("../../assets/login-bg.png") no-repeat center center;
      background-size: 100% 100%;
      .logoSrc {
        width: 50px;
        height: 50px;
        border-radius: 50%;
      }
      .login-info_header {
        color: #fff;
        justify-content: flex-start;
        margin: 30px 0;
        .logoSrc {
          margin-right: 10px;
        }
      }
      .login-btn {
        width: 100%;
        padding: 15px 20px;
        font-size: 20px;
      }
    }
  }
  @media screen and (max-width: 1800px) and (min-width: 1500px) {
    .login-content {
      .system-info {
        margin-right: 80px;
        padding: 40px;
        &::before {
          content: "";
          width: 42px;
          height: 42px;
        }
        &::after {
          content: "";
          width: 42px;
          height: 42px;
        }
        .system-info_title {
          letter-spacing: 19px;
          font-size: 45px;
        }
        .system-info_subtitle {
          font-size: 30px;
          letter-spacing: 5px;
          margin-top: 60px;
          &::after {
            content: "";
            width: 120px;
          }
        }
      }
      .login-info {
        width: 340px;
        height: 340px;
        padding: 16px;
      }
    }
  }
  @media screen and (max-width: 1500px) {
    .login-content {
      .system-info {
        margin-right: 50px;
        padding: 30px;
        &::before {
          content: "";
          width: 30px;
          height: 30px;
        }
        &::after {
          content: "";
          width: 30px;
          height: 42px;
        }
        .system-info_title {
          letter-spacing: 16px;
          font-size: 35px;
        }
        .system-info_subtitle {
          font-size: 25px;
          letter-spacing: 5px;
          margin-top: 50px;
          &::after {
            content: "";
            width: 90px;
          }
        }
      }
      .login-info {
        width: 300px;
        height: 300px;
        padding: 14px;
      }
    }
  }
}
.login-uploadPws-dialog {
  color: #fff;
  .dialog-title-wrap {
    color: #fff;
    .dialog-title {
      margin-left: 5px;
    }
  }
  .uplod-info {
    color: #fff;
    line-height: 25px;
    margin-bottom: 20px;
  }
  .upload-success {
    text-align: center;
    font-size: 20px;
    color: #fff;
  }
  .success-icon {
    width: 20px;
    height: 20px;
    margin-right: 10px;
  }
}
</style>

相关文章

  • 用户,角色,权限,路由拦截,使用vuex 保存状态

    需求分析 系统需要权限管理不同的功能模块,采用的是是RBAC设计模式,权限划分主要分为4种情况 1、登录之前:模块...

  • Vue-router 路由拦截

    路由拦截 当某些页面需要权限访问时,可以使用路由拦截对用户权限进行判断。 实现 在自定义路由时添加自定义字段 re...

  • Vue中的导航守卫(路由守卫)

    先看官方文档导航守卫就是进行路由拦截,我们可以通过路由拦截,来判断用户是否登录,该页面用户是否有权限浏览,需要结合...

  • 在Vue 项目中你可能会遇到的问题

    一、项目的登录拦截及用户权限访问控制问题。 一个很常见的需求就是对未登录的用户进行路由拦截和用户的权限访问,如果你...

  • 面试官:你用vue-ele 做后台管理做过哪些功能

    1、路由权限的设计,动态路由表的生成2、路由守卫,登录权限拦截,请求头设置,接口状态码,提示信息封装3、layou...

  • Vue添加动态路由

    需求 后台管理需要根据登陆的用户权限做菜单控制 思路 后台返回用户权限,保存到vuex中,判断权限是否有惨淡导航,...

  • 权限系统的思想

    用户登录,将接口返回token存储,跳转到‘/’,使用路由拦截器,如果token能取值,拼接路由,并把动态路由存放...

  • jeesite 系列之shiro

    通常的web权限设计 将操作对应的url保存到权限表中 建立角色表,把权限分配给对应的角色 将角色分配给对应的用户...

  • Shiro3-基于url拦截-用户认证实现

    环境搭建 数据库 使用mysql数据库,并创建用户表,角色表,权限表,用户角色表,角色权限表 sys_user表:...

  • 权限的理解与Shiro

    权限的理解 权限的话,权限做起来就是5张表;用户表、角色表、用户角色表、权限表、权限角色表。给用户授角色给角色授权...

网友评论

      本文标题:用户,角色,权限,路由拦截,使用vuex 保存状态

      本文链接:https://www.haomeiwen.com/subject/wexadltx.html