feat: 加入购物车优化

This commit is contained in:
2026-07-27 18:31:15 +08:00
parent 8542b437ad
commit 45489cd0c4
4 changed files with 216 additions and 115 deletions
+8 -1
View File
@@ -16,7 +16,8 @@ const saveCartItems = items => {
export const useCartStore = defineStore('joyfulSelectionCart', {
state: () => ({
items: []
items: [],
loaded: false
}),
getters: {
totalCount: state => state.items.reduce((total, item) => total + Number(item.count || 0), 0),
@@ -25,11 +26,17 @@ export const useCartStore = defineStore('joyfulSelectionCart', {
actions: {
loadCart() {
this.items = readCartItems()
this.loaded = true
},
persistCart() {
saveCartItems(this.items)
this.loaded = true
},
addItem(item) {
if (!this.loaded) {
this.loadCart()
}
const index = this.items.findIndex(cartItem => cartItem.cartId === item.cartId)
if (index > -1) {
@@ -0,0 +1,178 @@
<template>
<van-sku
v-model="show"
:sku="sku"
:goods="skuGoods"
:goods-id="currentGoods.id"
:initial-sku="initialSku"
:hide-stock="sku.hide_stock"
:show-add-cart-btn="mode === 'cart'"
:show-buy-btn="mode === 'buy'"
:preview-on-click-image="true"
add-cart-text="加入购物车"
buy-text="立即购买"
@add-cart="onAddCartClicked"
@buy-clicked="onBuyClicked"
/>
</template>
<script>
import { Toast } from 'vant'
import { useCartStore } from '@/store/cart'
const colorOptions = [
{ id: 'cream', name: '奶油白' },
{ id: 'blue', name: '雾霾蓝' },
{ id: 'orange', name: '暖橙色' }
]
const sizeOptions = [
{ id: 'standard', name: '标准款' },
{ id: 'plus', name: '升级款' }
]
const emptyGoods = {
id: '',
name: '',
price: '0.00',
image: '',
tag: '赠品'
}
export default {
name: 'JoyfulSkuPopup',
data: () => ({
show: false,
mode: 'cart',
currentGoods: emptyGoods
}),
computed: {
skuGoods() {
return {
title: this.currentGoods.name,
picture: this.currentGoods.image
}
},
initialSku() {
return {
s1: colorOptions[0].id,
s2: sizeOptions[0].id
}
},
sku() {
const price = Math.round(Number(this.currentGoods.price) * 100)
return {
tree: [
{
k: '颜色',
k_s: 's1',
v: colorOptions
},
{
k: '尺寸',
k_s: 's2',
v: sizeOptions
}
],
list: colorOptions.flatMap(color =>
sizeOptions.map(size => ({
id: `${this.currentGoods.id}-${color.id}-${size.id}`,
s1: color.id,
s2: size.id,
price,
stock_num: 99
}))
),
price: this.currentGoods.price,
stock_num: 99,
none_sku: false,
hide_stock: true
}
}
},
methods: {
open(goods, mode = 'cart') {
this.currentGoods = goods
this.mode = mode
this.show = true
},
getCartPayload(data) {
const selectedSku = data.selectedSkuComb || {}
const selectedColor = colorOptions.find(item => item.id === selectedSku.s1) || colorOptions[0]
const selectedSize = sizeOptions.find(item => item.id === selectedSku.s2) || sizeOptions[0]
const count = data.selectedNum || 1
return {
cartId: selectedSku.id || `${this.currentGoods.id}-${selectedColor.id}-${selectedSize.id}`,
id: this.currentGoods.id,
name: this.currentGoods.name,
desc: `${selectedColor.name} / ${selectedSize.name}`,
price: this.currentGoods.price,
image: this.currentGoods.image,
tag: this.currentGoods.tag,
count,
sku: {
color: selectedColor.name,
size: selectedSize.name
}
}
},
addCart(data) {
const cartStore = useCartStore()
cartStore.addItem(this.getCartPayload(data))
},
onAddCartClicked(data) {
this.addCart(data)
Toast('已加入购物车')
this.show = false
this.$emit('add-cart')
},
onBuyClicked(data) {
this.addCart(data)
this.show = false
this.$emit('buy')
this.$router.push('/joyful-selection/checkout')
}
}
}
</script>
<style scoped lang="less">
::v-deep .van-sku-header {
min-height: 118px;
}
::v-deep .van-sku-header__img-wrap {
width: 110px;
height: 110px;
border-radius: 8px;
}
::v-deep .van-sku-header__goods-info {
min-height: 110px;
padding-top: 2px;
}
::v-deep .van-sku__goods-name {
color: #111;
font-size: 15px;
font-weight: 700;
line-height: 20px;
}
::v-deep .van-sku__price-symbol,
::v-deep .van-sku__price-num {
color: #ff5b20;
}
::v-deep .van-sku-row__item--active {
color: #ff5b20;
border-color: #ff5b20;
}
::v-deep .van-sku-actions .van-button--danger {
background: #ff5b20;
border-color: #ff5b20;
}
</style>
@@ -59,19 +59,27 @@
<van-goods-action>
<van-goods-action-icon icon="chat-o" text="客服" dot />
<van-goods-action-icon icon="cart-o" text="购物车" badge="5" @click="$router.push('/joyful-selection/cart')" />
<van-goods-action-icon icon="cart-o" text="购物车" :badge="cartBadge" @click="$router.push('/joyful-selection/cart')" />
<van-goods-action-icon icon="star" text="已收藏" color="#ff5000" />
<van-goods-action-button type="warning" text="加入购物车" @click="$toast('已加入购物车')" />
<van-goods-action-button type="danger" text="立即购买" @click="$router.push('/joyful-selection/checkout')" />
<van-goods-action-button type="warning" text="加入购物车" @click="openSku('cart')" />
<van-goods-action-button type="danger" text="立即购买" @click="openSku('buy')" />
</van-goods-action>
<joyful-sku-popup ref="skuPopup" @add-cart="loadCart" />
</main>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { useCartStore } from '@/store/cart'
import { getJoyfulProduct } from '../product-data'
import JoyfulSkuPopup from '../components/JoyfulSkuPopup.vue'
export default {
name: 'JoyfulSelectionProductDetail',
components: {
JoyfulSkuPopup
},
data: () => ({
count: 1,
features: [
@@ -82,7 +90,20 @@ export default {
]
}),
computed: {
...mapState(useCartStore, ['totalCount']),
cartBadge() {
return this.totalCount > 0 ? this.totalCount : ''
},
product() { return getJoyfulProduct(this.$route.query.id) }
},
created() {
this.loadCart()
},
methods: {
...mapActions(useCartStore, ['loadCart']),
openSku(mode) {
this.$refs.skuPopup.open(this.product, mode)
}
}
}
</script>
@@ -47,25 +47,13 @@
</div>
</section>
<van-sku
v-model="showSku"
:sku="sku"
:goods="skuGoods"
:goods-id="currentGoods.id"
:initial-sku="initialSku"
:hide-stock="sku.hide_stock"
buy-text="立即购买"
add-cart-text="加入购物车"
@buy-clicked="onBuyClicked"
@add-cart="onAddCartClicked"
/>
<joyful-sku-popup ref="skuPopup" />
</main>
</template>
<script>
import { Toast } from 'vant'
import { joyfulProducts } from '../product-data'
import { useCartStore } from '@/store/cart'
import JoyfulSkuPopup from '../components/JoyfulSkuPopup.vue'
const productImages = joyfulProducts.map(item => item.image)
@@ -162,26 +150,14 @@ const goods = productCategories.flatMap(category =>
}))
)
const colorOptions = [
{ id: 'cream', name: '奶油白' },
{ id: 'blue', name: '雾霾蓝' },
{ id: 'orange', name: '暖橙色' }
]
const sizeOptions = [
{ id: 'standard', name: '标准款' },
{ id: 'plus', name: '升级款' }
]
const defaultGoods = goods[0]
export default {
name: 'JoyfulSelectionProductList',
components: {
JoyfulSkuPopup
},
data: () => ({
selectedCategoryId: 'recommend',
showSku: false,
keyword: '',
currentGoods: defaultGoods,
categories,
goods
}),
@@ -193,48 +169,6 @@ export default {
return this.keyword
? categoryGoods.filter(item => item.name.includes(this.keyword))
: categoryGoods
},
skuGoods() {
return {
title: this.currentGoods.name,
picture: this.currentGoods.image
}
},
initialSku() {
return {
s1: colorOptions[0].id,
s2: sizeOptions[0].id
}
},
sku() {
const price = Math.round(Number(this.currentGoods.price) * 100)
return {
tree: [
{
k: '颜色',
k_s: 's1',
v: colorOptions
},
{
k: '尺寸',
k_s: 's2',
v: sizeOptions
}
],
list: colorOptions.flatMap(color =>
sizeOptions.map(size => ({
id: `${this.currentGoods.id}-${color.id}-${size.id}`,
s1: color.id,
s2: size.id,
price,
stock_num: 99
}))
),
price: this.currentGoods.price,
stock_num: 99,
none_sku: false,
hide_stock: true
}
}
},
methods: {
@@ -242,37 +176,7 @@ export default {
this.$router.push({ path: '/joyful-selection/product-detail', query: { id: item.id } })
},
openSku(item) {
this.currentGoods = item
this.showSku = true
},
onBuyClicked() {
Toast('请选择规格后购买')
},
onAddCartClicked(data) {
const selectedSku = data.selectedSkuComb || {}
const selectedColor = colorOptions.find(item => item.id === selectedSku.s1) || colorOptions[0]
const selectedSize = sizeOptions.find(item => item.id === selectedSku.s2) || sizeOptions[0]
const count = data.selectedNum || 1
const cartStore = useCartStore()
cartStore.addItem({
cartId: selectedSku.id || `${this.currentGoods.id}-${selectedColor.id}-${selectedSize.id}`,
id: this.currentGoods.id,
name: this.currentGoods.name,
desc: `${selectedColor.name} / ${selectedSize.name}`,
price: this.currentGoods.price,
image: this.currentGoods.image,
tag: this.currentGoods.tag,
count,
sku: {
color: selectedColor.name,
size: selectedSize.name
}
})
Toast('已加入购物车')
this.showSku = false
this.$refs.skuPopup.open(item, 'cart')
}
}
}
@@ -532,13 +436,4 @@ export default {
font-size: 20px;
}
.product-page ::v-deep .van-sku-actions .van-button--danger {
background: #ff5b20;
border-color: #ff5b20;
}
.product-page ::v-deep .van-sku-row__item--active {
color: #ff5b20;
border-color: #ff5b20;
}
</style>