123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- <template>
- <view class="product-detail">
- <!-- 商品图片轮播 -->
- <view class="product-images">
- <swiper
- class="image-swiper"
- :indicator-dots="true"
- :autoplay="false"
- indicator-color="rgba(255,255,255,0.5)"
- indicator-active-color="#FF6600"
- @change="onImageChange"
- >
- <swiper-item v-for="(image, index) in productImages" :key="index">
- <image :src="image.url" class="product-image" mode="aspectFill"></image>
- </swiper-item>
- </swiper>
- <!-- 图片指示器 -->
- <view class="image-indicator">{{ currentImage + 1 }}/{{ productImages.length }}</view>
- </view>
-
- <!-- 商品基本信息 -->
- <view class="product-info">
- <view class="product-price">
- <text class="price-symbol">¥</text>
- <text class="price-value">{{ productDetail.price }}</text>
- <view class="points-badge">
- <text class="points-text">+{{ productDetail.points }}工分</text>
- </view>
- </view>
- <view class="product-name">{{ productDetail.name }}</view>
- <view class="delivery-info">
- <text class="delivery-text">送至: {{ deliveryAddress }}</text>
- <text class="delivery-arrow">></text>
- </view>
- </view>
-
- <!-- 商品特点模块 -->
- <view class="product-features">
- <view class="section-header">
- <text class="section-title">商品特点</text>
- <text class="section-subtitle">品质用心展示</text>
- </view>
-
- <!-- 商品展示图 -->
- <view class="feature-showcase">
- <view class="showcase-left">
- <image :src="productDetail.packageImage" class="package-image" mode="aspectFit"></image>
- </view>
- <view class="showcase-right">
- <image :src="productDetail.mainImage" class="main-product-image" mode="aspectFit"></image>
- </view>
- </view>
-
- <!-- 产品信息表格 -->
- <view class="product-specs">
- <view class="spec-item" v-for="(spec, key) in productSpecs" :key="key">
- <text class="spec-label">{{ spec.label }}</text>
- <text class="spec-value">{{ spec.value }}</text>
- </view>
- </view>
- </view>
-
- <!-- 品质与价值模块 -->
- <view class="quality-section">
- <view class="section-header">
- <text class="section-title">品质与价值</text>
- <text class="quality-arrow">↓</text>
- </view>
-
- <view class="quality-content">
- <view class="quality-text">
- <text class="quality-title">茅台品质</text>
- <text class="quality-description">{{ productDetail.qualityDescription }}</text>
- </view>
- <view class="quality-image">
- <image :src="productDetail.qualityImage" class="quality-product-image" mode="aspectFit"></image>
- </view>
- </view>
- </view>
-
- <!-- 底部购买按钮 -->
- <view class="bottom-actions">
- <view class="action-buttons">
- <button class="action-btn add-to-cart" @click="addToCart">
- <text class="btn-text">加入购物车</text>
- </button>
- <button class="action-btn buy-now" @click="buyNow">
- <text class="btn-text">立即购买</text>
- </button>
- </view>
- </view>
-
- <!-- 加载状态 -->
- <view v-if="loading" class="loading-container">
- <view class="loading-content">
- <view class="loading-spinner"></view>
- <text class="loading-text">{{ loadingText }}</text>
- </view>
- </view>
- </view>
- </template>
- <script>
- import { isLoggedIn, requireAuth, PROTECTED_ACTIONS } from '@/utils/auth.js'
- import { getProductDetail, getProductImages } from '@/api/product.js'
-
- export default {
- name: 'ProductDetail',
- data() {
- return {
- productId: '',
- loading: true,
- loadingText: '加载中...',
- currentImage: 0,
-
- // 商品图片
- productImages: [],
-
- // 商品详情
- productDetail: {
- name: '',
- price: 0,
- points: 0,
- mainImage: '',
- packageImage: '',
- qualityImage: '',
- qualityDescription: ''
- },
-
- // 商品规格信息
- productSpecs: {},
-
- // 配送地址
- deliveryAddress: '北京市大兴区上德中心'
- }
- },
-
- onLoad(options) {
- if (options.id) {
- this.productId = options.id
- this.loadProductDetail()
- } else {
- uni.showToast({
- title: '商品ID无效',
- icon: 'none'
- })
- setTimeout(() => {
- uni.navigateBack()
- }, 1500)
- }
- },
-
- methods: {
- /**
- * 加载商品详情
- */
- async loadProductDetail() {
- try {
- this.loading = true
- this.loadingText = '加载商品详情...'
-
- console.log('🔄 开始加载商品详情:', this.productId)
-
- // 并行加载商品详情和图片
- const [detailResponse, imagesResponse] = await Promise.all([
- getProductDetail(this.productId),
- getProductImages(this.productId, 2) // 图片分类==2
- ])
-
- if (detailResponse.success) {
- this.productDetail = {
- name: detailResponse.data.name,
- price: detailResponse.data.price,
- points: detailResponse.data.points || 400,
- mainImage: detailResponse.data.mainImage,
- packageImage: detailResponse.data.packageImage,
- qualityImage: detailResponse.data.qualityImage,
- qualityDescription: detailResponse.data.qualityDescription || '采用高温制曲、二次投料,一年为一个生产周期,具有独特的酿造工艺。'
- }
-
- // 构建商品规格信息
- this.productSpecs = {
- name: {
- label: '产品名称',
- value: detailResponse.data.name
- },
- material: {
- label: '原料',
- value: detailResponse.data.material || '水、高粱、小麦'
- },
- alcohol: {
- label: '酒精度数',
- value: detailResponse.data.alcohol || '53%vol.'
- },
- origin: {
- label: '厂址',
- value: detailResponse.data.origin || '贵州省仁怀市茅台镇'
- },
- type: {
- label: '酒品类型',
- value: detailResponse.data.type || '酱香型白酒'
- },
- capacity: {
- label: '容量',
- value: detailResponse.data.capacity || '500ml'
- },
- storage: {
- label: '储存条件',
- value: detailResponse.data.storage || '干燥、通风、阴凉的环境条件下存储'
- },
- company: {
- label: '公司',
- value: detailResponse.data.company || '贵州茅台酒股份有限公司'
- }
- }
-
- console.log('✅ 商品详情加载成功')
- } else {
- throw new Error(detailResponse.message || '商品详情加载失败')
- }
-
- if (imagesResponse.success) {
- // 限制最多6张图片
- this.productImages = imagesResponse.data.images.slice(0, 6).map(img => ({
- url: img.url,
- id: img.id
- }))
- console.log('✅ 商品图片加载成功,共', this.productImages.length, '张')
- } else {
- console.warn('⚠️ 商品图片加载失败,使用默认图片')
- // 使用默认图片
- this.productImages = [{
- url: this.productDetail.mainImage,
- id: 'default'
- }]
- }
-
- } catch (error) {
- console.error('❌ 商品详情加载失败:', error)
- uni.showToast({
- title: '商品详情加载失败',
- icon: 'none',
- duration: 2000
- })
- } finally {
- this.loading = false
- }
- },
-
- /**
- * 图片轮播切换事件
- */
- onImageChange(e) {
- this.currentImage = e.detail.current
- },
-
- /**
- * 加入购物车
- */
- addToCart() {
- requireAuth(() => {
- console.log('🛒 加入购物车:', this.productId)
- // TODO: 实现加入购物车逻辑
- uni.showToast({
- title: '已加入购物车',
- icon: 'success'
- })
- }, {
- action: PROTECTED_ACTIONS.ADD_TO_CART,
- returnUrl: `/pages/product/detail?id=${this.productId}`
- })
- },
-
- /**
- * 立即购买
- */
- buyNow() {
- requireAuth(() => {
- console.log('🛒 立即购买:', this.productId)
- // TODO: 实现立即购买逻辑
- uni.navigateTo({
- url: `/pages/order/confirm?productId=${this.productId}`
- })
- }, {
- action: PROTECTED_ACTIONS.BUY_NOW,
- returnUrl: `/pages/product/detail?id=${this.productId}`
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .product-detail {
- min-height: 100vh;
- background: #f8f8f8;
- padding-bottom: 120rpx; /* 为底部按钮留出空间 */
- }
- /* 商品图片轮播 */
- .product-images {
- position: relative;
- height: 600rpx;
- background: #ffffff;
-
- .image-swiper {
- width: 100%;
- height: 100%;
-
- .product-image {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
-
- .image-indicator {
- position: absolute;
- bottom: 30rpx;
- right: 30rpx;
- background: rgba(0, 0, 0, 0.7);
- color: #ffffff;
- padding: 8rpx 16rpx;
- border-radius: 20rpx;
- font-size: 24rpx;
- z-index: 10;
- }
- }
- /* 商品基本信息 */
- .product-info {
- background: #ffffff;
- padding: 30rpx;
- margin-bottom: 20rpx;
-
- .product-price {
- display: flex;
- align-items: center;
- margin-bottom: 20rpx;
-
- .price-symbol {
- font-size: 32rpx;
- color: #ff4757;
- font-weight: bold;
- margin-right: 8rpx;
- }
-
- .price-value {
- font-size: 48rpx;
- color: #ff4757;
- font-weight: bold;
- margin-right: 20rpx;
- }
-
- .points-badge {
- background: #FF6600;
- padding: 8rpx 16rpx;
- border-radius: 20rpx;
-
- .points-text {
- color: #ffffff;
- font-size: 24rpx;
- font-weight: bold;
- }
- }
- }
-
- .product-name {
- font-size: 36rpx;
- color: #333;
- font-weight: bold;
- margin-bottom: 20rpx;
- line-height: 1.4;
- }
-
- .delivery-info {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- .delivery-text {
- font-size: 28rpx;
- color: #666;
- }
-
- .delivery-arrow {
- font-size: 24rpx;
- color: #999;
- }
- }
- }
- /* 商品特点模块 */
- .product-features {
- background: #ffffff;
- margin-bottom: 20rpx;
-
- .section-header {
- padding: 30rpx 30rpx 20rpx;
- border-bottom: 1rpx solid #f0f0f0;
-
- .section-title {
- display: block;
- font-size: 36rpx;
- color: #333;
- font-weight: bold;
- margin-bottom: 10rpx;
- }
-
- .section-subtitle {
- font-size: 26rpx;
- color: #999;
- }
- }
-
- .feature-showcase {
- display: flex;
- padding: 30rpx;
- gap: 30rpx;
-
- .showcase-left,
- .showcase-right {
- flex: 1;
- height: 200rpx;
- background: #f8f8f8;
- border-radius: 12rpx;
- overflow: hidden;
-
- .package-image,
- .main-product-image {
- width: 100%;
- height: 100%;
- object-fit: contain;
- }
- }
- }
-
- .product-specs {
- padding: 0 30rpx 30rpx;
-
- .spec-item {
- display: flex;
- padding: 20rpx 0;
- border-bottom: 1rpx solid #f5f5f5;
-
- &:last-child {
- border-bottom: none;
- }
-
- .spec-label {
- width: 200rpx;
- font-size: 28rpx;
- color: #666;
- flex-shrink: 0;
- }
-
- .spec-value {
- flex: 1;
- font-size: 28rpx;
- color: #333;
- line-height: 1.4;
- }
- }
- }
- }
- /* 品质与价值模块 */
- .quality-section {
- background: #ffffff;
- margin-bottom: 20rpx;
-
- .section-header {
- padding: 30rpx 30rpx 20rpx;
- border-bottom: 1rpx solid #f0f0f0;
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- .section-title {
- font-size: 36rpx;
- color: #333;
- font-weight: bold;
- }
-
- .quality-arrow {
- font-size: 24rpx;
- color: #999;
- }
- }
-
- .quality-content {
- display: flex;
- padding: 30rpx;
- gap: 30rpx;
-
- .quality-text {
- flex: 1;
-
- .quality-title {
- display: block;
- font-size: 32rpx;
- color: #333;
- font-weight: bold;
- margin-bottom: 20rpx;
- }
-
- .quality-description {
- font-size: 28rpx;
- color: #666;
- line-height: 1.6;
- }
- }
-
- .quality-image {
- width: 200rpx;
- height: 200rpx;
- background: #f8f8f8;
- border-radius: 12rpx;
- overflow: hidden;
-
- .quality-product-image {
- width: 100%;
- height: 100%;
- object-fit: contain;
- }
- }
- }
- }
- /* 底部购买按钮 - 重新设计布局 */
- .bottom-actions {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
- padding: 20rpx 24rpx;
- box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.12);
- border-top: 1rpx solid #dee2e6;
-
- .action-buttons {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- width: 100%;
- gap: 5%;
- }
-
- .action-btn {
- width: 45%;
- height: 80rpx;
- border: 2rpx solid transparent;
- border-radius: 40rpx;
- font-size: 28rpx;
- font-weight: 700;
- position: relative;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- flex-shrink: 0;
-
- &.add-to-cart {
- background: linear-gradient(135deg, #dc3545 0%, #dc3545 100%);
- color: #ffffff;
- border-color: #dc3545;
-
- &:active {
- background: linear-gradient(135deg, #218838 0%, #1ea085 100%);
- transform: scale(0.95);
- }
- }
-
- &.buy-now {
- background: linear-gradient(135deg, #dc3545 0%, #fd7e14 100%);
- color: #ffffff;
- border-color: #dc3545;
-
- &:active {
- background: linear-gradient(135deg, #c82333 0%, #e55a00 100%);
- transform: scale(0.95);
- }
- }
-
- &:active {
- box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
- }
-
- .btn-text {
- position: relative;
- z-index: 1;
- text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2);
- }
- }
- }
- /* 加载状态 */
- .loading-container {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(255, 255, 255, 0.95);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 9999;
-
- .loading-content {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .loading-spinner {
- width: 60rpx;
- height: 60rpx;
- border: 4rpx solid #f3f3f3;
- border-top: 4rpx solid #FF6600;
- border-radius: 50%;
- animation: loading-spin 1s linear infinite;
- margin-bottom: 20rpx;
- }
-
- .loading-text {
- font-size: 28rpx;
- color: #666;
- }
- }
- }
- @keyframes loading-spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- </style>
|