<template> <div> <!-- 路由 --> <div> <router-link to="https://www.jb51.net/"> <svg-icon icon-class="wx"></svg-icon> <!-- <svg> <use xlink:href="#icon-wx" ></use> </svg>--> Home </router-link>| <router-link to="/about"> <svg-icon icon-class="hg"></svg-icon>About </router-link> </div> <!-- 4.路由视图 --> <!-- 问题:router-link和router-view是哪来的 --> <router-view></router-view> </div> </template> <script> export default { name: "app", components: {} }; </script> <style> #app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
2.2 准备页面
src/views/About.vue
<template> <div> <h1>This is an about page</h1> </div> </template>
src/views/Home.vue
<template> <div> <img alt="Vue logo" src="https://www.jb51.net/assets/logo.png" /> <HelloWorld msg="Welcome to Your Vue.js App" /> </div> </template> <script> // @ is an alias to /src import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "home", components: { HelloWorld } }; </script>
src/views/Login.vue
<template> <div> <h2>用户登录</h2> <div> <input type="text" v-model="username" /> <button @click="login">登录</button> </div> </div> </template> <script> export default { data() { return { username: "admin" }; }, methods: { login() { this.$store .dispatch("user/login", { username: this.username }) .then(() => { this.$router.push({ path: this.$route.query.redirect || "https://www.jb51.net/" }); }) .catch(error => { alert(error); }); } } }; </script>
2.3 身份认证
import router from "./router"; import store from "./store"; const whiteList = ["/home", "/login"]; // 无需令牌白名单 // 全局路由守卫 router.beforeEach(async (to, from, next) => { // 获取令牌判断用户是否登录 const hasToken = localStorage.getItem("token"); // 已登录 if (hasToken) { if (to.path === "/login") { // 若已登录没有必要显示登录页,重定向至首页 next({ path: "https://www.jb51.net/" }); } else { // 去其他路由,暂时放过 // next() // 接下来执行用户角色逻辑, todo // 1.判断用户是否拥有角色 const hasRoles = store.state.user.roles && store.state.user.roles.length > 0; if (hasRoles) { next(); } else { // 2.获取用户角色 const roles = await store.dispatch("user/getInfo"); const accessRoutes = await store.dispatch("permission/generateRoutes", roles); // 动态添加路由到路由器 router.addRoutes(accessRoutes); // 跳转 next({ ...to }); } } } else { // 未登录 if (whiteList.indexOf(to.path) !== -1) { // 白名单中路由放过 next(); } else { // 重定向至登录页 next(`/login?redirect=${to.path}`); } } });
2.4 用户信息设置
import Vue from "vue"; import Vuex from "vuex"; import user from './modules/user' import permission from './modules/permission' Vue.use(Vuex); export default new Vuex.Store({ modules: { user, permission } });
src/store/modules/user.js
const state = { token: localStorage.getItem("token"), // 其他用户信息 roles: [] }; const mutations = { SET_TOKEN: (state, token) => { state.token = token; }, SET_ROLES: (state, roles) => { state.roles = roles; }, }; const actions = { // 模拟用户登录 login({ commit }, userInfo) { const { username } = userInfo; return new Promise((resolve, reject) => { setTimeout(() => { if (username === "admin" || username === "jerry") { commit("SET_TOKEN", username); localStorage.setItem("token", username); resolve(); } else { reject("用户名、密码错误"); } }, 1000); }); }, getInfo({ commit, state }) { return new Promise((resolve) => { setTimeout(() => { const roles = state.token === 'admin' ? ['admin'] : ['editor'] commit('SET_ROLES', roles) resolve(roles) }, 1000); }) } }; export default { namespaced: true, state, mutations, actions, };
2.5 用户路由权限 src/store/modules/permission.js
// 导入asyncRoutes,过滤它看当前用户是否拥有响应权限 import {asyncRoutes, constRoutes} from '@/router' const state = { routes: [], // 完整路由 addRoutes: [], // 权限路由 } const mutations = { // routes: 用户可访问的权限路由 SET_ROUTES: (state, routes) => { state.addRoutes = routes; state.routes = constRoutes.concat(routes); } } const actions = { generateRoutes({commit}, roles) { // 过滤出能访问的路由表 const routes = filterAsyncRoutes(asyncRoutes, roles) commit('SET_ROUTES', routes) return routes; } } function filterAsyncRoutes(routes, roles) { const res = []; routes.forEach(route => { // 复制一份路由 const tmp = {...route}; // 拥有访问权限 if (hasPermission(roles, tmp)) { if (tmp.children) { // 递归子路由 tmp.children = filterAsyncRoutes(tmp.children, roles) } res.push(tmp); } }) return res; } function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { // 路由定义中没有roles选项,则不需要权限即可访问 return true; } } export default { namespaced: true, state, mutations, actions }
2.6 最终效果图