• 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 {
                         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)
      });
    },

    就先粗略的介绍到这里,有什么问题提出来我及时改正,谢谢!

  • 相关阅读:
    boost pool 和 object_pool
    boost::ref
    Source Insight设置
    windows7下硬盘安装ubuntu14.04
    POJ 2778 DNA Sequence (AC自己主动机 + dp)
    cocos2dx 运动+旋转动画 CCSequence CCAnimation CCAnimate CCMoveTo CCCallFuncN
    LeetCode 2 Add Two Numbers
    MySQL数据库导入外部*.sql文件具体步骤
    [ExtJS5学习笔记]第十节 Extjs5新增特性之ViewModel和DataBinding
    svn项目导入
  • 原文地址:https://www.cnblogs.com/hejun26/p/12161591.html
Copyright © 2020-2023  润新知