【vue3.0】21.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)
......
    },

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):