Vue+Vant+Vuex實現本地購物車功能


通常,我們做移動端商城的時候,通常會有購物車模塊,那購物車模塊有兩種實現方式,一是儲存在后台通過接口獲取到購物車信息,二是存在用戶手機本地,第一種方法只要調用接口獲取比較簡單,這里介紹的是第二種方法,利用vuex將購物車數據存在本地的方法。

vue項目創建方法和vuex的寫法之前博文都有介紹,這里就不再重復了;

vant安裝:

# 通過 npm 安裝
npm i vant -S

# 通過 yarn 安裝
yarn add vant

具體使用方法請去它的官網了解

地址:https://youzan.github.io/vant/#/zh-CN/

購物車頁面編寫和本地的價格計算:

1.我們把vant按需引入,我們用到了Icon, Checkbox, Stepper, SubmitBar, Toast這些組件;

2.為了方便復制即用,我在本地寫了一些購物車死數據goods用於渲染;

3.雖說是把商品信息購物車存在本地,實際上我們只需要存商品id和商品數量num

4.我們需要通過單選的change事件全選事件步進器增加減少進行價格換算;

為了方便復制粘貼直接看效果,直接上demo

demo.html

<template>
  <div class="shopCart">
    <div class="cartList">
      <ul v-if="goods.length > 0">
        <li v-for="item in goods" :key="item.id">
          <van-checkbox
            :value="item.id"
            v-model="item.isChecked"
            checked-color="#15C481"
            @click="chooseChange(item.id, item)"
          ></van-checkbox>
          <div class="shopdetail">
            <div class="detailimg">
              <img :src="item.thumb" />
            </div>
            <div class="detailtext">
              <div class="shoptitle van-multi-ellipsis--l2">
                {{ item.title }}
              </div>
              <div class="shoppricenum">
                <p class="shopprice">
                  ¥{{ item.price
                  }}{{ item.lvd > 0 ? "+" + item.lvd + "LVD" : "" }}
                </p>
                <div class="shopnum">
                  <van-stepper v-model="item.num" @change="onChange(item)" />
                </div>
              </div>
            </div>
          </div>
        </li>
      </ul>
      <div class="nohaveshop" v-else>
        <van-icon name="shopping-cart-o" />
        <p class="p1">你的購物車空空如也~~</p>
        <p class="p2">快去采購吧!</p>
      </div>
    </div>
    <div class="cartfotter" v-if="goods.length > 0">
      <van-submit-bar button-text="去結算" @submit="onSubmit">
        <van-checkbox
          v-model="allchecked"
          checked-color="#15C481"
          @click="checkAll"
          >全選</van-checkbox
        >
        <div class="buyprice">
          <p class="p1">合計</p>
          <p class="p2">
            ¥{{ totalprice }}{{ totallvd > 0 ? "+" + totallvd + "LVD" : "" }}
          </p>
        </div>
      </van-submit-bar>
    </div>
  </div>
</template>

