前言
- Pinia基本使用见上一篇文章:传送门点这里
- 效果展示:
Pinia + Vue3
一、功能介绍
- 借鉴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