Files
2023-11-14 17:21:03 +08:00

783 lines
16 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>
<layout class="goodsDetail">
<!-- <uv-navbar-->
<!-- fixed-->
<!-- :safeAreaInsetTop="true"-->
<!-- title=""-->
<!-- bg-color="transparent"-->
<!-- @leftClick="goBack"-->
<!-- />-->
<Header
:propUp="false"
:scrollTop="scrollTop"
>
</Header>
<view v-if="detailData">
<swiper
class="swiper detail"
circular
indicator-dots
autoplay
>
<swiper-item
v-for="(item, index) in sliderImageData"
:key="index"
>
<view class="swiper-item">
<image
class="image"
:src="item"
mode="widthFix"
/>
</view>
</swiper-item>
</swiper>
<view class="goodsDetail-info">
<view class="goodsDetail-price-row">
<view
class="goodsDetail-price goodsDetail-price-primary"
v-if="storeAttr || storeInfo"
>
¥{{ storeAttr && storeAttr.price || storeInfo && storeInfo.price || '0.00' }}
</view>
<view
class="goodsDetail-price goodsDetail-price-original"
v-if="(storeAttr&& storeAttr.otPrice) || (storeInfo&&storeInfo.otPrice)"
>
¥{{ storeAttr.otPrice || storeInfo.otPrice }}
</view>
</view>
<view class="goodsDetail-storeName">{{ storeInfo.storeName }}</view>
<view class="goodsDetail-info-action">
<view class="goodsDetail-info-action-tag">
<!-- <space>
<uv-tags
plain
type="primary"
text="满100减10"
></uv-tags>
<uv-tags
plain
type="primary"
text="满100减10"
></uv-tags>
</space> -->
</view>
<view
class="goodsDetail-info-action-desc"
v-if="storeInfo.stock"
>
仅剩{{ storeInfo.stock }}
</view>
</view>
</view>
<view class="row-context">
<view class="label-row">
<view class="left">
<view class="label">运费</view>
</view>
<view class="value">
{{ detailData.tempName !== '规定运费' ? detailData.tempName : storeInfo.postage + '元' }}
</view>
</view>
<view
class="label-row"
@click="discountCouponSelect('select')"
>
<view class="label">
<view class="text">
促销{{ detailData.couponSplicing }}
</view>
</view>
<view class="value">
领券
<uv-icon name="arrow-right" />
</view>
</view>
<view
class="label-row"
@click="handleOpenSelect('select')"
>
<view class="label">
<view class="text">
选择 {{ !storeAttr ? '请选择商品规格' : storeAttr.sku }}
</view>
</view>
<view class="value">
<uv-icon name="arrow-right" />
</view>
</view>
</view>
<view class="card full">
<view class="card-head" :style="{borderBottom:detailData.replyCount<=0?'none':'1rpx solid #e6e6e6'}">
<view class="card-title">商品评价({{ detailData.replyCount }})</view>
<view
class="card-more"
@click="push({url: '/pages/goodsReply/goodsReply'},{data:{id: detailData.storeInfo.id}})"
><span>
查看更多
</span>
<uv-icon
name="arrow-right"
color="rgb(187, 187, 187)"
size="10"
/>
</view>
</view>
<view
class="card-content"
v-if="detailData.reply"
>
<detail-reply :data="detailData.reply"></detail-reply>
</view>
</view>
<blank size="15"></blank>
<view class="card full">
<view class="card-head">
<view class="card-title">
商品详情
</view>
</view>
<view class="goods-detail-content">
<rich-text
:nodes="storeInfo.description"
bindtap="onOpenLink"
/>
</view>
</view>
<blank size="15"></blank>
<container min>
<view class="center-title">
<view class="center-title-line"></view>
<view class="title">热门推荐</view>
<view class="center-title-line"></view>
</view>
</container>
<good-attr-select
ref="selectAttrPanel"
:id="storeInfo.id"
:storeInfo="storeInfo"
:productAttr="detailData.productAttr"
:productValue="detailData.productValue"
@select="handleSelectAttr"
/>
<view class="coupon-select">
<good-coupon-select
ref="selectCouponPanel"
:id="storeInfo.id"
@select="handleSelectCoupon"
/>
</view>
</view>
<view
class="action-bar"
v-if="detailData"
>
<view class="action-icons">
<view class="action-icons-item">
<view
class="action-icon"
@click="goToService"
>
<img
class="action-icon-img"
src="@/static/images/icon-kefu.png"
/>
<text class="action-icon-label">客服</text>
</view>
</view>
<view class="action-icons-item">
<view
class="action-icon"
@click="goToShopCart"
>
<view class="action-icon-badge">
<uv-badge
type="error"
max="99"
:value="cardCount"
></uv-badge>
</view>
<img
class="action-icon-img"
src="@/static/images/icon-gouwuche.png"
/>
<text class="action-icon-label">购物车</text>
</view>
</view>
<view class="action-icons-item">
<view
class="action-icon"
@click="handleCollect"
>
<image
v-if="!storeInfo?.userCollect"
class="action-icon-img"
src="@/static/images/icon-shouceng.png"
/>
<image
v-if="storeInfo?.userCollect"
class="action-icon-img"
src="@/static/images/icon-shouceng-o.png"
/>
<text class="action-icon-label">收藏</text>
</view>
</view>
</view>
<view class="action-btns">
<uv-button
type="info"
text="加入购物车"
plain
@click="handleOpenSelect('cart')"
></uv-button>
<uv-button
type="primary"
text="立即购买"
@click="handleOpenSelect('buy')"
></uv-button>
</view>
</view>
</layout>
</template>
<script setup>
import { ref, unref } from 'vue'
import { collectSingle, getProductDetail, unCollectSingle } from '@/api/product'
import { getCartAdd, getCartCount } from '@/api/cart'
import { onLoad, onPageScroll } from '@dcloudio/uni-app'
import { useRouter } from "@/hooks/useRouter";
import { useInterface } from "@/hooks/useInterface";
import Header from '@/components/Header/index.vue'
import GoodCouponSelect from "@/components/good-coupon-select/good-coupon-select.vue";
import UvIcon from "@/uni_modules/uv-icon/components/uv-icon/uv-icon.vue";
const {push, getParams, pushToTab, goBack} = useRouter();
const {toast} = useInterface()
const detailData = ref(null)
const sliderImageData = ref([])
const storeInfo = ref(null)
const actionType = ref(null)
const storeAttr = ref(null)
const storeNum = ref(null)
const cardCount = ref(null)
const selectAttrPanel = ref(false)
const selectCouponPanel = ref(false)
const selectCoupon = ref(false)
const handleGetDetail = async (id) => {
try {
const detail = await getProductDetail(id)
uni.hideLoading();
if (detail) {
detailData.value = detail
sliderImageData.value = detail.storeInfo.sliderImage.split(',')
storeInfo.value = detail.storeInfo
const description = storeInfo.value.description
storeInfo.value.description = description.replace(/<img /g, "<img style='width:100%;' ")
let attr = []
detail.productAttr.forEach((item, i) => {
attr[i] = item.attrValueArr[0]
})
let selectedAttrStr = attr.join(',')
storeAttr.value = detail.productValue[selectedAttrStr]
}
} catch (error) {
uni.hideLoading();
}
}
onLoad((options) => {
uni.showLoading({
title: '加载中'
});
const params = getParams(options)
handleGetDetail(params.id)
handleGetCartCount(params.id)
})
const goToService = () => {
toast({title: '😒敬请期待😒'})
}
const goToShopCart = () => {
pushToTab({url: '/pages/shoppingCart/shoppingCart'})
}
const handleOpenSelect = (action) => {
actionType.value = action
selectAttrPanel.value.open()
}
const discountCouponSelect = () => {
selectCouponPanel.value.open()
}
const handleSelectAttr = (attr) => {
selectAttrPanel.value.close()
storeAttr.value = attr.store
storeNum.value = attr.num
switch (actionType.value) {
case 'select':
break;
case 'cart':
handleCardAdd()
break;
case 'buy':
handleBuy()
break;
}
actionType.value = ''
}
const handleSelectCoupon = (coupon) => {
selectCoupon.value = coupon
}
const handleCardAdd = async () => {
let res = await getCartAdd({
cartNum: storeNum.value,
productId: storeAttr.value.productId,
uniqueId: storeAttr.value.unique,
new: 0
})
handleGetCartCount()
uni.showToast({
title: '已加入购物车',
duration: 2000
});
}
const handleBuy = async () => {
uni.showLoading({
title: '加载中'
});
let res = await getCartAdd({
cartNum: storeNum.value,
productId: storeAttr.value.productId,
uniqueId: storeAttr.value.unique,
new: 1
})
uni.hideLoading();
push({url: '/pages/submitOrder/submitOrder'}, {data: {cartId: res.cartId}})
}
const handleCollect = async () => {
const goodInfo = unref(storeInfo)
const isCollect = goodInfo.userCollect
const data = {
productId: goodInfo.id,
type: 'collect',
category: 'common'
}
if (isCollect) {
// 取消
await unCollectSingle(data)
} else {
// 收藏
await collectSingle(data)
}
goodInfo.userCollect = !goodInfo.userCollect
isCollect ? toast({title: '已取消收藏'}) : toast({title: '已收藏'})
}
const handleGetCartCount = async () => {
const count = await getCartCount()
cardCount.value = count.count
}
const scrollTop = ref(0)
onPageScroll((e) => {
scrollTop.value = e.scrollTop
})
</script>
<style lang="scss">
.search {
padding: 0 17px;
}
.logo {
width: 63px;
height: 25px;
.image {
width: 63px;
height: 25px;
display: block;
}
}
.swiper {
width: 100%;
.image {
width: 100%;
display: block;
}
}
.goodsDetail {
width: 100%;
&-storeName {
line-height: 40rpx;
font-size: 32rpx;
color: #333333;
margin-bottom: 20rpx;
}
&-price {
&-row {
margin-bottom: 17rpx;
display: flex;
align-items: center;
.goods-price {
}
}
&-primary {
line-height: 50rpx;
font-size: 50rpx;
color: #EE6D46;
}
&-default {
line-height: 40rpx;
font-size: 28rpx;
color: #333333;
}
&-original {
margin-left: 20rpx;
line-height: 28rpx;
font-size: 28rpx;
color: #999999;
text-decoration: line-through;
}
}
&-info {
background-color: #fff;
padding: 30rpx 35rpx;
// display: flex;
// align-items: flex-end;
// justify-content: space-between;
&-left {
}
&-action {
display: flex;
justify-content: space-between;
&-tags {
.uv-tags {
margin-right: 10rpx;
}
}
&-desc {
line-height: 28rpx;
font-size: 20rpx;
color: #999999;
}
}
}
&-image {
&-img {
}
}
&-list {
width: 100%;
display: flex;
flex-direction: row;
padding: 14rpx;
.goods {
&-thumb {
margin-bottom: 0;
width: 220rpx;
height: 220rpx;
&-img {
width: 100%;
height: 100%;
display: block;
}
}
&-content {
padding-right: 40rpx;
margin-left: 30rpx;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
}
}
// .goods {
// padding: 16rpx 14rpx;
// &-header {
// display: flex;
// align-items: flex-start;
// }
// &-thumb {
// width: 220rpx;
// height: 220rpx;
// &-img {
// width: 100%;
// height: 100%;
// display: block;
// }
// }
// &-content {
// margin-top: 24rpx;
// margin-left: 40rpx;
// flex: 1
// }
// &-storeName {
// line-height: 40rpx;
// font-size: 28rpx;
// font-weight: 500;
// color: #333333;
// margin-bottom: 35rpx;
// }
// &-info {
// display: flex;
// align-items: center;
// justify-content: space-between;
// &-left {
// display: flex;
// align-items: flex-end;
// }
// &-action {
// &-btn {}
// &-desc {
// color: #999999;
// font-size: 24rpx;
// line-height: 40rpx;
// }
// }
// }
// &-price {
// &-default {
// line-height: 28rpx;
// font-size: 20rpx;
// color: #999999;
// }
// &-primary {
// line-height: 42rpx;
// font-size: 30rpx;
// font-weight: 500;
// color: #EE6D46;
// margin-left: 5rpx;
// }
// }
// &-desc {
// color: #999999;
// font-size: 24rpx;
// line-height: 40rpx;
// }
// &-model {
// display: inline-flex;
// align-items: center;
// width: auto;
// height: 40rpx;
// border: 1px solid #CCCCCC;
// opacity: 1;
// border-radius: 0rpx;
// padding: 0 10rpx;
// margin-bottom: 28rpx;
// &-label {
// line-height: 38rpx;
// font-size: 24rpx;
// color: #999999;
// }
// &-value {
// line-height: 38rpx;
// font-size: 24rpx;
// color: #333333;
// margin-right: 10rpx;
// }
// &-action {
// width: 11rpx;
// height: 7rpx;
// }
// }
// &-model-info {
// display: inline-flex;
// align-items: center;
// width: auto;
// height: 40rpx;
// opacity: 1;
// border-radius: 0rpx;
// margin-bottom: 28rpx;
// &-label {
// line-height: 38rpx;
// font-size: 24rpx;
// color: #999999;
// }
// &-value {
// line-height: 38rpx;
// font-size: 24rpx;
// color: #333333;
// margin-right: 10rpx;
// }
// &-action {
// width: 11rpx;
// height: 7rpx;
// }
// }
// }
}
.buy-progress {
display: flex;
align-items: center;
justify-content: space-between;
&-info {
flex: 1;
&-desc {
color: #999999;
font-size: 24rpx;
line-height: 32rpx;
}
}
&-action {
margin-left: 17rpx;
}
}
.buy-num {
&-info-desc {
color: #999999;
font-size: 24rpx;
line-height: 32rpx;
}
}
.goods-detail-content {
padding: 34rpx;
img {
max-width: 100%;
}
}
:deep(.coupon-select) .uni-popup__wrapper {
height: auto;
max-height: 1000rpx;
overflow-y: auto;
}
:deep(.uv-icon) {
flex-shrink: 0 !important;
}
// ======================= 👇 kahu ===
.row-context {
margin: 30rpx 0 ;
.label-row {
@include useFlex(space-between, center);
@include usePadding(30, 20);
width: 100%;
background: $white-color;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.label {
max-width: 85%;
@include useFlex(flex-start, center);
color: $tips-color;
white-space: nowrap;
font-size: 24rpx;
flex-grow: 0;
.text {
max-width: 100%;
overflow: hidden;
flex-grow: 0;
text-overflow: ellipsis;
}
}
.value {
@include useFlex(flex-end, center);
flex-shrink: 0;
}
}
}
</style>