美文网首页让前端飞
【vue3.0】21.0 某东到家( 廿一)——订单商品列表布局

【vue3.0】21.0 某东到家( 廿一)——订单商品列表布局

作者: bobokaka | 来源:发表于2021-11-29 01:39 被阅读0次

    继续完善订单页面。
    这里需要将一些方法进行抽离。
    src\effects\cartEffects.js

    import { computed } from 'vue'
    import { useStore } from 'vuex'
    // 添加、减少到购物车功能
    export const useCommonCartEffect = shopId => {
      const store = useStore()
      const cartList = store.state.cartList // 加入购物车的商品
      /**
       * 加入或减少购物车数量
       * @param {String} shopId 店铺id
       * @param {String} productId 商品id
       * @param {Object} productInfo 商品信息集
       * @param {Number} num 加入购物车的数量
       */
      const changeCartItemInfo = (shopId, productId, productInfo, num) => {
        console.log(
          'changeCartItemInfo:',
          'shopId:' + shopId,
          'productId:' + productId,
          'productInfo:' + JSON.stringify(productInfo),
          'num:' + num
        )
        // 更新vuex中的值
        store.commit('changeItemToCart', { shopId, productId, productInfo, num })
      }
      // 获得加入购物车商品的信息集
      const productList = computed(() => {
        const productInfoList = cartList[shopId]?.productList || [] // 不存在默认空数组
        return productInfoList
      })
    
      // 获得商铺名称
      const shopName = computed(() => {
        const shopName = cartList[shopId]?.shopName || '' // 不存在默认空数组
        return shopName
      })
    
      // 计算shopId下所有cartList的商品数量total、价钱之和totalPrice
      const calculations = computed(() => {
        const productList = cartList[shopId]?.productList
        const resultData = {
          // 总商品数量
          total: 0,
          // 总金额
          totalPrice: 0,
          // 全选
          isAllChecked: true
        }
        if (productList) {
          for (const i in productList) {
            const product = productList[i]
            // 总商品数量
            resultData.total += product.count
            // 总金额
            if (product.checked === true) {
              resultData.totalPrice += product.count * product.price
            }
            // 全选
            if (product.count > 0 && !product.checked) {
              resultData.isAllChecked = false
            }
          }
          resultData.totalPrice = resultData.totalPrice.toFixed(2) // 保留2位小数
        }
    
        return resultData
      })
      // const total = computed(() => {
      //   const productList = cartList[shopId]?.productList
      //   let count = 0
      //   if (productList) {
      //     for (const i in productList) {
      //       const product = productList[i]
      //       count += product.count
      //     }
      //   }
      //   return count
      // })
      // const totalPrice = computed(() => {
      //   const productList = cartList[shopId]?.productList
      //   let count = 0
      //   if (productList) {
      //     for (const i in productList) {
      //       const product = productList[i]
      //       if (product.checked === true) {
      //         count += product.count * product.price
      //       }
      //     }
      //   }
      //   return count.toFixed(2) // 保留2位小数
      // })
    
      // 全选的计算属性
      // const allChecked = computed(() => {
      //   const productList = cartList[shopId]?.productList
      //   let result = true
      //   if (productList) {
      //     for (const i in productList) {
      //       const product = productList[i]
      //       if (product.count > 0 && !product.checked) {
      //         result = false
      //         break
      //       }
      //     }
      //   }
      //   return result
      // })
    
      return { cartList, shopName, calculations, productList, changeCartItemInfo }
    }
    
    

    这样,src\views\shop\Cart.vue做了减法:

    ......
    <script>
    import { ref } from 'vue'
    import { useRoute } from 'vue-router' // 路由跳转方法
    import { useStore } from 'vuex' // 路由跳转方法
    import { useCommonCartEffect } from '@/effects/cartEffects'
    
    const useCartEffect = shopId => {
      const store = useStore()
      const { cartList, productList, changeCartItemInfo, calculations } = useCommonCartEffect(
        shopId
      )
      const getProductCartCount = (shopId, productId) => {
        return cartList?.[shopId]?.productList?.[productId]?.count || 0
      }
      // 单个勾选或者不勾选
      const changeCartItemChecked = (shopId, productId) => {
        store.commit('changeItemChecked', { shopId, productId })
      }
      // 清除购物车按钮
      const cleanCartProducts = shopId => {
        store.commit('changeCleanCartProducts', { shopId })
      }
      // 购物车全选或者取消全选
      const setCartItemsChecked = shopId => {
        store.commit('setCartItemsChecked', { shopId })
      }
    
      return {
        cartList,
        calculations,
        productList,
        changeCartItemChecked,
        changeCartItemInfo,
        cleanCartProducts,
        getProductCartCount,
        setCartItemsChecked
      }
    }
    
    // 展示隐藏购物车
    const toggleCartEffect = () => {
      const showCart = ref(false)
      // 显示隐藏购物车具体内容
      const handleCartShowChange = () => {
        showCart.value = !showCart.value
      }
      return { showCart, handleCartShowChange }
    }
    export default {
      name: 'Cart',
      setup() {
        const route = useRoute()
        const shopId = route.params.id // 店铺id
        // 展示隐藏购物车
        const { showCart, handleCartShowChange } = toggleCartEffect()
        // 计算总价和加入购物车的总数量
        const {
          cartList,
          calculations,
          productList,
          changeCartItemChecked,
          changeCartItemInfo,
          cleanCartProducts,
          getProductCartCount,
          setCartItemsChecked
        } = useCartEffect(shopId)
        return {
          cartList,
          calculations,
          productList,
          shopId,
          showCart,
          handleCartShowChange,
          changeCartItemChecked,
          changeCartItemInfo,
          cleanCartProducts,
          getProductCartCount,
          setCartItemsChecked
        }
      }
    }
    </script>
    ......
    

    进一步完善src\views\orderConfirmation\OrderConfirmation.vue

    <template>
      <div class="wrapper">
        <div class="top">
          <div class="top__bgcolor" />
          <div class="top__header">
            <div class="top__header__back" @click="handleBackClick">
              <i class="custom-icon custom-icon-back"></i>
            </div>
            <span>确认订单</span>
          </div>
          <div class="top__receiver">
            <div class="top__receiver__title">收货地址</div>
            <div class="top__receiver__address">
              西安一二三大学四五六科技园2号楼
            </div>
            <div class="top__receiver__info">
              <span class="top__receiver__info__name">张三(先生)</span>
              <span class="top__receiver__info__phone">18012341234</span>
            </div>
            <div class="top__receiver__icon">
              <i class="custom-icon custom-icon-back"></i>
            </div>
          </div>
        </div>
        <div class="products">
          <div class="products__title">{{shopName}}</div>
          <div>
            <div class="products__item" v-for="item in productList" :key="item._id">
              <img class="products__item__img" :src="item.imgUrl" />
              <div class="products__item__detail">
                <h4 class="products__item__title">{{ item.name }}</h4>
                <p class="products__item__price">
                  <span>
                    <span class="products__item__yen"> &yen; </span>
                    {{ item.price }}×{{item.count}}
                  </span>
                  <span class="products__item__total">
                    <span class="products__item__yen"> &yen; </span>
                    {{( item.price*item.count ).toFixed(2)}}
                  </span>
                </p>
              </div>
            </div>
          </div>
        </div>
        <div class="order">
          <div class="order__price">
            实付金额 &yen;<b>{{calculations.totalPrice}}</b>
          </div>
          <div class="order__btn">
            <!-- <router-link :to="{ path: `/orderConfirmation/${shopId}` }"> -->
            提交订单
            <!-- </router-link> -->
          </div>
        </div>
      </div>
    </template>
    
    <script>
    // import { ref } from 'vue'
    import { useCommonCartEffect } from '@/effects/cartEffects'
    import { useRoute, useRouter } from 'vue-router' // 路由跳转方法
    export default {
      name: 'OrderConfirmation',
      setup() {
        const route = useRoute()
        const router = useRouter()
        const shopId = route.params.shopId // 店铺id
        const { shopName, productList, calculations } = useCommonCartEffect(shopId)
    
        const handleBackClick = () => {
          router.back()
        }
    
        return {
          shopName,
          productList,
          calculations,
          handleBackClick
        }
      }
    }
    </script>
    <style lang="scss" scoped>
    @import '@/style/viriables.scss';
    @import '@/style/mixins.scss';
    .wrapper {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: #eee;
      overflow-y: scroll; //防止超出屏幕
    }
    .top {
      position: relative;
      height: 1.96rem;
      background-size: 100% 1.59rem;
      /* 渐变轴为0度,相当于从下到上,
       高度4%位置从rgba(0, 145, 255, 0) 开始渐变
       到高度50%位置的蓝色(#0091ff)结束 */
      background-image: linear-gradient(0deg, rgba(0, 145, 255, 0) 4%, #0091ff 50%);
      background-repeat: no-repeat;
    
      &__header {
        position: relative;
        padding-top: 0.26rem;
        line-height: 0.24rem;
        color: #fff;
        text-align: center;
        font-size: 0.16rem;
        &__back {
          position: absolute;
          font-size: 0.22rem;
          left: 0.18rem;
        }
      }
      &__receiver {
        position: absolute;
        left: 0.18rem;
        right: 0.18rem;
        bottom: 0rem;
        height: 1.11rem;
        background: #fff;
        border-radius: 0.04rem;
        &__title {
          line-height: 0.22rem;
          padding: 0.16rem 0 0.14rem 0.16rem;
          font-size: 0.16rem;
          color: #333;
        }
        &__address {
          line-height: 0.2rem;
          padding: 0 0.4rem 0 0.16rem;
          font-size: 0.16rem;
          color: #333;
        }
        &__info {
          padding: 0.06rem 0 0 0.16rem;
          &__name &__phone {
            margin-right: 0.1rem;
            line-height: 0.18rem;
            font-size: 0.12rem;
            color: #666;
          }
        }
        &__icon {
          //旋转180度
          transform: rotate(180deg);
          position: absolute;
          right: 0.16rem;
          top: 0.53rem;
          font-size: 0.16rem;
          color: #666;
        }
      }
    }
    .products {
      margin: 0.16rem 0.18rem 0.2rem 0.18rem;
      background: #fff;
      &__title {
        padding: 0.16rem 0.16rem 0 0.16rem;
        font-size: 0.16rem;
        color: #333;
      }
      &__item {
        position: relative;
        display: flex;
        padding: 0.16rem;
        &__img {
          width: 0.46rem;
          height: 0.46rem;
          margin-right: 0.16rem;
        }
        // 配合解决超出长度以省略号显示而不会出现换行
        &__detail {
          overflow: hidden;
          flex: 1;
        }
    
        &__title {
          margin: 0;
          line-height: 0.2rem;
          font-size: 0.14rem;
          color: $content-font-color;
          // 超出长度以省略号显示而不会出现换行
          @include ellipsis;
        }
        &__price {
          display: flex;
          margin: 0.06rem 0 0 0;
          line-height: 0.2rem;
          font-size: 0.14rem;
          color: $height-light-font-color;
        }
        &__total {
          text-align: right;
          color: #000;
          flex: 1;
        }
        &__yen {
          font-size: 0.12rem;
        }
      }
    }
    
    .order {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      display: flex;
      box-sizing: border-box; //往内塞入border
      line-height: 0.49rem;
      height: 0.49rem;
      border-top: 0.01rem solid $content-bg-color;
      background: $bg-color;
      &__price {
        flex: 1;
        text-indent: 0.24rem;
        font-size: 0.14rem;
        color: #333;
      }
      &__btn {
        width: 0.98rem;
        background-color: #4fb0f9;
        text-align: center;
        color: $bg-color;
        font-size: 0.14rem;
        // 去掉a标签的下划线
        a {
          color: $bg-color;
          text-decoration: none; //去掉文本修饰
        }
      }
    }
    </style>
    

    效果如下:


    image.png

    此时在浏览器进行如下的操作:


    image.png

    发现当商品很多时,还是会被撑开。
    进一步完善:

    ......
        <div class="products">
          <div class="products__title">{{shopName}}</div>
          <div class="products__list">
    ......
          </div>
        </div>
    ......
    <style lang="scss" scoped>
    ......
    .products {
    ......
      &__title {
    ......
      }
      &__list {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0.6rem;
        top: 2.06rem;
      }
    ......
    }
    ......
    </style>
    
    image.png

    继续完善css

      &__list {
        overflow-y: scroll;
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0.6rem;
        top: 2.05rem;
      }
    
    image.png

    发现滚动效果出来了。继续尝试调试:

     &__list {
       overflow-y: scroll;
       position: absolute;
       padding: 0 0.16rem;
       left: 0;
       right: 0;
       bottom: 0.6rem;
       top: 2.46rem;
       background: #fff;
     }
    
    image.png

    效果很接近。继续调试:

      &__list {
        overflow-y: scroll;
        position: absolute;
        margin: 0 0.18rem;
        left: 0;
        right: 0;
        bottom: 0.6rem;
        top: 2.46rem;
        background: #fff;
      }
    
    image.png

    到这里我们想要的效果基本都出来了。
    继续微调优化:

    .products {
    ......
      &__title {
     ......
      }
      &__list {
     ......
      }
      &__item {
        position: relative;
        display: flex;
        padding: 0.16rem 0.16rem 0 0.16rem;
    ......
      }
    }
    
    
    image.png

    当元素很多的时候,这样效果已经很好了,但当元素较少时,就会如下样式:


    image.png

    解决办法:创建一个wrapper容器包裹list,然后通过list的背景色自动撑开容器。

    ......
        <div class="products">
          <div class="products__title">{{shopName}}</div>
          <div class="products__wrapper">
            <div class="products__list">
    ......
            </div>
         </div>
        </div>
    ......
    <style lang="scss" scoped>
    ......
    .products {
      margin: 0.16rem 0.18rem 0.2rem 0.18rem;
      background: #fff;
      &__title {
        padding: 0.16rem;
        font-size: 0.16rem;
        color: #333;
      }
      // list外层的wrapper容器
      &__wrapper {
        overflow-y: scroll;
        position: absolute;
        margin: 0 0.18rem;
        left: 0;
        right: 0;
        bottom: 0.6rem;
        top: 2.6rem;
      }
      &__list {
        background: #fff;
      }
      &__item {
    ......
        padding: 0 0.16rem 0.16rem 0.16rem;
    ......
      }
    }
    ......
    </style>
    
    image.png

    这样效果就出来了。
    完善逻辑:

        <div class="products">
          <div class="products__title">{{shopName}}</div>
          <div class="products__wrapper">
            <div class="products__list">
              <template v-for="item in productList" :key="item._id">
                <div class="products__item" v-if="item.count>0">
    ......
                </div>
              </template>
            </div>
          </div>
        </div>
    

    顺便修复一下之前的一个bug:
    src\store\index.js

       /**
         * 加入或减少购物车数量
         * @param {*} state
         * @param {String} shopId 店铺id
         * @param {String} productId 商品id
         * @param {Object} productInfo 商品信息集
         * @param {Number} num 加入购物车的数量
         * @param {*} payload
         */
        changeItemToCart(state, payload) {
    ......
          product.count <= 0 && (shopInfo.productList[productId].count = 0)
    ......
        },
    

    相关文章

      网友评论

        本文标题:【vue3.0】21.0 某东到家( 廿一)——订单商品列表布局

        本文链接:https://www.haomeiwen.com/subject/pkgcxrtx.html