基础思想:如果已经添加过相同的商品,就在其数量count上加一,如果没有添加过,就直接push到购物车列表中
cart.js
// 封装购物车模块
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useCartStore = defineStore('cart', () => {
// 1. 定义state - cartList
const cartList = ref([])
// 2. 定义action - addCart
const addCart = (goods) => {
console.log('添加', goods)
// 添加购物车操作
// 已添加过 - count + 1
// 没有添加过 - 直接push
// 思路:通过匹配传递过来的商品对象中的skuId能不能在cartList中找到,找到了就是添加过
const item = cartList.value.find((item) => goods.skuId === item.skuId)
if (item) {
// 找到了
item.count++
} else {
// 没找到
cartList.value.push(goods)
}
}
return {
cartList,
addCart
}
}, {
persist: true,
})
调用这个方法
// sku规格被操作时,获取这个sku
let skuObj = {}
const skuChange = (sku) => {
skuObj = sku
console.log(skuObj)
}
const cartStore = useCartStore()
const count = ref(1);
const countChange = () => {
console.log(count);
}
const addCart = () => {
if(skuObj.skuId) {
cartStore.addCart({
id: goods.value.id,
name: goods.value.name,
picture: goods.value.mainPictures[0],
price: goods.value.price,
count: count.value,
skuId: skuObj.skuId,
attrsText: skuObj.specsText
})
} else {
ElMessage.warning("请选择规格")
}
}
绑定这两个方法
<!-- 数据组件 -->
<el-input-number v-model="count" @change="countChange()"> </el-input-number>
<!-- 按钮组件 -->
<div>
<el-button size="large" class="btn" @click="addCart">
加入购物车
</el-button>
</div>
<script setup>
</script>
<template>
<div class="cart">
<a class="curr" href="javascript:;">
<i class="iconfont icon-cart"></i><em>2</em>
</a>
<div class="layer">
<div class="list">
<!--
<div class="item" v-for="i in cartList" :key="i">
<RouterLink to="">
<img :src="i.picture" alt="" />
<div class="center">
<p class="name ellipsis-2">
{{ i.name }}
</p>
<p class="attr ellipsis">{{ i.attrsText }}</p>
</div>
<div class="right">
<p class="price">¥{{ i.price }}</p>
<p class="count">x{{ i.count }}</p>
</div>
</RouterLink>
<i class="iconfont icon-close-new" @click="store.delCart(i.skuId)"></i>
</div>
-->
</div>
<div class="foot">
<div class="total">
<p>共 10 件商品</p>
<p>¥ 100.00 </p>
</div>
<el-button size="large" type="primary" >去购物车结算</el-button>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.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.4s 0.2s;
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>
<script setup>
import { useCartStore } from '@/stores/cart'
const cartStore = useCartStore()
</script>
<template>
<div class="cart">
<a class="curr" href="javascript:;">
<i class="iconfont icon-cart"></i><em>{{ cartStore.cartList.length }}</em>
</a>
<div class="layer">
<div class="list">
<div class="item" v-for="i in cartStore.cartList" :key="i">
<RouterLink to="">
<img :src="i.picture" alt="" />
<div class="center">
<p class="name ellipsis-2">
{{ i.name }}
</p>
<p class="attr ellipsis">{{ i.attrsText }}</p>
</div>
<div class="right">
<p class="price">¥{{ i.price }}</p>
<p class="count">x{{ i.count }}</p>
</div>
</RouterLink>
<i class="iconfont icon-close-new" @click="cartStore.delCart(i.skuId)"></i>
</div>
</div>
<div class="foot">
<div class="total">
<p>共 {{ cartStore.allCount }} 件商品</p>
<p>¥ {{ cartStore.allPrice.toFixed(2) }} </p>
</div>
<el-button size="large" type="primary" @click="$router.push('/cartlist')">去购物车结算</el-button>
</div>
</div>
</div>
</template>
1- 添加删除action函数
// 删除购物车
const delCart = async (skuId) => {
// 思路:
// 1. 找到要删除项的下标值 - splice
// 2. 使用数组的过滤方法 - filter
const idx = cartList.value.findIndex((item) => skuId === item.skuId)
cartList.value.splice(idx, 1)
}
2- 组件触发action函数并传递参数
<i class="iconfont icon-close-new" @click="cartStore.delCart(i.skuId)"></i>
const allCount = computed(()=> cartList.value.reduce((a,c)=>a+c.count, 0))
const allPrice = computed(()=> cartList.value.reduce((a,c)=>a+c.count*c.price, 0))