zhwl-miniapp/pages/shop/detail.vue
2025-06-26 12:38:35 +08:00

437 lines
19 KiB
Vue

<template>
<view class="waper">
<u-navbar :autoBack="true" :bgColor="scrollTop > 20 ? '#fff' : 'transparent'" :leftIconColor="scrollTop > 20 ? '#303133' : '#fff'">
<view slot='center' style="font-size: 36rpx; font-weight: bold;">{{scrollTop > 20 ? '文创详情' : ''}}</view>
</u-navbar>
<template v-if="detail">
<view class="swiper">
<swiper autoplay :interval="3000" :duration="1000" circular>
<swiper-item v-for="(img, index) in detail.images" :key="index">
<view class="swiper-item">
<image :src="$utils.setImgUrl(img)" mode=""></image>
</view>
</swiper-item>
</swiper>
</view>
<view class="waper_content" :style="'padding-bottom: ' + paddingBottom + 'px;'">
<view class="money">
<view>{{detail.price.toFixed(2)}}</view>
<view>库存{{detail.stock || 0}}</view>
</view>
<view class="titl">
<view>{{detail.title}}</view>
<view>已售{{(detail.showSales ? detail.showSales : 0) + detail.sales}}</view>
</view>
<view class="list_waper">
<view class="list" v-if="areaInfo">
<view class="left">配送</view>
<view class="right" style="padding: 0;">
<view class="text">{{areaInfo.provinceName + areaInfo.cityName + areaInfo.areaName}}</view>
</view>
</view>
<view class="list" @click="showService = true;">
<view class="left">售后</view>
<view class="right">
<view class="text">
<text v-for="(item, index) in detail.serviceList.slice(0, 3)" :key="index">{{item.name}}</text>
</view>
<view class="icon">
<u-icon name="arrow-right" color="#333" size="30rpx"></u-icon>
</view>
</view>
</view>
<view class="list">
<view class="left">运费</view>
<view class="right">
<view class="text">{{detail.dispatchName}}</view>
</view>
</view>
</view>
<view class="store-box" v-if="detail.storeId">
<view class="store">
<image class="img" :src="$utils.setImgUrl(detail.storeImage)" mode="aspectFill"></image>
<view class="store-title" v-if="detail.storeName">{{detail.storeName}}</view>
</view>
<view class="btn" @click="goStore">进入店铺</view>
</view>
<view class="detail" v-if="detail.params.length">
<view class="detail_title">
<text>参数</text>
</view>
<view class="detail_cont">
<view class="goods-list-box" v-for="(item, index) in detail.params" :key="index">
<view class="goods-list-item">{{item.title}}</view>
<view class="goods-list-item">{{item.content}}</view>
</view>
</view>
</view>
<view class="detail">
<view class="detail_title">
<text>商品介绍</text>
</view>
<view class="detail_cont">
<u-parse :content="detail.content"></u-parse>
</view>
</view>
</view>
<view class="bottom" :style="'padding-bottom: ' + safeAreaBottom + 'px'">
<view class="bottom_waper">
<view @click="buyGoods('card')">加入购物车</view>
<view @click="buyGoods('order')">立即购买</view>
</view>
</view>
</template>
<u-popup :show="showService" bgColor="transparent">
<view class="service_waper" v-if="detail">
<view class="list_waper">
<view class="list" v-for="(item, index) in detail.serviceList" :key="index">
<view>{{item.name}}</view>
<view>{{item.description}}</view>
</view>
</view>
<view class="btn" @click="showService = false;">我知道了</view>
<view class="close" @click="showService = false;">
<u-icon name="close" color="#333" size="32rpx"></u-icon>
</view>
</view>
</u-popup>
<u-popup :show="showOrder" bgColor="transparent">
<view class="order_waper" v-if="orderInfo">
<view class="info">
<view class="img">
<image :src="$utils.setImgUrl(orderInfo.image)" mode=""></image>
</view>
<view class="text">{{orderInfo.buyMoney.toFixed(2)}}</view>
</view>
<view class="list_waper" v-if="orderInfo.isSku === 1">
<view class="list" v-for="(i, index) in orderInfo.skuList" :key="index">
<view class="title">{{i.name}}</view>
<view class="select">
<view v-for="(item, itemIndex) in i.children" :key="itemIndex" :class="{ active: item.active }" @click="selectSku(index, itemIndex)">{{item.name}}</view>
</view>
</view>
</view>
<view class="number">
<view class="left">数量</view>
<view class="right">
<view class="right_info" v-if="orderInfo.buyMax > 0">
<u-number-box v-model="orderInfo.buyNum" :min="1" :max="orderInfo.buyMax"></u-number-box>
</view>
<view class="null" v-else>无货</view>
</view>
</view>
<view class="btn" @click="submitForm">确认</view>
<view class="close" @click="clearOrder">
<u-icon name="close" color="#333" size="32rpx"></u-icon>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data () {
return {
scrollTop: 0,
safeAreaBottom: 0,
paddingBottom: 0,
detail: null,
showService: false,
areaInfo: null,
showOrder: false,
orderInfo: null
}
},
onPageScroll (e) {
this.scrollTop = e.scrollTop;
},
async onLoad (options) {
this.safeAreaBottom = this.$safeAreaBottom;
this.paddingBottom = this.safeAreaBottom + uni.upx2px(152);
let info = await this.$shop.getGoodsDetail({ id: options.id });
info.data.images = info.data.images.split(',');
if (info.data.content) {
info.data.content = this.$utils.replaceImg(info.data.content);
}
info.data.params = JSON.parse(info.data.params);
this.detail = info.data;
let area = await this.$shop.getUserInfoAddressDefault();
if (area.data) this.areaInfo = area.data;
},
methods: {
async submitForm () {
if (this.orderInfo.isSku === 1) {
let num = 0;
this.orderInfo.skuList.forEach((item) => {
item.children.forEach((info) => {
if (info.active) num ++
})
})
if (num < this.orderInfo.skuList.length) {
uni.showToast({ mask: true, title: '请选择规格', icon: 'none' })
return false;
}
}
if (this.orderInfo.buyMax > 0) {
const type = this.orderInfo.type;
let info = null;
let skuPriceId = this.orderInfo.skuPriceId || this.orderInfo.zdyWcscGoodsSkuPrice.id;
if (type == 'card') {
info = await this.$shop.addshoppingcart({ goodsId: this.orderInfo.id, goodsNum: this.orderInfo.buyNum, skuPriceId: skuPriceId })
uni.showToast({ mask: true, title: '操作成功', icon: 'success' });
}
if (type == 'order') {
uni.navigateTo({ url: `/pages/shop/submit?id=${this.orderInfo.id}&skuId=${skuPriceId}&buyNum=${this.orderInfo.buyNum}&addressId=${this.areaInfo ? this.areaInfo.id : ''}` })
}
}
this.clearOrder();
},
buyGoods (type) {
let obj = Object.assign({}, this.detail);
obj.type = type;
obj.buyNum = 1;
obj.buyMoney = obj.price;
obj.buyMax = obj.stock;
if (obj.skuList) {
obj.skuList.forEach((i, index) => {
i.children.forEach((item, itemIndex) => {
obj.skuList[index].children[itemIndex].active = false;
})
})
}
this.orderInfo = obj;
this.showOrder = true;
},
clearOrder () {
this.showOrder = false;
this.orderInfo = null;
},
selectSku (parentIndex, childIndex) {
this.orderInfo.skuList[parentIndex].children.forEach((i, index) => {
this.orderInfo.skuList[parentIndex].children[index].active = false;
})
this.orderInfo.skuList[parentIndex].children[childIndex].active = true;
this.getActiveData();
this.$forceUpdate()
},
getActiveData () {
let arr = [];
this.orderInfo.skuList.forEach((i, index) => {
i.children.forEach((item, itemIndex) => {
if (item.active) arr.push(String(item.id))
})
})
if (arr.length != this.orderInfo.skuList.length) {
this.orderInfo.buyMax = this.orderInfo.stock;
this.orderInfo.buyMoney = this.orderInfo.price;
} else {
this.orderInfo.skuPriceList.forEach((item) => {
if (item.goodsSkuIds == arr.toString()) {
this.orderInfo.buyMax = item.stock;
this.orderInfo.buyMoney = item.price;
this.orderInfo.skuPriceId = item.id;
}
})
}
},
goStore() {
uni.navigateTo({
url: '/pages/shop/shopDetail?id=' + this.detail.storeId
})
},
}
}
</script>
<style lang="scss">
.swiper{
width: 100%; height: 750rpx;
swiper, .swiper-item{ width: 100%; height: 100%; }
}
.waper_content{
width: 100%; box-sizing: border-box; padding: 48rpx 32rpx 0; background: #FBFBFB; border-radius: 50rpx 50rpx 0 0; position: relative; z-index: 3; margin-top: -80rpx;
.money{
width: 100%; height: 78rpx; display: flex; justify-content: space-between; align-items: flex-end;
view{
&:nth-child(1){
color: #FF3333; padding-left: 42rpx; position: relative; font-size: 64rpx; font-weight: bold;
&::after{ content: ""; font-size: 24rpx; position: absolute; left: 0; bottom: 14rpx; }
}
&:nth-child(2){ color: rgba(0,0,0,0.45); font-size: 28rpx; padding-bottom: 5rpx; }
}
}
.titl{
width: 100%; margin-top: 16rpx; box-sizing: border-box; padding-right: 100rpx; position: relative;
view{
&:nth-child(1){ color: rgba(0,0,0,0.85); font-size: 36rpx; font-weight: 500; line-height: 50rpx; }
&:nth-child(2){ color: rgba(0,0,0,0.45); font-size: 28rpx; position: absolute; line-height: 34rpx; right: 0; top: 8rpx; }
}
}
.list_waper{
width: 100%; margin-top: 32rpx; background: #fff; border-radius: 10rpx; box-sizing: border-box; padding: 4rpx 24rpx;
.list{
width: 100%; box-sizing: border-box; border-bottom: 2rpx solid #E8E8E8; padding: 18rpx 0; padding-left: 88rpx; position: relative;
&:last-child{ border-bottom: 0; }
.left{ color: rgba(0,0,0,0.85); font-size: 28rpx; line-height: 48rpx; position: absolute; left: 0; top: 50%; transform: translateY(-50%); }
.right{
width: 100%; height: 100%; display: flex; align-items: center; box-sizing: border-box; padding-right: 44rpx; position: relative;
.text{
width: 100%; color: rgba(0,0,0,0.45); font-size: 28rpx; display: flex; flex-wrap: wrap; line-height: 48rpx;
text{
margin-right: 24rpx; position: relative; line-height: 48rpx;
&:last-child{
margin-right: 0;
}
}
}
.icon{ position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
}
}
}
.store-box {
background: #FFFFFF;
border-radius: 10rpx;
margin-top: 20rpx;
padding: 24rpx;
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
.store {
display: flex;
align-items: center;
flex-direction: row;
.img {
width: 116rpx;
height: 116rpx;
border-radius: 10rpx;
margin-right: 28rpx;
}
.store-title {
font-weight: 500;
font-size: 32rpx;
color: rgba(0,0,0,0.85);
width: 300rpx;
overflow:hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow:ellipsis;
}
}
.btn {
width: 168rpx;
height: 64rpx;
background: #03AE80;
border-radius: 100rpx;
text-align: center;
line-height: 64rpx;
font-size: 24rpx;
color: #FFFFFF;
}
}
.detail{
width: 100%; margin-top: 20rpx; box-sizing: border-box; padding: 24rpx; border-radius: 10rpx; background: #fff;
.detail_title{
position: relative; font-weight: 500; font-size: 32rpx; color: rgba(0,0,0,0.85);
text{ position: relative; z-index: 3; }
}
.detail_cont{
width: 100%; margin-top: 20rpx;
.goods-list-box {
display: flex; align-items: center;
border-left: 1rpx solid rgba(0, 0, 0, 0.1);
&:first-child {
border-top: 1rpx solid rgba(0, 0, 0, 0.1);
}
.goods-list-item {
font-size: 28rpx;
border: 1rpx solid rgba(0, 0, 0, 0.1);
border-top: none;
border-left: none;
line-height: 56rpx;
height: 56rpx;
padding: 0 12rpx;
box-sizing: border-box;
&:first-child { width: 40%; }
&:last-child { width: 60%; }
}
}
}
}
}
.bottom{
box-shadow: 0 -2rpx 12rpx 0 rgba(0,0,0,0.05); width: 100%; background: #fff; position: fixed; left: 0; bottom: 0; z-index: 6;
.bottom_waper{
width: 100%; height: 120rpx; box-sizing: border-box; padding: 0 32rpx; display: flex; justify-content: flex-end; align-items: center;
view{
width: 230rpx; height: 80rpx; text-align: center; line-height: 80rpx; font-size: 30rpx; color: #03AE80; font-weight: 500; border-radius: 66rpx 0 0 66rpx; background: rgba(1, 190, 105, 0.2);
&:last-child{ background: #03AE80; border-radius: 0 66rpx 66rpx 0; color: #fff; }
}
}
}
.service_waper{
width: 100%; height: 882rpx; background: #fff; border-radius: 50rpx 50rpx 0 0; box-sizing: border-box; padding: 32rpx 24rpx 17rpx; position: relative;
.list_waper{
width: 100%; height: 700rpx; overflow-y: auto;
.list{
width: 100%; margin-bottom: 26rpx;
&:last-child{ margin-bottom: 0; }
view{
&:nth-child(1){ color: rgba(0,0,0,0.85); font-size: 36rpx; font-weight: 500; line-height: 42rpx; }
&:nth-child(2){ margin-top: 14rpx; line-height: 33rpx; color: rgba(0,0,0,0.45); font-size: 28rpx; }
}
}
}
.btn{ width: 686rpx; height: 80rpx; background: #03AE80; border-radius: 66rpx; text-align: center; line-height: 80rpx; color: #fff; font-size: 32rpx; font-weight: 500; position: absolute; bottom: 17rpx; left: 50%; transform: translateX(-50%); }
.close{ position: absolute; right: 32rpx; top: 38rpx; }
}
.order_waper{
width: 100%; min-height: 462rpx; background: #fff; border-radius: 50rpx 50rpx 0 0; box-sizing: border-box; padding: 32rpx 24rpx 17rpx; position: relative;
.close{ position: absolute; right: 32rpx; top: 38rpx; }
.info{
width: 100%; height: 188rpx; box-sizing: border-box; padding-left: 208rpx; position: relative;
.img{
width: 188rpx; height: 100%; position: absolute; left: 0; top: 0;
image{ border-radius: 10rpx; }
}
.text{
color: #FF3333; padding-left: 42rpx; position: relative; font-size: 64rpx; font-weight: bold; display: flex; align-items: flex-end; height: 100%;
&::after{ content: ""; font-size: 36rpx; font-weight: 500; color: #FF3333; position: absolute; left: 0; bottom: 4rpx; }
}
}
.list_waper{
width: 100%; margin-top: 40rpx;
.list{
width: 100%; margin-bottom: 40rpx;
&:last-child{ margin-bottom: 0; }
.title{ line-height: 46rpx; color: #000; font-size: 32rpx; font-weight: bold; }
.select{
width: 100%; font-size: 0;
view{
display: inline-block; vertical-align: top; height: 60rpx; margin-top: 24rpx; border-radius: 45rpx; line-height: 60rpx; margin-right: 32rpx; padding: 0 30rpx; background: #F4F4F4; color: #333; font-size: 24rpx;
&.active{ background: #03AE80; color: #fff; }
}
}
}
}
.number{
width: 100%; margin-top: 32rpx; display: flex; justify-content: space-between; align-items: center;
.left{ line-height: 38rpx; color: #000; font-size: 32rpx; font-weight: 500; }
.right{
.null{ color: #666; font-size: 32rpx; }
.right_info{
border: 2rpx solid #E1E1E1; border-radius: 50rpx;
.u-number-box__input{ margin: 0; background: transparent !important; border: 2rpx solid #E1E1E1; border-top: none; border-bottom: none; }
.u-number-box__plus{ background: transparent !important; }
.u-icon__icon{ color: #03AE80 !important; font-weight: 400 !important; }
.u-number-box__minus{ background: transparent !important; }
.u-number-box__minus--disabled .u-icon__icon{ color: #c8c9cc !important; }
}
}
}
.btn{ width: 686rpx; height: 80rpx; background: #03AE80; border-radius: 66rpx; text-align: center; line-height: 80rpx; color: #fff; font-size: 32rpx; font-weight: 500; position: absolute; bottom: 17rpx; left: 50%; transform: translateX(-50%); }
}
</style>