三、个人中心
TabBar 处理
通过分析页面,我们可以看到,首页、问答、视频、我的 都使用的是同一个底部标签栏,我们没必要在每个页面中都写一个,所以为了通用方便,我们可以使用 Vue Router 的嵌套路由来处理。
- 父路由:一个空页面,包含一个 tabbar,中间留子路由出口
- 子路由
一、创建 tabbar 组件并配置路由
这里主要使用到的 Vant 组件:
1、创建 src/views/layout/index.vue
<template> <div class="layout-container"> <router-view />
<van-tabbar class="layout-tabbar" route> <van-tabbar-item to="/"> <i slot="icon" class="toutiao toutiao-shouye"></i> <span class="text">首页</span> </van-tabbar-item> <van-tabbar-item to="/qa"> <i slot="icon" class="toutiao toutiao-wenda"></i> <span class="text">问答</span> </van-tabbar-item> <van-tabbar-item to="/video"> <i slot="icon" class="toutiao toutiao-shipin"></i> <span class="text">视频</span> </van-tabbar-item> <van-tabbar-item to="/my"> <i slot="icon" class="toutiao toutiao-wode"></i> <span class="text">我的</span> </van-tabbar-item> </van-tabbar> </div> </template>
<script> export default { name: 'LayoutIndex', components: {}, props: {}, data () { return { } }, computed: {}, watch: {}, created () {}, mounted () {}, methods: {} } </script>
<style scoped lang="less"> .layout-container { .layout-tabbar { i.toutiao { font-size: 40px; } span.text { font-size: 20px; } } } </style>
|
2、然后将 layout 组件配置到一级路由
{ path: '/', component: () => import('@/views/layout') }
|
访问 /
测试。
二、分别创建首页、问答、视频、我的页面组件
首页组件:
<template> <div class="home-container">首页</div> </template>
<script> export default { name: 'HomePage', components: {}, props: {}, data () { return {} }, computed: {}, watch: {}, created () {}, mounted () {}, methods: {} } </script>
<style scoped></style>
|
问答组件:
<template> <div class="qa-container">问答</div> </template>
<script> export default { name: 'QaPage', components: {}, props: {}, data () { return {} }, computed: {}, watch: {}, created () {}, mounted () {}, methods: {} } </script>
<style scoped></style>
|
视频组件:
<template> <div class="video-container">首页</div> </template>
<script> export default { name: 'VideoPage', components: {}, props: {}, data () { return {} }, computed: {}, watch: {}, created () {}, mounted () {}, methods: {} } </script>
<style scoped></style>
|
我的组件:
<template> <div class="my-container">首页</div> </template>
<script> export default { name: 'MyPage', components: {}, props: {}, data () { return {} }, computed: {}, watch: {}, created () {}, mounted () {}, methods: {} } </script>
<style scoped></style>
|
二、将四个主页面配置为 tab-bar 的子路由
{ path: '/', name: 'tab-bar', component: () => import('@/views/tab-bar'), children: [ { path: '', name: 'home', component: () => import('@/views/home') }, { path: 'qa', name: 'qa', component: () => import('@/views/qa') }, { path: 'video', name: 'video', component: () => import('@/views/video') }, { path: 'my', name: 'my', component: () => import('@/views/my') } ] }
|
最后测试。
页面布局
未登录头部状态
<template> <div class="my-container"> <div class="header"> <img class="mobile-img" src="~@/assets/mobile.png" @click="$router.push('/login')" > </div> <div class="grid-nav"></div> <van-cell title="消息通知" is-link url="" /> <van-cell title="实名认证" is-link url="" /> <van-cell title="用户反馈" is-link url="" /> <van-cell title="小智同学" is-link url="" /> <van-cell title="系统设置" is-link url="" /> </div> </template>
<script> export default { name: 'MyIndex', components: {}, props: {}, data () { return {} }, computed: {}, watch: {}, created () {}, mounted () {}, methods: {} } </script>
<style scoped lang="less"> .my-container { > .header { height: 361px; background: url("~@/assets/banner.png") no-repeat; background-size: cover; display: flex; justify-content: center; align-items: center; .mobile-img { width: 132px; height: 132px; } } } </style>
|
已登录头部
宫格导航
单元格导航
处理已登录和未登录的页面展示
<div v-if="$store.state.user" class="user-info-wrap"> ... </div>
<div v-else class="not-login" @click="$router.push('/login')"> ... </div>
<van-cell-group v-if="$store.state.user"> ... </van-cell-group>
|
用户退出
1、给退出按钮注册点击事件
2、退出处理
onLogout () { this.$dialog.confirm({ title: '确认退出吗?' }).then(() => { this.$store.commit('setUser', null) }).catch(() => { console.log('取消执行这里') }) }
|
最后测试。
展示登录用户信息
步骤:
1、在 api/user.js
中添加封装数据接口
export const getUserInfo = () => { return request({ method: 'GET', url: '/app/v1_0/user', headers: { Authorization: `Bearer ${store.state.user.token}` } }) }
|
2、在 views/my/index.vue
请求加载数据
+ import { getUserInfo } from '@/api/user'
export default { name: 'MyPage', components: {}, props: {}, data () { return { + userInfo: {} } }, computed: {}, watch: {}, +++ created () { if (this.$store.state.user) { this.loadUser() } }, mounted () {}, methods: { +++ async loadUser () { try { const { data } = await getUserInfo() this.user = data.data } catch (err) { console.log(err) this.$toast('获取数据失败') } } } }
|
3、模板绑定
优化设置 Token
项目中的接口除了登录之外大多数都需要提供 token 才有访问权限。
通过接口文档可以看到,后端接口要求我们将 token 放到请求头 Header
中并以下面的格式发送。
字段名称:Authorization
字段值:Bearer token
,注意 Bearer
和 token
之间有一个空格
方式一:在每次请求的时候手动添加(麻烦)。
axios({ method: "", url: "", headers: { Authorization: "Bearer token" } })
|
方式二:使用请求拦截器统一添加(推荐,更方便)。
sequenceDiagram participant A as 发起请求 participant B as 请求拦截器 participant C as 服务端 A-->>B: http://xxx Note right of B: 设置 token B->>C: 请求发出
|
在 src/utils/request.js
中添加拦截器统一设置 token:
import axios from 'axios' import store from '@/store'
const request = axios.create({ baseURL: 'http://ttapi.research.itcast.cn/' })
request.interceptors.request.use(function (config) { const { user } = store.state if (user && user.token) { config.headers.Authorization = `Bearer ${user.token}` } return config }, function (error) { return Promise.reject(error) })
export default request
|