外卖webAPP(三)
一,搭建商品详情信息页面
注;新建shop路由组件,然后点击点餐的路由子组件(shopgoods),点击评价的路由子组件(shopratings),点击商家的路由子组件(shopInfo),顶部有个固定组件(shopheader)
在views中新建shop组件,再建shopgoods子组件,shopratings, shopInfo子组件,然后在compoents中新建shopheader组件
配置路由组件
import Shop from '@/views/Shop/shop.vue' import shopGoods from '@/views/Shop/shopGoods' import shopInfo from '@/views/Shop/shopInfo' import shopRatings from '@/views/Shop/shopRatings' { path: '/shop', component: Shop, children: [ { path: '/shop/shopgoods', component: shopGoods }, { path: '/shop/shopinfo', component: shopInfo }, { path: '/shop/shopratings', component: shopRatings }, { path:'', redirect: '/shop/goods' } ] }
在shop组件中
<template> <div> <ShopHeader></ShopHeader> <div class="item"> <div class="tab-item"> <router-link to="/shop/goods"> 点餐</router-link> </div> <div class="tab-item"> <router-link to="/shop/shopratings"> 评价</router-link> </div> <div class="tab-item"> <router-link to="/shop/shopinfo"> 商家</router-link> </div> </div> <router-view></router-view> </div> </template> <script> import ShopHeader from '@/components/ShopHeader' export default { name: 'Shop', data() { return {} }, components: { ShopHeader } } </script> <style scoped lang="stylus"></style>
1.1, mockjs模拟请求操作,组件获取数据
创建mock文件夹,创建data.json, mockServer.js(提供服务,模拟接口) 安装mockjs npm i mockjs
mockServer.js 文件内容
/* 使用mockjs提供mock数据接口 */ import Mock from 'mockjs' import data from './data.json' // 返回goods的接口 Mock.mock('/goods', { code: 0, data: data.goods }) // 返回ratings的接口 Mock.mock('/ratings', { code: 0, data: data.ratings }) // 返回info的接口 Mock.mock('/info', { code: 0, data: data.info }) // export default ??? 不需要向外暴露任何数据, 只需要保存能执行即可
在入口文件main.js导入mock,
import './mock/MockServer.js' // 加载mockServer即可
在 api文件夹中新建mockAjax.js, 二次封装关于mock的ajax
// 对axios的二次封装 import axios from 'axios' const service = axios.create({ timeout: 4000 }) // 请求拦截器 service.interceptors.request.use(config => { return config }) // 响应拦截器 service.interceptors.response.use( response => { return response.data }, error => { alert('请求出错' + error.message || '未知错误') //以后不允许用户继续处理: 中断promise链 return new Promise(() => {}) //返回pending状态的promise 中断 } ) export default service
在api---index.js中,使用mock的接口去封装接口函数
import mockAjax from './mockAjax' * 获取商家信息 */ export const reqShopInfo = () => mockAjax.get('/info') /** * 获取商家评价数组 */ export const reqShopRatings = () => mockAjax.get('/ratings') /** * 获取商家商品数组 */ export const reqShopGoods = () => mockAjax.get('/goods')
在vuex中发送请求,获取数据
// 异步获取商家信息 async getShopInfo({ commit }) { const result = await reqShopInfo() if (result.code === 0) { const info = result.data commit('RECEIVE_INFO', info) } }, // 异步获取商家评价列表 async getShopRatings({ commit }) { const result = await reqShopRatings() if (result.code === 0) { const ratings = result.data commit('RECEIVE_RATINGS', ratings) } }, // 异步获取商家商品列表 async getShopGoods({ commit }) { const result = await reqShopGoods() if (result.code === 0) { const goods = result.data commit('RECEIVE_GOODS', goods) } }
RECEIVE_INFO(state, info) { state.info = info }, RECEIVE_RATINGS(state, ratings) { state.ratings = ratings }, RECEIVE_GOODS(state, goods) { state.goods = goods }
goods: [], // 商品列表 ratings: [], // 商家评价列表 info: {}, // 商家信息
在shop组件中dispatch到vuex,
mounted() { this.$store.dispatch('getShopInfo') }
1.2, 搭建shopHeader组件页面
首先dispatch到vuex中,获取info商家的数据
mounted() { this.$store.dispatch('getShopInfo') },
computed: { ...mapState(['info']),
顶部左侧的返回按钮,需要路由返回
<div class="shop-header"> <nav class="shop-nav" :style="{backgroundImage: `url(${info.bgImg})`}"> <a class="back" @click="$router.back()"> <i class="iconfont icon-arrow_left"></i> </a>
填充数据,关于a.b.c三级表达式获取不到值的问题
将颜色类名动态获取效果
data() { return { supportClasses: ['activity-green', 'activity-red', 'activity-orange'] }
报错提示
此时有两种解决方式
第一种:在他们的祖先元素,做个v-if判断即可
<div class="shop-header-discounts" v-if="info.supports" > <div class="discounts-left"> <div class="activity" :class="supportClasses[info.supports[0].type]"> <span class="content-tag"> <span class="mini-tag">{{info.supports[0].name}}</span> </span> <span class="activity-content ellipsis">{{info.supports[0].content}}</span> </div>
第二种,在vuex的getters中直接定义supports数据
const getters = { info: state => state.info || {}, supports: state => state.info.supports || [] }
在shopHeader中获取数据
computed: { ...mapState(['info']), ...mapGetters(['supports']), // 对于a.b.c三级表达式未取到值的做法 supportOne() { return this.supports[0] ? this.supports[0] : {} } }
<div class="shop-header-discounts"> <div class="discounts-left"> <div class="activity" :class="supportClasses[supportOne.type]"> <span class="content-tag"> <span class="mini-tag">{{ supportOne.name }}</span> </span> <span class="activity-content ellipsis">{{ supportOne.content }}</span> </div>
一共有两个下拉框,一个时品牌信息下拉框,一个时优惠下拉框
此时需要用到变量标识(布尔值),点击按钮,v-show 动态显示弹框
data() { return { supportClasses: ['activity-green', 'activity-red', 'activity-orange'], shopShow: false, supportShow: false }
<div class="shop-header-discounts" @click="toggleSupportShow">
<div class="shop-content" @click="toggleShopShow">
<div class="activity-sheet" v-show="supportShow"> <div class="activity-sheet-content"> <h2 class="activity-sheet-title">优惠活动</h2>
<div class="shop-brief-modal" v-show="shopShow"> <div class="brief-modal-content"> <h2 class="content-title"> <span class="content-tag"> <span class="mini-tag">品牌</span>
methods:{ toggleShopShow () { this.shopShow = !this.shopShow }, toggleSupportShow () { this.supportShow = !this.supportShow }
关闭按钮也需要点击事件
<div class="mask-footer" > <span class="iconfont icon-close" @click="toggleShopShow"></span>
<div class="activity-sheet-close" @click="toggleSupportShow"> <span class="iconfont icon-close"></span> </div>
两个弹框需要一个过渡
样式
.shop-brief-modal position fixed top 0 left 0 right 0 bottom 0 display flex justify-content center align-items center z-index 52 flex-direction column color #333 &.fade-enter-active,&.fade-leave-active transition opacity 1s &.fade-enter,&.fade-leave-to opacity 0 .brief-modal-cover
弹框数据填充
<transition name="fade"> <div class="activity-sheet" v-show="supportShow"> <div class="activity-sheet-content"> <h2 class="activity-sheet-title">优惠活动</h2> <ul class="list"> <li class="activity-item " v-for="(support, index) in supports" :key="support.type" :class="supportClasses[support.type]"> <span class="content-tag"> <span class="mini-tag">{{support.name}}</span> </span> <span class="activity-content" >{{support.content}}</span > </li>
1.3,shopgoods组件页面搭建
先dispatch到vuex中,获取数据
mounted() { this.$store.dispatch('getShopGoods')
computed: { ...mapState(['goods']) }
数据填充到模板
实现2个列表滑动, 1.当前分类, 2.当滑动右侧列表时,更新当前分类
需要用到better-scroll第三方插件, http://ustbhuangyi.github.io/better-scroll/
https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html (重要)
安装; npm install better-scroll -S # 安装带有所有插件的 BetterScroll
引入,import BetterScroll from 'better-scroll'
<div class="menu-wrapper" ref="menuWrapper"> <ul> <li class="menu-item current" v-for="(good, index) in goods" :key="index" > <span class="text bottom-border-1px"> <img class="icon" :src="good.icon" v-if="good.icon" /> {{ good.name }} </span> </li> </ul> </div>
.menu-wrapper为包裹容器的div
mounted() { this.$store.dispatch('getShopGoods', () => {// 数据更新后执行 this.$nextTick(() => { // 页面更新显示后执行 new BetterScroll('.menu-wrapper') new BetterScroll('.foods-wrapper') }) }) },
// 异步获取商家商品列表 async getShopGoods({ commit },callback) { const result = await reqShopGoods() if (result.code === 0) { const goods = result.data commit('RECEIVE_GOODS', goods) // 数据更新了, 通知一下组件 callback && callback() } }
1.4,创建BetterScroll,给创建BetterScroll绑定滑动监听(scroll),已经结束监听(scroll-End),计算滚动的Y轴位置
逻辑分析,滑动右侧食物的列表,自动定位到左侧食物类别的名称。。右侧每个li类别有个颜色类名current,我们计算一个索引变量(currentIndex),条件(tops,scrollY)
tops数组的findIndex()遍历,条件,找到滚动在当前的位置是否在当前li和下一个li之间,找到li的索引值, 然后左侧食物类别,该currentIndex和模板遍历的li的index判断,
current类名是否生效。
1.获取右侧食物滑动的滚动的y轴位置,
2.获取每个右侧食物类别的高度(li),将他们组成一个数组
3.计算滚动到当前位置时右侧食物类别(li)的索引,此时的索引就是左侧食物类别的索引
有个小bug,如果左侧食物快速滚动,右侧食物类别不会自动滚动,如要监听scrollEnd事件,让滚动强行停止,计算scrollY坐标
data() { return { scrollY: 0, tops: [] } }, mounted() { this.$store.dispatch('getShopGoods', () => { // 数据更新后执行 this.$nextTick(() => { // 页面更新显示后执行 this._initScroll() this._initTops() }) }) }, computed: { ...mapState(['goods']), currentIndex() { // 得到条件数据 const { tops, scrollY } = this // 根据条件计算产生一个结果,计算鼠标的y轴位置在哪个li区间,返回的是符合条件的第一个索引 const index = tops.findIndex((item, index) => { // scrollY>=当前top && scrollY<下一个top, 不能小于等于下一个 return scrollY >= item && scrollY < tops[index + 1] }) return index } }, methods: { // 计算滚动后的位置 _initScroll() { // 列表显示之后创建BetterScroll new BetterScroll('.menu-wrapper') this.foodsScroll = new BetterScroll('.foods-wrapper', { probeType: 2, // 因为惯性滑动不会触发 click: true }) // 给右侧列表绑定scroll监听,获取y轴坐标 this.foodsScroll.on('scroll', ({ x, y }) => { // 绝对值 this.scrollY = Math.abs(y) // console.log(x, this.scrollY) }) // 给右侧列表绑定scroll结束的监听 this.foodsScroll.on('scrollEnd', ({ x, y }) => { console.log('scrollEnd', x, y) this.scrollY = Math.abs(y) }) }, //计算li的top值,组成一个数组 _initTops() { const tops = [] let top = 0 tops.push(top) // 找到所有分类的li,获取他们的top值 const lis = this.$refs.foodsUl.getElementsByClassName('food-list-hook') // const lis = this.$refs.foodsUl.children // 伪数组转换成真数组 Array.from(lis).forEach(item => { top += item.clientHeight tops.push(top) }) this.tops = tops console.log(this.tops) }
模板,左侧食物类别名称
<div class="menu-wrapper" ref="menuWrapper"> <ul> <li class="menu-item" v-for="(good, index) in goods" :key="index" :class="{ current: index === currentIndex }" > <span class="text bottom-border-1px"> <img class="icon" :src="good.icon" v-if="good.icon" /> {{ good.name }} </span> </li> </ul>
右侧食物信息
<div class="foods-wrapper" ref="foodsWrapper"> <ul ref="foodsUl"> <li class="food-list-hook" v-for="(good, index) in goods" :key="index" >
此时,滑动右侧食物的列表,自动定位到左侧食物类别的名称。
点击左侧食物类别名称,左侧的食物信息自动滑动到当前位置
<div class="menu-wrapper" ref="menuWrapper"> <ul> <li class="menu-item" v-for="(good, index) in goods" :key="index" :class="{ current: index === currentIndex }" @click="clickMenuItem(index)" >
// 点击左侧食物类别名称,自动滑动右侧的食物信息 clickMenuItem(index) { // 获取当前的食物信息位置 const scrollY = this.tops[index] // 立即更新scrollY(让点击的分类项成为当前分类) this.scrollY = scrollY // 让左侧食物信息自动滚动到当前食物 this.foodsScroll.scrollTo(0, -scrollY, 300) }
1.5,在右侧食物信息中,将加减数量功能拆分一个组件CartControl,
在shopGoods中定义子组件CartControl
<ul ref="foodsUl"> <li class="food-list-hook" v-for="(good, index) in goods" :key="index"> <h1 class="title">{{good.name}}</h1> <ul> <li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index" @click="showFood(food)"> <div class="icon"> <img width="57" height="57" :src="food.icon"> </div> <div class="content"> <h2 class="name">{{food.name}}</h2> <p class="desc">{{food.description}}</p> <div class="extra"> <span class="count">月售{{food.sellCount}}份</span> <span>好评率{{food.rating}}%</span> </div> <div class="price"> <span class="now">¥{{food.price}}</span> <span class="old" v-if="food.oldPrice">¥{{food.oldPrice}}</span> </div> <div class="cartcontrol-wrapper"> <CartControl :food="food"/> </div>
在cartControl组件中,
1.页面渲染时,默认是没有商品数量的,food中没有count数量属性,此时,点击加减号按钮,需要dispatch到vuex中去,在vuex中更改数据count
需要用到Vue.set(),来动态绑定自定义属性。起初没有数量,默认是没有减号按钮的
2.判断是否是加减按钮,需要传递一个标识来判断,布尔值
props: {
food: Object
},
<template> <div class="cartcontrol"> <!-- 减号 --> <div class="iconfont icon-remove_circle_outline" v-if="food.count" @click="updateFoodCount(false)"></div> <div class="cart-count" v-if="food.count">{{food.count}}</div> <!-- 加号 --> <div class="iconfont icon-add_circle" @click="updateFoodCount(true)"></div> </div> </template>
methods:{ updateFoodCount(isAdd){ // 分发到vuex,去更改food.count数量 this.$store.dispatch('updateFoodCount',{food:this.food, isAdd}) } }
在vuex中
// 同步更新food中的count值 updateFoodCount({ commit }, { isAdd, food }) { // 判断加减 if (isAdd) { commit('INCREMENT_FOOD_COUNT', food) } else { // 减号 commit('DECREMENT_FOOD_COUNT', food) } }
INCREMENT_FOOD_COUNT(state, food) { if(!food.count){ // 第一次增加判断,不然count是Nan // food.conut= 1 // 新增属性(没有数据绑定) /* 对象 属性名 属性值 */ Vue.set(food,'count',1) // 让新增的属性也有数据绑定 }else{ food.count++ } }, DECREMENT_FOOD_COUNT(state, food) { if (food.count) { // 只有有值才去减 food.count-- } }
3. 当点击加号按钮,然后点击减号数量,知道数量为0,那么减号和数量都会消失,v-if, 给减号的消失加一个动画,旋转向右消失
<template> <div class="cartcontrol"> <transition name="move"> <!-- 减号 --> <div class="iconfont icon-remove_circle_outline" v-if="food.count" @click="updateFoodCount(false)"></div> </transition> <div class="cart-count" v-if="food.count">{{food.count}}</div> <!-- 加号 --> <div class="iconfont icon-add_circle" @click="updateFoodCount(true)"></div> </div> </template>
.icon-remove_circle_outline display: inline-block padding 6px line-height 24px font-size 24px color $green &.move-enter-active, &.move-leave-active transition all .3s &.move-enter, &.move-leave-to opacity 0 transform translateX(15px) rotate(180deg)
1.6,食物信息弹框功能在shopGoods中定义子组件food
食物信息弹框功在shopGoods中定义子组件food,点击每个食物li,弹框,点击返回按钮,弹框消失
给子组件绑定一个监听事件,当子组件点击返回按钮,通知父组件,更改isFoodShow
data() { return { scrollY: 0, tops: [], food:{}, isFoodShow: false, }
// 点击右侧每个食物,弹框 showFood(food){ // 数据赋值 this.food = food this.isFoodShow = true },
在子组件food中,点击返回按钮,
props:{
food: Object
},
<div class="back" @click="toggleShow"> <i class="iconfont icon-arrow_left"></i> </div>
// 点击返回按钮 toggleShow(){ // 给父组件传递信息,让他修改变量isShowFood this.$emit('foodShow') }
父组件shopGoods
// 子组件向父组件传递信息 foodShow(){ this.isFoodShow = false }
foods模板填充数据,也有一个加减数量的组件
<div class="food"> <div class="food-content"> <div class="image-header"> <img :src="food.image" /> <p class="foodpanel-desc">{{food.info}}</p> <div class="back" @click="toggleShow"> <i class="iconfont icon-arrow_left"></i> </div> </div> <div class="content"> <h1 class="title">{{food.name}}</h1> <div class="detail"> <span class="sell-count">月售 {{food.sellCount}} 份</span> <span class="rating">好评率 {{food.rating}}%</span> </div> <div class="price"> <span class="now">¥{{food.price}}</span> <span class="old" v-show="food.oldPrice">¥{{food.oldPrice}}</span> </div> <div class="cartcontrol-wrapper"> <CartControl :food="food"></CartControl> </div> </div> </div> <div class="food-cover" @click="toggleShow"></div> </div>
此时有个小bug,点击右侧食物列表的li的加号按钮,应该是出现加减号,以及数量,但是food弹框出现了
因为点击加号的时候(updateFoodCount),事件冒泡出来了,传递到了li的点击事件showFood
<ul> <li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index" @click="showFood(food)" > <div class="icon"> <img width="57" height="57" :src="food.icon" /> </div> <div class="content"> <h2 class="name">{{ food.name }}</h2> <p class="desc">{{ food.description }}</p> <div class="extra"> <span class="count">月售 {{ food.sellCount }} 份</span> <span>好评率 {{ food.rating }}%</span> </div> <div class="price"> <span class="now">¥{{ food.price }}</span> <span class="old" v-if="food.oldPrice" >¥{{ food.oldPrice }}</span > </div> <div class="cartcontrol-wrapper"> <CartControl :food="food"></CartControl> </div> </div> </li>
<template> <div class="cartcontrol"> <transition name="move"> <!-- 减号 --> <div class="iconfont icon-remove_circle_outline" v-if="food.count" @click="updateFoodCount(false)"></div> </transition> <div class="cart-count" v-if="food.count">{{food.count}}</div> <!-- 加号 --> <div class="iconfont icon-add_circle" @click="updateFoodCount(true)"></div> </div> </template>
解决方式
<template> <div class="cartcontrol"> <transition name="move"> <!-- 减号 --> <div class="iconfont icon-remove_circle_outline" v-if="food.count" @click.stop="updateFoodCount(false)"></div> </transition> <div class="cart-count" v-if="food.count">{{food.count}}</div> <!-- 加号 --> <div class="iconfont icon-add_circle" @click.stop="updateFoodCount(true)"></div> </div>
1.7,底部购物车功能实现shopCart, shopGoods的子组件
新建shopCart组件,功能逻辑
1.在vuex中自定义一个购物车商品空数组cartFoods,当点击食物添加按钮,此时需要将该food对象push到cartFood数组中
2.然后在vuex中getters中计算中食物总数量totalCount, 食物总价格totalPrice,
3.在shopCart中,从vuex获取info,totalCount,totalPrice
4.highlight类名只要有食物数量才会高亮,并且有才会显示, 右下角的的起送费有三种不同的文本,并且有个enough类名显示高亮,not-enough不显示高亮
判断依据是总价格是否大于最小的配送价格
info: {}, // 商家信息 cartFoods: [], // 购物车中食物的列表
vuex中getters
const getters = { info: state => state.info || {}, supports: state => state.info.supports || [], totalCount: state => state.cartFoods.reduce((pre, item) => pre + item.count, 0), // totalCount(state){ // return state.cartFoods.reduce((pre,item)=> pre + item.count, 0) // }, totalPrice: state => state.cartFoods.reduce((pre, item) => pre + item.count * item.price, 0) }
computed: { ...mapGetters(['totalCount', 'totalPrice']), ...mapState(['info', 'cartFoods']), // 类名判断 PayClass(){ const {totalPrice} = this const {minPrice} = this.info return totalPrice < minPrice ? ' not-enough' : ' enough' }, // 支付文本判断,等于0, 小于minPrice 大于minPrice PayText(){ const {totalPrice} = this const {minPrice} = this.info if(totalPrice ===0){ return `¥${minPrice}元起送` }else if(totalPrice < minPrice){ return `还差${minPrice - totalPrice}元起送` }else{ return `结算` } }
模板填充
<div class="shopcart"> <div class="content"> <div class="content-left"> <div class="logo-wrapper"> <div class="logo " :class="{ highlight :totalCount}"> <i class="iconfont icon-shopping_cart " :class="{ highlight :totalCount}"></i> </div> <div class="num" v-if="totalCount">{{totalCount}}</div> </div> <div class="price " :class="{highlight: totalCount}">¥{{totalPrice}}</div> <div class="desc">另需配送费¥{{info.deliveryPrice}}元</div> </div> <div class="content-right"> <div class="pay not-enough" :class="PayClass"> {{PayText}} </div> </div> </div>
在vuex的添加和减少逻辑中,添加food,移除food
INCREMENT_FOOD_COUNT(state, food) { if (!food.count) { // 第一次增加判断,不然count是Nan // food.conut= 1 // 新增属性(没有数据绑定) /* 对象 属性名 属性值 */ Vue.set(food, 'count', 1) // 让新增的属性也有数据绑定 // 将food添加到cartFoods中 state.cartFoods.push(food) } else { food.count++ } }, DECREMENT_FOOD_COUNT(state, food) { if (food.count) { // 只有有值才去减 food.count-- if(food.count ===0){ // 将food从cartFoods中移除,找到当前食物的索引 state.cartFoods.splice(state.cartFoods.indexOf(food),1) } } }
点击购物车黑色方块,显示购物车列表,再次点击购物车黑色方块,清空按钮,外部的遮罩层购物车列表消失
data() { return { isShow:false, }
<div class="shopcart"> <div class="content">
购物车黑色部分 <div class="content-left" @click="toggleShow">
<div class="shopcart-list" > <div class="list-header"> <h1 class="title">购物车</h1> <span class="empty" @click="toggleShow">清空</span> </div> <div class="list-content" v-show="isShow"> <ul> <li class="food" v-for="(cartFood, index) in cartFoods" :key="index"> <span class="name">{{cartFood.name}}</span> <div class="price"><span>¥{{cartFood.price}}</span></div> <div class="cartcontrol-wrapper"> <div class="cartcontrol"> <!-- 加减按钮 --> <CartControl :food="cartFood"></CartControl> </div> </div> </li> </ul> </div> </div> </div>
遮罩层, <div class="list-mask" @click="toggleShow" v-show="isShow" ></div>
methods:{ toggleShow(){ this.isShow = !this.isShow }
<div class="shopcart-list" v-show="listShow"> <div class="list-header" > <h1 class="title">购物车</h1> <span class="empty" @click="toggleShow">清空</span> </div> <div class="list-content" > <ul> <li class="food" v-for="(cartFood, index) in cartFoods" :key="index"> <span class="name">{{cartFood.name}}</span> <div class="price"><span>¥{{cartFood.price}}</span></div> <div class="cartcontrol-wrapper"> <div class="cartcontrol"> <!-- 加减按钮 --> <CartControl :food="cartFood"></CartControl> </div> </div> </li> </ul> </div> </div> </div> <div class="list-mask" @click="toggleShow" v-show="listShow" ></div>
data() { return { isShow:false, } },
计算显示和影藏的标识
// 购物车显示与影藏 listShow(){ if(this.totalCount ===0){ //总数量为0,关闭列表弹框 //关闭最后一个食物后,让isShow为false,防止下一次再增加食物后,自动弹框 this.isShow = false return false } // 总数量大于0 return this.isShow }
methods:{ toggleShow(){ // 只有当总数量大于0时切换 if(this.totalCount){ this.isShow = !this.isShow } }
1.10, 实现购物车列表的滚动功能
在购物车列表显示的时候创建scroll实例,注,必须创建单个实例scroll,不然对加减按钮有影响,需要判断之前是否有scroll实例
// 购物车显示与影藏 listShow(){ if(this.totalCount ===0){ //总数量为0,关闭列表弹框 //关闭最后一个食物后,让isShow为false,防止下一次再增加食物后,自动弹框 this.isShow = false return false } if(this.isShow){ this.$nextTick(()=>{ // 实现BScroll的实例是一个单例 if(!this.scroll){ this.scroll = new BScroll('.list-content',{ click : true }) }else{ // 已经创建滚动实例了 this.scroll.refresh() // 让滚动条刷新一下: 重新统计内容的高度 } }) } // 总数量大于0 return this.isShow }
1.11,点击清空按钮,清空购物车的食物
在vuex中,清空每个food的count属性,清空cartFoods数组
<div class="shopcart-list" v-show="listShow"> <div class="list-header"> <h1 class="title">购物车</h1> <span class="empty" @click="clearCart">清空</span> </div>
// 点击清空按钮 async clearCart() { let result = await this.$MessageBox .confirm('确定执行此操作?') .catch(error => error) if (result === 'confirm') { this.$store.dispatch('clearCart') } else if (result === 'cancel') { console.log('已取消') } }
clearCart({commit}){ commit('CLEAR_CART') }
CLEAR_CART(state){ // 清除food中的count state.cartFoods.forEach((item) => Vue.delete(item, 'count')) // 不行,自定义属性不能这样删除 // state.cartFoods.forEach((item) => delete item.count) // 移除购物车中所有购物项 state.cartFoods = [] }