Files
wagoo-douy3/src/pages/client/shop/details.vue
2026-04-04 18:09:09 +08:00

725 lines
17 KiB
Vue

<template>
<view class="shop-details-container">
<view class="banner-swiper">
<swiper class="swiper-wrapper" circular :indicator-dots="false" :autoplay="true" @change="bannerChange">
<swiper-item v-for="(banner, i) in details.product_pic_json" :key="i">
<image class="swiper-img" :src="banner.photos_image" />
</swiper-item>
</swiper>
<view class="flex-row-center dot-box">
<view v-for="(banner, index) in bannerList" :key="index" class="dot-item"
:class="{ active: bannerIndex === index }"></view>
</view>
</view>
<view class="good-info">
<view class="info-cell">
<!-- <view class="flex-row-start label">
<image class="hot-icon" :src="`${imgPrefix}mall-hot.png`"></image>
<view class="fs-20 app-fc-main label-name">{{details.sales}}人买过</view>
</view> -->
<view class="flex-row-between" style="margin-top: 20rpx">
<view class="">
<text class="app-fc-mark fs-28"></text>
<text class="app-fc-mark fs-28">{{picList[0].actual_price / 100 || 0 }}</text>
<!-- <text class="fs-24 origin-price">
¥{{ minPrice.price_shichang || 0 }}
</text> -->
</view>
<!-- <text class="fs-22 app-fc-cancel">
已售 {{ details.xiaoliang || 0 }}
</text> -->
</view>
<view class="fs-29 good-name">
{{ details.product_name || "" }}
</view>
<!-- <view class="flex-row-start">
<text class="fs-20 sale-btn" v-for="(label, i) in labelList" :key="i"
:class="[i > 0 ? 'sale-today app-fc-mark' : 'app-fc-white']">
{{ label }}
</text>
</view> -->
</view>
<view class="info-cell shipping-info-cell">
<view class="selected-row" @click="showSpecModal">
<text class="selected-label">已选</text>
<text class="selected-value">{{ selectedSpec || "请选择规格" }}</text>
<image class="arrow-right-icon" :src="require('./static/arrow_right.png')" />
</view>
<view class="service-features">
<view class="feature-item">
<image :src="`${imgPrefix}mall-detailTips.png`" class="mallPng"></image>
<!-- <view class="check-icon"></view> -->
<text class="feature-text">快递发货</text>
</view>
<view class="feature-item" style="margin-left: 20rpx;">
<!-- <view class="check-icon"></view> -->
<image :src="`${imgPrefix}mall-detailTips.png`" class="mallPng"></image>
<text class="feature-text">七天无理由退货</text>
</view>
</view>
</view>
<view class="info-cell" v-if="remarkList.length">
<view class="width-fill flex-row-between">
<text class="fs-28 postage-info">
用户评价
<text class="fs-24 app-fc-normal">
({{ details.pingjia_num }})
</text>
</text>
<view class="flex-row-end" @click="jumpToRemark">
<text class="fs-26 app-fc-main">查看全部</text>
<image class="arrow-icon" :src="require('./static/arrow_right.png')" />
</view>
</view>
<view class="remark-list">
<remark-item v-for="item in remarkList" :key="item.id" :data="item" />
</view>
</view>
</view>
<view class="fs-24 app-fc-normal rich-text-title"> 商品详情 </view>
<view class="rich-text-content" v-html="details.content"></view>
<view class="place-view"></view>
<view class="flex-row-between details-bottom">
<view class="flex-row-start">
<view class="flex-center opt-item" @click="collectAction">
<image class="opt-icon" :src="
details.shoucang_id
? `${imgPrefix}shopDetail-collect_done.png`
: `${imgPrefix}shopDetail-collect.png`
" mode="widthFix" />
<text class="fs-20 app-fc-main">收藏</text>
</view>
<view class="flex-center opt-item service" @click="jumpToWeChat">
<image class="opt-icon" :src="`${imgPrefix}shopDetail-contact.png`" mode="widthFix" />
<text class="fs-20 app-fc-main">客服</text>
</view>
<view class="flex-center opt-item service cart-item-wrapper" @click="jumpToCart">
<view class="cart-icon-wrapper">
<image class="opt-icon" :src="`${imgPrefix}shopDetail-car.png`" mode="widthFix" />
<view v-if="cartCount > 0" class="cart-badge">
<text class="cart-badge-text">{{ cartCount > 99 ? '99+' : cartCount }}</text>
</view>
</view>
<text class="fs-20 app-fc-main">购物车</text>
</view>
</view>
<view class="flex-row-start opt-btns">
<view class="flex-center fs-30 app-fc-white opt-btn opt-btn-cart" @click="addToCart">
加入购物车
</view>
<view class="flex-center fs-30 app-fc-white opt-btn" @click="goBuy">
立即购买
</view>
</view>
</view>
<add-goods-modal v-if="showModal" :optText="type === 'cart' ? '加入购物车' : '立即购买'" :data="details"
@optAction="optAction" @change="(val) => (showModal = val)" />
<contact-modal v-if="showConcactModal" @close="showConcactModal = false" />
</view>
</template>
<script>
import {
addCart,
getGoodsDetail,
collectShop,
getCartList
} from "@/api/shop";
import {
imgPrefix
} from '@/utils/common';
import RemarkItem from "@/components/goods/RemarkItem.vue";
import AddGoodsModal from "@/components/goods/AddGoodsModal.vue";
import ContactModal from "@/components/ContactModal.vue";
import {
getRemarkList
} from "../../../api/shop";
import {
jumpToWeChat,
showLoginConfirmModal
} from "@/utils/common";
export default {
components: {
RemarkItem,
AddGoodsModal,
ContactModal,
},
data() {
return {
imgPrefix,
bannerIndex: 0,
picList:[],
bannerList: [],
details: {},
showModal: false,
type: "", // cart/gobuy
goodId: "",
showConcactModal: false,
remarkList: [],
petOrderId: "",
petOrderAddressId: "",
selectedSpec: "", // 已选规格
cartCount: 0, // 购物车商品数量
};
},
computed: {
minPrice() {
let minPrice = {};
let minPriceValue = 0;
(this.details?.price_list || []).map((v) => {
if (!minPriceValue || minPriceValue > +v.price) {
minPriceValue = +v.price;
minPrice = {
...v
};
}
});
return minPrice;
},
labelList() {
return (this.details?.label || "").split(",").filter((v) => !!v);
},
sliverFee() {
return +this.details?.yunfei || 0;
},
},
onLoad(options) {
this.product_id = options.product_id
// console.log(this.product_id ,'??')
const {
id,
petOrderId = "",
petOrderAddressId = ""
} = options;
this.goodId = id;
this.petOrderId = petOrderId;
this.petOrderAddressId = petOrderAddressId;
this.getDetails(this.product_id);
// this.getRemarkListData();
this.getCartCount();
},
onShow() {
// 页面显示时更新购物车数量
this.getCartCount();
},
methods: {
jumpToWeChat,
// 跳转购物车
jumpToCart() {
uni.redirectTo({
url: '/pages/client/cart/index'
});
},
// 获取购物车数量
getCartCount() {
getCartList({
p: 1,
num: 999
}).then((res) => {
const cartList = res?.info || [];
// 计算购物车商品总数量
this.cartCount = cartList.reduce((total, item) => {
return total + (item.number || 0);
}, 0);
}).catch(() => {
this.cartCount = 0;
});
},
// 获取商品详情
getDetails(id) {
getGoodsDetail({
product_id: Number(id)
}).then((res) => {
this.details = res.data || {};
// console.log(this.details,'--')
this.picList = res.data?.prices
// console.log(this.picList,'??')
this.bannerList = this.details?.pic_list || [];
// 默认选择第一个规格
this.setDefaultSpec();
});
},
// 设置默认规格
setDefaultSpec() {
const priceList = this.details?.price_list || [];
const shuxingList = this.details?.shuxing_list || [];
if (priceList.length > 0) {
const firstPrice = priceList[0];
const firstShuxing = shuxingList.length > 0 ? shuxingList[0] : null;
// 使用 price_name 或 shuxing_name
const specName = firstPrice.price_name || firstShuxing?.name || "";
this.selectedSpec = specName ? `${specName}*1` : "";
}
},
// 获取评论列表
// getRemarkListData() {
// getRemarkList({
// p: 1,
// num: 1,
// type: 1,
// guanlian_id: this.goodId
// }).then(
// (res) => {
// this.remarkList = res?.info || [];
// }
// );
// },
// 跳转评论列表
jumpToRemark() {
uni.navigateTo({
url: `/pages/client/remark/list?shopId=${this.goodId}`,
});
},
bannerChange(e) {
this.bannerIndex = e.detail.current;
},
addToCart() {
const {
goods_id,
price_list,
shuxing_list
} = this.details;
// 只有一个规格时,直接加入购物车
// if (price_list?.length === 1 && shuxing_list.length === 1) {
// this.addCartAction({
// goods_id,
// price_id: price_list?.[0]?.price_id,
// number: 1,
// shuxing_name: shuxing_list?.[0]?.name,
// });
// return;
// }
this.showModal = true;
this.type = "cart";
},
// 加入购物车
addCartAction({number}) {
const data = {
item_id:this.details.id,
item_type:this.details.type,
price_id:this.details.prices[0].id,
price_desc:this.details.prices[0].price_desc,
item_name:this.details.product_name,
item_price:this.details.prices[0].actual_price,
number:number,
item_pic:this.details.product_pic,
is_select:1,
property:this.details.property_name,
}
// console.log(this.details,'??')
addCart(data).then(() => {
uni.showToast({
title: "已加入购物车!",
icon: "none"
});
this.showModal = false;
// 更新购物车数量
this.getCartCount();
})
.catch((err) => {
uni.showToast({
title: err || "加入购物车失败!",
icon: "none"
});
});
},
optAction({ goods_id, shuxing_name, price_id, number}) {
// if (kucun <= 0) {
// uni.showToast({
// title: "该商品已售罄",
// icon: "none",
// });
// return;
// }
// 更新已选规格显示
const priceInfo = (this.details?.price_list || []).find(
(v) => v.price_id === price_id
);
if (priceInfo) {
this.selectedSpec = `${priceInfo.price_name || shuxing_name}*${number}`;
}
if (this.type === "cart") {
// 加入购物车
this.addCartAction({
goods_id,
price_id,
number,
shuxing_name,
});
} else {
// 立即购买
this.jumpToOrder({
goods_id,
shuxing_name,
price_id,
number
});
}
},
// 显示规格选择modal
showSpecModal() {
this.showModal = true;
this.type = "cart";
},
goBuy() {
const {
goods_id,
price_list,
shuxing_list
} = this.details;
// 只有一个规格时,直接加入购物车
// if (price_list?.length === 1 && shuxing_list.length === 1) {
// this.jumpToOrder({ goods_id, shuxing_name, price_id, 1 })
// return;
// }
this.showModal = true;
this.type = "gobuy";
},
// 立即购买
jumpToOrder({
goods_id,
shuxing_name,
price_id,
number
}) {
uni.navigateTo({
url: `/pages/client/order/create`,
success: (res) => {
// 通过eventChannel向被打开页面传送数据
const priceInfo = (this.details?.prices || []).find(
(v) => price_id === v.id
);
res.eventChannel.emit("createOrder", {
goodList: [{
...this.details,
goods_id,
shuxing_name,
price_id,
number,
goods_name: this.details.goods_name,
price_name: priceInfo?.price_name,
goods_price: +((priceInfo?.actual_price / 100) * number).toFixed(2),
}, ],
});
},
});
},
// 收藏
async collectAction() {
const token = uni.getStorageSync('token');
if (!token) {
const willLogin = await showLoginConfirmModal();
if (!willLogin) return;
return;
}
collectShop({
goods_id: this.details.goods_id,
type: this.details.shoucang_id ? 2 : 1,
})
.then(() => {
this.getDetails();
})
.catch((err) => {
uni.showToast({
title: err || "收藏失败!",
icon: "none"
});
});
},
},
};
</script>
<style lang="scss" scoped>
.shop-details-container {
height: 100%;
position: relative;
.banner-swiper {
position: relative;
width: 100%;
height: 750rpx;
.swiper-wrapper {
width: 100%;
height: 750rpx;
.swiper-img {
width: 100%;
height: 100%;
border-radius: 40rpx;
}
}
.dot-box {
position: absolute;
bottom: 30rpx;
left: 0;
width: 100%;
.dot-item {
width: 12rpx;
height: 12rpx;
border-radius: 12rpx;
background: rgba(255, 255, 255, 0.6);
margin-right: 12rpx;
&.active {
width: 28rpx;
height: 12rpx;
background: #ffffff;
}
}
}
}
.good-info {
margin: 20rpx;
.info-cell {
background: #fff;
border-radius: 20rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.origin-price {
color: #726e71;
text-decoration: line-through;
margin-left: 18rpx;
line-height: 44rpx;
}
.good-name {
margin-top: 16rpx;
}
.sale-btn {
padding: 6rpx 12rpx;
background: $app_color_main;
border-radius: 4rpx;
border: 1rpx solid $app_color_main;
margin-right: 12rpx;
margin-bottom: 24rpx;
&.sale-today {
background: transparent;
}
}
.postage-info {
color: #9b939a;
margin-right: 32rpx;
}
.arrow-icon {
width: 11rpx;
height: 18rpx;
margin-left: 20rpx;
}
.shipping-info-cell {
.selected-row {
display: flex;
align-items: center;
margin-bottom: 24rpx;
cursor: pointer;
.selected-label {
font-size: 24rpx;
color: #999;
margin-right: 12rpx;
}
.selected-value {
flex: 1;
font-size: 24rpx;
color: #3D3D3D;
}
.arrow-right-icon {
width: 11rpx;
height: 18rpx;
flex-shrink: 0;
}
}
.shipping-row {
display: flex;
align-items: center;
margin-bottom: 24rpx;
.shipping-label {
font-size: 24rpx;
color: #999;
margin-right: 12rpx;
}
.shipping-value {
font-size: 24rpx;
color: #3D3D3D;
}
}
.service-features {
background: #F5F5F5;
border-radius: 12rpx;
padding: 12rpx 20rpx;
display: flex;
align-items: center;
border-radius: 339px;
.feature-item {
display: flex;
align-items: center;
.mallPng {
width: 20rpx;
height: 20rpx;
}
.feature-text {
font-size: 20rpx;
color: #3D3D3D;
margin-left: 4rpx;
}
}
}
}
.remark-list {
border-top: 2rpx solid #ebebeb;
margin-top: 36rpx;
::v-deep {
.remark-item {
width: 100%;
margin: 0;
padding: 40rpx 0;
}
}
}
}
.rich-text-title {
margin: 32rpx 0 24rpx;
text-align: center;
}
.rich-text-content {
background: #fff;
padding: 24rpx;
}
.place-view {
height: 190rpx;
}
.details-bottom {
position: fixed;
bottom: 0;
padding-bottom: 20rpx;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
padding-top: 20rpx;
padding-left: 40rpx;
padding-right: 36rpx;
left: 0;
width: 100vw;
background: #fff;
box-sizing: border-box;
.opt-item {
&.service {
margin-left: 36rpx;
}
.opt-icon {
width: 33rpx;
height: 33rpx;
margin-bottom: 8rpx;
}
&.cart-item-wrapper {
.cart-icon-wrapper {
position: relative;
display: inline-block;
}
.cart-badge {
position: absolute;
top: -16rpx;
right: -16rpx;
min-width: 32rpx;
height: 32rpx;
background: #FF19A0;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 8rpx;
box-sizing: border-box;
.cart-badge-text {
color: #FFFFFF;
font-size: 20rpx;
font-weight: 500;
line-height: 1;
}
}
}
}
.opt-btns {
width: 376rpx;
height: 76rpx;
border-radius: 92rpx;
overflow: hidden;
.opt-btn {
width: 188rpx;
height: 100%;
background: $app_color_main;
font-size: 28rpx;
&.opt-btn-cart {
background: #F8C142;
}
}
}
}
}
.label {
background-color: #ffecf3;
display: inline-flex;
border-radius: 4rpx;
.hot-icon {
width: 28rpx;
height: 28rpx;
}
.label-name {
padding: 4rpx;
color: #FF19A0;
}
}
</style>