用Vue實現購物車組件

前言

本實例主要介紹利用Vue實現購物車組件,頂部可以切換標籤“全部”、“好評”和“買過”的數據,標籤“全部”下還可以切換不同分類顯示商品;也可對商品進行加減,並進行跨標籤和跨分類的最終價合計;

最終效果:

 

實現過程

一. 子組件代碼如下(shoppingCart.vue),原理分析:

1. 切換標籤函數toggleType,點擊時,賦值this.currentIndex = index,同時利用計算屬性更新menu的值,從而實現切換標籤數據功能;而this.menuTypeIndex = 0爲了解決從多分類到少分類切換報錯的問題;

2. 切換分類函數toggleGoods,點擊時,賦值this.menuTypeIndex = index,同時利用計算屬性更新goods的值,從而實現切換分類數據功能;

3. 添加函數clickAdd,每次點擊時,數量加1,同時利用JQ實現一個商品圖片從上往下掉落到配送員箱子的過程;然後箱子的數字加1,並增加原價、優惠價和配送費用等;

4. 減少函數clickMinus,每次點擊時,數量減1,同時減少原價、優惠價和配送費用等。

<template>
  <div class="sc-box">
    <div class="sc-type">
      <ul>
        <li :class="{active:currentIndex === index}" v-for="(item, index) in goodsList" :key="index" data-index="index" @click="toggleType(index)">{{item.type}}</li>
      </ul>
    </div>
    <div class="sc-content">
      <div class="sc-leftMenu" v-if="menu.length > 1">
        <ul>
          <li :class="{active:menuTypeIndex === index}" v-for="(item, index) in menu" :key="index" data-index="index" @click="toggleGoods(index)">{{item.menuType}}</li>
        </ul>
      </div>
      <div class="sc-goods">
        <ul>
          <li v-for="(item, index) in goods" :key="index" data-index="index">
            <img :class="'goods-image'+index" :src="item.imgSrc" alt="" />
            <div class="goods-mes">
              <div class="goods-name">{{item.name}}</div>
              <div class="goods-description">{{item.description}}</div>
              <div class="goods-price"><span>¥</span>{{item.price}}<span class="originalPrice">¥{{item.originalPrice}}</span></div>
              <div class="goods-buttons">
                <span class="button minus" @click="clickMinus(index)" v-if="item.number !== 0"><img src="../assets/images/shoppingCart/minus.png" alt="" /></span>
                <span class="number" v-if="item.number !== 0">{{item.number}}</span>
                <span class="button add" @click="clickAdd(index)"><img src="../assets/images/shoppingCart/add.png" alt="" /></span>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
    <div class="sc-operation">
      <div class="sc-contact">
        <img src="../assets/images/shoppingCart/contact.png" alt="" />
        <span>聯繫商家</span>
      </div>
      <div class="sc-deliveryMes">
        <div class="deliveryNumber"><img src="../assets/images/shoppingCart/delivery.png" alt="" /><span v-if="count!==0">{{count}}</span></div>
        <div class="priceMes">
          <div class="totalPrice">¥{{totalPrice}}<span>¥{{originalTotalPrice}}</span></div>
          <div class="deliveryMes">另需配送費¥{{totalDeliveryCost}}<span v-if="totalDeliveryCost !== 0">¥{{totalOriginalDeliveryCost}}</span> 支持自取</div>
        </div>
      </div>
      <div class="sc-pay" @click="clickPay">去結算</div>
    </div>
  </div>
