355 lines
7.7 KiB
Vue
355 lines
7.7 KiB
Vue
<template>
|
||
<section class="cart-panel" :class="{ 'cart-panel--page': pageMode }">
|
||
<van-notice-bar v-if="showNotice" text="满 199 元包邮,快去凑单吧~" left-icon="info-o" />
|
||
<section class="cart-list">
|
||
<div v-if="!items.length" class="cart-empty">
|
||
<van-empty description="购物车还是空的" />
|
||
<van-button
|
||
class="cart-empty__button"
|
||
round
|
||
type="danger"
|
||
@click="$router.push('/joyful-selection/products')"
|
||
>去购物</van-button>
|
||
</div>
|
||
<template v-else>
|
||
<article
|
||
v-for="item in items"
|
||
:key="item.cartId"
|
||
class="cart-item"
|
||
>
|
||
<van-checkbox
|
||
:value="isItemChecked(item)"
|
||
class="item-check"
|
||
checked-color="#ff5b20"
|
||
@input="toggleItem(item, $event)"
|
||
/>
|
||
<van-image class="item-thumb" :src="item.image" fit="cover" />
|
||
<div class="item-info">
|
||
<h3>{{ item.name }}</h3>
|
||
<p class="item-desc">{{ item.desc }}</p>
|
||
<van-tag class="item-tag" plain type="danger">{{ item.tag }}</van-tag>
|
||
<div class="item-bottom">
|
||
<b>¥{{ item.price }}</b>
|
||
<van-stepper
|
||
v-model="item.count"
|
||
class="quantity-stepper"
|
||
integer
|
||
min="0"
|
||
button-size="30"
|
||
input-width="36"
|
||
@change="onCountChange(item, $event)"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</template>
|
||
</section>
|
||
<van-submit-bar
|
||
v-if="items.length"
|
||
:fixed="fixedSubmit"
|
||
:price="selectedTotalPrice"
|
||
:button-text="submitText"
|
||
@submit="handleCheckout"
|
||
>
|
||
<van-checkbox
|
||
:value="isAllChecked"
|
||
:indeterminate="isPartChecked"
|
||
checked-color="#ff5b20"
|
||
@input="toggleAll"
|
||
>全选</van-checkbox>
|
||
</van-submit-bar>
|
||
</section>
|
||
</template>
|
||
|
||
<script>
|
||
import { mapActions, mapState } from 'pinia'
|
||
import { useCartStore } from '@/store/cart'
|
||
|
||
export default {
|
||
name: 'JoyfulCartPanel',
|
||
props: {
|
||
fixedSubmit: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
pageMode: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
showNotice: {
|
||
type: Boolean,
|
||
default: true
|
||
}
|
||
},
|
||
data: () => ({
|
||
selectedIds: []
|
||
}),
|
||
computed: {
|
||
...mapState(useCartStore, ['items']),
|
||
cartIds() {
|
||
return this.items.map(item => item.cartId)
|
||
},
|
||
selectedItems() {
|
||
return this.items.filter(item => this.selectedIds.includes(item.cartId))
|
||
},
|
||
selectedTotalCount() {
|
||
return this.selectedItems.reduce((total, item) => total + Number(item.count || 0), 0)
|
||
},
|
||
selectedTotalPrice() {
|
||
return this.selectedItems.reduce((total, item) => total + Math.round(Number(item.price) * 100) * Number(item.count || 0), 0)
|
||
},
|
||
isAllChecked() {
|
||
return this.items.length > 0 && this.selectedIds.length === this.items.length
|
||
},
|
||
isPartChecked() {
|
||
return this.selectedIds.length > 0 && this.selectedIds.length < this.items.length
|
||
},
|
||
submitText() {
|
||
return `去结算(${this.selectedTotalCount})`
|
||
}
|
||
},
|
||
watch: {
|
||
cartIds(newIds, oldIds) {
|
||
const wasAllChecked = oldIds.length > 0 && oldIds.every(cartId => this.selectedIds.includes(cartId))
|
||
const validSelectedIds = this.selectedIds.filter(cartId => newIds.includes(cartId))
|
||
|
||
this.selectedIds = wasAllChecked ? newIds : validSelectedIds
|
||
}
|
||
},
|
||
created() {
|
||
this.loadCart()
|
||
this.selectAllItems()
|
||
},
|
||
methods: {
|
||
...mapActions(useCartStore, ['loadCart', 'persistCart', 'removeItems']),
|
||
selectAllItems() {
|
||
this.selectedIds = this.items.map(item => item.cartId)
|
||
},
|
||
isItemChecked(item) {
|
||
return this.selectedIds.includes(item.cartId)
|
||
},
|
||
toggleItem(item, checked) {
|
||
if (checked && !this.isItemChecked(item)) {
|
||
this.selectedIds = [...this.selectedIds, item.cartId]
|
||
return
|
||
}
|
||
|
||
if (!checked) {
|
||
this.selectedIds = this.selectedIds.filter(cartId => cartId !== item.cartId)
|
||
}
|
||
},
|
||
toggleAll(checked) {
|
||
this.selectedIds = checked ? this.items.map(item => item.cartId) : []
|
||
},
|
||
onCountChange(item, count) {
|
||
if (Number(count) < 1) {
|
||
this.removeItems([item.cartId])
|
||
return
|
||
}
|
||
|
||
this.persistCart()
|
||
},
|
||
handleCheckout() {
|
||
if (!this.items.length || !this.selectedItems.length) {
|
||
this.$toast('请选择要结算的商品')
|
||
return
|
||
}
|
||
|
||
this.$emit('checkout', this.selectedItems)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.cart-panel {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex-direction: column;
|
||
background: #fff;
|
||
}
|
||
|
||
.cart-panel--page {
|
||
min-height: calc(100vh - 46px);
|
||
padding-bottom: 108px;
|
||
}
|
||
|
||
.cart-panel ::v-deep .van-notice-bar {
|
||
height: 34px;
|
||
flex: none;
|
||
color: #ff7a38;
|
||
background: #fff3ea;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.cart-list {
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow-y: auto;
|
||
padding: 0 16px 12px;
|
||
background: #fff;
|
||
}
|
||
|
||
.cart-empty {
|
||
padding-top: 48px;
|
||
text-align: center;
|
||
}
|
||
|
||
.cart-empty__button {
|
||
width: 120px;
|
||
height: 38px;
|
||
background: #ff5b20;
|
||
border-color: #ff5b20;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.cart-item {
|
||
display: flex;
|
||
min-height: 128px;
|
||
align-items: center;
|
||
padding: 16px 0;
|
||
background: #fff;
|
||
}
|
||
|
||
.cart-item + .cart-item {
|
||
border-top: 1px solid #f5f5f5;
|
||
}
|
||
|
||
.item-check {
|
||
flex: none;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.cart-panel ::v-deep .van-checkbox__icon--checked .van-icon {
|
||
border-color: #ff5b20;
|
||
background-color: #ff5b20;
|
||
}
|
||
|
||
.item-thumb {
|
||
width: 94px;
|
||
height: 94px;
|
||
flex: none;
|
||
overflow: hidden;
|
||
border-radius: 5px;
|
||
background: #f7f7f7;
|
||
}
|
||
|
||
.item-info {
|
||
display: flex;
|
||
min-width: 0;
|
||
min-height: 94px;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
padding-left: 12px;
|
||
}
|
||
|
||
.item-info h3 {
|
||
display: -webkit-box;
|
||
overflow: hidden;
|
||
margin: 0 0 4px;
|
||
color: #222;
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
line-height: 20px;
|
||
text-overflow: ellipsis;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
}
|
||
|
||
.item-desc {
|
||
overflow: hidden;
|
||
margin: 0 0 6px;
|
||
color: #999;
|
||
font-size: 12px;
|
||
line-height: 16px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.item-tag {
|
||
width: fit-content;
|
||
color: #ff5b20;
|
||
border-color: #ff9b73;
|
||
font-size: 11px;
|
||
line-height: 14px;
|
||
}
|
||
|
||
.item-bottom {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.item-bottom b {
|
||
color: #ff5b20;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.quantity-stepper {
|
||
flex: none;
|
||
}
|
||
|
||
.cart-panel ::v-deep .quantity-stepper .van-stepper__minus,
|
||
.cart-panel ::v-deep .quantity-stepper .van-stepper__plus,
|
||
.cart-panel ::v-deep .quantity-stepper .van-stepper__input {
|
||
height: 30px;
|
||
margin: 0;
|
||
border: 1px solid #edf0f4;
|
||
background: #fff;
|
||
color: #222;
|
||
}
|
||
|
||
.cart-panel ::v-deep .quantity-stepper .van-stepper__minus {
|
||
width: 32px;
|
||
border-radius: 6px 0 0 6px;
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
.cart-panel ::v-deep .quantity-stepper .van-stepper__input {
|
||
border-right: 0;
|
||
border-left: 0;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.cart-panel ::v-deep .quantity-stepper .van-stepper__plus {
|
||
width: 32px;
|
||
border-radius: 0 6px 6px 0;
|
||
}
|
||
|
||
.cart-panel ::v-deep .van-submit-bar {
|
||
position: relative;
|
||
height: 58px;
|
||
flex: none;
|
||
border-top: 1px solid #f0f0f0;
|
||
background: #fff;
|
||
}
|
||
|
||
.cart-panel--page ::v-deep .van-submit-bar {
|
||
position: fixed;
|
||
bottom: 50px;
|
||
}
|
||
|
||
.cart-panel ::v-deep .van-submit-bar__bar {
|
||
height: 58px;
|
||
padding: 0 18px;
|
||
}
|
||
|
||
.cart-panel ::v-deep .van-submit-bar__price {
|
||
color: #ff5b20;
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.cart-panel ::v-deep .van-submit-bar__button {
|
||
width: 104px;
|
||
height: 40px;
|
||
border-radius: 22px;
|
||
background: #ff5b20;
|
||
border-color: #ff5b20;
|
||
font-weight: 700;
|
||
}
|
||
</style>
|