VueDemo-14-16状态管理器
14.状态管理器Vuex 是什么? | VuexVuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。状态传值父组件给子组件传值子组件给父组件传值非父子组件(兄弟组件)传值子组件直接使用父组件的数据和方法父组件直接使用子组件的数据和方法跨组件传值状态管理器多个视图依赖于同一状态。来自不
14.状态管理器
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
状态传值
父组件给子组件传值
子组件给父组件传值
非父子组件(兄弟组件)传值
子组件直接使用父组件的数据和方法
父组件直接使用子组件的数据和方法
跨组件传值
状态管理器
-
多个视图依赖于同一状态。
-
来自不同视图的行为需要变更同一状态。
14.0 构建首页的头部组件
<template> <div class="box"> <header class="header"> <ul> <li>北京</li> <li>搜索框</li> <li v-if="true">登录</li> <li v-else>我的</li> </ul> </header> <div class="content" ref="content"> .... </div> </div> </template> <script> ..... </script> <style lang='stylus' scoped> // scoped 代表该样式只在当前组件是有效的,不会影响其他组件的样式 .... .header ul height 100% display flex li flex 1 </style>
14.1 第一版登录状态
异步操作在组件,组件中直接提交mutations
定义状态管理器
// src/store/index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { // ? 为什么要从本地存储设置 状态管理器的初始值 // 因为每次刷新页面都会使 状态管理器 重置 loginState: localStorage.getItem('loginState') === 'true' }, mutations: { changeLoginState (state, payload) { // state 所有的状态 payload 参数 state.loginState = payload } }, actions: { }, modules: { } })
登录时保存状态
// 修改状态管理器中的代码 --- 代码不要看图,只看图中的代码位置,实际代码替换如下 this.$store.commit('changeLoginState', true)
底部组件获取登录状态
<template> <!-- div.container>div.box>header.header+div.content --> <div class="container"> <router-view></router-view> <footer class="footer" v-if="!$route.meta.hidden"> <ul> <router-link to="/home" tag="li"> <span class="iconfont icon-shouye"></span> <p>首页</p> </router-link> <router-link to="/kind" tag="li"> <span class="iconfont icon-fenlei"></span> <p>分类</p> </router-link> <router-link to="/cart" tag="li"> <span class="iconfont icon-gouwuche"></span> <p>购物车</p> </router-link> <router-link v-if="loginState" to="/user" tag="li"> <span class="iconfont icon-My"></span> <p>我的</p> </router-link> <router-link v-else to="/login" tag="li"> <span class="iconfont icon-My"></span> <p>未登录</p> </router-link> </ul> </footer> <div class="tip">请将屏幕竖向浏览</div> </div> </template> <script> export default { // 使用计算属性获取状态管理器中的状态 computed: { loginState () { return this.$store.state.loginState } } } </script> <style lang="stylus"> .... </style>
// src/views/home/index.vue <template> <div class="box"> <header class="header"> <ul> <li>北京</li> <li>搜索框</li> <router-link tag="li" to="/user" v-if="loginState">我的</router-link> <router-link tag="li" to="/login" v-else>登录</router-link> </ul> </header> <div class="content" ref="content"> .... </div> </div> </template> <script> export default { computed: { loginState () { return this.$store.state.loginState } }, .... } </script> <style lang='stylus' scoped> // scoped 代表该样式只在当前组件是有效的,不会影响其他组件的样式 .... .header ul height 100% display flex li flex 1 </style>
退出登录
<template> <div class="box"> <header class="header">user header</header> <div class="content"> <button v-if="loginState" @click="logout">退出</button> <button v-else @click="login"> 登录</button> </div> </div> </template> <script> export default { computed: { loginState () { return this.$store.state.loginState } }, methods: { logout () { localStorage.removeItem('token') localStorage.removeItem('userid') localStorage.removeItem('loginState') this.$store.commit('changeLoginState', false) // 二次渲染的关键 this.$router.push('/login') }, login () { this.$router.push('/login') } } } </script>
总结:登录时,异步的登录操作放到了 登录组件中
14.2 第二版登录状态
登录时,异步的登录操作放到了 状态管理器中,通过组件触发状态管理器执行 异步操作
登录的代码拷贝到了 store/index.js中的actions内
import Vue from 'vue' import Vuex from 'vuex' import router from '../router' import { Dialog, Toast } from 'vant' import { login } from './../api/user' Vue.use(Vuex) export default new Vuex.Store({ state: { // ? 为什么要从本地存储设置 状态管理器的初始值 // 因为每次刷新页面都会使 状态管理器 重置 loginState: localStorage.getItem('loginState') === 'true' }, mutations: { changeLoginState (state, payload) { // state 所有的状态 payload 参数 state.loginState = payload } }, actions: { // context 上下文对象 --- store // params 参数 loginAction (context, parmas) { login({ loginname: parmas.loginname, password: parmas.password }).then(res => { if (res.data.code === '10010') { // 账户不存在,提醒用户是否要立即注册 Dialog.confirm({ message: '该用户还未注册,是否立即注册', confirmButtonText: '立即注册', confirmButtonColor: '#ff6666', cancelButtonText: '取消', cancelButtonColor: '#999' }) .then(() => { router.push('/register/step1') }) .catch(() => { // on cancel }) } else if (res.data.code === '10011') { // 提醒用户密码错误, 视情况而定是否需要清空密码输入框 this.password = '' Toast('密码错误') } else { // 登录成功 Toast('登录成功') localStorage.setItem('userid', res.data.data.userid) // 知道是谁 localStorage.setItem('token', res.data.data.token) // 后端验证用户的登录状态 localStorage.setItem('loginState', true) // 前端自检登录状态 // 使用状态管理器修改状态 context.commit('changeLoginState', true) // 修改状态管理器中的loginState的值为true // 返回上一页 router.back() } }) } }, modules: { } })
<template> <div class="box"> <header class="header"> <van-nav-bar title="嗨购登录" left-arrow @click-left="$router.back()" ></van-nav-bar> </header> <div class="content"> <div class="form" v-if="type === '1'"> <van-field v-model="loginname" placeholder="用户名/手机号/邮箱" clearable/> <van-field v-model="password" type="password" placeholder="请输入密码" clearable/> <p class="passwordTip" v-if="passwordTipFlag">输入至少6位,包含至少一个大写字母,1个小写字母,1个数字</p> <div class="my-button"> <van-button color="#ff6666" :disabled="adminameFlag" block round @click="adminameLoginFn">登录</van-button> </div> </div> <div class="form" v-if="type === '0'"> <van-field v-model="tel" type="tel" placeholder="手机号码" clearable/> <van-field v-model="telcode" center clearable placeholder="请输入短信验证码" > <template #button> <van-button size="small" >发送短信验证码</van-button> </template> </van-field> <div class="my-button"> <van-button color="#ff6666" block round>登录</van-button> </div> </div> <!-- 登录方式以及 注册提示 --> <ul class="more"> <li @click="changeType"> <span v-if="type === '1'">短信验证码登录</span> <span v-else>账号密码登录</span> </li> <router-link to="/register/step1" tag="li"> 手机快速注册 </router-link> </ul> <div class="my-divider"> <van-divider>其他登录方式</van-divider> </div> <div class="my-login-type"> <van-row type="flex" justify="center"> <van-col span="6"> <van-image :src="qq" width="48" height="48"/> <p>QQ</p> </van-col> <van-col span="6"> <van-image :src="wx" width="48" height="48"/> <p>微信</p> </van-col> <van-col span="6"> <van-image :src="apple" width="48" height="48"/> <p>苹果</p> </van-col> </van-row> </div> </div> </div> </template> <script> import Vue from 'vue' import { NavBar, Field, Button, Divider, Col, Row, Image as VanImage } from 'vant' Vue.use(NavBar) Vue.use(Field) Vue.use(Button) Vue.use(Divider) Vue.use(Col) Vue.use(Row) Vue.use(VanImage) export default { data () { return { qq: 'data:image/png;base64,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', wx: 'data:image/png;base64,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', apple: 'data:image/png;base64,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', tel: '', password: '', loginname: '', telcode: '', type: '1' // 1表示账户名密码 0表示手机验证码 } }, computed: { adminameFlag () { if (this.loginname !== '' && /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])\S*$/.test(this.password)) { return false } else { return true } }, passwordTipFlag () { if (this.password === '' || /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])\S*$/.test(this.password)) { return false } else { return true } } }, methods: { changeType () { this.type = this.type === '1' ? '0' : '1' }, adminameLoginFn () { console.log('1111111') // 异步操作 this.$store.dispatch('loginAction', { loginname: this.loginname, password: this.password }) } } } </script> <style lang="stylus"> .container .box .content padding 30px 15px background-color #ffffff .form .my-button margin-top 30px .more margin-top 20px display flex li flex 1 &:nth-child(1) text-align left &:nth-child(2) text-align right .my-divider margin-top 80px .passwordTip color #f66 font-size 12px </style>
异步操作在组件,组件中通过
this.$store.commit()
改变数据异步操作在状态管理器,组件中通过
this.$store.dispatch()
触发,通过其内部的 context.commit() 改变数据组件中通过this.$store.state获取状态,可以配合计算属性使用
14.3 使用辅助函数mapState获取状态
之前的案例是 通过 计算属性 计算得到 状态,通过 this.$store.state获取得到
mapState的是一个对象或者数组
// App.vue
<template> <!-- div.container>div.box>header.header+div.content --> <div class="container"> <!-- <div class="box"> <header class="header">header</header> <div class="content">content</div> </div> --> <router-view></router-view> <footer class="footer" v-if="!$route.meta.hidden"> <ul> <router-link to="/home" tag="li"> <span class="iconfont icon-shouye"></span> <p>首页</p> </router-link> <router-link to="/kind" tag="li"> <span class="iconfont icon-fenlei"></span> <p>分类</p> </router-link> <router-link to="/cart" tag="li"> <span class="iconfont icon-gouwuche"></span> <p>购物车</p> </router-link> <router-link v-if="loginState" to="/user" tag="li"> <span class="iconfont icon-My"></span> <p>我的</p> </router-link> <router-link v-else to="/login" tag="li"> <span class="iconfont icon-My"></span> <p>未登录</p> </router-link> </ul> </footer> <div class="tip">请将屏幕竖向浏览</div> </div> </template> <script> import { mapState } from 'vuex' export default { // 使用计算属性获取状态管理器中的状态 computed: { // loginState () { // return this.$store.state.loginState // } ...mapState({ loginState: state => state.loginState }) } } </script> <style lang="stylus"> ... </style>
// src/views/home/index.vue
<template> <div class="box"> .... </div> </template> <script> ... import { mapState } from 'vuex' export default { computed: { // loginState () { // return this.$store.state.loginState // } ...mapState({ loginState: state => state.loginState }) }, .... } </script> <style lang='stylus' scoped> /... </style>
// src/views/user/index.vue
<template> <div class="box"> <header class="header">user header</header> <div class="content"> <button v-if="loginState" @click="logout">退出</button> <button v-else @click="login"> 登录</button> </div> </div> </template> <script> import { mapState } from 'vuex' export default { computed: { // loginState () { // return this.$store.state.loginState // } ...mapState({ loginState: state => state.loginState }) }, methods: { logout () { localStorage.removeItem('token') localStorage.removeItem('userid') localStorage.removeItem('loginState') this.$store.commit('changeLoginState', false) // 二次渲染的关键 this.$router.push('/login') }, login () { this.$router.push('/login') } } } </script>
14.4 使用辅助函数mapMutations 改变状态
这个辅助函数使用场景: 组件中去改变 mutation
// views/user/index.vue
<template> <div class="box"> <header class="header">user header</header> <div class="content"> <button v-if="loginState" @click="logout">退出</button> <button v-else @click="login"> 登录</button> </div> </div> </template> <script> import { mapState, mapMutations } from 'vuex' export default { computed: { // loginState () { // return this.$store.state.loginState // } ...mapState({ loginState: state => state.loginState }) }, methods: { ...mapMutations({ changeLoginState: 'changeLoginState' // key随意 - 事件,value即为mutation中的值,一般情况下二者相同 }), logout () { localStorage.removeItem('token') localStorage.removeItem('userid') localStorage.removeItem('loginState') // this.$store.commit('changeLoginState', false) // 二次渲染的关键 this.changeLoginState(false) // 事件中通过 this.key事件(参数) this.$router.push('/login') }, login () { this.$router.push('/login') } } } </script>
14.5 使用辅助函数 mapActions 触发 acitons
组件触发 action,aciton提交mutation
// views/login/index.vue
<template> <div class="box"> <header class="header"> <van-nav-bar title="嗨购登录" left-arrow @click-left="$router.back()" ></van-nav-bar> </header> <div class="content"> <div class="form" v-if="type === '1'"> <van-field v-model="loginname" placeholder="用户名/手机号/邮箱" clearable/> <van-field v-model="password" type="password" placeholder="请输入密码" clearable/> <p class="passwordTip" v-if="passwordTipFlag">输入至少6位,包含至少一个大写字母,1个小写字母,1个数字</p> <div class="my-button"> <van-button color="#ff6666" :disabled="adminameFlag" block round @click="adminameLoginFn">登录</van-button> </div> </div> <div class="form" v-if="type === '0'"> <van-field v-model="tel" type="tel" placeholder="手机号码" clearable/> <van-field v-model="telcode" center clearable placeholder="请输入短信验证码" > <template #button> <van-button size="small" >发送短信验证码</van-button> </template> </van-field> <div class="my-button"> <van-button color="#ff6666" block round>登录</van-button> </div> </div> <!-- 登录方式以及 注册提示 --> <ul class="more"> <li @click="changeType"> <span v-if="type === '1'">短信验证码登录</span> <span v-else>账号密码登录</span> </li> <router-link to="/register/step1" tag="li"> 手机快速注册 </router-link> </ul> <div class="my-divider"> <van-divider>其他登录方式</van-divider> </div> <div class="my-login-type"> <van-row type="flex" justify="center"> <van-col span="6"> <van-image :src="qq" width="48" height="48"/> <p>QQ</p> </van-col> <van-col span="6"> <van-image :src="wx" width="48" height="48"/> <p>微信</p> </van-col> <van-col span="6"> <van-image :src="apple" width="48" height="48"/> <p>苹果</p> </van-col> </van-row> </div> </div> </div> </template> <script> import Vue from 'vue' import { NavBar, Field, Button, Divider, Col, Row, Image as VanImage } from 'vant' import { mapActions } from 'vuex' Vue.use(NavBar) Vue.use(Field) Vue.use(Button) Vue.use(Divider) Vue.use(Col) Vue.use(Row) Vue.use(VanImage) export default { data () { return { qq: 'data:image/png;base64,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', wx: 'data:image/png;base64,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', apple: 'data:image/png;base64,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', tel: '', password: '', loginname: '', telcode: '', type: '1' // 1表示账户名密码 0表示手机验证码 } }, computed: { adminameFlag () { if (this.loginname !== '' && /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])\S*$/.test(this.password)) { return false } else { return true } }, passwordTipFlag () { if (this.password === '' || /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])\S*$/.test(this.password)) { return false } else { return true } } }, methods: { ...mapActions({ loginAction: 'loginAction' }), changeType () { this.type = this.type === '1' ? '0' : '1' }, adminameLoginFn () { console.log('1111111') // 异步操作 // this.$store.dispatch('loginAction', { // loginname: this.loginname, // password: this.password // }) this.loginAction({ loginname: this.loginname, password: this.password }) } } } </script> <style lang="stylus"> .container .box .content padding 30px 15px background-color #ffffff .form .my-button margin-top 30px .more margin-top 20px display flex li flex 1 &:nth-child(1) text-align left &:nth-child(2) text-align right .my-divider margin-top 80px .passwordTip color #f66 font-size 12px </style>
14.6 getters以及ma pGetters
// src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' import router from '../router' import { Dialog, Toast } from 'vant' import { login } from './../api/user' Vue.use(Vuex) export default new Vuex.Store({ state: { list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5], // ? 为什么要从本地存储设置 状态管理器的初始值 // 因为每次刷新页面都会使 状态管理器 重置 loginState: localStorage.getItem('loginState') === 'true' }, getters: { // 可以看作是state的计算属性,类似于computed len: state => state.list.length }, mutations: { changeLoginState (state, payload) { // state 所有的状态 payload 参数 state.loginState = payload } }, actions: { // context 上下文对象 --- store // params 参数 loginAction (context, parmas) { login({ loginname: parmas.loginname, password: parmas.password }).then(res => { if (res.data.code === '10010') { // 账户不存在,提醒用户是否要立即注册 Dialog.confirm({ message: '该用户还未注册,是否立即注册', confirmButtonText: '立即注册', confirmButtonColor: '#ff6666', cancelButtonText: '取消', cancelButtonColor: '#999' }) .then(() => { router.push('/register/step1') }) .catch(() => { // on cancel }) } else if (res.data.code === '10011') { // 提醒用户密码错误, 视情况而定是否需要清空密码输入框 this.password = '' Toast('密码错误') } else { // 登录成功 Toast('登录成功') localStorage.setItem('userid', res.data.data.userid) // 知道是谁 localStorage.setItem('token', res.data.data.token) // 后端验证用户的登录状态 localStorage.setItem('loginState', true) // 前端自检登录状态 // 使用状态管理器修改状态 context.commit('changeLoginState', true) // 修改状态管理器中的loginState的值为true // 返回上一页 router.back() } }) } }, modules: { } })
// src/views/user/index.vue
<template> <div class="box"> <header class="header">user header</header> <div class="content"> <button v-if="loginState" @click="logout">退出</button> <button v-else @click="login"> 登录</button> {{ listLen }} </div> </div> </template> <script> import { mapState, mapMutations, mapGetters } from 'vuex' export default { computed: { // loginState () { // return this.$store.state.loginState // } ...mapState({ loginState: state => state.loginState }), // listLen () { // return this.$store.getters.len // } ...mapGetters({ listLen: 'len' }) }, methods: { ...mapMutations({ changeLoginState: 'changeLoginState' // key随意 - 事件,value即为mutation中的值,一般情况下二者相同 }), logout () { localStorage.removeItem('token') localStorage.removeItem('userid') localStorage.removeItem('loginState') // this.$store.commit('changeLoginState', false) // 二次渲染的关键 this.changeLoginState(false) // 事件中通过 this.key事件(参数) this.$router.push('/login') }, login () { this.$router.push('/login') } } } </script>
15 分模块的状态管理器
便于团队的开发协作
vue的实例中 可以 使用 computed 计算属性
vuex 中可以 使用 getters 计算属性
15.1 构建用户的相关的模块
// store/modules/user.js import router from '../../router' import { Dialog, Toast } from 'vant' import { login } from './../../api/user' export default { namespaced: true, // 命名空间,作用很大,用来标识是哪一个模块 state: { list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5], // ? 为什么要从本地存储设置 状态管理器的初始值 // 因为每次刷新页面都会使 状态管理器 重置 loginState: localStorage.getItem('loginState') === 'true' }, mutations: { changeLoginState (state, payload) { // state 所有的状态 payload 参数 state.loginState = payload } }, actions: { // context 上下文对象 --- store // params 参数 loginAction (context, parmas) { login({ loginname: parmas.loginname, password: parmas.password }).then(res => { if (res.data.code === '10010') { // 账户不存在,提醒用户是否要立即注册 Dialog.confirm({ message: '该用户还未注册,是否立即注册', confirmButtonText: '立即注册', confirmButtonColor: '#ff6666', cancelButtonText: '取消', cancelButtonColor: '#999' }) .then(() => { router.push('/register/step1') }) .catch(() => { // on cancel }) } else if (res.data.code === '10011') { // 提醒用户密码错误, 视情况而定是否需要清空密码输入框 this.password = '' Toast('密码错误') } else { // 登录成功 Toast('登录成功') localStorage.setItem('userid', res.data.data.userid) // 知道是谁 localStorage.setItem('token', res.data.data.token) // 后端验证用户的登录状态 localStorage.setItem('loginState', true) // 前端自检登录状态 // 使用状态管理器修改状态 context.commit('changeLoginState', true) // 修改状态管理器中的loginState的值为true // 返回上一页 router.back() } }) } } }
// store/index.js import Vue from 'vue' import Vuex from 'vuex' import user from './modules/user' Vue.use(Vuex) export default new Vuex.Store({ getters: { // 可以看作是state的计算属性,类似于computed len: state => state.user.list.length }, modules: { // 整合模块 user } })
Src/App.vue
<template> <!-- div.container>div.box>header.header+div.content --> <div class="container"> <!-- <div class="box"> <header class="header">header</header> <div class="content">content</div> </div> --> <router-view></router-view> <footer class="footer" v-if="!$route.meta.hidden"> <ul> <router-link to="/home" tag="li"> <span class="iconfont icon-shouye"></span> <p>首页</p> </router-link> <router-link to="/kind" tag="li"> <span class="iconfont icon-fenlei"></span> <p>分类</p> </router-link> <router-link to="/cart" tag="li"> <span class="iconfont icon-gouwuche"></span> <p>购物车</p> </router-link> <router-link v-if="loginState" to="/user" tag="li"> <span class="iconfont icon-My"></span> <p>我的</p> </router-link> <router-link v-else to="/login" tag="li"> <span class="iconfont icon-My"></span> <p>未登录</p> </router-link> </ul> </footer> <div class="tip">请将屏幕竖向浏览</div> </div> </template> <script> import { mapState } from 'vuex' export default { // 使用计算属性获取状态管理器中的状态 computed: { // loginState () { // return this.$store.state.user.loginState // } ...mapState({ loginState: state => state.user.loginState }) } } </script> <style lang="stylus"> ... </style>
// src/views/home/index.vue
<template> <div class="box"> .... </template> <script> import Vue from 'vue' import { Swipe, SwipeItem, Grid, GridItem, Image as VanImage, CountDown, Icon, List, PullRefresh } from 'vant' import { getBannerList, getSeckillList, getProList } from '@/api/home' // @ 代表的就是src的目录 import ProList from '@/components/ProList' import { mapState } from 'vuex' Vue.use(Swipe) Vue.use(SwipeItem) Vue.use(Grid) Vue.use(GridItem) Vue.use(VanImage) Vue.use(CountDown) Vue.use(Icon) Vue.use(List) Vue.use(PullRefresh) export default { computed: { // loginState () { // return this.$store.state.loginState // } ...mapState({ loginState: state => state.user.loginState }) }, .... } </script> <style lang='stylus' scoped> .... </style>
15.2 状态管理器中使用模块
15.3 登录时改变状态
Src/views/login/index.vue
<template> <div class="box"> <header class="header"> <van-nav-bar title="嗨购登录" left-arrow @click-left="$router.back()" ></van-nav-bar> </header> <div class="content"> <div class="form" v-if="type === '1'"> <van-field v-model="loginname" placeholder="用户名/手机号/邮箱" clearable/> <van-field v-model="password" type="password" placeholder="请输入密码" clearable/> <p class="passwordTip" v-if="passwordTipFlag">输入至少6位,包含至少一个大写字母,1个小写字母,1个数字</p> <div class="my-button"> <van-button color="#ff6666" :disabled="adminameFlag" block round @click="adminameLoginFn">登录</van-button> </div> </div> <div class="form" v-if="type === '0'"> <van-field v-model="tel" type="tel" placeholder="手机号码" clearable/> <van-field v-model="telcode" center clearable placeholder="请输入短信验证码" > <template #button> <van-button size="small" >发送短信验证码</van-button> </template> </van-field> <div class="my-button"> <van-button color="#ff6666" block round>登录</van-button> </div> </div> <!-- 登录方式以及 注册提示 --> <ul class="more"> <li @click="changeType"> <span v-if="type === '1'">短信验证码登录</span> <span v-else>账号密码登录</span> </li> <router-link to="/register/step1" tag="li"> 手机快速注册 </router-link> </ul> <div class="my-divider"> <van-divider>其他登录方式</van-divider> </div> <div class="my-login-type"> <van-row type="flex" justify="center"> <van-col span="6"> <van-image :src="qq" width="48" height="48"/> <p>QQ</p> </van-col> <van-col span="6"> <van-image :src="wx" width="48" height="48"/> <p>微信</p> </van-col> <van-col span="6"> <van-image :src="apple" width="48" height="48"/> <p>苹果</p> </van-col> </van-row> </div> </div> </div> </template> <script> import Vue from 'vue' import { NavBar, Field, Button, Divider, Col, Row, Image as VanImage } from 'vant' import { mapActions } from 'vuex' Vue.use(NavBar) Vue.use(Field) Vue.use(Button) Vue.use(Divider) Vue.use(Col) Vue.use(Row) Vue.use(VanImage) export default { data () { return { qq: 'data:image/png;base64,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', wx: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAD5VJREFUeAHtXXuMXUUZn5lz72532W27t9CWttKuGjSUmEBaBQxYBLYIVVEpAfyDSkxIS4xGIgE1ppqgCSRq4qMSDVajxnSFmIBAF0jrgyKKaLAlhKBtTVvabXt3u7vsdveeM+Pvd9qzO+fcs3fv45y7t+29fZx5fvPN75v5zsw3jyNFg/42b96sei/pzQy0DWROOiczrnIzWmtHOEIax0jjGiWUkD77WhiZkVp60ghPGKWUl9EZd443x+0a63LXv77eBT3diFU9VYEG4Gyz2ay27tzacsI90VrQhVYtdSZJtpRRblZlx+dl5o1vWLNhYrNsDIHMqgAI+pa+LW2jLaNtXsFrSRLwmWg5WWeifaJ9bGPPxrHZFEbdBWCMkd07u1vz4/l2qJU5MwFVj3ioq5O51tzo3jV7x6WEGqvjr24CIPBLn1raNtQy1JG0ekkKL6qpuRNzRw6uOzhWL0GkLgACv7hvcfuIM9JhtHGSAitNOlJJr8PrGDncc3g0bUGkKoCVe1a27D+wf36jtviZhMgesXzZ8sE9K/dMzJS22vhUBLB+23qnr71vbqGl0FYtY42ULzuRHesZ7Rnqva3XS5qvxAWwbNuytvzc/PzJMXrSHM8WPcw1ckO5wQO3HRhLkoXEBEBdn3s+N3fcjJ+XJIONRqtVtr6Tvz4/lNS7IREBrNmxJvPK2CtdOqOzjQZYGvwoVxVWta0a2HntTrdW+jULYOW2lS37LtiXMwWYBs6hn8xKveLoivye22p7QdckgBU7Vszpd/q7xMnTNplzSAB+VecIs9BbOLDv2n0nq6161QJY8uSS9sHWwfnVFnw25Zs/Pn/w0McPjVZTp6oE0AS/GOpqhVCx3qbaabb8YgEQE2JTHFM6pCIB8IXr6/zSNM/ZWGJDjCoBoGwBcKjJ0c45+8ItB1UMRogRsSonOdOU9Q7gJKvzmc7zz5VxfrngTZeO84Thjw0fK2eyVlYP4Ay3Cf50cBeHEytiVhxTHDKjAGjbOdvNC8Ww1B5CzIjdTJRKCoBWTd+wNhOVZnwsAsSOGMZGng4s+Q6Y99S8rrPFpFwKhDTjaMo+se7EwHRlTNsDuJjSBH862MoPJ4bEcrocsQLgqIcrWdNlaoZXhgCxJKZxuWIFwDXcM3UZMa6Ssx1GLIlpHB9FEwZKquOFjg7RIPvItNGtUunLhZRXgqflQskFUpucwVMYs8CvlJTHEXYcYXmBJ1bj9iPuJaPVq0qq8biK1zvM35RgTNEif1G3aARDG0B/v3TMTdhweJUQ5nKA1VolYABfvorNjLuMJ5+GMN6okk4i2eIMdiEB+DPevs4LZkv9oNzrwdDnAfrVidS4iIj8M3Zd/Qy7HZ4viqpDAHdZDPcMH7VnyCEB+Asshf5cHXgJFWEc706h5SYA3x2KSM/zX6ipLdJzfpNeEfGUF2YX5u0FnJAA5j47N1fP7YJGTSwXJvNdAH9FPLunQ6X4D9TRy0j3CtTJ21rKvOPpvG5z8kyhxryc56icMiaHdBdC7axCug8JI95Tkq6QLwnp3id1y/7S6ZKL5TbIoRuHfL5JdVIA3Cj78HMPL06uqNKUtHQ/J4X8GlJNM12XL0plfl2QelfWzR4tTS0+tpApXJA16iqj5WchvA/HpxJjRpiHlMn8fJr4xIPvv+H+w8GG4EkBLNq+6LxhOTwv8dIiBLWjs0qbR6GL10aigJEYxWjnccQ9Bn35ZlF8DQF4v1yMyt6NQm4FmSKhI267VvIe5alCDcWUlbXTdJ44svbIO0w8OQ/gFvGycteQyB9SGvNYHPhSiudco6+RRj2QNPh+RSFQ0na1vpplRatBniR4I4/RuKT9Nta+AKh+0t6fr5WeIx2xFa38o3aFoIaOo/VtEtrZkJXZt+24NNx+GSiLZbLsUBngjTyS11B4wh5iTcxJFnwIUZfRj/SKW74Ub7pZfXt2PHuEfNT7V2gtLMoU1G/RKC62y6Y6Esa52w5L2h2Mhnwp8FhQ0gXY9HBwa31U7aCS//aU+fRsgU/+WDZ5IC9hfsVa8myHJe0OMPcFwDNZSRcQ0APtxVKJbwZ+PlHh1zxXrM+4mWnNtHb6NN3kgbyQJ7sc8kze7bAk3QHmiqcR05z5ZjLqEWPE1OgKIx3jqE2O4wwnWaFaaJEX8uSPwk4TIs/kvRa6pfISc2LvHN10NDs4ZzDWUleKQDlxxnExGZIPhNIq83XMQP8UCmsADw65Dgql0SPlDRY73cLRL2L0dNAKS8x5qPPQScVzuIlRjBBCpe4JBUm5C4L/VSgs4uE8Afr3q0JqGNH0q3QzLJJsRm81dHzewKNNvKgOdmSNbmKveAi6Rjqx2V3pzsMSxHV2JPw/s/1xbumZr+AlcS9mp4v4j24/LC5xibBq6UR5ZB1YlxJFVR1F7LGFxU1FAI6S10OnTtLGmPuIdOWMVkhYCjlTDf3iwkIJYjxxeeLColnJI3mdDEcd/LpMBiTnIPbKP/6fHM0pSlqunvLAAGBMLwBI/IyVXUYSbvJIXkO0InUJxdXgIfaKdy/UQGParDjvvNKOxLBut+2fzo3K/y4aFxcWTRP1x+WJC4vmoz/Ka7QucXmqCgP2GV58kcryI85l07oW/Dwp/ldyg8zphMaRj6ANwiZ3ShURtFNhAaXynrXQIa9qinVUg3VJ/kfsZfuz7RfCJJd4LzDKewv4Txr4PM9cmlGzP/EqB0ZXu12OI6d6rBRjUjvvLSdvRWlw8nLqypeKcpaRGMTtVLJVNsTiuM3TdG7ZJsMHsw0UdRo/NHzfFJEGbdA8ZNPFeHqJ7W9ktxyXF9n8YVQ0ZPuTdCvo/1BLTYy4lKHZI140qejRxPi1CTniXbYXy5v9IX9SHqog3jSVFL0InVAPEFp9IBLfsF7lhc3TRovX02CW2ONekJTux5FYQLd/0nzK9jay2yjzGZs/+P9p+5NyE3vFO9aSImjTwajnafinXrxGvA8rTZfYaRrSLd3VQCS0QIM17L5UeD11vx06XAq/jMwMYWy7wyaNNdd7bX8jurEz864QX1KiJ2fD6jSUoHoPLxdUvF2wehKlc2ojH4+kuMUYN6Vdb5GSqvBi49rVmBPdYmeFevip7U/STewVr3ZMkqhNC5OMpzGj/YcdJpT6TtqL3qHyyvTQ4imF+h7Ujz0pfUM6kqo0lR+xV7xXMxXqp4lCzT0Ip6XmTLcS5icwMaQzuamiMjQJZKTEyp3BrrrTPylcrcQXJXaGBUFJP4m94qWmSRO26Tna2YMm9ZgdhuW+G7D69H07bLbcfkPQ+ofg6eYQD1L8wPGcKXNEKDIZD7H3u1vH9o6Faa4Le1mv3XEFd7yF5gIwAz6qtPOtZKpTORWd1S3K9Xfp9di5sXFrjzHqJmlkao2TO6VH1o70+6YI3ihrM5C02yk4o65j7oR+fcumjRnIPSZTtx3RdtHYjK0vVp55Ao0iBD6M0XuF1hvSBJ+MBJj7AuB1viHuUvBw+wfMEXfArjK5M9gvRnvhaX8KZdsk/bVi5X1ZCrMdaucyOw7ut9wWD5OwdIaddlkB5r4AeJeyHZmWWysnD50b3vZnnANplWfT5cgLJvI7sNbwPHrifYhrseMx9nnTdfSt9dooFmA+OeTq3NF5ftr7Q4XjfRJ2lR9PVhwGO1hDPhj4jSxcJJWzDgbCJRiPH8QIaheGsq/VYi7BPevvxu4K9rw7IPyuoKzQU8lez9Xf4OQxFJ6Sh/dWD187fIzkJxfNeZE1tqeHW0XCDGCIfbu9SgZAnmQL8KR3GcakX4J1/DrcrnuqUWDwx+6J5cC8kfovaKF/1Frs1q36aGYiczyqozmURAOaj3Ngi6SjV0HJX4EbcLEvySxBi8df/Bf5QSiHtRH3Yz76QiZVy3y4YB9rcWpf2mQPSPuARsEULswo9Tew4qs9nyUlvoAX8Sf8YWmYx9I+KHAwfgK96RgEA7fMAWCeay53bjEByW7zPP1QvVq9XaHYAxpMkOYRJa3cu9Bqv20zQvDwZ7IRhOJS8EBQx6CSfgHV9kuYAXwVkEIxJUlGjyhNqiDm4hXu/YX+8EuyJLnyI6VUl0EdhDPEgQ/jFyTyDFTRpTBjXAMJLQhnqtAnxTB62cso+Q/4JMTvVcGZCFW6QnK1JifGQ2LqVRPihffnd/Z1umlMynCQujMCf6Qu8u94WT6Mxe9djPC7BTJox7sU6v0jcF6J3rICq1PL8IzbqqjRwgfQwnngYz8o/NVz/Jf4bgFzAunJQqTIOns5+fK/UWB1+pAA0OIMDmqPpHEpH042vgRz9I3ROgOYf6GVP4IPw+zkHoHo77Q5YDfS/YhxmLgpz/NyjjHtRjvtGEawwRx3jDOItznwn6LgpGbFmSqjEhe/TUCM7TxsGKEfRia8qmBh0nf9GxxXFJ5+EEPMDWDiJN6cfWjxT0gv82KIgbPUw28SjFw30j+jAFj/ep2YPEuxjq2WfTLSTlDUAxjJXoB3waxdWWAzeDa4qfujVxQE9SpWuohhN+GXI4JEzWdtCBDLqOoJKMb2gCCyeWVZgET1z6qvLGOR/GxHahu3qq/TmZMTG698DEtwXLIHMJ//SZL5+XgjVgnCzShMbAdzAzN98mRGARDIrue65jXvDq2sSfFTJwM3DJyYKVfsSziaid9M4XW80fCmPx4BYkXM4mPDoWUJgG9wfjOFn+0IZ2/6oggQIx+ryIw3mi7wl6WCgsS8mn3v4r0LmjeoB4hEnvikSffh7uOVfFemIgGwuLpc7BGp15niDS7gqITfslSQTZD3nfH2Pzus6RaCmBCbSrGouAcEBTTC9ZYBL7P9jLuOslyeqhYAC/DVUfMzVjV9xqpiFWRLll2OL51zcXTEOrPu1agdG8OaBEBCfOOvFquPnUvzBNaVda5ktGODbrtrUkE2IZqwmx/ztBEpz11zDwiK4WSNU2/aP85KAx4Ma6wb6zidaTnAopJnYgIICqXx6eahm/tphg3CzvQn68I6zWRYq6aeiamguMKbnzSPQyUclqoAWBTfDfx4gX9/vm6cUzFhGMI+LqB3eB0jh3sOF933H05Zuy91AQQsUhBLn1raNtQy1JHGvqOgnFqeXLvl1pGD6w6OJannS/FUNwEETFAQ3Tu7W/Pj+fZ63tQelB/35HZB7ljzN02VacWMo1NNWN0FYDPJDcFb+ra08S7l1LfG2wXDzS3i3KW8sWfjWHCTeSRJXbyzKgC7hhTG1p1bW3ijLC81TVpNUb3wWBBPpvBwxGyCbte7YQRgM0U3LzXtvaQ3w6sdebsgL7jz77fDNV88C2Bc664jjNF58YV/kOPU8X+Ph6B5DpdHQXkaEfQacjHp/9O9fBVNexFBAAAAAElFTkSuQmCC', apple: 'data:image/png;base64,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', tel: '', password: '', loginname: '', telcode: '', type: '1' // 1表示账户名密码 0表示手机验证码 } }, computed: { adminameFlag () { if (this.loginname !== '' && /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])\S*$/.test(this.password)) { return false } else { return true } }, passwordTipFlag () { if (this.password === '' || /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])\S*$/.test(this.password)) { return false } else { return true } } }, methods: { ...mapActions({ loginAction: 'user/loginAction' }), changeType () { this.type = this.type === '1' ? '0' : '1' }, adminameLoginFn () { console.log('1111111') // 异步操作 // this.$store.dispatch('user/loginAction', { // loginname: this.loginname, // password: this.password // }) this.loginAction({ loginname: this.loginname, password: this.password }) } } } </script> <style lang="stylus"> .container .box .content padding 30px 15px background-color #ffffff .form .my-button margin-top 30px .more margin-top 20px display flex li flex 1 &:nth-child(1) text-align left &:nth-child(2) text-align right .my-divider margin-top 80px .passwordTip color #f66 font-size 12px </style>
15.4 退出时改变状态
s r c/views/user/idnex.vue
<template> <div class="box"> <header class="header">user header</header> <div class="content"> <button v-if="loginState" @click="logout">退出</button> <button v-else @click="login"> 登录</button> {{ listLen }} </div> </div> </template> <script> import { mapState, mapMutations, mapGetters } from 'vuex' export default { computed: { // loginState () { // return this.$store.state.user.loginState // } ...mapState({ loginState: state => state.user.loginState }), // listLen () { // return this.$store.getters.len // } ...mapGetters({ listLen: 'len' }) }, methods: { ...mapMutations({ changeLoginState: 'user/changeLoginState' // key随意 - 事件,value即为mutation中的值,一般情况下二者相同 }), logout () { localStorage.removeItem('token') localStorage.removeItem('userid') localStorage.removeItem('loginState') // this.$store.commit('user/changeLoginState', false) // 二次渲染的关键 this.changeLoginState(false) // 事件中通过 this.key事件(参数) this.$router.push('/login') }, login () { this.$router.push('/login') } } } </script>
16.使用状态管理器管理购物车的数据 - 作业
16.1 添加模块
s r c/store/cart.js
import { getCartList } from './../../api/cart' export default { namespaced: true, state: { cartList: [] }, mutations: { changeCartList (state, data) { state.cartList = data } }, actions: { getCartListAction (context, params) { return new Promise(resolve => { // 后续的操作交给组件 getCartList(params).then(res => { resolve(res) }) }) } } }
16.2 注册模块
// src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' import user from './modules/user' import cart from './modules/cart' Vue.use(Vuex) export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, getters: { // 可以看作状状态管理器的计算属性 showNum (state) { // 控制底部选项卡中数量显示还是不显示 // state 这个参数其实就是 所有的状态 return state.user.isLogin }, totalNum (state) { // 判断很关键 return state.cart.cartList && state.cart.cartList.reduce((sum, item) => { return item.flag ? sum + item.num : sum + 0 }, 0) }, totalPrice (state) { return state.cart.cartList && state.cart.cartList.reduce((sum, item) => { return item.flag ? sum + item.originprice * item.num : sum + 0 }, 0) * 100 } }, modules: { user, cart } })
16.3 组件使用状态
s r c/views/cart/index.vue
<template> <div class="box"> <header class="header"> <van-nav-bar :title="'购物车-' + totalNum" left-arrow @click-left="$router.back()" /> </header> <div class="content"> <div v-if="empty"> <van-empty description="购物车空空如也"> <router-link to="/kind"> <van-button round type="danger" class="bottom-button"> 立即选购 </van-button> </router-link> </van-empty> </div> <div v-else> <van-row v-for="item of cartList" :key="item.cartid"> <van-col span="3" class="checkboxList"> <van-checkbox @change="updateFlag(item.cartid, item.flag)" v-model="item.flag"></van-checkbox> </van-col> <van-col span="21"> <van-swipe-cell> <van-card :price="item.originprice" :title="item.proname" :thumb="item.img1" > <template #num> <van-stepper @change="updateNum(item.cartid, item.num)" v-model="item.num" max="5" theme="round" button-size="22" /> </template> </van-card> <template #right> <van-button @click="removeItem(item.cartid)" square text="删除" type="danger" class="delete-button" /> </template> </van-swipe-cell> </van-col> </van-row> <van-submit-bar :disabled="flag" :price="totalPrice" button-text="提交订单" @submit="onSubmit"> <van-checkbox @click="updateAllFlag" v-model="checked">全选</van-checkbox> </van-submit-bar> </div> </div> </div> </template> <script> import Vue from 'vue' import { mapState, mapActions, mapMutations, mapGetters } from 'vuex' import { NavBar, Stepper, Empty, Button, Col, SubmitBar, Row, Card, Checkbox, SwipeCell } from 'vant' import { remove, updateCartNum, selectall, selectone } from './../../api/cart' Vue.use(NavBar) Vue.use(Empty) Vue.use(Button) Vue.use(Col) Vue.use(Row) Vue.use(Checkbox) Vue.use(Card) Vue.use(SwipeCell) Vue.use(Stepper) Vue.use(SubmitBar) export default { data () { return { // cartList: [], // 8.删除购物车的初始化状态 empty: true, checked: false } }, computed: { // 1.获取状态管理器中的登录状态 ...mapState({ isLogin: state => state.user.isLogin, // 9.获取购物车中的状态 cartList: state => state.cart.cartList }), // 10.获取vuex中的totalNum - 不添加命名空间 ...mapGetters({ // key 代表当前组件中需要的字段,value代表的是vuex中的 getters中设置的计算属性 totalNum: 'totalNum', totalPrice: 'totalPrice' }), // totalNum () { // return this.cartList.reduce((sum, item) => { // return item.flag ? sum + item.num : sum + 0 // }, 0) // }, // totalPrice () { // return this.cartList.reduce((sum, item) => { // return item.flag ? sum + item.originprice * item.num : sum + 0 // }, 0) * 100 // }, flag () { // 提交订单按钮可点不可点 return this.totalNum <= 0 } }, methods: { // 3.生成获取购物车数据的方法 ...mapActions({ getCartListAction: 'cart/getCartListAction' }), // 5.生成修改购物车数据的方法 ...mapMutations({ changeCartList: 'cart/changeCartList' }), onSubmit () { }, updateFlag (cartid, flag) { console.log(cartid, flag) selectone({ cartid, flag }).then(res => { this.getCartListData() }) }, updateAllFlag () { selectall({ userid: localStorage.getItem('userid'), type: this.checked }).then(res => { this.getCartListData() }) }, getCartListData () { // 4.调用vuex中的actions this.getCartListAction({ userid: localStorage.getItem('userid') }).then(res => { // 没有数据 if (res.data.code === '10020') { this.empty = true // 6.修改vuex中的状态 this.changeCartList([]) } else { this.empty = false // 7.修改vuex中的状态 this.changeCartList(res.data.data) // 判断全选是不是被选中 // every 所有的条件都满足返回为true,只要有一个不满足返回为false this.checked = res.data.data.every(item => item.flag) } }) }, updateNum (cartid, num) { console.log(cartid, num) updateCartNum({ cartid, num }).then(res => { this.getCartListData() // 重新获取最新的数据 }) }, removeItem (cartid) { remove({ cartid }).then(res => { this.getCartListData() // 重新获取最新的数据 }) } }, mounted () { // 2.依据状态管理器中的登录状态进一步操作 if (this.isLogin) { this.getCartListData() } else { this.$router.push('/login') } } } </script> <style lang="stylus" scoped> .checkboxList display flex height 1.04rem justify-content center align-items center background-color #fafafa .delete-button { height: 100%; } </style>
16.4 添加底部的数量标签
s r c/app.vue
<template> <div class="container"> <!-- <div class="box"> <header class="header">header</header> <div class="content">content</div> </div> --> <router-view></router-view> <footer class="footer" v-if="!$route.meta.hidden"> <ul> <!-- router-link 默认会渲染为 a 标签 使用tag属性设置 转换的标签 --> <router-link to="/home" tag="li"> <span class="iconfont icon-shouye"></span> <p>首页</p> </router-link> <router-link to="/kind" tag="li"> <span class="iconfont icon-leimupinleifenleileibie"></span> <p>分类</p> </router-link> <router-link to="/cart" tag="li"> <span class="iconfont icon-shopping-cart"></span> <p>购物车{{ isLogin ? totalNum : ''}}</p> </router-link> <router-link v-if="isLogin" to="/user" tag="li"> <span class="iconfont icon-wode"></span> <p>我的</p> </router-link> <router-link v-else to="/login" tag="li"> <span class="iconfont icon-wode"></span> <p>未登录</p> </router-link> </ul> </footer> <div class="tip">请将屏幕竖向浏览</div> </div> </template> <script> import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' // 组件中获取状态管理器的状态可以使用计算属性 export default { methods: { // 2.页面的刷新,导致数据的重置,获取最新的数据 ...mapActions({ getCartListAction: 'cart/getCartListAction' }), ...mapMutations({ changeCartList: 'cart/changeCartList' }) }, mounted () { // 3.获取实时的购物车中的数据 this.getCartListAction({ userid: localStorage.getItem('userid') }).then(res => { if (res.data.code === '10020') { this.changeCartList([]) } else { this.changeCartList(res.data.data) } }) }, computed: { // isLogin () { // return this.$store.state.user.isLogin // } // ... 扩展运算符其实代表的就是合并对象 // computed 是一个对象,还可以写其他的自定义的计算属性 ...mapState({ // 使用mapState辅助函数获取状态管理器中的状态,结合 扩展运算符完成 isLogin: state => state.user.isLogin }), // 1.获取购物车中的数量 ...mapGetters({ totalNum: 'totalNum' }) } } </script> <style lang="stylus"> 。。。。 </style>
16.5 详情页面底部的数量
<template> <div class="box"> <header class="header"> <!-- 使用导航栏组件 --> <van-nav-bar :title="proname" left-arrow @click-left="$router.back()" > <template #right> <!-- 头部右侧的下拉选择菜单 --> <van-popover v-model="showPopover" theme="dark" trigger="click" :actions="actions" placement="bottom-end" @select = "moreEvent" > <template #reference> <van-icon name="more-o" size="18"/> </template> </van-popover> </template> </van-nav-bar> </header> <div class="content"> <van-swipe class="detail-swipe" indicator-color="white"> <van-swipe-item @click="previewImage(index)" v-for="(item, index) of banners" :key="index"> <van-image fit="cover" :src="item" /> </van-swipe-item> </van-swipe> <!-- 播放视频的图标 --> <van-icon v-if="flag" @click="openOverLay" name="play-circle-o" class="playBtn" size="36" color="#f66"/> <!-- 详情 --> <van-tag type="danger">{{ brand }}</van-tag> {{ category }} <h3>{{ proname }}</h3> <div>{{ originprice }}</div> <van-goods-action> <van-goods-action-icon icon="chat-o" text="客服" color="#ee0a24" /> <!-- 2.展示数量 --> <van-goods-action-icon icon="cart-o" text="购物车" :badge=" isLogin && totalNum > 0 ? totalNum : ''" /> <van-goods-action-icon icon="star" text="已收藏" color="#ff5000" /> <van-goods-action-button type="warning" @click="addProToCart" text="加入购物车" /> <van-goods-action-button type="danger" text="立即购买" /> </van-goods-action> <!-- 分享面板 --> <van-share-sheet v-model="showShare" title="立即分享给好友" :options="options" /> </div> <!-- 视频播放的遮罩层 --> <van-overlay :show="show" @click="closeOverLay" class="vdoBox"> <video ref="vdo" width="100%" src="https://vod.300hu.com/4c1f7a6atransbjngwcloud1oss/1b147065379705072794210305/v.f20.mp4?dockingId=f782a297-b570-4701-bcea-9ae0c0749efe&storageSource=3" controls> </video> </van-overlay> </div> </template> <script> import Vue from 'vue' import { mapState, mapGetters, mapMutations, mapActions } from 'vuex' import { getProDetail } from './../../api/detail' import { addCart } from './../../api/cart' import { Toast, ImagePreview, Overlay, ShareSheet, Popover, NavBar, Swipe, SwipeItem, Tag, GoodsAction, GoodsActionIcon, GoodsActionButton } from 'vant' Vue.use(Toast) Vue.use(ImagePreview) Vue.use(Overlay) Vue.use(ShareSheet) Vue.use(Popover) Vue.use(NavBar) Vue.use(Swipe) Vue.use(SwipeItem) Vue.use(Tag) Vue.use(GoodsAction) Vue.use(GoodsActionIcon) Vue.use(GoodsActionButton) export default { computed: { // ...mapState({ isLogin: state => state.user.isLogin }), // 1.获取vuex中的totalNum数量 ...mapGetters({ totalNum: 'totalNum' }) }, data () { return { proid: '', proname: '', originprice: 0, discount: 0, banners: [], sales: 0, category: '', brand: '', showPopover: false, // 控制右侧更多菜单的显示和隐藏 actions: [{ text: '首页' }, { text: '分类' }, { text: '购物车' }, { text: '我的' }, { text: '分享' }], showShare: false, // 控制分享面板的显示和隐藏 options: [ // 分享面板 { name: '微信', icon: 'wechat' }, { name: '微博', icon: 'weibo' }, { name: '复制链接', icon: 'link' }, { name: '分享海报', icon: 'poster' }, { name: '二维码', icon: 'qrcode' } ], show: false, // 控制视频播放遮罩层的显示和隐藏 flag: true // 控制播放按钮的显示和隐藏 } }, mounted () { this.proid = this.$route.params.proid getProDetail(this.proid).then(res => { console.log(res) // 当前接口的banenrs数据有问题,需要前端自行处理 ---- 不意味着以后别的项目也需要 this.banners = res.data.data.banners[0].split(',') this.proname = res.data.data.proname this.originprice = res.data.data.originprice this.discount = res.data.data.discount this.sales = res.data.data.sales this.category = res.data.data.category this.brand = res.data.data.brand }) }, methods: { // 3.为了加入购物车之后更新数据 ...mapActions({ getCartListAction: 'cart/getCartListAction' }), ...mapMutations({ changeCartList: 'cart/changeCartList' }), addProToCart () { // 前端校验登录 本地存储的都是字符串 if (localStorage.getItem('isLogin') === 'true') { // 前端已经表示登录 // 调用加入购物车接口 addCart({ // 一定要把参数传递完整,否则会有小发现 userid: localStorage.getItem('userid'), proid: this.proid, num: 1 }).then(res => { Toast('加入购物车成功') // 4.更新购物车的数据 this.getCartListAction({ userid: localStorage.getItem('userid') }).then(res => { if (res.data.code === '10020') { this.changeCartList([]) } else { this.changeCartList(res.data.data) } }) }) } else { this.$router.push('/login') } }, previewImage (index) { ImagePreview({ // 预览图片 images: this.banners, startPosition: index }) }, openOverLay () { // 打开遮罩层,播放视频,隐藏按钮 this.show = true this.flag = false this.$refs.vdo.play() }, closeOverLay () { // 关闭遮罩层,暂停视频,显示按钮 this.$refs.vdo.pause() this.flag = true this.show = false }, moreEvent (action, index) { // 右上角更多菜单 console.log(index, action) switch (index) { case 0: this.$router.push('/home') break case 1: this.$router.push('/kind') break case 2: this.$router.push('/cart') break case 3: this.$router.push('/user') break case 4: this.showShare = true // 控制分享面板的显示 break default: break } } } } </script> <style lang="stylus"> .detail-swipe height 2.6rem .van-popover[data-popper-placement=bottom-end] .van-popover__arrow { right: 0px; } .playBtn position fixed top 2.4rem left 50% transform translateX(-50%) z-index 1000 .vdoBox display flex justify-content center align-items center </style>
如果删掉本地存储的数据,当用户在首页后者详情页面重新刷新页面时,自动跳转到了 登录页面, ------ qq 微信
但是我们希望 首页,分类,详情,活动,即使用户不登录也可以查看 ---- 修改axios的配置
// http://www.axios-js.com/zh-cn/docs/ import axios from 'axios' import router from './../router' import store from './../store' // **************************重中之重**************************************** // 开发环境 yarn serve // 生产环境 yarn build // development production const isDev = process.env.NODE_ENV === 'development' // 创建axios实例 // http://www.axios-js.com/zh-cn/docs/#axios-create-config const request = axios.create({ // baseUrl 实际请求的地址是 baseURL + 请求地址 // http://121.89.205.189/api/banner/list ===》 baseURL + '/banner/list' // baseURL: 'http://121.89.205.189/api', // 项目上线时无需修改baseURL地址 ---- 需要提前知道线上接口的地址 // baseURL: isDev ? 'http://121.89.205.189/api' : 'http://121.89.205.189/api', // 如果使用了 代理 解决跨域问题 baseURL: isDev ? '' : 'http://121.89.205.189/api', timeout: 6000 // 网络超时时间 }) // axios 的拦截器 // http://www.axios-js.com/zh-cn/docs/#%E6%8B%A6%E6%88%AA%E5%99%A8 // 请求拦截器 ---- 所有的数据在请求之前都会执行的代码 --- 显示loading的动画效果/给接口添加token request.interceptors.request.use((config) => { // 在请求之前做些什么 // 给所有的请求都传递token信息 config.headers.common.token = localStorage.getItem('token') || '' return config }, (error) => { return Promise.reject(error) }) // 响应拦截器 ---- 在拿到接口的数据之前都会执行的代码 --- 隐藏loading的动画效果/验证token request.interceptors.response.use((response) => { // 在响应时做些什么 // token 没有传递 token 传递了只不过是错误的 token 传递了 失效了 if (response.data.code === '10119') { console.log(111111111) // 引入路由器,跳转到登录页面 ****************************重中之重**************************************** if (store.state.user.isLogin) { router.push('/login') } } return response }, (error) => { return Promise.reject(error) }) // 暴露自定义的axios export default request
16.6 总结
-
选择是否需要分模块(不管项目大小都可分 - 参照16点,但是一般小项目部分模块 - 参照 15点)
-
分模块步骤
-
1.先写模块(state, mutations, actions, namespaced)
namespaced 是关键,使用 mutations 和 actions 时需要 指明模块的名称
-
2.注册模块 (state, getters, mutations, actions, modules)
modules是关键,它的key就是模块的名称
getters 是计算属性
state 可以是 所有组件都需要的模块的状态
-
3.组件中使用辅助函数执行业务(mapState, mapGetters,mapMutations, mapActions)
-
更多推荐
所有评论(0)