Files
yxe-mp/pages/product/detail.vue
T
2026-09-29 15:17:13 +08:00

290 lines
6.3 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="product-page">
<view class="page-hero">
<AppNavbar class="page-navbar" title="商品详情"/>
</view>
<view class="product-detail" v-if="product.goodsId">
<view class="gallery card">
<swiper class="swiper" circular autoplay indicator-dots indicator-color="#d8e6ff"
indicator-active-color="#2563eb" v-if="product.files">
<swiper-item v-for="(img, idx) in JSON.parse(product.files)" :key="idx">
<image class="cover" :src="img.url" mode="aspectFill"></image>
</swiper-item>
</swiper>
<!-- <view class="badge rating">评分 {{ product.rating }} / 5</view>-->
</view>
<view class="card block">
<view class="price-row">
<view class="price">¥{{ product.salePrice }}</view>
<view class="origin">¥{{ product.originPrice }}</view>
<view class="coupon" @tap="openCoupon">领券</view>
</view>
<view class="name">{{ product.goodsName }}</view>
<!-- <view class="sell-point">{{ // product.sellPoint }}</view>-->
<!-- <view class="meta">规格:{{ product.specs }} · 厂家:{{ product.vendor }}</view>-->
</view>
<!-- <view class="card block">-->
<!-- <view class="section-title">商品规格</view>-->
<!-- <view class="specs">-->
<!-- <view class="chip" v-for="spec in product.specOptions" :key="spec" :class="{'active': spec === selectedSpec}"-->
<!-- @tap="selectedSpec = spec">{{ spec }}-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<view class="card block">
<view class="section-title">图文详情</view>
<u-parse :content="product.content"/>
</view>
<!-- <view class="card block">-->
<!-- <view class="section-title">热卖推荐</view>-->
<!-- <view class="recommend" v-for="item in recommends" :key="item.id" @tap="openRecommend(item)">-->
<!-- <view class="rec-name">{{ item.name }}</view>-->
<!-- <view class="rec-meta">{{ item.specs }} · ¥{{ item.price }}</view>-->
<!-- </view>-->
<!-- </view>-->
<view class="action-bar">
<u-icon name="share" label="分享" label-pos="bottom" size="40rpx"/>
<view class="action-group">
<view class="primary-btn" @tap="addCart">加入购物车</view>
<view class="primary-btn buy" @tap="buyNow">立即购买</view>
</view>
</view>
<Login ref="Login" @done="getUserData"/>
</view>
</view>
</template>
<script>
import {goodsInfoReq} from "@/api/goods";
import Login from "@/components/Login.vue";
import {userInfoReq} from "@/api/user";
import {getUserInfo} from "@/util/user";
import {addCartReq} from "@/api/cart";
import AppNavbar from "@/components/AppNavbar.vue";
export default {
components: {Login, AppNavbar},
data() {
return {
product: {},
selectedSpec: '10袋/盒',
recommends: [],
isLoggedIn: false,
}
},
onLoad(options) {
if (options.id) {
this.id = options.id
this.getInfo()
}
},
onShow() {
if (getUserInfo().token) {
this.isLoggedIn = true
this.getUserData()
}
},
methods: {
async getUserData() {
const {data} = await userInfoReq()
this.isLoggedIn = true
},
async getInfo() {
const {data} = await goodsInfoReq(this.id)
this.product = data
},
openCoupon() {
uni.navigateTo({
url: '/packageUser/coupon/index'
})
},
async addCart() {
if (!this.isLoggedIn) return this.$refs.Login.open()
await addCartReq({
goodsId: this.product.goodsId,
num: 1
})
uni.showToast({
title: '已加入购物车',
icon: 'success'
})
},
buyNow() {
const goodsDataList = [{
goodsId: this.product.goodsId,
num: 1
}]
uni.navigateTo({
url: `/pages/order/checkout?goodsDataList=${JSON.stringify(goodsDataList)}`
})
},
openRecommend(item) {
uni.navigateTo({
url: `/pages/product/detail?name=${encodeURIComponent(item.name)}&price=${item.price}&origin=${item.price}`
})
}
}
}
</script>
<style lang="scss" scoped>
.product-page {
min-height: 100vh;
padding: 0 20rpx 100rpx;
background: #f5f5f5;
}
.page-hero {
padding: 30rpx 6rpx 18rpx;
background: url('https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/462bfa04cf0eff8698884c38b9d17519.png') no-repeat
top center / 100% auto;
}
.page-navbar {
padding: 0 6rpx;
}
.product-detail {
padding: 0 6rpx 100rpx;
}
.gallery {
padding: 0;
overflow: hidden;
}
.swiper {
height: 320rpx;
}
.cover {
width: 100%;
height: 320rpx;
}
.rating {
position: absolute;
top: 12rpx;
right: 12rpx;
}
.block {
margin-top: 18rpx;
}
.price-row {
display: flex;
align-items: center;
gap: 12rpx;
}
.price {
color: #ff3b30;
font-size: 36rpx;
font-weight: 800;
}
.origin {
color: #a1aabc;
text-decoration: line-through;
}
.coupon {
color: #ff8354;
background: #fff1e9;
border-radius: 999rpx;
padding: 6rpx 12rpx;
border: 1rpx dashed #ffb08a;
}
.name {
font-size: 34rpx;
font-weight: 800;
color: var(--text-main);
margin: 8rpx 0;
}
.sell-point {
color: #ff3b30;
font-size: 26rpx;
margin-bottom: 8rpx;
}
.meta {
color: var(--text-sub);
}
.specs {
display: flex;
gap: 10rpx;
flex-wrap: wrap;
}
.chip.active {
background: linear-gradient(135deg, var(--brand-primary), var(--brand-primary-soft));
color: #fff;
border-color: transparent;
}
.detail-text {
color: var(--text-main);
line-height: 1.6;
}
.recommend {
padding: 12rpx 0;
border-bottom: 1rpx solid #edf1f7;
}
.recommend:last-child {
border-bottom: none;
}
.rec-name {
font-weight: 700;
color: var(--text-main);
}
.rec-meta {
color: var(--text-sub);
font-size: 24rpx;
margin-top: 4rpx;
}
.action-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
gap: 10rpx;
padding: 12rpx 24rpx;
background: #ffffff;
box-shadow: 0 -6rpx 16rpx rgba(31, 111, 232, 0.08);
}
.collect {
color: var(--brand-primary);
font-weight: 700;
}
.action-group {
margin-left: 20rpx;
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
}
.buy {
margin-left: 20rpx;
background: linear-gradient(135deg, #ff8354, #ff9f72);
}
</style>