购物车案例
- 准备工作
-
- 首页默认加载,其余页面懒加载
- 调用defineStore方法构建store
- 入口main做对应配置,找指南,快速开始,把elementplus引入进来
import { createApp } from "vue";
import { createPinia } from "pinia";
import "./style.css";
import App from "./App.vue";
import router from "./router";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
const app = createApp(App);
const pinia = createPinia();
app.use(router);
app.use(pinia);
app.use(ElementPlus);
app.mount("#app");
-
- api就是用来模拟后台接口的
- 在app.vue中做购物车首页,头部不进入路由,在头部的底部放RouterView
- 给app 的header添加插槽#header,写类名添加样式,头部插槽内的内容不会发生变化
<template><div><el-card class="box-card"><template #header><div class="card-header"><h1>购物车首页</h1><div class="btn"><RouterLink to="/productlist"><el-button type="primary">商品列表</el-button></RouterLink><RouterLink to="/shoppingcar"><el-button type="primary">购物车</el-button></RouterLink></div></div></template><RouterView /></el-card></div>
</template>
-
- 写购物车首页的标题
- 写el-button:商品列表,购物车。并用routerLink包裹,做一个导航,并为RouterLink添加to页面
- 在商品列表页面,写一个el-table,
<template><div class="product-list"><el-table :data="shoppingCarStore.productList" stripe style="width: 100%"><el-table-column prop="id" label="序号" align="center" /><el-table-column prop="title" label="商品名称" align="center" /><el-table-column prop="price" label="商品价格" align="center" /><el-table-column prop="number" label="库存" align="center" /><el-table-column prop="address" label="操作" align="center"><template #default="scope"><el-button type="success" @click="add(scope.row)">添加到购物车</el-button></template></el-table-column></el-table></div></template>
- 在store中写方法,初始化商品列表和初始化商品数据的方法
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { getProducts } from "../api/product";
import { ElMessage } from "element-plus";
import { buyProducts } from "../api/product.js";
const useShoppingCarStore = defineStore("shopping-car", () => {//初始化商品列表数据const productList = ref([]);//初始化购物车列表数据const shopCarList = ref([]);
-
- 一秒钟之后获取到数据(模拟从接口获取数据),
//1s中之后返回数组数据
export const getProducts = async ()=>{//为了模拟真实的接口请求await wait(1000)return products
}
-
- 方法在数据获取的部分有定义。
- 把两个需要给视图使用的方法从store中导出,并在商品列表中导入方法
- 绑定在table中,
- 给table添加按钮
-
- 改变button样式
- 绑定add方法,在页面中传参scope.row,代表点选的那一行。(scope代表插槽的作用域)在方法中传参row
<script setup>
import { useShoppingCarStore } from "../store";
const shoppingCarStore = useShoppingCarStore();
shoppingCarStore.getProductList();
function add(row) {//调用 将数据添加到购物车的方法shoppingCarStore.addShopCarList(row);
}
</script>
- 在store中写给购物车添加数据的方法
-
- 减去商品列表页面的库存:res的item id === row id时,下转判断,判断完自减--
- 添加购物车列表页面的商品数量,_res的item id === row id时,下转判断
-
-
- 在store中定义初始化购物车列表
- 判断购物车中是否已经存在这个商品
- 如果存在,将商品数量+1(_res.number++
- 如果没有,添加一个新商品(起始数量一定是1
-
-
- 判断当库存<1,写一个提示,商品已经妹有了,存在一秒。
- 导出方法并在列表页调用,放在add方法中,add传入的参数为row,指在点选的那一行。
const useShoppingCarStore = defineStore("shopping-car", () => {//初始化两行
......//初始化商品列表数据的方法(使用async方法,没有一大堆回调函数const getProductList = async () => {const res = await getProducts();productList.value = res;};// const getProductList = () => {// getProducts().then((res) => {// productList.value = res;// });// };//给购物车添加数据的方法const addShopCarList = (row) => {//1. 减去商品列表页面的商品库存const res = productList.value.find((item) => item.id === row.id);if (res.number < 1) {ElMessage.error({message: "没有库存辣!",duration: 1000,});return;}res.number--;//2. 添加购物车列表页面的商品数量const _res = shopCarList.value.find((item) => item.id === row.id);//2.1 判断购物车列表数据中是否包含当前的商品 如果包含 让数量自增//2.2 如果不包含 将这个商品添加到购物车列表if (!_res) {shopCarList.value.push({id: row.id,title: row.title,price: row.price,number: 1,});} else {_res.number++;}//给购物车列表中的商品按照id的升序做一个排序shopCarList.value.sort((a, b) => {return a.id - b.id;});};
...return{...addShopCarList,...};
- 在购物车列表中写el-table
-
- 引入useshoppingcarstore,并绑定数据.shopCarList
- 绑定prop属性,表示绑定了哪个属性
- 在store中添加排序功能,商品加入购物车后按照id自动排序,写在_res判断下面
-
- shopCarList.value.sort((a,b) => {})
getProductsList方法最好使用async和await语法,而不是等价的.then。因为async和await语法不涉及大量的回调函数,也就不用进行大量的回调函数嵌套,处理起来更简单不容易出错。
//初始化商品列表数据的方法const getProductList = async () => {const res = await getProducts();productList.value = res;};// const getProductList = () => {// getProducts().then((res) => {// productList.value = res;// });// };
- 写普通的table计算shopCarList里的商品总价
-
- 使用computed计算属性计算商品总价。
- const一个totalPrice
const useShoppingCarStore = defineStore("shopping-car", () => {//初始化商品列表数据
......//计算shopcarList里的商品总价const totalPrice = computed(() => {//reducereturn shopCarList.value.reduce((pre, cur) => {return pre + cur.number * cur.price;}, 0);});.....return {...totalPrice,...};
});
-
- return一个reduce方法,它是一个数组遍历中常用的方法。第一个参数为回调逻辑,第二个参数为累加初始值(大多数为0)
- 把商品总价渲染到页面
- 在商品列表页写一个结算商品的button,
-
- 绑定点击事件calc(vue2不能直接绑click,需要加字符;vue3的组件绑定会判断是否为原生事件)
- 在store中写calc方法
- 结算方法已经写在products中,引入到store中。
- 该方法通过一个随机数来随机判断结算是否成功。、
- 这是一个异步方法,需要async和await
- 判断结算是否成功,成功弹出成功,并清空数组;
- 结算失败弹出失败,不清空列表,需要再次结算。
const useShoppingCarStore = defineStore("shopping-car", () => {......//结算商品列表的方法const calc = async () => {const res = await buyProducts();if (res) {ElMessage.success({message: "结算成功",duration: 1000,});shopCarList.value = [];} else {ElMessage.error({message: "结算失败",duration: 1000,});}};return {....calc,};
});
- 购物车中,对结算框v-if判断,如果购物车列表没有数据,则不显示结算框。
<template><div class="shopping-car"><el-table :data="shoppingCarStore.shopCarList"><el-table-column prop="id" label="序号" align="center" /><el-table-column prop="title" label="名称" align="center" /><el-table-column prop="price" label="价格" align="center" /><el-table-column prop="number" label="数量" align="center" /></el-table><div class="btn" v-if="shoppingCarStore.shopCarList.length"><h2>商品总价是:{{ shoppingCarStore.totalPrice }}</h2><el-button type="primary" @click="calc">结算商品</el-button></div></div>
</template><script setup>
import { useShoppingCarStore } from "../store";
const shoppingCarStore = useShoppingCarStore();
function calc() {//触发商品结算的方法 calc()shoppingCarStore.calc();
}
</script>