<script>
import { Icon, Checkbox, Stepper, SubmitBar, Toast } from "vant";
export default {
  components: {
    [Icon.name]: Icon,
    [Checkbox.name]: Checkbox,
    [Stepper.name]: Stepper,
    [SubmitBar.name]: SubmitBar,
    [Toast.name]: Toast
  },
  data() {
    return {
      goods: [
        {
          id: 1,
          title: "Zoneid 2019 新款羊羔絨藍白拼接立領夾克男 9.9成新",
          price: 980,
          lvd: 12,
          num: 1,
          thumb:
            "https://img.yzcdn.cn/public_files/2017/10/24/2f9a36046449dafb8608e99990b3c205.jpeg"
        },
        {
          id: 2,
          title: "青春女裝復古時尚保暖拼接翻領夾克",
          price: 1200,
          lvd: 0,
          num: 2,
          thumb:
            "https://img.yzcdn.cn/public_files/2017/10/24/f6aabd6ac5521195e01e8e89ee9fc63f.jpeg"
        },
        {
          id: 3,
          title: "成熟男裝新款西裝立體拼接立領夾克和正裝",
          price: 1000,
          lvd: 8,
          num: 1,
          thumb:
            "https://img.yzcdn.cn/public_files/2017/10/24/320454216bbe9e25c7651e1fa51b31fd.jpeg"
        }
      ],
      allchecked: false,
      selectedData: [],
      // 總價
      totalprice: 0,
      totallvd: 0
    };
  },
  created: function() {
    this.count();
  },
  computed: {},
  methods: {
    // 單選的change事件
    chooseChange(i, item) {
      Toast(i);
      if (this.selectedData.indexOf(i) > -1) {
        console.log(i);
        var arrs = this.selectedData.filter(function(item) {
          return item != i;
        });
        this.selectedData = arrs;
        item.isChecked = false;
        // this.remove(this.selectedData, i);
        this.count();
        console.log(this.selectedData);
      } else {
        this.selectedData.push(i);
        item.isChecked = true;
        this.count();
      }
      if (this.selectedData.length < this.goods.length) {
        this.allchecked = false;
      } else {
        this.allchecked = true;
      }
      this.count();
      console.log(this.selectedData);
    },
    // 商品數量
    onChange(item) {
      Toast(item.num);
      this.count();
      console.log(this.goods);
    },
    // 計算價格
    count: function() {
      var totalPrice = 0; //臨時總價
      var totalLvd = 0; //臨時lvd
      this.goods.forEach(function(val) {
        if (val.isChecked) {
          totalPrice += val.num * val.price; //累計總價
          totalLvd += val.num * val.lvd; //累計lvd
        }
      });
      this.totalprice = totalPrice;
      this.totallvd = totalLvd;
    },
    // 全選
    checkAll() {
      let list = this.goods;
      if (this.allchecked === true) {
        list.forEach(element => {
          element.isChecked = false;
        });
        this.selectedData = [];
        this.count();
        console.log("111" + this.selectedData);
      } else {
        list.forEach(element => {
          element.isChecked = true;
          if (this.selectedData.indexOf(element.id) < 0) {
            this.selectedData.push(element.id);
          }
        });
        this.count();
        console.log("222" + this.selectedData);
      }
    },
    // 去結算
    onSubmit() {
      // 選擇購買的商品
      var cartgoods = [];
      this.goods.forEach(function(item) {
        if (item.isChecked) {
          cartgoods.push({ id: item.id, num: item.num });
        }
      });
      if (cartgoods.length === 0) {
        Toast("請選擇商品購買");
      } else {
        this.$router.push("shopBuy");
      }
      console.log(cartgoods);
    }
  }
};
</script>

<style lang="scss" scoped>
.shopCart {
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: #f6f6f6;
  .cartList {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 16px;
    ul {
      width: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-bottom: 100px;
      li {
        width: 100%;
        height: 96px;
        background-color: #fff;
        display: flex;
        flex-direction: row;
        align-items: center;
        margin-bottom: 12px;
        .van-checkbox {
          margin-left: 17px;
          ::v-deep .van-checkbox__icon {
            height: 14px;
            line-height: 14px;
            .van-icon {
              width: 14px;
              height: 14px;
              font-size: 12px;
              border: 1px solid #a5a5a5;
            }
          }
        }
        .shopdetail {
          display: flex;
          flex-direction: row;
          align-items: center;
          margin-left: 13px;
          .detailimg {
            width: 64px;
            height: 64px;
            background: rgba(165, 165, 165, 1);
            border-radius: 4px;
            img {
              width: 100%;
              height: 100%;
              border-radius: 4px;
            }
          }
          .detailtext {
            width: 230px;
            height: 60px;
            display: flex;
            flex-direction: column;
            margin-left: 8px;
            position: relative;
            .shoptitle {
              width: 180px;
              text-align: justify;
              font-size: 12px;
              color: #212121;
              line-height: 17px;
            }
            .shoppricenum {
              width: 100%;
              display: flex;
              flex-direction: row;
              align-items: center;
              justify-content: space-between;
              position: absolute;
              bottom: 0px;
              .shopprice {
                font-size: 12px;
                color: #15c481;
                font-weight: 600;
              }
              .shopnum {
                display: flex;
                ::v-deep .van-stepper {
                  button {
                    width: 14px;
                    height: 14px;
                    border: 1px solid #333333;
                    border-radius: 50px;
                    background-color: #fff;
                  }
                  .van-stepper__minus::before {
                    width: 8px;
                  }
                  .van-stepper__plus::before {
                    width: 8px;
                  }
                  .van-stepper__plus::after {
                    height: 8px;
                  }
                  .van-stepper__input {
                    font-size: 12px;
                    color: #333333;
                    background-color: #fff;
                    padding: 0px 12px;
                  }
                }
              }
            }
          }
        }
      }
    }
    .nohaveshop {
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-top: 100px;
      .van-icon {
        font-size: 60px;
        color: #666;
      }
      p {
        font-size: 14px;
        color: #999;
      }
      .p1 {
        margin-top: 20px;
      }
    }
  }
  .cartfotter {
    width: 100%;
    height: 60px;
    position: fixed;
    bottom: 0;
    left: 0;
    .van-submit-bar__bar {
      height: 60px;
      font-size: 16px;
      .van-checkbox {
        margin-left: 17px;
        ::v-deep .van-checkbox__icon {
          height: 14px;
          line-height: 14px;
          .van-icon {
            width: 14px;
            height: 14px;
            font-size: 12px;
            border: 1px solid #a5a5a5;
          }
        }
        ::v-deep .van-checkbox__label {
          font-size: 16px;
          color: #212121;
          margin-left: 9px;
        }
      }
      .buyprice {
        flex: 1;
        padding-right: 8px;
        text-align: right;
        display: flex;
        flex-direction: column;
        .p1 {
          font-size: 10px;
          color: #001410;
        }
        .p2 {
          font-size: 12px;
          color: #15c481;
          margin-top: 4px;
        }
      }
      .van-button--danger {
        width: 130px;
        height: 60px;
        background: rgba(21, 196, 129, 1);
        border: none;
        font-size: 16px;
        color: #ffffff;
      }
    }
  }
}
</style>

