建议大家先去看我第一篇小兔鲜的文章,强烈建议,非常建议,十分建议,从头开始看更完整。
最近正在学习vue3小兔鲜
下面是学习笔记
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eB0x8EDy-1668073110525)(media/01-16459814763577.png)]
思路流程
Pinia
中,组件只需要触发 actions
函数actions
中通过 member
信息去区分登录状态
actions
修改 Pinia
中的数据即可actions
修改 Pinia
中的数据即可, Pinia
实现持久化,同步保存在本地本节目标:为购物车业务定义专属的 Store,多个组件都要用到购物车数据。
Pinia
基本结构
import { defineStore } from "pinia";
const useCartStore = defineStore("cart",{
// 状态
state: () => ({
// 购物车列表
list: [],
}),
// 计算
getters: {},
// 方法
actions: {},
});
export default useCartStore;
注意:记得到 src\store\index.ts
合并新 Store
本节目标: 完成商品详情的添加购物车操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KNv8qgRq-1668073110527)(media/03.png)]
实现步骤
actions
中封装加入购物车的接口。actions
函数调用接口。接口基本信息
Path: /member/cart
Method: POST
请求参数
Body
名称 | 类型 | 是否必须 | 默认值 | 备注 | 其他信息 |
---|---|---|---|---|---|
skuId | string | 必须 | SKUID | ||
count | integer | 必须 | 数量 |
文件:src\store\modules\cart.ts
const useCartStore = defineStore("cart", {
...
// 方法
actions: {
// 加入购物车
async addCart(data: { skuId: string; count: number }) {
const res = await http("POST", "/member/cart", data);
console.log("POST", "/member/cart", res.data.result);
},
},
});
在商品详情组件调用 actions
函数加入购物车。
skuId
和 count
skuId
需提示用户。
...
加入购物车
...
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YcHoMlnw-1668073110529)(media/02.png)]
本节目标:
在网站头部购物车图片处,鼠标经过展示购物车列表
实现步骤
getters
返回有效商品总数和有效商品列表代码落地
1)新建头部购物车组件
src/views/Layout/components/app-header-cart.vue
<script setup lang="ts">
//
script>
<template>
<div class="cart">
<a class="curr" href="javascript:;">
<i class="iconfont icon-cart">i><em>20em>
a>
<div class="layer">
<div class="list">
<div class="item" v-for="i in 4" :key="i">
<RouterLink to="">
<img
src="https://yanxuan-item.nosdn.127.net/ead73130f3dbdb3cabe1c7b0f4fd3d28.png"
alt=""
/>
<div class="center">
<p class="name ellipsis-2">
和手足干裂说拜拜 ingrams手足皲裂修复霜
p>
<p class="attr ellipsis">颜色:修复绿瓶 容量:150mlp>
div>
<div class="right">
<p class="price">¥45.00p>
<p class="count">x2p>
div>
RouterLink>
<i class="iconfont icon-close-new">i>
div>
div>
<div class="foot">
<div class="total">
<p>共 3 件商品p>
<p>¥135.00p>
div>
<XtxButton type="plain">去购物车结算XtxButton>
div>
div>
div>
template>
<style scoped lang="less">
.cart {
width: 50px;
position: relative;
z-index: 600;
.curr {
height: 32px;
line-height: 32px;
text-align: center;
position: relative;
display: block;
.icon-cart {
font-size: 22px;
}
em {
font-style: normal;
position: absolute;
right: 0;
top: 0;
padding: 1px 6px;
line-height: 1;
background: @helpColor;
color: #fff;
font-size: 12px;
border-radius: 10px;
font-family: Arial;
}
}
&:hover {
.layer {
opacity: 1;
transform: none;
}
}
.layer {
opacity: 0;
transition: all 0.2s 0.1s;
transform: translateY(-200px) scale(1, 0);
width: 400px;
height: 400px;
position: absolute;
top: 50px;
right: 0;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
background: #fff;
border-radius: 4px;
padding-top: 10px;
&::before {
content: '';
position: absolute;
right: 14px;
top: -10px;
width: 20px;
height: 20px;
background: #fff;
transform: scale(0.6, 1) rotate(45deg);
box-shadow: -3px -3px 5px rgba(0, 0, 0, 0.1);
}
.foot {
position: absolute;
left: 0;
bottom: 0;
height: 70px;
width: 100%;
padding: 10px;
display: flex;
justify-content: space-between;
background: #f8f8f8;
align-items: center;
.total {
padding-left: 10px;
color: #999;
p {
&:last-child {
font-size: 18px;
color: @priceColor;
}
}
}
}
}
.list {
height: 310px;
overflow: auto;
padding: 0 10px;
&::-webkit-scrollbar {
width: 10px;
height: 10px;
}
&::-webkit-scrollbar-track {
background: #f8f8f8;
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: #eee;
border-radius: 10px;
}
&::-webkit-scrollbar-thumb:hover {
background: #ccc;
}
.item {
border-bottom: 1px solid #f5f5f5;
padding: 10px 0;
position: relative;
i {
position: absolute;
bottom: 38px;
right: 0;
opacity: 0;
color: #666;
transition: all 0.5s;
}
&:hover {
i {
opacity: 1;
cursor: pointer;
}
}
a {
display: flex;
align-items: center;
img {
height: 80px;
width: 80px;
}
.center {
padding: 0 10px;
width: 200px;
.name {
font-size: 16px;
}
.attr {
color: #999;
padding-top: 5px;
}
}
.right {
width: 100px;
padding-right: 20px;
text-align: center;
.price {
font-size: 16px;
color: @priceColor;
}
.count {
color: #999;
margin-top: 5px;
font-size: 16px;
}
}
}
}
}
}
style>
2)header组件使用购物车组件
src/views/Layout/components/header/index.vue
Path: /member/cart
Method: GET
请求参数:无
action
const useCartStore = defineStore('cart', {
// ...
actions: {
// 获取购物车列表
async getCartList() {
// 已登录,调用接口
const res = await http('GET', '/member/cart');
console.log('GET', '/member/cart', res.data.result);
},
},
});
src\types\api\cart.d.ts
// 单个购物车商品
export interface CartItem {
id: string;
skuId: string;
name: string;
attrsText: string;
// specs: any[];
picture: string;
price: string;
nowPrice: string;
nowOriginalPrice: string;
selected: boolean;
stock: number;
count: number;
isEffective: boolean;
// discount?: any;
isCollect: boolean;
postFee: number;
}
// 购物车列表
export type CartList = CartItem[];
src\types\index.d.ts
// types 总管,统一导出类型声明
export * from './api/home';
export * from './api/category';
export * from './api/goods';
export * from './api/member';
export * from './api/qq';
+ export * from './api/cart';
src\store\modules\cart.ts
,并完善加入购物车后,主动获取服务器最新列表。+ import type { CartList } from '@/types';
const useCartStore = defineStore('cart', {
// 状态
state: () => ({
// 购物车列表
- list: [],
+ list: [] as CartList,
}),
// 方法
actions: {
// 加入购物车
async addCart(data: { skuId: string; count: number }) {
// 已登录,调用接口
const res = await http('POST', '/member/cart', data);
// 成功提示
message({ type: 'success', text: '加入购物车成功' });
+ // 加入成功后,主动获取服务器最新列表
+ this.getCartList();
},
// 获取购物车列表
async getCartList() {
// 已登录,调用接口
- const res = await http('GET', '/member/cart');
+ const res = await http('GET', '/member/cart');
+ // 保存购物车列表数据
+ this.list = res.data.result;
},
},
});
注意:
- 列表渲染的时候
key
值用skuId
,RouterLink
跳转的时候用产品id
- 购物车列表的
price
为加入时价格,最新价格是nowPrice
2
+
+
+
+
+
+ {{ item.name }}
+
+ {{ item.attrsText }}
+
+
+ ¥{{ item.nowPrice }}
+ x{{ item.count }}
+
+
+
+
共 3 件商品
¥135.00
去购物车结算
说明:
本节目标: 使用
Pinia
的getters
属性计算 有效商品列表,有效商品总数,有效商品总金额。
1)使用 getters
得到有效商品列表,总数量, 总价
getters: {
// 计算有效商品列表 isEffective = true filter
effectiveList(state): CartList {
return state.list.filter((item) => item.stock > 0 && item.isEffective);
},
// 有效商品总数量 把effctiveList中的每一项的count叠加起来
effectiveListCount(): number {
return this.effectiveList.reduce((sum, item) => sum + item.count, 0);
},
// 总钱数 = 所有单项的钱数累加 单项的钱数 = 数量 * 单价
effectiveListPrice(): string {
return this.effectiveList
.reduce((sum, item) => sum + item.count * Number(item.nowPrice), 0)
.toFixed(2);
},
},
2)渲染头部购物车模板
+ {{ cart.effectiveListCount }}
-
+
{{ item.name }}
{{ item.attrsText }}
¥{{ item.nowPrice }}
x{{ item.count }}
+ 共 {{ cart.effectiveListCount }} 件商品
+ ¥{{ cart.effectiveListPrice }}
去购物车结算
4. 删除功能实现
本节目标: 实现头部购物车中的列表删除功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SI9sMaBh-1668073110531)(media/04.png)]
接口:删除/清空购物车商品
基本信息
Path: /member/cart
Method: DELETE
请求参数
Body
名称
类型
是否必须
默认值
备注
其他信息
ids
string []
必须
SKUID 集合
item 类型: string
实现步骤
- 编写 actions 进行删除操作
- 在头部购物车进行 action 调用
- 删除请求发送后,主动获取购物车最新列表
- 优化:购物车没有数据的时候,不需要渲染列表
代码落地
1) Pinia 的actions代码
src/store/modules/cart.js
actions: {
// 删除购物车商品
async deleteCart(ids: string[]) {
// 发送请求让后端删除商品
await http("DELETE", "/member/cart", {ids: ids});
// 主动获取最新购物车列表
this.getCartList();
// 提示
message({ type: 'success', text: '删除成功' });
},
},
3)小优化
购物车没有数据的时候,不显示移入鼠标效果
<div class="layer" v-if="cart.effectiveList.length > 0">
列表购物车实现-已登录
1. 路由和组件
本节目标:完成购物车组件基础布局和路由配置与跳转链接。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l2KGNhdX-1668073110532)(media/05.png)]
1)新建购物车页面组件
src/views/Cart/index.vue
2)准备路由
{
path: '/cart',
component: () => import('@/views/Cart/index.vue')
}
- 点击购物车按钮跳转
{{ cart.effectiveListCount }}
去购物车结算
2.列表数据展示
本节目标:
实现购物车商品列表展示功能
- 渲染有效商品
- 最新价格是
nowPrice
- 计数器需要设置最大库存值
max
- 价格小计需要
toFixed(2)
保留两位小数
<tbody>
<tr v-for="goods in cart.effectiveList" :key="goods.skuId">
<td><XtxCheckBox :model-value="goods.selected" />td>
<td>
<div class="goods">
<RouterLink :to="`/product/${goods.id}`">
<img :src="goods.picture" :alt="goods.name" />
RouterLink>
<div>
<p class="name ellipsis">{{ goods.name }}p>
<p class="attr">{{ goods.attrsText }}p>
div>
div>
td>
<td class="tc">
<p>¥{{ goods.nowPrice }}p>
td>
<td class="tc">
<XtxCount :model-value="goods.count" :max="goods.stock" />
td>
<td class="tc">
<p class="f16 red">
¥{{ (Number(goods.nowPrice) * goods.count).toFixed(2) }}
p>
td>
<td class="tc">
<p><a class="green" href="javascript:;">删除a>p>
td>
tr>
tbody>
3. 删除操作实现
本节目标:
实现商品删除功能
思路分析
- 点击删除按钮记录当前点击的商品
skuId
- 调用 action 函数实现删除即可。
代码落地
删除按钮绑定事件触发删除action函数
删除光购物车之后使用元素占位
<tr>
<td colspan="6">
<div class="cart-none" style="text-align: center">
<img src="@/assets/images/none.png" alt="" />
<p>购物车内暂时没有商品p>
<div class="btn" style="margin: 20px">
<XtxButton type="primary">
继续逛逛
XtxButton>
div>
div>
td>
tr>
4. 单选操作实现
本节目标:
实现的商品单选功能
从单选开始,我们进入到一个 Pinia
+ 表单数据的交互功能实现,这里面有些小坑,我们先来看一下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xwg905z8-1668073110533)(media/06.png)]
思路分析
- 通过切换选择框组件获得当前的最新状态
- 获取点击单选框的商品
skuId
- 获取点击单选框的
selected
的最新状态
接口:修改购物车商品
Path: /member/cart/:id
Method: PUT
请求参数
路径参数
参数名称
示例
备注
id
SKUID
Body
名称
类型
是否必须
默认值
备注
其他信息
selected
boolean
非必须
是否选中
count
integer
非必须
数量
代码落地
1)定义 Pinia
中操作 selected 的 action 函数
const useCartStore = defineStore('cart', {
// 方法
actions: {
// 修改购物车商品(是否选中,商品数量)
async updateCart( skuId: string,data: { selected?: boolean; count?: number } ) {
// 已登录,调用接口
const res = await http('PUT', `/member/cart/${skuId}`, data);
// 主动获取最新列表
this.getCartList();
},
},
});
2)获取到当前选择框的最新状态
5. 修改数量实现
本节目标:
实现商品数量修改功能
思路分析
- 参考单选操作实现。
- 温馨提醒:XtxUI 组件库为了方便开发者使用,提供两种自定义事件获取值 @update:model-value 和 @change 功能是等价的。
代码落地
2)绑定事件触发 action
6. 全选切换实现
本节目标:
实现商品全选切换功能。
接口:购物车全选/取消全选
Path: /member/cart/selected
Method: PUT
接口描述:
ids参数如果不传,表示用户访问的是全选和取消全选操作,后端根据 selected 确定用户是全选和取消全选
请求参数
Body
名称
类型
是否必须
默认值
备注
其他信息
selected
boolean
必须
是否选中
ids
string []
非必须
skuId集合
item 类型: string
├─
非必须
skuId
思路分析
- 通过
getters
计算出选中状态 (注意: Pinia
的 getters
没有 set
)
- 封装调用修改全选接口的
actions
。
- 实现全选效果。
- 方案1:
:model-value
和 @update:model-value
组合。
- 方案2:
v-model
和 computed
组合。
代码落地
1)在 src\store\modules\cart.ts
中封装 actions
和 getters
。
const useCartStore = defineStore("cart", {
// 计算
getters: {
...
// 计算全选状态
isAllSelected(): boolean {
return (
this.effectiveList.length > 0 &&
this.effectiveList.every((v) => v.selected)
);
},
},
// 方法
actions: {
...
// 购物车全选/取消全选
async updateCartAllSelected(data: { selected: boolean; ids?: string[] }) {
// 已登录,调用接口
const res = await http("PUT", "/member/cart/selected", data);
// 获取购物车列表
this.getCartList();
},
},
});
2)页面中调用
全选
7. 操作栏数据计算和渲染
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cj7Jtst-1668073110534)(media/image-20210819135539000.png)]
// 已选择的列表
selectedList ():CartList {
return this.effectiveList.filter(item => item.selected)
},
// 已选择的商品总数
selectedListCount ():number {
return this.selectedList.reduce((sum, item) => sum + item.count, 0)
},
// 已选择的列表总价
selectedListPrice ():string {
return this.selectedList.reduce((sum, item) => sum + item.count * Number(item.nowPrice), 0).toFixed(2)
}
共 {{ cart.effectiveListCount }} 件有效商品,已选择 {{ cart.selectedListCount }} 件,有效商品合计:
¥{{ cart.selectedListPrice }}
下单结算
【转折点】退出登录
本节目标:
退出登录,为实现未登录版购物车做准备。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vzdh3ZmG-1668073110535)(media/01.png)]
问题思考
- 思考1:退出登录后,直接调用购物车系列接口会报错,怎么办?
- 调用接口前,判断是否已登录。
- 思考2:退出登录后,为什么还能看到购物车数据,要怎么处理?
- 数据存储到
Pinia
中,退出登录后要清空购物车列表。
- 思考3:如何判断用户登录还是未登录,要如何处理??
- 获取
member
模块的 是否有 token
即可。
- 思考4:根据上面需求,该如何设计程序?
- 给操作购物车数据的
actions
内部都添加判断,已登录调用接口,未登录本地操作。
退出登录清空购物车
购物车模块:src\store\modules\cart.ts
const useCartStore = defineStore("cart", {
// 方法
actions: {
...
+ // 清空购物车
+ clearCart() {
+ // 退出登录需清空购物车
+ this.list = [];
+ },
},
});
会员中心模块:src\store\modules\member.ts
const useMemberStore = defineStore("member",{
...
actions: {
...
// 退出登录
async logout() {
this.profile = {} as Profile;
clearStorageProfile();
router.push("/login");
+ // 退出登录-主动清空购物车数据
+ const { cart } = useStore();
+ cart.clearCart();
},
},
});
已登录和未登录程序设计
const useCartStore = defineStore("cart",{
...
// 计算
getters: {
+ // 登录状态,购物车模块缓存 member.isLogin 登录状态
+ isMemberLogin(): boolean {
+ const { member } = useStore();
+ return member.isLogin;
+ },
...
},
// 方法
actions: {
// 加入购物车
async addCart(data: CartItem) {
+ // 判断登录状态
+ if (this.isMemberLogin) {
+ // 1️⃣已登录情况 - 调用接口
const { skuId, count } = data;
const res = await http("POST", "/member/cart", { skuId, count });
this.getCartList();
+ } else {
+ // 2️⃣未登录情况 - 操作本地数据(相当于高级版todos)
+ }
message({ type: "success", text: "添加成功~" });
}
...
},
});
export default useCartStore;
退出登录还需要清空 Pinia
中的购物车数据。
购物车操作-未登录
1. 加入购物车
本节目标:
实现加入购物车业务。
实现步骤
- 点击加入购物车的时候,从商品详情中收集购物车商品展示所需数据。(字段很多,操作小心)
- actions 中完成添加操作(未登录)。
落地代码
商品详情页:src\views\Goods\index.vue
购物车模块:src/store/modules/cart.js
actions: {
// 加入购物车
async addCart(data: CartItem) {
// 解构出接口所需参数
const { skuId, count } = data;
if (this.isMemberLogin) {
// 已登录情况 - 调用接口
const res = await http("POST", "/member/cart", { skuId, count });
this.getCartList();
} else {
// 未登录情况 - 操作本地数据(相当于高级版todos)
// 添加商品分两种情况:
const cartItem = this.list.find((item) => item.skuId === skuId);
if (cartItem) {
// 情况1:已添加过的的商品,累加数量即可
cartItem.count += count;
} else {
// 情况2:新添加的商品,前添加到数组中
this.list.unshift(data);
}
}
message({ type: "success", text: "添加成功~" });
},
}
2. 删除购物车
本节目标:
实现删除操作。
实现步骤
- actions 中完成删除操作(未登录)。
落地代码
完善业务:src/store/modules/cart.js
actions: {
// 删除购物车商品
async deleteCart(data: { ids: string[] }) {
// 是否登录两种情况
if (this.isMemberLogin) {
// 已登录,调用接口
const res = await http('DELETE', '/member/cart', data);
console.log('DELETE', '/member/cart', res.data.result);
// 主动获取最新列表
this.getCartList();
// 成功提示
message({ type: 'success', text: '删除成功' });
} else {
// 未登录,操作本地数据(相当于高级版todos)
+ this.list = this.list.filter((v) => !data.ids.includes(v.skuId));
}
},
}
3. 购物车列表持久化存储
本节目标:
实现 Pinia
购物车列表的持久化存储。
实现步骤
- 前面我们已经安装了
Pinia
持久化存储插件,在模块中开启插件功能即可。
代码落地
src/store/modules/cart.js
const useCartStore = defineStore("cart", {
+ // 购物车开启持久化
+ persist: true,
...
});
步骤验证:开启持久化设置后,打开浏览器本地存储面板检查是否生效。
4. 选中状态切换&修改数量
本节目标:
实现商品选中状态的切换和修改商品数量。
实现步骤
- actions 中完成修改操作(未登录)。
- 注意点:由于修改数量和修改选择状态都是同一个 action 完成,所以本地修改前需要判断。
代码落地
完善业务:src/store/modules/cart.js
actions: {
// 修改购物车商品-修改选中-修改数量
async updateCart( skuId: string,data: { selected?: boolean; count?: number } ) {
if (this.isMemberLogin) {
// 已登录情况 - 调用接口
await http("PUT", `/member/cart/${skuId}`, data);
this.getCartList();
} else {
// 未登录情况 - 操作本地数据(相当于高级版todos)
+ // 根据 skuId 查找待修改的商品项
+ const cartItem = this.list.find((v) => v.skuId === skuId);
+ // 如果能查找到
+ if (cartItem) {
+ // 解构出需要修改的字段
+ const { selected, count } = data;
+ // 由于修改数量和修改选择状态都是同一个 action 完成,所以本地修改前需要判断
+ if (count !== undefined) cartItem.count = count;
+ if (selected !== undefined) cartItem.selected = selected;
+ }
}
},
}
5. 全选切换-本地
本节目标:
实现商品选中状态的切换。
实现步骤
- actions 中完成全选切换操作(未登录)。
代码落地
完善业务:src/store/modules/cart.js
actions: {
// 购物车全选/取消全选
async updateCartAllSelected(data: { selected: boolean; ids?: string[] }) {
if (this.isMemberLogin) {
// 已登录情况 - 调用接口
await http("PUT", "/member/cart/selected", data);
this.getCartList();
} else {
// 未登录情况 - 操作本地数据(相当于高级版todos)
+ this.list.forEach((item) => {
+ item.selected = data.selected;
+ });
}
},
}
6. 更新本地购物车商品关键信息
本节目标: 未登录情况下,更新本地购物车商品关键信息。
原因解释:本地存储的库存信息和价格不是服务器最新的,所以需要主动更新最新商品关键信息。
关键信息主要包括:商品最新价格,商品最新库存,商品是否还有效。
接口:查询商品库存价格信息
Path: /goods/stock/:id
Method: GET
请求参数
路径参数
参数名称
示例
备注
id
1352956998412406785
SKU_ID
实现思路
- 获取购物车列表的 action 中,未登录情况 下主动获取商品库存价格。
- 购物车的商品库存,价格,是否有效,更新成最新的。
代码落地
完善业务:src/store/modules/cart.js
actions: {
// 获取购物车列表
async getCartList() {
if (this.isMemberLogin) {
// 已登录情况 - 调用接口
const res = await http("GET", "/member/cart");
this.list = res.data.result;
} else {
+ // 未登录情况,首次打开时需查询购物车列表商品最新价格,库存,是否有效
+ this.list.forEach(async (item) => {
+ // 根据 skuId 获取最新商品信息
+ const res = await http("GET", `/goods/stock/${item.skuId}`);
+ // 保存最新商品信息
+ const newCartItemInfo = res.data.result;
+ // 更新商品现价
+ item.nowPrice = newCartItemInfo.nowPrice;
+ // 更新商品库存
+ item.stock = newCartItemInfo.stock;
+ // 更新商品是否有效
+ item.isEffective = newCartItemInfo.isEffective;
+ });
}
},
}
两个版本购物车同步
1. 登录后合并购物车
本节目标:
登录后把本地的购物车数据合并到后端服务
本地已经存好的所有购物车列表数据都需要和服务器合并一下。
实现思路
-
编写并调用 合并购物车的 action
函数 (将对于购物车的处理,统一到 Pinia
中)
思考:在哪调用 合并购物车的 action
函数?
答:登录完成后。
接口:合并购物车
基本信息
Path: /member/cart/merge
Method: POST
请求参数
Body
名称
类型
是否必须
默认值
备注
其他信息
object []
必须
购物车sku集合
item 类型: object
├─ skuId
string
必须
skuId
├─ selected
boolean
必须
是否选中
├─ count
integer
必须
数量
登录后合并购物车
落地代码
1)编写合并购物车的 actions
函数
- 映射接口所需参数
- 调用合并购物车的接口
- 主动获取最新列表
购物车模块:src/store/modules/cart.js
actions: {
// 合并购物车
async mergeLocalCart() {
// 映射接口所需参数
const data = this.list.map(({ skuId, selected, count }) => ({
skuId,
selected,
count,
}));
// 调用合并购物车的接口
await http("POST", "/member/cart/merge", data);
// 合并成功,重新获取购物车列表
this.getCartList();
},
}
2)在登录完成功后,调用合并购物车 actions
函数
用户模块:src\store\modules\member.ts
const useMemberStore = defineStore("member", {
...
// 方法
actions: {
// 登录成功通用业务
loginSuccess(profile: Profile) {
// 登录成功后的业务
// 1. 成功提示
message({ type: 'success', text: '登录成功~' });
// 2. 存储数据
this.profile = profile;
// 获取目标页面 target,如果没有目标页,则设置默认值 '/' 去首页
const { target = '/' } = router.currentRoute.value.query;
// 3. 跳转页面
router.push(target as string);
+ // 4. 合并购物车
+ const { cart } = useStore();
+ cart.mergeLocalCart();
},
},
});