460 lines
9.5 KiB
Vue
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>
|