需求分析
系统需要权限管理不同的功能模块,采用的是是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>
网友评论