Vue3+Pinia—购物车实例


前言


一、功能介绍

  • 借鉴vuex官方例子修改
  • 本例子分为视频展示列表与购物车内容展示两个主要模块
  • 通过Pinia创建的实例来对商品的状态进行控制

二、创建步骤

1.创建API接口模拟数据

  • 由上篇文章创建好vite项目后,src下对应的层级结构为:
    在这里插入图片描述
  • shop.ts
    代码如下:
export interface IProducts {
  id: number;
  title: string;
  price: number;
  inventory: number; //库存
}

const _products: IProducts[] = [
  { id: 1, title: "牛仔裤", price: 122, inventory: 2 },
  { id: 2, title: "卫衣", price: 222, inventory: 5 },
  { id: 3, title: "运动鞋", price: 322, inventory: 6 },
];
// wait,封装了promise的定时器
export const getProducts = async () => {
  await wait(100);
  // 模拟接收数据的请求
  return _products;
};

export const buyProducts = async () => {
  await wait(100);
  // 模拟结算后发送请求
  return Math.random() > 0.5;
};
async function wait(delay: number) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, delay);
  });
}

2.页面文件

  • productsList.vue
<template>
  <div>
    <ul>
      <li v-for="i in ProductsStore.allProducts">
        名称:{{ i.title }}——{{ i.price }}<button
          :disabled="i.inventory < 1"
          @click="cartStore.addProductToCart(i)"
        >
          添加到购物车
        </button>
        <br />
        <br />
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { useProductsStore } from "@/store/products";
import { useCartStore } from "@/store/cart";

// 读取数据
const ProductsStore = useProductsStore();
// 操作actions中的get函数,初始化allProducts的值,取出使用
ProductsStore.getAllProducts();
// 添加到购物车
const cartStore = useCartStore();
// cartStore.addProductToCart();
</script>

<style scoped></style>
  • shoppingCart.vue
<template>
  <div class="cart">
    <h2>你的购物车</h2>
    <p>请添加商品到购物车中</p>
    <ul>
      <li v-for="c in cartStore.cartProducts">
        名称:{{ c.title }}——价格:{{ c.price }}——数量:{{ c.quantity }}
      </li>
    </ul>
    <p>商品总价: {{ cartStore.sum }}</p>
    <p>
      <button @click="cartStore.check">结算</button>
    </p>
    <p v-if="cartStore.isSettle === 1">结算成功</p>
    <p v-else-if="cartStore.isSettle === 0">结算失败</p>
  </div>
</template>

<script setup lang="ts">
import { useCartStore } from "@/store/cart";
const cartStore = useCartStore();
</script>

<style scoped></style>

  • App.vue
<script setup lang="ts">
import ProductsList from "@/components/productsList.vue";
import ShoppingCart from "@/components/shoppingCart.vue";
</script>

<template>
  <div style="text-align: left">
    <h1>Vue3+Pinia—购物车实例</h1>
    <hr />
    <h2>商品列表</h2>
    <ProductsList />
    <hr />
    <ShoppingCart />
  </div>
  <!-- <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div> -->
  <!-- <HelloWorld /> -->
</template>

<style scoped></style>

3.创建Store

  • products.ts
import { defineStore } from "pinia";
import { IProducts, getProducts } from "@/api/shop";
export const useProductsStore = defineStore("products", {
  state: () => {
    return {
      // 默认是空类型,设置allProducts类型为IProducts
      allProducts: [] as IProducts[],
    };
  },
  actions: {
    async getAllProducts() {
      // res是IProducts类型的数据,不能将其赋值给allProducts,所以要将allProducts赋值赋值为一样的数据类型
      const res = await getProducts();
      this.allProducts = res;
    },
    decProducts(product: IProducts) {
      const res = this.allProducts.find((p) => p.id === product.id);
      // 如果有,则减库存
      if (res) {
        res.inventory--;
      }
    },
  },
  getters: {},
});
  • cart.ts
import { IProducts, buyProducts } from "@/api/shop";
import { defineStore } from "pinia";
import { useProductsStore } from "@/store/products";

// 1. ‘ & ’合并类型,将CartProduct与IProducts合并,表示接口类型合并
// 使用omit过滤合并对象中不需要的类型:Omit<要合并的接口类型,'不需要的过滤对象'>
// 如 type C={} & Oimit(a,'b'):表示C类型与a类型合并,同时使用Omit过滤掉不需要的b属性
// 属性只剩下{quantity,id,price,title}
type CartProduct = {
  quantity: number;
} & Omit<IProducts, "inventory">;

// actions封装逻辑,
export const useCartStore = defineStore("cart", {
  state: () => {
    return {
      cartProducts: [] as CartProduct[],
      isSettle: 2,
    };
  },
  getters: {
    // 计算总账单
    sum() {
      let s = 0;
      for (let i of this.cartProducts) {
        // console.log(i);
        s = s + i.price * i.quantity;
      }
      return s;
    },
  },
  actions: {
    addProductToCart(products: IProducts) {
      console.log("addProductToCart", products);
      // 1.看商品是否有库存
      if (products.inventory < 1) {
        return;
      }
      // 2.检查购物车是否有该商品
      const carItems = this.cartProducts.find((p) => p.id === products.id);
      // 3,有则让商品数量+1
      if (carItems) {
        // 如果有这个商品则新建一个变量原来存储当前商品数量
        // carItems.数量++;但是IProducts里无该类型,所以要加一个quantity表示新类型
        carItems.quantity++;
      }
      // 4.没有则添加到购物车列表
      else {
        this.cartProducts.push({
          id: products.id,
          title: products.title,
          price: products.price,
          quantity: 1,
        });
      }
      // 更新商品库存
      const productsStore = useProductsStore();
      productsStore.decProducts(products);
    },
    // 判断结算是否成功
    async check() {
      const res = await buyProducts();
      if (res) {
        this.isSettle = 1;
      } else {
        this.isSettle = 0;
      }
    },
  },
});

更多源码见Git地址


总结

  • 借用了vuex官方的例子来改造
  • pinia比vuex轻便很多,不需要操作mutations
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值