Files

622 lines
15 KiB
Vue
Raw Normal View History

2023-11-14 17:21:03 +08:00
<template>
<layout class="goodsDetail">
2023-11-15 19:59:37 +08:00
<view :style="computedHeightStyle"></view>
2023-11-22 18:55:55 +08:00
<Header :scroll-top="scrollTop" ref="headerRef" :propUp="false"> </Header>
<view v-if="!showEmpty">
<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="aspectFill" />
</view>
</swiper-item>
</swiper>
<view class="goodsDetail-info">
<view class="goodsDetail-price-row">
<view
2023-11-14 17:21:03 +08:00
class="goodsDetail-price goodsDetail-price-primary"
v-if="storeAttr || storeInfo"
2023-11-22 18:55:55 +08:00
>
¥{{
(storeAttr && storeAttr.price) ||
(storeInfo && storeInfo.price) ||
"0.00"
}}
</view>
<view
2023-11-14 17:21:03 +08:00
class="goodsDetail-price goodsDetail-price-original"
2023-11-15 19:59:37 +08:00
v-if="storeAttr && storeInfo"
2023-11-22 18:55:55 +08:00
>
¥{{
(storeAttr && storeAttr.otPrice) ||
(storeInfo && storeInfo.otPrice)
}}
</view>
2023-11-14 17:21:03 +08:00
</view>
2023-11-22 18:55:55 +08:00
<view class="goodsDetail-storeName">{{ storeInfo.storeName }}</view>
<view class="goodsDetail-info-action">
<view class="goodsDetail-info-action-tag">
<!-- <space>
2023-11-14 17:21:03 +08:00
<uv-tags
plain
type="primary"
text="满100减10"
></uv-tags>
<uv-tags
plain
type="primary"
text="满100减10"
></uv-tags>
</space> -->
2023-11-22 18:55:55 +08:00
</view>
<view class="goodsDetail-info-action-desc" v-if="storeInfo.stock">
仅剩{{ storeInfo.stock }}
</view>
2023-11-14 17:21:03 +08:00
</view>
</view>
2023-11-22 18:55:55 +08:00
<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 + "元"
}}
2023-11-14 17:21:03 +08:00
</view>
</view>
2023-11-22 18:55:55 +08:00
<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>
2023-11-14 17:21:03 +08:00
</view>
2023-11-22 18:55:55 +08:00
<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" />
2023-11-14 17:21:03 +08:00
</view>
</view>
</view>
2023-11-22 18:55:55 +08:00
<view class="card full">
2023-11-14 17:21:03 +08:00
<view
2023-11-22 18:55:55 +08:00
class="card-head"
:style="{
borderBottom:
detailData.replyCount <= 0 ? 'none' : '1rpx solid #e6e6e6',
}"
>
<view class="card-title"
>商品评价({{ detailData.replyCount }})</view
>
<view
2023-11-14 17:21:03 +08:00
class="card-more"
2023-11-22 18:55:55 +08:00
@click="
push(
{ url: '/pages/goodsReply/goodsReply' },
{ data: { id: detailData.storeInfo.id } }
)
"
><span> 查看更多 </span>
<uv-icon
2023-11-14 17:21:03 +08:00
name="arrow-right"
color="rgb(187, 187, 187)"
size="10"
2023-11-22 18:55:55 +08:00
/>
</view>
2023-11-14 17:21:03 +08:00
</view>
2023-11-22 18:55:55 +08:00
<view class="card-content" v-if="detailData.reply">
<detail-reply :data="detailData.reply"></detail-reply>
2023-11-14 17:21:03 +08:00
</view>
</view>
2023-11-22 18:55:55 +08:00
<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>
2023-11-14 17:21:03 +08:00
</view>
2023-11-22 18:55:55 +08:00
<blank size="15"></blank>
2023-11-14 17:21:03 +08:00
2023-11-22 18:55:55 +08:00
<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
2023-11-14 17:21:03 +08:00
ref="selectAttrPanel"
:id="storeInfo.id"
:storeInfo="storeInfo"
:productAttr="detailData.productAttr"
:productValue="detailData.productValue"
@select="handleSelectAttr"
2023-11-22 18:55:55 +08:00
/>
<view class="coupon-select">
<good-coupon-select
2023-11-14 17:21:03 +08:00
ref="selectCouponPanel"
:id="storeInfo.id"
@select="handleSelectCoupon"
2023-11-22 18:55:55 +08:00
/>
</view>
2023-11-14 17:21:03 +08:00
</view>
2023-11-22 18:55:55 +08:00
<view
2023-11-14 17:21:03 +08:00
class="action-bar"
2023-11-22 18:55:55 +08:00
:class="detailData.storeInfo.isShow === 0 && 'off'"
2023-11-14 17:21:03 +08:00
v-if="detailData"
2023-11-22 18:55:55 +08:00
>
<view class="off-tip">商品已经下架了啦要不要瞧瞧别的</view>
<view class="action-cont">
<view class="action-icons">
<view class="action-icons-item">
<view class="action-icon" @click="goToService">
<img class="action-icon-img" :src="productCustomerIcon" />
<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>
<image class="action-icon-img" :src="productCartIcon" />
<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="productCollectIcon"
/>
<image
v-if="storeInfo?.userCollect"
class="action-icon-img"
:src="productAffirmCollectIcon"
/>
<text class="action-icon-label">收藏</text>
</view>
2023-11-14 17:21:03 +08:00
</view>
</view>
2023-11-22 18:55:55 +08:00
<view class="action-btns">
<uv-button
type="info"
text="加入购物车"
:disabled="detailData.storeInfo.isShow === 0"
plain
@click="handleOpenSelect('cart')"
></uv-button>
<uv-button
type="primary"
text="立即购买"
:disabled="detailData.storeInfo.isShow === 0"
@click="handleOpenSelect('buy')"
></uv-button>
2023-11-14 17:21:03 +08:00
</view>
</view>
</view>
2023-11-22 18:55:55 +08:00
<ReturnTop :scroll-top="scrollTop" />
2023-11-14 17:21:03 +08:00
</view>
2023-11-22 18:55:55 +08:00
<GoodEmpty v-else></GoodEmpty>
2023-11-14 17:21:03 +08:00
</layout>
</template>
<script setup>
2023-11-22 18:55:55 +08:00
import {productCustomerIcon, productCollectIcon, productCartIcon, productAffirmCollectIcon} from "@/utils/images";
2023-11-15 19:59:37 +08:00
import { computed, onMounted, ref, unref } from 'vue'
2023-11-14 17:21:03 +08:00
import { collectSingle, getProductDetail, unCollectSingle } from '@/api/product'
import { getCartAdd, getCartCount } from '@/api/cart'
2023-11-22 18:55:55 +08:00
import { onLoad, onPageScroll, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
2023-11-14 17:21:03 +08:00
import { useRouter } from "@/hooks/useRouter";
import { useInterface } from "@/hooks/useInterface";
2023-11-22 18:55:55 +08:00
import Header from "@/components/Header/index.vue";
2023-11-14 17:21:03 +08:00
import GoodCouponSelect from "@/components/good-coupon-select/good-coupon-select.vue";
import UvIcon from "@/uni_modules/uv-icon/components/uv-icon/uv-icon.vue";
2023-11-15 19:59:37 +08:00
import { useScroll } from "@/hooks/useScroll";
2023-11-17 20:55:32 +08:00
import ReturnTop from "@/components/ReturnTop/index.vue"
2023-11-22 18:55:55 +08:00
import GoodEmpty from "@/pages/goodsDetail/components/GoodEmpty.vue";
import { SharePathMap, useShare } from "@/hooks/useShare";
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 headerRef = ref();
const showEmpty = ref(false)
2023-11-15 19:59:37 +08:00
const computedHeightStyle = computed(() => {
const style = {width: '100 %', height: 0,background:'#f5f5f5'}
if (!headerRef.value || !headerRef.value.heightInfo) return style
return {...style, height: `${ headerRef.value.heightInfo.statusBarHeight }px`}
})
onMounted(() => {
})
2023-11-14 17:21:03 +08:00
2023-11-22 18:55:55 +08:00
const {shareAppMessage,shareTimeline,goodsDetailShare} = useShare(SharePathMap.GOODS_DETAIL)
onShareAppMessage(shareAppMessage)
onShareTimeline(shareTimeline)
2023-11-14 17:21:03 +08:00
const handleGetDetail = async (id) => {
try {
2023-11-22 18:55:55 +08:00
const detail = await getProductDetail(id);
2023-11-14 17:21:03 +08:00
if (detail) {
2023-11-22 18:55:55 +08:00
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 = [];
2023-11-14 17:21:03 +08:00
detail.productAttr.forEach((item, i) => {
attr[i] = item.attrValueArr[0]
})
let selectedAttrStr = attr.join(',')
storeAttr.value = detail.productValue[selectedAttrStr]
2023-11-22 18:55:55 +08:00
goodsDetailShare(detail.storeInfo)
} else {
showEmpty.value = true
2023-11-14 17:21:03 +08:00
}
} catch (error) {
}
2023-11-22 18:55:55 +08:00
};
2023-11-14 17:21:03 +08:00
onLoad((options) => {
const params = getParams(options)
handleGetDetail(params.id)
handleGetCartCount(params.id)
})
2023-11-17 20:55:32 +08:00
const {scrollTop} = useScroll()
2023-11-14 17:21:03 +08:00
const goToService = () => {
2023-11-22 18:55:55 +08:00
toast({ title: "敬请期待" });
};
2023-11-14 17:21:03 +08:00
const goToShopCart = () => {
2023-11-17 20:55:32 +08:00
push({
2023-11-22 18:55:55 +08:00
url: "/pages/shoppingCartJump/shoppingCart",
});
};
2023-11-14 17:21:03 +08:00
const handleOpenSelect = (action) => {
2023-11-22 18:55:55 +08:00
if (detailData.value.storeInfo.isShow === 1) {
actionType.value = action;
selectAttrPanel.value.open();
}
};
2023-11-14 17:21:03 +08:00
const discountCouponSelect = () => {
2023-11-22 18:55:55 +08:00
selectCouponPanel.value.open();
};
2023-11-14 17:21:03 +08:00
const handleSelectAttr = (attr) => {
2023-11-22 18:55:55 +08:00
selectAttrPanel.value.close();
storeAttr.value = attr.store;
storeNum.value = attr.num;
2023-11-14 17:21:03 +08:00
switch (actionType.value) {
2023-11-22 18:55:55 +08:00
case "select":
2023-11-14 17:21:03 +08:00
break;
2023-11-22 18:55:55 +08:00
case "cart":
handleCardAdd();
2023-11-14 17:21:03 +08:00
break;
2023-11-22 18:55:55 +08:00
case "buy":
handleBuy();
2023-11-14 17:21:03 +08:00
break;
}
2023-11-22 18:55:55 +08:00
actionType.value = "";
};
2023-11-14 17:21:03 +08:00
const handleSelectCoupon = (coupon) => {
2023-11-22 18:55:55 +08:00
selectCoupon.value = coupon;
};
2023-11-14 17:21:03 +08:00
const handleCardAdd = async () => {
let res = await getCartAdd({
cartNum: storeNum.value,
productId: storeAttr.value.productId,
uniqueId: storeAttr.value.unique,
2023-11-22 18:55:55 +08:00
new: 0,
});
handleGetCartCount();
2023-11-14 17:21:03 +08:00
uni.showToast({
2023-11-22 18:55:55 +08:00
title: "已加入购物车",
duration: 2000,
2023-11-14 17:21:03 +08:00
});
2023-11-22 18:55:55 +08:00
};
2023-11-14 17:21:03 +08:00
const handleBuy = async () => {
uni.showLoading({
2023-11-22 18:55:55 +08:00
title: "加载中",
2023-11-14 17:21:03 +08:00
});
let res = await getCartAdd({
cartNum: storeNum.value,
productId: storeAttr.value.productId,
uniqueId: storeAttr.value.unique,
2023-11-22 18:55:55 +08:00
new: 1,
});
2023-11-14 17:21:03 +08:00
uni.hideLoading();
2023-11-22 18:55:55 +08:00
push(
{ url: "/pages/submitOrder/submitOrder" },
{ data: { cartId: res.cartId } }
);
};
2023-11-14 17:21:03 +08:00
const handleCollect = async () => {
2023-11-22 18:55:55 +08:00
const goodInfo = unref(storeInfo);
const isCollect = goodInfo.userCollect;
2023-11-14 17:21:03 +08:00
const data = {
productId: goodInfo.id,
2023-11-22 18:55:55 +08:00
type: "collect",
category: "common",
};
2023-11-14 17:21:03 +08:00
if (isCollect) {
// 取消
2023-11-22 18:55:55 +08:00
await unCollectSingle(data);
2023-11-14 17:21:03 +08:00
} else {
// 收藏
2023-11-22 18:55:55 +08:00
await collectSingle(data);
2023-11-14 17:21:03 +08:00
}
2023-11-22 18:55:55 +08:00
goodInfo.userCollect = !goodInfo.userCollect;
isCollect ? toast({ title: "已取消收藏" }) : toast({ title: "已收藏" });
};
2023-11-14 17:21:03 +08:00
const handleGetCartCount = async () => {
2023-11-22 18:55:55 +08:00
const count = await getCartCount();
cardCount.value = count.count;
};
2023-11-14 17:21:03 +08:00
</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;
}
&-primary {
line-height: 50rpx;
font-size: 50rpx;
2023-11-22 18:55:55 +08:00
color: #ee6d46;
2023-11-14 17:21:03 +08:00
}
&-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;
&-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;
}
}
}
}
.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 {
2023-11-15 19:59:37 +08:00
margin: 30rpx 0;
2023-11-14 17:21:03 +08:00
.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;
2023-11-15 19:59:37 +08:00
font-size: 28rpx;
color: #333;
}
}
}
.swiper {
.swiper-item {
width: 100%;
height: 100%;
.image {
width: inherit;
height: inherit;
2023-11-14 17:21:03 +08:00
}
}
}
</style>