725 lines
17 KiB
Vue
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> |