zoukankan      html  css  js  c++  java
  • 外卖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'

    better-scroll插件和swiper插件一样,需要先监视数据,然后在this,$nextTick()
    需要传一个回调函数,只要vuex中数据获取到了,即可通知组件更新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

    解决方法;将点击加减号的click事件设置阻止冒泡
      <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>
    CartControl组件
    <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不显示高亮

    判断依据是总价格是否大于最小的配送价格

    5.点击食物减号,当见到食物数量为0,需要将当前food对象从cartFoods移除
     
    vuex中state
     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)
    }
     
     在shopCart获取
      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)
          }
        }
      }
     1.8, 购物车列表显示功能

    点击购物车黑色方块,显示购物车列表,再次点击购物车黑色方块,清空按钮,外部的遮罩层购物车列表消失

     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
        }
     1.9,此时会出现3个bug
    1.当购物车列表的总数为0,弹框依然会存在,修改,当总数为0时,切换变量标识为false, 如果总数大于0,为true
    2. 此时当购物车列表总数为0,弹框不会出现,当下一次添加食物数量,购物车弹框自动出来, 因为第一次的isShow为true,而且有数量了,就会自动出现购物车弹框
    修改,当购物车数量为0,isShow设置为false即可
    3. 当点击黑色方块时,在添加一个食物数量,购物车弹框自动出现,应为点击黑色方框,isShow为true, 改正,在点击toggleShow, 只要有数量才可以改变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 = []
      }
  • 相关阅读:
    [转]zookeeper-端口说明
    ACM-ICPC(9/26)
    ACM-ICPC(9/25)
    Linux的文件权限与目录配置
    Uva 11468 AC自动机或运算
    Uva 11922 Splay
    HDU 6214 最小割边
    Uva 10559 消除方块
    HDU 6194 后缀数组
    Uva 11491 暴力贪心
  • 原文地址:https://www.cnblogs.com/fsg6/p/14320349.html
Copyright © 2011-2022 走看看