上面代碼沒用到vuex,目的是方便復制在進去就能直接運行,上面代碼同樣適用於接口獲取購物車的情況,進行本地價格計算,vuex我在下面分開介紹,按需添加進去;

store下的index.js

import Vue from "vue";
import Vuex from "vuex";
import app from "@/store/module/cart";
Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {
    cart
  }
});

上方代碼我們創建了一個cart,專門用於購物車模塊;

cart.js

export default {
  state: {
    // 購物車
    cartGoods: []
  },
  getters: {
    cartGoodIds(state) {
      return state.cartGoods.map(item => item.productId);
    }
  },
  mutations: {
    INIT_CART(state) {
      if (localStorage && localStorage.getItem("cartGoods")) {
        const goods = JSON.parse(localStorage.getItem("cartGoods"));
        state.cartGoods = goods;
      }
    },
    ADD_TO_CART(state, addGood) {
      const findGood = state.cartGoods.find(
        item => item.productId === addGood.productId
      );
      if (findGood) {
        findGood.num = findGood.num + addGood.num;
      } else {
        state.cartGoods.push(addGood);
      }
      if (localStorage) {
        localStorage.setItem("cartGoods", JSON.stringify(state.cartGoods));
      }
    },
    INPUT_TO_CART(state, inpGood) {
      const findGood = state.cartGoods.find(
        item => item.productId === inpGood.productId
      );
      const largeGood = state.cartGoods.find(item => item.num > inpGood.num);
      const equalGood = state.cartGoods.find(item => item.num == inpGood.num);
      const smallGood = state.cartGoods.find(item => item.num < inpGood.num);
      if (findGood && largeGood) {
        findGood.num = findGood.num - (findGood.num - inpGood.num);
      }
      if (findGood && equalGood) {
        findGood.num = inpGood.num;
      }
      if (findGood && smallGood) {
        findGood.num = inpGood.num;
      }
      if (localStorage) {
        localStorage.setItem("cartGoods", JSON.stringify(state.cartGoods));
      }
    }
  }
};

 

上方我們定義了一些計算方法和事件方法,來在其他頁面調用時改變購物車儲存的信息;

其他頁面:

// 導入
import { mapGetters, mapMutations } from "vuex";
// 計算
computed: {
   ...mapGetters(["cartGoodIds"])
},
// 方法
methods: {
    ...mapMutations(["ADD_TO_CART", "INPUT_TO_CART", "INIT_CART"]),
}
// 使用
// 增加
// 增加
plusadd(item) {
  this.$store.commit("ADD_TO_CART", {
    productId: item.id,
    num: 1
  });
},
// 減少
minuscut(item) {
  this.$store.commit("ADD_TO_CART", {
    productId: item.id,
    num: -1
  });
},
// 輸入
blurinput(item) {
  this.$store.commit("INPUT_TO_CART", {
    productId: item.id,
    num: parseInt(item.num)
  });
},

 

就先粗略的介紹到這里,有什么問題提出來我及時改正,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM