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

猜你喜欢

转载自blog.csdn.net/CherishTaoTao/article/details/126478442