当我们点击登录按钮时,发生了什么
1、login.vue 页面
<template>
<div class="login-wrap" :style="{backgroundImage: 'url('+ bg +')', backgroundSize: 'cover' }">
<div class="login-form-wrap">
<div class="title">登录</div>
<div class="from-wrap">
<Form ref="loginForm" :model="loginForm" :rules="ruleValidate">
<FormItem prop="account">
<div class="username-wrap">
<Input
prefix="ios-contact"
placeholder="用户名"
style="width: 100%"
v-model="loginForm.account"
@on-focus="cancelTip()"
/>
</div>
</FormItem>
<FormItem prop="password">
<div class="pass-wrap">
<Input
prefix="md-lock"
type="password"
placeholder="密码"
style="width: 100%"
v-model="loginForm.password"
@on-focus="cancelTip()"
/>
<div class="ivu-form-item-error-tip">{{errTip}}</div>
</div>
</FormItem>
</Form>
<div class="login-btn">
<Button long class="login-btn-active" @click="submitLogin('loginForm')">
<span v-if="!loading">登录</span>
<span v-else>loading</span>
</Button>
</div>
</div>
</div>
</div>
</template>
2、login.ts
import { Component, Vue, Watch } from 'vue-property-decorator'
@Component({})
export default class About extends Vue {
bg: string = require('@/assets/images/loginBG.png')
loginForm: any = {
account: null,
password: ''
}
ruleValidate: any = {
account: [
{ required: true, message: '用户名不能为空', triggle: 'blur' }
],
password: [
{ required: true, message: '密码不能为空', triggle: 'blur' },
]
}
errTip: string| null = null
loading: boolean = false
submitLogin(name: any) {
this.loading = true
let ref = this.$refs[name] as any
ref.validate((valid: any) => {
if (valid) {
const sha256: any = require("js-sha256").sha256;
const loginParams: any = {
account: this.loginForm.account,
password: sha256(this.loginForm.password)
};
localStorage.setItem('account', this.loginForm.account)
this.$store.dispatch('login', loginParams).then(res => {
this.loading = false
this.$router.push({
path: "/um/overview/info"
})
}).catch(error => {
console.log('login-err', this.$store.getters.msg)
this.errTip = this.$store.getters.msg
this.loading = false
})
}
})
}
cancelTip() {
this.errTip = null
}
}
3、vuex中的login方法,获取用户权限方法getInfo,以及登出logout方法
新建store文件夹,store下新建module文件夹
module文件夹下的user.ts
import { MutationTree, ActionTree } from 'vuex'
import router, { resetRouter } from '@/router'
import { login, getInfo, logout } from '@/api/user'
interface UserState {
token?: string | null,
name?: string | null,
permissionList?: any,
queueName?: string,
userId?: number,
msg?: string | null
}
export interface UserInfo {
id?: number | null
account?: string | null
password?: string | null
name?: string | null
areaId?: number | null
roleId?: number | null
}
const state: UserState = {
token: '',
name: '',
permissionList: [],
queueName: '',
userId: 0,
msg: ''
}
// 更改state的值
const mutations: MutationTree<UserState> = {
SET_TOKEN: (state: UserState, token: string) {
state.token = token
localStorage.setItem('token', token)
},
GET_TOKEN: (state: UserState) => {
state.token = localStorage.getItem('token')
},
REMOVE_TOKEN: (state: UserState) => {
state.token = ''
localStorage.removeItem('token')
},
SET_NAME: (state: UserState, name: string) => {
state.name = name
},
SET_ROUTELIST: (state: UserState, routeList: any[]) => {
state.permissionList = routeList.join('')
},
SET_USERID: (state: UserState, userId: number) => {
state.userId = userId
},
SET_MSG: (state: UserState, msg: string) => {
state.msg = msg
}
}
// action
const actions: ActionTree<UserState, any> = {
login({ commit }, userInfo: UserInfo) {
const { account, password } = userInfo
return new Promise((resolve: any, reject: any) => {
login({ account: account, password: password }).then((res: any) => {
let { code, data } = res.data
if ( code === 200 ) {
commit('SET_TOKEN', data.token)
commit('SET_USERID', data.userId)
resolve(data)
} else {
reject(data)
}
}).catch(error => {
commit('SET_MSG', error)
reject(error)
})
})
},
// 获取用户权限信息
getInfo({ commit }, userInfo: UserInfo) {
return new Promise((resolve: any, reject: any) => {
getInfo().then((res: any) => {
let { code, data } = res.data
if (code === 200) {
const { name, permissionList } = data
commit('SET_NAME', name)
commit('SET_ROUTELIST', permissionList)
resolve(data)
} else {
reject(data)
}
}).catch((error: any) => {
reject(error)
})
})
},
// 登出
logout({ commit }, userInfo: UserInfo) {
return new Promise((resolve: any, reject: any) => {
logout().then((res: any) => {
commit('SET_NAME', '')
commit('SET_ROUTELIST', [])
commit('REMOVE_TOKEN')
commit('SET_USERID', -1)
commit('SET_MSG', '')
localStorage.removeItem('token')
resetRouter()
resolve()
}).catch((error: any) => {
reject(error)
})
})
}
}
export default {
state,
mutations,
actions
}
4、在module文件夹下新建一个userRouters.ts,用来过滤改用户所需要的权限
import { GetterTree, MutationTree, ActionTree } from 'vuex'
import { asyncRoutes, constantRoutes } from '@/router'
interface RouteInfo {
routers: any,
addRouters: any
}
/**
* 判断用户是否拥有此菜单
* @param menus
* @param route
*/
// 深拷贝方法
function deepCopy(obj: any) { // 只拷贝对象
if (typeof obj !== 'object') return;
// 根据obj的类型判断是新建一个数组还是一个对象
let newObj: any = obj instanceof Array ? [] : {};
for (let key in obj) {
// 遍历obj,并且判断是obj的属性才拷贝
if (obj.hasOwnProperty(key)) {
// 判断属性值的类型,如果是对象递归调用深拷贝
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
/**
* 递归过滤异步路由表,返回符合用户菜单权限的路由表
* @param asyncRouterMap
* @param menus
*/
function filterAsyncRouter(asyncRouters: any, routeNames: any) {
let o: any = []
Array.isArray(asyncRouters) && asyncRouters.forEach((router: any) => {
if(router.children){
router.children = filterAsyncRouter(router.children, routeNames)
o.push(router)
} else {
let part = router.path.replace("/",":");
routeNames.forEach((routeName: string) => {
routeName === part && (o.push(router))
})
}
})
return o
}
const state: RouteInfo = {
routers: constantRoutes, // 本用户所有的路由,包括了固定的路由和下面的addRouters
addRouters: [], // 本用户的角色赋予的新增的动态路由
}
const mutations: MutationTree<RouteInfo> = {
SET_ROUTERS: (state: RouteInfo, permissionRoute: any) => {
state.addRouters = permissionRoute;
state.routers = constantRoutes.concat(state.addRouters); // 将固定路由和新增路由进行合并, 成为本用户最终的全部路由信息
}
}
const actions: ActionTree<RouteInfo, any> = {
generateRoutes({ commit }, userPermission: any) {
return new Promise((resolve) => {
let accessedRouters: any;
let isAdmin: any
let menus: any[] = [];
if (userPermission.name === 'admin') {
isAdmin = true
} else {
isAdmin = false
}
if (isAdmin) {
accessedRouters = asyncRoutes;
} else {
menus = menus.concat(userPermission.permissionList)
const tempMap: any = deepCopy(asyncRoutes);
accessedRouters = filterAsyncRouter(tempMap, Array.from(new Set(menus)));
}
// 执行设置路由的方法
commit('SET_ROUTERS', accessedRouters);
resolve(accessedRouters);
});
}
}
export default {
state,
mutations,
actions
}
5、store文件夹下新建getters.ts文件
const getters = {
token: (state: any) => state.user.token,
name: (state: any) => state.user.name,
routelist: (state: any) => state.user.permissionList,
userId: (state: any) => state.user.userId,
msg: (state: any) => state.user.msg
}
export default getters
6、store文件夹下新建index.ts
import Vue from 'vue';
import Vuex from 'vuex';
import getters from './getters'
Vue.use(Vuex);
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./module', true, /\.ts$/)
// 不需要 `import app from './modules/app'`
// 自动识别模块中的所有vuex模块
const modules = modulesFiles.keys().reduce((modules: any, modulePath: any) => {
// set './app.ts' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
export default new Vuex.Store({
getters,
modules
});
7、新建utils文件夹,先新建request.ts文件,封装axios
import axios from 'axios'
import store from '@/store'
import router from '@/router'
// 创建axios实例
const service = axios.create({
baseURL: '/api',
// timeout: 5000, // request timeout
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// request拦截器
service.interceptors.request.use(
(config: any) => {
store.commit('GET_TOKEN')
if (store.state.user.token) {
let Authorization = 'Authorization'
config.headers.common[Authorization] = store.state.user.token
}
return config
},
(error: any) => {
console.log(error) // for debug
return Promise.reject(error)
}
)
// respone拦截器
service.interceptors.response.use(
(response: any) => {
const res: any = response.data
if (response.status !== 200) {
console.log(response.status)
} else {
if (res.code === 20011 || res.data === 500) {
store.dispatch('logout').then(() => {
location.reload();
});
router.push({
path: '/login'
})
} else {
return response
}
}
},
error => {
return Promise.reject(error)
}
)
export default service
8、在utils文件夹下新建permission.ts,用来做路由的判断跳转等
import router from '@/router/index'
import store from '../store'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'; // Progress 进度条样式
import { Route } from 'vue-router';
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList: string[] = ['/login'] // no redirect 的白名单
const permission = async() => {
router.beforeEach(async(to: Route, from: Route, next: any) => {
NProgress.start()
// 确定用户是否登录
store.commit('GET_TOKEN')
const hasToken = store.state.user.token
// 是否有token
if (hasToken) {
// 如果有token并且要进登录页面,直接定位到首页
if (to.path === '/login') {
next('/um/overview/info')
NProgress.done()
} else {
//确定用户是否已通过getInfo获得其权限角色
const hasRoles:boolean = store.getters.name && store.getters.name.length > 0
if (hasRoles) {
next()
} else {
try {
// 获取用户信息
const data = await store.dispatch('getInfo')
// 生成动态路由
const accessRoutes: any[] = await store.dispatch('generateRoutes', data)
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.commit('REMOVE_TOKEN')
next(`/login`)
NProgress.done()
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
// 白名单内 直接进入
next()
} else {
// 没有访问权限的其他页将重定向到登录页。
next(`/login`)
NProgress.done()
}
}
// 判断是否需要权限验证
// if (to.meta.requireAuth) {
// }
})
router.afterEach(() => {
NProgress.done()
})
}
export {
permission
}
9、定义的router如下:
新建router文件夹,在此文件夹下新建router.ts
/**
* meta 可配置参数
* @param {boolean} icon 页面icon
* @param {boolean} keepAlive 是否缓存页面
* @param {string} title 页面标题
*/
import mainPage from '@/views/um/ummain/mainPage/mainPage.vue'
export const constantRoutes: any[] = [
{
path: '/login',
name: 'login',
component: (resolve: any) => require(['@/views/um/login/login.vue'], resolve)
},
{
path: "/",
redirect: "/login"
}
]
export const asyncRoutes: any[] = [
{
path: '/um',
name: 'mainPage',
component: mainPage,
redirect: '/um/overview',
children: [
{
path: 'overview/info',
name: '总览',
component: (resolve: any) => require(["@/views/um/overview/mainlyPage/mainlyPage.vue"], resolve),
meta: {
requireAuth: true
}
},
{
path: 'maintainRecord/add',
name: '添加维保记录',
component: (resolve: any) => require(["@/views/um/details/maintenanceRecord/maintenanceRecord.vue"], resolve),
meta: {
requireAuth: true
}
},
{
path: 'maintainRecord/update',
name: '修改维保记录',
component: (resolve: any) => require(["@/views/um/details/maintenanceRecord/maintenanceRecord.vue"], resolve),
meta: {
requireAuth: true
}
},
{
path: 'maintainRecord/info',
name: '详情维保记录',
component: (resolve: any) => require(["@/views/um/details/maintenanceRecord/maintenanceRecord.vue"], resolve),
meta: {
requireAuth: true
}
},
{
path: 'maintainRecord/list',
name: '维保管理',
component: (resolve: any) => require(["@/views/um/details/maintenanceManage/maintenanceManage.vue"], resolve),
meta: {
requireAuth: true
}
},
{
path: 'video',
name: '视频管理',
component: (resolve: any) => require(["@/views/um/details/videoManageAll/videoManageAll.vue"], resolve),
meta: {
requireAuth: true
}
},
{
path: 'equipment/list',
name: '台账查询',
component: (resolve: any) => require(["@/views/um/details/equipmentManage/equipmentManage.vue"], resolve),
meta: {
requireAuth: true
}
}
]
]
10、在router文件夹下新建index.ts
import Vue from 'vue'
import Router from 'vue-router'
import { constantRoutes, asyncRoutes } from './router'
Vue.use(Router)
const createRouter = () =>
new Router({
scrollBehavior: () => {
y: 0
},
routes: constantRoutes,
mode: 'history'
})
const router: any = createRouter()
export function resetRouter() {
const newRouter: any = createRouter()
router.matcher = newRouter.matcher // 重置 router
}
const originalPush = Router.prototype.push
export { constantRoutes, asyncRoutes }
export default router