</template>
<script>
import $ from 'jquery'
export default {
  props: {},
  data() {
    return {
      currentIndex: 0, // 當前類型index
      menuTypeIndex: 0, // 左邊菜單index
      goodsList: [{ // 整個商品數據
          type: '全部',
          menu: [{
            menuType: '推薦',
            goods: [{
                name: '排骨飯套餐',
                imgSrc: require('../assets/images/shoppingCart/timg1.jpg'),
                description: '物美價廉',
                price: 15.50,
                originalPrice: 25.36,
                number: 0,
                deliveryCost: 1,
                originalDeliveryCost: 2,
              },
              {
                name: '宮保雞丁套餐',
                imgSrc: require('../assets/images/shoppingCart/timg2.jpg'),
                description: '物美價廉',
                price: 5.50,
                originalPrice: 25.36,
                number: 0,
                deliveryCost: 2,
                originalDeliveryCost: 4,
              }
            ]
          }, {
            menuType: '折扣',
            goods: [{
              name: '排骨飯套餐',
              imgSrc: require('../assets/images/shoppingCart/timg1.jpg'),
              description: '物美價廉',
              price: 15.50,
              originalPrice: 25.36,
              number: 0,
              deliveryCost: 1,
              originalDeliveryCost: 2,
            }]
          }]
        },
        {
          type: '好評',
          menu: [{
            menuType: '推薦',
            goods: [{
              name: '排骨飯套餐',
              imgSrc: require('../assets/images/shoppingCart/timg1.jpg'),
              description: '物美價廉',
              price: 15.50,
              originalPrice: 25.36,
              number: 0,
              deliveryCost: 1,
              originalDeliveryCost: 2,
            }]
          }]
        },
        {
          type: '買過',
          menu: [{
            menuType: '推薦',
            goods: [{
                name: '排骨飯套餐',
                imgSrc: require('../assets/images/shoppingCart/timg1.jpg'),
                description: '物美價廉',
                price: 15.50,
                originalPrice: 25.36,
                number: 0,
                deliveryCost: 1,
                originalDeliveryCost: 2,
              },
              {
                name: '宮保雞丁套餐',
                imgSrc: require('../assets/images/shoppingCart/timg2.jpg'),
                description: '物美價廉',
                price: 5.50,
                originalPrice: 25.36,
                number: 0,
                deliveryCost: 2,
                originalDeliveryCost: 4,
              }
            ]
          }]
        }
      ],
      count: 0, // 總數量
      originalTotalPrice: 0, // 原總價
      totalPrice: 0, // 總價
      totalDeliveryCost: 0, // 優惠後總運費
      totalOriginalDeliveryCost: 0, // 總運費
      flag: true // 用來判斷是否執行動畫
    }
  },
  mounted() {},
  computed: {
    menu() {
      return this.goodsList[this.currentIndex].menu;
    },
    goods() {
      return this.menu[this.menuTypeIndex].goods;
    }
  },
  methods: {
    toggleType(index) {
      this.currentIndex = index;
      this.menuTypeIndex = 0; // 默認menu第一個,解決非第一個切換時報錯
    },
    toggleGoods(index) {
      this.menuTypeIndex = index;
    },
    clickMinus(index) {
      this.goods[index].number -= 1;
      this.count -= 1;
      // 價格變化
      this.originalTotalPrice -= parseFloat(this.goods[index].originalPrice); // 原總價增加
      this.totalPrice -= parseFloat(this.goods[index].price); // 優惠總價增加
      this.totalDeliveryCost -= this.goods[index].deliveryCost; // 優惠總配送費增加
      this.totalOriginalDeliveryCost -= this.goods[index].originalDeliveryCost; // 優惠總配送費增加
    },
    clickAdd(index) {
      if (this.flag) {
        this.flag = false;
        this.goods[index].number += 1;

        let that = this;
        // 動畫跳動效果
        let count = that.count + 1;
        let originalTotalPrice = parseFloat(that.originalTotalPrice) + parseFloat(that.goods[index].originalPrice); // 原總價增加
        let totalPrice = parseFloat(that.totalPrice) + parseFloat(that.goods[index].price); // 優惠總價增加
        let totalDeliveryCost = that.totalDeliveryCost + that.goods[index].deliveryCost; // 優惠總配送費增加
        let totalOriginalDeliveryCost = that.totalOriginalDeliveryCost + that.goods[index].originalDeliveryCost; // 優惠總配送費增加
        let $initImg = $('.goods-image' + index); // 被複制的圖片對象
        let $targetLocation = $('.deliveryNumber img'); // 目標購物車的圖片對象
        let $moveImg = $initImg.clone().css({ // 生成點擊添加行的圖片副本,並變成圓形
          'position': 'absolute',
          'z-index': 99,
          'width': $initImg.width() * 0.5,
          'height': $initImg.height() * 0.5,
          'border-radius': '50%'
        }).css($initImg.offset()).appendTo('body'); // 並把位移到圖片位置且添加到body
        $moveImg
          .animate({ // 先勻速向左上
            left: $initImg.offset().left - 30,
            top: $initImg.offset().top - 50
          }, 200, 'linear')
          .animate({ // 然後慢慢移到目標位置
            left: $targetLocation.offset().left + $targetLocation.width() - $moveImg.width() * 1.5,
            top: $targetLocation.offset().top + $targetLocation.height() - $moveImg.height() * 1.5
          }, 600, () => {
            $moveImg.fadeOut(100, () => { // 最後慢慢消失
              $moveImg.detach(); // 刪除掉移動對象$moveImg
              that.count = count;
              // 價格變化
              that.originalTotalPrice = originalTotalPrice.toFixed(2);
              that.totalPrice = totalPrice.toFixed(2);
              that.totalDeliveryCost = totalDeliveryCost;
              that.totalOriginalDeliveryCost = totalOriginalDeliveryCost;

              this.flag = true;
            });
          })
      };
    },
    clickPay() {
      console.log(this.totalPrice, this.totalDeliveryCost);
    }
  }
}

