Files
points-mall/supplied-h5/src/view/joyful-selection/product-detail/index.vue
T

460 lines
9.5 KiB
Vue

<template>
<div>
<joyful-selection-product-detail-skeleton v-if="loading" />
<main v-else class="detail-page">
<header class="detail-top">
<van-icon name="arrow-left" @click="$router.back()" />
<h1>商品详情</h1>
<div>
<van-icon name="star-o" />
<van-icon name="share-o" />
</div>
</header>
<section class="image-section">
<van-image class="product-image" :src="product.image" fit="cover" />
<span class="image-index">1/{{ imageCount }}</span>
</section>
<section class="product-info">
<h2>{{ product.name }}</h2>
<p>{{ product.detail }}</p>
<div class="price-row">
<b>¥{{ product.price }}</b>
<del>¥{{ product.originPrice }}</del>
<van-tag class="point-tag" plain type="danger">{{ product.tag }}</van-tag>
</div>
</section>
<section class="option-card">
<van-cell title="选择规格" value="默认规格" is-link />
<van-cell title="商品数量">
<template #right-icon>
<div class="quantity-box">
<van-stepper v-model="count" integer min="1" button-size="28" input-width="42" />
<span>库存充足</span>
</div>
</template>
</van-cell>
</section>
<section class="service-card">
<h3>商品服务</h3>
<van-cell title="正品保障" value="品质保证,安心兑换" is-link>
<template #icon><van-icon class="service-icon" name="shield-o" /></template>
</van-cell>
<van-cell title="配送" value="满 99 元免配送费" is-link>
<template #icon><van-icon class="service-icon" name="logistics" /></template>
</van-cell>
</section>
<section class="detail-content">
<h3>商品详情</h3>
<div class="feature-grid">
<article v-for="item in features" :key="item.title">
<span><van-icon :name="item.icon" /></span>
<b>{{ item.title }}</b>
<p>{{ item.desc }}</p>
</article>
</div>
</section>
<van-action-bar>
<van-action-bar-icon icon="chat-o" text="客服" dot />
<van-action-bar-icon icon="cart-o" text="购物车" :badge="cartBadge" @click="openCartPopup" />
<van-action-bar-icon icon="star" text="已收藏" color="#ff5000" />
<van-action-bar-button type="warning" text="加入购物车" @click="openSku('cart')" />
<van-action-bar-button type="danger" text="立即购买" @click="openSku('buy')" />
</van-action-bar>
<joyful-sku-popup ref="skuPopup" @add-cart="loadCart" />
<van-popup v-model:show="showCartPopup" class="cart-popup" position="bottom" round>
<header class="cart-popup-head">
<b>购物车</b>
<van-icon name="cross" @click="showCartPopup = false" />
</header>
<joyful-cart-panel
ref="cartPanel"
class="cart-popup-panel"
@checkout="goCheckout"
/>
</van-popup>
</main>
</div>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { getFunction } from '@/api/supplied-service'
import { useCartStore } from '@/store/cart'
import JoyfulSkuPopup from '../products/components/JoyfulSkuPopup.vue'
import JoyfulCartPanel from '../cart/components/JoyfulCartPanel.vue'
import JoyfulSelectionProductDetailSkeleton from '@/skeleton-view/joyful-selection/product-detail/index.vue'
const emptyProduct = {
id: '',
name: '',
detail: '',
price: '0.00',
originPrice: '0.00',
image: '',
picInfo: [],
tag: ''
}
export default {
name: 'JoyfulSelectionProductDetail',
components: {
JoyfulSkuPopup,
JoyfulCartPanel,
JoyfulSelectionProductDetailSkeleton
},
data: () => ({
loading: true,
product: emptyProduct,
showCartPopup: false,
count: 1,
features: [
{ icon: 'certificate', title: '品质优选', desc: '精选商品,安心兑换' },
{ icon: 'logistics', title: '快速配送', desc: '下单后尽快安排发货' },
{ icon: 'shield-o', title: '售后保障', desc: '商品问题及时处理' },
{ icon: 'gold-coin-o', title: '积分抵扣', desc: '支持积分支付兑换' }
]
}),
computed: {
...mapState(useCartStore, ['totalCount']),
cartBadge() {
return this.totalCount > 0 ? this.totalCount : ''
},
imageCount() {
return Math.max(this.product.picInfo?.length || 1, 1)
}
},
created() {
this.loadCart()
this.loadProduct()
},
methods: {
...mapActions(useCartStore, ['loadCart']),
async loadProduct() {
this.loading = true
try {
const data = await getFunction('product-detail', { id: this.$route.query.id })
this.product = data || emptyProduct
} catch (error) {
console.warn('查询商品详情失败', error)
this.$toast(error.message || '查询商品详情失败')
} finally {
this.loading = false
}
},
openCartPopup() {
this.loadCart()
this.showCartPopup = true
this.$nextTick(() => {
if (this.$refs.cartPanel) {
this.$refs.cartPanel.selectAllItems()
}
})
},
goCheckout(items) {
sessionStorage.setItem('joyful-selection-checkout-items', JSON.stringify(items))
this.showCartPopup = false
this.$router.push({ path: '/joyful-selection/checkout', query: { source: 'cart' } })
},
openSku(mode) {
if (!this.product.id) {
return
}
this.$refs.skuPopup.open(this.product, mode)
}
}
}
</script>
<style scoped lang="less">
.detail-page {
min-height: 100vh;
padding-bottom: 60px;
background: #f5f5f5;
}
.detail-top {
position: fixed;
z-index: 10;
top: 0;
right: 0;
left: 0;
display: flex;
height: 54px;
align-items: center;
justify-content: space-between;
padding: 0 18px;
color: #111;
background: rgba(247, 248, 250, .88);
backdrop-filter: blur(8px);
}
.detail-top > .van-icon {
font-size: 26px;
}
.detail-top h1 {
margin: 0;
font-size: 18px;
font-weight: 700;
}
.detail-top div {
display: flex;
gap: 20px;
}
.detail-top div .van-icon {
font-size: 24px;
}
.image-section {
position: relative;
padding-top: 54px;
background: #eef0f4;
}
.product-image {
display: block;
width: 100%;
height: 365px;
background: #fff;
}
.image-index {
position: absolute;
right: 18px;
bottom: 26px;
min-width: 44px;
padding: 5px 10px;
border-radius: 16px;
color: #fff;
font-size: 14px;
line-height: 1;
text-align: center;
background: rgba(0, 0, 0, .36);
}
.product-info,
.option-card,
.service-card,
.detail-content {
margin: 10px 12px 0;
border-radius: 12px;
background: #fff;
}
.product-info {
margin-top: 12px;
padding: 18px 16px 16px;
}
.product-info h2 {
margin: 0 0 12px;
color: #111;
font-size: 20px;
font-weight: 700;
line-height: 1.25;
}
.product-info p {
margin: 0;
color: #777;
font-size: 14px;
line-height: 1.7;
}
.price-row {
display: flex;
align-items: center;
gap: 12px;
margin-top: 17px;
}
.price-row b {
color: #ff5b20;
font-size: 24px;
font-weight: 700;
line-height: 1;
}
.price-row del {
color: #999;
font-size: 13px;
}
.point-tag {
color: #ff2f3a;
border-color: #ff2f3a;
font-size: 13px;
}
.option-card {
overflow: hidden;
padding: 4px 16px;
}
.option-card ::v-deep .van-cell {
padding: 15px 0;
}
.option-card ::v-deep .van-cell::after {
right: 0;
left: 0;
}
.option-card ::v-deep .van-cell__title {
color: #222;
font-size: 15px;
font-weight: 700;
}
.option-card ::v-deep .van-cell__value {
color: #888;
font-size: 14px;
}
.quantity-box {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 6px;
}
.quantity-box span {
color: #999;
font-size: 12px;
}
.service-card {
overflow: hidden;
padding: 16px 16px 9px;
}
.service-card h3,
.detail-content h3 {
margin: 0 0 13px;
color: #111;
font-size: 16px;
font-weight: 700;
}
.service-card ::v-deep .van-cell {
padding: 11px 0;
}
.service-card ::v-deep .van-cell::after {
display: none;
}
.service-card ::v-deep .van-cell__title {
color: #222;
font-size: 15px;
}
.service-card ::v-deep .van-cell__value {
color: #888;
font-size: 14px;
}
.service-icon {
margin-right: 10px;
color: #ff5b20;
font-size: 20px;
}
.detail-content {
padding: 16px;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
}
.feature-grid article {
min-width: 0;
text-align: center;
}
.feature-grid span {
display: flex;
width: 48px;
height: 48px;
align-items: center;
justify-content: center;
margin: 0 auto 10px;
border-radius: 50%;
color: #2d558c;
background: #f0f2f6;
}
.feature-grid span .van-icon {
font-size: 25px;
}
.feature-grid b {
display: block;
overflow: hidden;
color: #222;
font-size: 12px;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.feature-grid p {
display: -webkit-box;
overflow: hidden;
margin: 4px 0 0;
color: #888;
font-size: 11px;
line-height: 1.35;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.cart-popup {
display: flex;
height: 76vh;
flex-direction: column;
overflow: hidden;
}
.cart-popup-head {
display: flex;
height: 48px;
flex: none;
align-items: center;
justify-content: center;
border-bottom: 1px solid #f2f2f2;
background: #fff;
}
.cart-popup-head b {
color: #222;
font-size: 16px;
font-weight: 700;
}
.cart-popup-head .van-icon {
position: absolute;
right: 16px;
color: #999;
font-size: 20px;
}
.cart-popup-panel {
flex: 1;
min-height: 0;
}
</style>