290 lines
6.3 KiB
Vue
290 lines
6.3 KiB
Vue
<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>
|