</script>
<style lang="less" scoped>
.sc-box {
  position: relative;
  margin: 20px;
  width: 375px;
  height: 667px;
  background-color: #fff;

  .sc-type {
    li {
      display: inline-block;
      background-color: #f5f5f5;
      color: #616161;
      margin-right: 10px;
      height: 32px;
      line-height: 32px;
      width: 80px;
      text-align: center;
      border-radius: 8px;
      cursor: pointer;
      transition: all 0.3s;

      &.active {
        background-image: linear-gradient(to right, #fedb39, #febb2e);
        color: #000;
        font-weight: bold;
      }
    }
  }

  .sc-content {
    margin-top: 30px;
    height: 546px;
    display: flex;
    overflow-y: auto;

    .sc-leftMenu {
      height: 100%;
      width: 80px;
      text-align: center;
      background-color: #f5f9fc;
      color: #616161;

      li {
        cursor: pointer;
        transition: all 0.3s;
        height: 50px;
        line-height: 50px;

        &.active {
          background-color: #fff;
          font-weight: bold;
          color: #000;
        }
      }
    }

    .sc-goods {
      margin: 0 10px;
      width: 100%;

      li {
        position: relative;
        display: flex;
        margin-bottom: 15px;

        img {
          width: 70px;
          height: 70px;
          border-radius: 8px;
        }

        .goods-mes {
          margin-left: 8px;

          .goods-name {
            font-size: 16px;
            font-weight: bold;
          }

          .goods-description {
            margin-top: 6px;
            color: #616161;
            font-size: 12px;
          }

          .goods-price {
            margin-top: 10px;
            color: #ff6262;
            font-size: 16px;

            span {
              font-size: 12px;

              &.originalPrice {
                color: #b4b4b4;
                margin-left: 4px;
                text-decoration: line-through;
              }
            }
          }

          .goods-buttons {
            position: absolute;
            bottom: 0;
            right: 0;

            .button {
              display: inline-block;
              width: 20px;
              height: 20px;
              line-height: 20px;
              text-align: center;
              border-radius: 50%;
              cursor: pointer;

              img {
                width: 10px;
                height: 10px;
              }
            }

            .minus {
              border: 1px solid #d0d0d0;
            }

            .number {
              display: inline-block;
              margin: 0 5px;
            }

            .add {
              background-image: linear-gradient(to right, #fedb39, #febb2e);
            }
          }
        }
      }
    }
  }

  .sc-operation {
    position: absolute;
    bottom: 0;
    width: 100%;
    font-size: 12px;
    color: #949494;
    display: flex;

    .sc-contact {
      display: flex;
      flex-direction: column;
      padding: 10px 10px 10px 15px;
      background-color: #000;
      border-top-left-radius: 30px;
      border-top-right-radius: 5px;
      border-bottom-right-radius: 5px;
      border-bottom-left-radius: 30px;

      img {
        width: 20px;
        height: 20px;
        margin: 0 auto 3px;
      }
    }

    .sc-deliveryMes {
      margin-left: 3px;
      padding-right: 15px;
      background-color: #000;
      display: flex;
      border-top-left-radius: 5px;
      border-bottom-left-radius: 5px;
      align-items: center;

      .deliveryNumber {
        position: relative;

        img {
          position: absolute;
          top: -58px;
          left: -18px;
          width: 100px;
          height: 100px;
        }

        span {
          display: inline-block;
          width: 20px;
          height: 20px;
          line-height: 20px;
          text-align: center;
          border-radius: 50%;
          background-color: #ff6262;
          color: #fff;
          position: absolute;
          left: 35px;
          top: 0;
        }
      }

      .priceMes {
        margin-left: 66px;

        .totalPrice {
          color: #fff;
          font-size: 16px;
          margin-bottom: 3px;

          span {
            color: #949494;
            margin-left: 3px;
            font-size: 12px;
            text-decoration: line-through;
          }
        }

        .deliveryMes {
          span {
            text-decoration: line-through;
            margin: 0 6px 0 3px;
          }
        }
      }
    }

    .sc-pay {
      font-size: 14px;
      font-weight: bold;
      color: #000;
      line-height: 59px;
      flex: 1;
      text-align: center;
      background-image: linear-gradient(to right, #fedb39, #febb2e);
      border-top-right-radius: 30px;
      border-bottom-right-radius: 30px;
      cursor: pointer;
    }
  }
}

</style>

二. 父組件代碼如下(shoppingCartPage.vue),原理分析:

父組件比較簡單,主要用來調用子組件。

<template>
  <div>
    <shopping-cart></shopping-cart>
  </div>
</template>
<script>
import ShoppingCart from '../components/shoppingCart'
export default {
  components: {
    ShoppingCart
  },
  data() {
    return {
      
    }
  },
  methods: {
    
  }
}

</script>
<style lang="less" scoped>
</style>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章