目录
一.主页模块
01.主页-从登录页跳转主页
目标
登陆页成功之后,跳转到主页
思路
路由跳转: this.$router.push(地址)
async doLogin() {
try {
// 在组件中调用带命名空间的action
// dispatch是异步的,需要加async await
+ await this.$store.dispatch('user/userLogin', this.loginForm)
// 登录成功,路由跳转
+ this.$router.push('/')
} catch (err) {
alert('用户登录,失败')
console.log('用户登录,失败', err)
}
}
补充:
Async Await使用场景
一般我们都用await去等待一个async函数完成,不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值,所以,await后面实际可以接收普通函数调用或者直接量。
解决了回调地狱的问题
支持并发执行
可以添加返回值 return xxx
可以在代码中添加try/catch捕获错误
dispatch是异步的,需要加async await
二.路由导航守卫
背景
在二次开发的项目中,已经提供了路由页面跳转的
导航守卫思路:
- 导入路由和vuex
- 设置router.beforeEach前置路由守卫
- 拿到token值(关键)
- 声明一个白名单,通过流程图来写代码的执行流程
- 如果存在token,则不能够到登录页,前往首页放行next();不存在token,若为白名单(不需要token值得地方)则直接跳到登录页
01.permission.js的作用
以用token为核心控制路由页面的跳转(permission.js)
02.路由守卫的格式
// to:要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
router.beforeEach((to, from, next) => {
console.log(to, from)
next()
})
小结:
- 使用一个独立的permission.js文件用来设置路由守卫,更方便代码维护;
- 页面路由跳转时,一定会进入前置路由守卫;
- 路由守卫中一定要调用next
03.路由导航守卫-实现两个跳转限制
目标
编写代码,完成功能:
- 登陆用户不能再次回到login
- 没有登陆就不能访问除login之外的其它页
思路
白名单:那些不需要token就可以直接访问的页面
const whiteList = ['/login', '/404'] // 白名单
分析
数组.includes(元素): 检查这个元素在数组中是否存在
['a','b'].includes('a') ==> true
['a','b'].includes('c') ==> false
src/permission.js
是专门处理路由权限的,所以我们在这里处理。它在main.js中已经引入过了
import router from './router'
import store from '@/store'
// 全局前置路由守卫
// to: 要去哪个页面
// from:从哪里来
// next:它是一个函数。
// 如果直接放行 next()
// 如果要跳到其它页 next(其它页)
const whiteList = '/login' // 白名单
router.beforeEach((to, from, next) => {
// 取出token(vuex)
const token = store.state.user.token
console.log('全局前置路由守卫', token)
if (token) {
// 去登录页
if (to.path === '/login') {
console.log('你已经登录了,就不能去login,转到主页')
next('/')
} else {
next()
}
} else {
// 没有登录,只能去白名单(那些不需要token就可以访问的页面)
if (to.path === whiteList) {
next()
} else {
console.log('你没有登录,转到登录页')
next('/login')
}
// next() // 重点强调next一定要调用
}
})
小结
- router.beforeEach(回调(三个参数))
- 导航守卫函数中,一定要调用next( )
- to.path: to是一个路由对象,path表示路径,是它的一个属性
04.路由导航守卫-路由跳转进度条
目标
在路由跳转时,在页面顶部补充一些进度条提示
思路
用插件:NProgress https://github.com/rstacruz/nprogress。 注意,在这个项目中已经使用过了NProgress,我们只需要参考来写就行了
// 导入
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
// 两个API
NProgress.start() // 启动进度条
NProgress.done() // 进度条结束
加入进度条的核心:全局路由导航守卫有两个钩子:beforeEach, afterEach.在beforeEach中启动进度条,在afterEach中停止进度条
三.主页样式修改
01.修改左侧导航注意点
要修改的文件及样式的位置:
src/layout/index.vue
<------
src/layout/components/Sidebar/index.vue
1.在修改样式代码之前,先关闭vscode中的easy scss插件,不然,它会自动生成css文件
2.在scss中,如果我们想要使用
@
别名,需要在前面加上一个~
才可以
02.设置头部内容的布局和样式注意点
知识点:
1.Breadcrumb 面包屑
显示当前页面的路径,快速返回之前的任意页面。
在
el-breadcrumb
中使用el-breadcrumb-item
标签表示从首页开始的每一级。Element 提供了一个separator
属性,在el-breadcrumb
标签中设置它来决定分隔符,它只能是字符串,默认为斜杠/
。<el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item><a href="/">活动管理</a></el-breadcrumb-item> <el-breadcrumb-item>活动列表</el-breadcrumb-item> <el-breadcrumb-item>活动详情</el-breadcrumb-item> </el-breadcrumb>
2.svg:SVG 教程 | 菜鸟教程 它是一种图片格式(用代码写出来的图片)
四.显示登录用户名-分析
分析:由于用户资料数据是需要在多个组件中共享的数据,所以我们按照我们之前定好的原则,如果接口获取到的数据需要共享,那么该状态数据的操作,我们都放到vuex中来集中管理。
流程图:
思路:
- 在进行路由跳转时,获取用户个人信息,保存到vuex(写actions --> mutations ---> state 在路由守卫中,发ajax请求)
- 进入主页后,从vuex中取出数据
详细流程
1.封装接口
在
src/api/user.js
中封装获取用户资料的方法/** * @description: 获取用户资料 * @param {*} * @return {*} */ export function getProfile() { return request({ url: '/sys/profile', method: 'post' }) }
2.初始化state
在
src/store/modules/user.js
文件中,补充userInfo对象const state = { token: getToken() || '', + userInfo: {} }
3.封装mutation
对于上面定义的数据项,我们需要补充俩个mutation来操作它
1.设置用户信息
2.删除用户信息
mutations:{ // 省略其他... // 设置用户信息 setUserInfo(state, userInfo) { state.userInfo = userInfo }, // 删除用户信息 reomveUserInfo(state) { state.userInfo = {} } }
4.封装action
按前面login时的操作一样,将获取用户信息的操作写在action中:
import { getProfile } from '@/api/user' actions:{ // 用来获取用户信息的action async getUserInfo(context) { // ajax获取数据 const rs = await getProfile() console.log('用来获取用户信息的,', rs) context.commit('setUserInfo', rs.data) } }
5.调用action
时机分析:
- 因为依赖token去获取用户数据 所以必须要在获取了token之后才能调用这个action函数
- 因为跳转到首页之后就要显示数据了,所以要求我们在跳转进入首页之前就提前拿回用户个人数据
确保两件事情:
- token已经存在
- 成功取回数据之后才能进行跳转
import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import router from './router' import store from '@/store' // 白名单数组 const whiteList = ['/login', '/404'] + router.beforeEach(async(to, from, next) => { NProgress.start() const token = store.state.user.token if (token) { if (to.path === '/login') { // 有token,还去登录 ---> 直接去主页 console.log('有token,还去登录 ---> 直接去主页') next('/') NProgress.done() } else { // 1. 获取个人信息 + await store.dispatch('user/getUserProfile') next() } } else { // 没有token,只能访问白名单 if (whiteList.includes(to.path)) { next() } else { console.log('没有token,只能访问白名单') next('/login') NProgress.done() } // whiteList.includes(to.path) ? next() : next('/login') } console.log(token, '路由跳转', from.path, '----->', to.path) }) router.afterEach(() => { NProgress.done() })
6.渲染数据
把用户名从vuex中取出来,显示在页面上
目标:
把用户名从vuex中取出来,显示在页面上
分析:
数据已经保存在了vuex中了,直接渲染即可
在src\layout\components\Navbar.vue 中,显示出来用户名
<div class="avatar-wrapper"> <img class="user-avatar" src="@/assets/common/bigUserHeader.png"> + <span>{{ $store.state.user.userInfo.username }}</span> <i class="el-icon-caret-bottom" /> </div>
小结
- 如何快速在项目中找到对应的文件: vscode中的搜索
- 在组件中,获取vuex中的值:
this.$store.state.模块名.数据项
五.获取用户头像并显示
目标:获取用户头像并显示
思路:
由于头像的信息是在另外一个单独的接口中,且获取头像的接口的参数还必须依赖用户id ,所以,我们需要:
- 补充一个用来获取个人头像的api
- 在actions中,获取个人信息成功后,取出用户id,再用用户id去获取个人头像,然后将头像数据也保存在vuex中
思路与获取用户名同理
知识点:
合并两个对象:
let e={a:1,b:2}
let f={c:3,d:4}
let c={...a,...b}
console.log(c)=>{1,2,3,4}
六.实现用户退出
目标
实现用户登出
思路
1.confirm - 弹框确认是否要退出
2.要退出
(1)是否需要调用接口
(2)清空本地的信息(token,个人信息...)
(3)返回登录页,携带当前的页面地址,以便再次登录,还能回来
封装用来退出的action
userLogout(context) {
context.commit('removeUserInfo')
context.commit('removeToken')
},
调用action
在layout/components/Navbar.vue
中,用户点击退出时,显示确认对话框:
logout() {
// 弹层询问,是否退出
this.$confirm('你确定要离开吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
await this.$store.dispatch('user/logout')
this.$router.push(`/login`)
}).catch(() => {
// 用户取消退出
})
}
小结:
1. 退出之前要询问,confirm对话框
2.退出之前要清空数据,由于这个数据是保存在vuex中的,使用action来删除
七.登录成功后进入指定的页面
目标
自己来指定登陆成功之后,希望要进入的页面
思路:
在访问login这个页面时,可以在地址栏中补充一个查询字符串来指定登陆成功之后要去到的页面
# 访问登录页,并且告诉它,登录成功之后要进入 /abc
http://localhost:9528/#/login?return_url=/abc
async doLogin() {
try {
// 在组件中调用带命名空间的action
// dispatch是异步的,需要加async await
await this.$store.dispatch('user/userLogin', this.loginForm)
// 登录成功,路由跳转
+ this.$router.push(this.$route.query.return_url || '/')
} catch (err) {
alert('用户登录,失败')
console.log('用户登录,失败', err)
}
},
注意:
- $route.path:只有路径的信息
- $route.fullPath:路径+查询参数的信息
- return_url: 这个名字是自己约定的,它要和login/index.vue中跳转代码保持一致。
- encodeURIComponent: 是js的内置API,用来对url进行编码
八.token失效处理
token作为用户的关键令牌信息不是长久有效的,一般都会有一个失效时间(由后端来决定什么时长后失效),如果超过失效时间,当前token就不能再作为用户标识请求数据了,这时候我们需要做一些额外的失效处理
思路分析:
后端:收到用户访问某个接口时,检查当前token是否失效,如果token已经失效,返给前端一个约定好的状态码 10002
前端:在响应拦截器中,分析接口的返回值,如果状态码为10002, 则进行token失效操作
由于页面跳转要用到路由,这里先引入
// 引入路由
import router from '@/router'
代码:
// 响应拦截器中
// 1. 根据后端返回数据判断本次操作是否成功,不成功主动报错
// 2. 如果成功,只返回有效数据
service.interceptors.response.use(
response => {
// 后端和前端的约定:success=true表示请求成功
if (response.data.success) {
return response.data
} else {
// 如果success为false 业务出错,直接触发reject
// 被catch分支捕获
return Promise.reject(new Error(response.data.message))
}
},
async error => {
console.log('请求出错啦', error)
if (error.response.data.code === 10002) {
console.log('token失效')
await store.dispatch('user/logout')
// .vue -- this.$route.fullPath
// .js -- router.currentRoute.fullPath
router.push('/login?return_url=' + encodeURIComponent(router.currentRoute.fullPath))
}
console.dir(error)
return Promise.reject(error)
}
)
以上方案为后端主导的方案,前端只需要拿到错误码做业务处理即可,此方案也是常用且安全的最优方案