Vue+Typescript+iview实现用户权限登录

当我们点击登录按钮时,发生了什么

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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值