bindbox-mini/pages/shop/detail.vue
邹方成 6f7207da2d feat: 优化UI设计并重构样式系统
refactor(components): 重构ElCard、FlipGrid、YifanSelector和PaymentPopup组件样式
refactor(pages): 优化地址管理、商品详情、订单列表、积分记录和活动页面UI
style: 更新uni.scss全局样式变量和设计系统
docs: 添加说明文档记录UI优化进度
2025-12-17 14:32:55 +08:00

164 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<view class="bg-decoration"></view>
<view class="loading" v-if="loading">加载中...</view>
<view v-else-if="detail.id" class="detail-wrap">
<image v-if="detail.main_image" class="main-image" :src="detail.main_image" mode="widthFix" />
<view class="info-card">
<view class="title">{{ detail.title || detail.name || '-' }}</view>
<view class="price-row">
<text class="price">¥{{ formatPrice(detail.price_sale || detail.price) }}</text>
<text class="points" v-if="detail.points_required">{{ detail.points_required }}积分</text>
</view>
<view class="stock" v-if="detail.stock !== null && detail.stock !== undefined">库存{{ detail.stock }}</view>
<view class="desc" v-if="detail.description">{{ detail.description }}</view>
</view>
</view>
<view v-else class="empty">商品不存在</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getProductDetail } from '../../api/appUser'
const detail = ref({})
const loading = ref(false)
function formatPrice(p) {
if (p === undefined || p === null) return '0.00'
return (Number(p) / 100).toFixed(2)
}
async function fetchDetail(id) {
loading.value = true
try {
const res = await getProductDetail(id)
detail.value = res || {}
} catch (e) {
detail.value = {}
} finally {
loading.value = false
}
}
onLoad((opts) => {
const id = opts && opts.id
if (id) fetchDetail(id)
})
</script>
<style lang="scss" scoped>
/* ============================================
奇盒潮玩 - 商品详情页
============================================ */
.page {
min-height: 100vh;
background: $bg-page;
padding-bottom: env(safe-area-inset-bottom);
}
.loading, .empty {
text-align: center;
padding: 120rpx 40rpx;
color: $text-secondary;
font-size: $font-md;
}
.detail-wrap {
padding-bottom: 40rpx;
animation: fadeInUp 0.4s ease-out;
}
.main-image {
width: 100%;
height: 750rpx; /* Square aspect ratio */
display: block;
background: $bg-secondary;
box-shadow: $shadow-sm;
}
.info-card {
margin: $spacing-lg;
margin-top: -60rpx;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: $radius-xl;
padding: $spacing-xl;
box-shadow: $shadow-lg;
position: relative;
z-index: 2;
}
.title {
font-size: $font-xl;
font-weight: 800;
color: $text-main;
margin-bottom: $spacing-md;
line-height: 1.4;
}
.price-row {
display: flex;
align-items: baseline;
gap: $spacing-sm;
margin-bottom: $spacing-lg;
}
.price {
font-size: $font-xxl;
font-weight: 900;
color: $brand-primary;
font-family: 'DIN Alternate', sans-serif;
&::before {
content: '¥';
font-size: $font-md;
margin-right: 4rpx;
}
}
.points {
font-size: $font-sm;
color: $brand-primary;
padding: 6rpx $spacing-md;
background: rgba($brand-primary, 0.1);
border-radius: 100rpx;
font-weight: 600;
}
.stock {
font-size: $font-sm;
color: $text-secondary;
margin-bottom: $spacing-lg;
background: $bg-secondary;
display: inline-block;
padding: 6rpx $spacing-md;
border-radius: $radius-sm;
}
.desc {
font-size: $font-lg;
color: $text-main;
line-height: 1.8;
padding-top: $spacing-lg;
border-top: 1rpx dashed $border-color-light;
&::before {
content: '商品详情';
display: block;
font-size: $font-md;
color: $text-secondary;
margin-bottom: $spacing-sm;
font-weight: 700;
}
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(40rpx); }
to { opacity: 1; transform: translateY(0); }
}
</style>