You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
zhishifufei_uniapp/pages/store/detail.vue

789 lines
19 KiB

<template>
<BaseContainer class="goods-detail">
<NavBar title="商品详情">
</NavBar>
<template v-if="storeInfo.id">
<swiper :style="{ height: width + 'px' }" class="swiper-container">
<swiper-item v-for="(item, index) in storeInfo.slider_image" :key="index" class="swiper-slide">
<image :src="item" mode="aspectFit" />
</swiper-item>
</swiper>
<view class="detail">
<view class="price">
<view class="now">
¥<text>{{ storeInfo.price }}</text>
</view>
<view class="origin">原价 ¥{{ storeInfo.vip_price }}</view>
</view>
<view class="share-icon" @click="handleCreateSharePoster">
<view class="iconfont">
<image src="@/static/images/store/share.png"></image>
</view>
<view>分享</view>
</view>
<view class="collect-icon" @click="collect">
<view :class="[
'iconfont',
storeInfo.collect ? 'iconshoucang2' : 'iconshoucang11',
]"></view>
<view>{{ storeInfo.collect ? "已收藏" : "收藏" }}</view>
</view>
<view class="title">{{ storeInfo.store_name }}</view>
<view class="other">
<view>销量 {{ storeInfo.sales }}</view>
<view>库存 {{ storeInfo.stock }}</view>
</view>
</view>
<!-- 相关讲师 -->
<RelatedLecturer v-if="lecturer" :lecturer="lecturer" />
<view class="second">
<view class="tab">
<view :class="{ on: tabOn == 1 }" @click="tabOn = 1">详情</view>
<view :class="{ on: tabOn == 2 }" @click="tabOn = 2">评价({{ whole }})</view>
<view :class="{ on: tabOn == 3 }" @click="tabOn = 3">
赠送课程({{ specialList.length }})
</view>
</view>
<view class="content">
<mp-html v-if="tabOn === 1" class="detail" container-style="padding: 30rpx;background: #ffffff;"
:content="storeInfo.description"></mp-html>
<view v-if="tabOn == 2" class="evaluate-section">
<view class="head">
<view class="score">
<view>
评分<text v-for="star in 5" :key="star" :class="{ on: star <= rate }" class="iconfont iconxing"></text>
</view>
<view>{{ positive_rate }}%<text>好评率</text></view>
</view>
<view class="cate">
<view :class="{ on: score === 4 }" @click="cateTab(4)">
全部({{ whole }})
</view>
<view :class="{ on: score === 3 }" @click="cateTab(3)">
好评({{ praise }})
</view>
<view :class="{ on: score === 2 }" @click="cateTab(2)">
中评({{ review }})
</view>
<view :class="{ on: score === 1 }" @click="cateTab(1)">
差评({{ difference }})
</view>
</view>
</view>
<!-- 评价列表 -->
<EvaluateList :evaluate-list="evaluateList" />
<view v-if="loading" class="loading">
<i class="fa fa-spinner fa-spin"></i>
</view>
<view v-if="finished && evaluateList.length" class="finished">已全部加载完</view>
<view v-if="finished && !evaluateList.length" class="empty">
<image :src="getImgPath('/wap/first/zsff/images/empty.png')" alt="暂无评价" />
<view>暂无评价</view>
</view>
</view>
<view v-show="tabOn == 3" class="special">
<navigator v-for="item in specialList" :key="item.id" :url="item.path">
<view class="image">
<image :src="item.image" />
<view>{{ item.typeName }}</view>
</view>
<view class="text">
<view class="title">{{ item.title }}</view>
<view class="label">
<view v-for="label in item.label">{{ label }}</view>
</view>
<view class="price">
¥<text>{{ item.money }}</text>
</view>
</view>
</navigator>
<view style="height:250rpx; text-align:center; margin-top:30rpx;color:#999999;">我也是有底线的</view>
</view>
</view>
</view>
<view class="third">
<view class="group flex">
<view class="btn flex flex-center-y flex-column" @click="goPage(1)">
<image :src="getImgPath('/wap/first/zsff/images/home.png')" />
<view class="btn-name">首页</view>
</view>
<view class="btn flex flex-center-y flex-column" @click="goPage(2)">
<image :src="getImgPath('/wap/first/zsff/images/chat.png')" />
<view class="btn-name">客服</view>
</view>
<view class="btn flex flex-center-y flex-column" @click="goPage(3)">
<image src="@/static/images/store/cart-icon.png" />
<view class="btn-name">购物车</view>
</view>
</view>
<view class="submit-btn flex flex-center" @click="joinCart">加入购物车</view>
<view class="submit-btn flex flex-center" @click="goBuy">立即购买</view>
</view>
<!-- 分享返佣 -->
<RebateGuide v-if="rebateMoney && isShareDisplaySwitch" :rebate-money="rebateMoney" @rebate-action="rebateAction" />
<TkiQrcode loadMake v-if="isQrcodeCanvasVisable" ref="qrcode" :showLoading="false" :val="qrcodeText"
@result="handleQrcodeCreateSuccess" />
<canvas canvas-id="poster" v-if="isPosterCanvasVisable" class="poster-canvas" />
</template>
</BaseContainer>
</template>
<script>
import userInfoMixins from "@/mixins/userInfo";
import {
getAssociatedTopics,
getReplyData,
getReplyList,
getStoreDetail,
createOrder,
} from "@/api/store";
import { getLecturer, getRebateAmount } from "@/api/auth";
import RebateGuide from "@/components/RebateGuide/index.vue";
import EvaluateList from "@/components/EvaluateList/index.vue";
import RelatedLecturer from "@/components/RelatedLecturer/index.vue";
import mpHtml from "mp-html/dist/uni-app/components/mp-html/mp-html.vue";
import posterMixin from "@/mixins/poster";
import { CUSTOMER_DETAIL_TYPE, CUSTOMER_SUPPORT_TYPE } from "@/constants/customer-type";
export default {
mixins: [userInfoMixins, posterMixin],
components: {
EvaluateList,
RebateGuide,
RelatedLecturer,
mpHtml,
},
data() {
const { screenWidth } = this.$util.getSystemInfo();
return {
poster_image: "",
id: 0,
storeInfo: {},
width: screenWidth,
loginShow: false,
isWeChat: false,
tabOn: 1,
score: 4,
page: 1,
limit: 16,
loading: false,
finished: false,
evaluateList: [],
rate: 5,
positive_rate: "0",
whole: 0,
praise: 0,
review: 0,
difference: 0,
site_url: "",
siteName: "",
rebateMoney: 0,
page2: 1,
specialList: [],
lecturer: null,
isShareDisplaySwitch: false, // 是否显示分享返佣
};
},
computed: {
url() {
return this.isWeChat ? "/pages/index/login" : "/pages/login/phone_check";
},
shareQrcodeData() {
return {
spread_uid: this.userInfo?.uid,
id: this.id,
};
},
},
created() {
//#ifdef MP-WEIXIN
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
});
//#endif
},
// 分享朋友
onShareAppMessage() {
return { imageUrl: this.storeInfo.image, title: this.storeInfo.store_name, path: '/pages/store/detail?id=' + this.id+'&spread_uid='+(this.userInfo.uid??0) }
},
onShareTimeline() {
return { imageUrl: this.storeInfo.image, title: this.storeInfo.store_name, };
},
onLoad(options) {
const { id } = this.$util.mergeLaunchParams(options);
this.id = id;
this.getInitData().then(() => {
this.getLecturer();
});
this.getEvaluateStatus();
this.getEvaluateList();
this.rebateAmount();
this.getAssociatedTopics();
},
onReachBottom() {
if (this.tabOn === 2) {
this.getEvaluateList();
}
},
methods: {
getInitData() {
return getStoreDetail(this.id).then(({ data }) => {
this.storeInfo = data.storeInfo;
this.isShareDisplaySwitch = data.isShareDisplaySwitch;
this.site_url = data.site_url
});
},
goPage(value) {
switch (value) {
case 1:
uni.switchTab({
url: "/pages/index/index",
});
break;
case 2:
this.$util.goSupport(CUSTOMER_DETAIL_TYPE.GOOD, CUSTOMER_SUPPORT_TYPE.STORE_GOOD, this.id, this.storeInfo.mer_id);
break;
}
},
// 立即购买
goBuy() {
this.$util.checkLogin(async () => {
try {
const { code, data, msg } = await createOrder({
productId: this.storeInfo.id,
cartNum: 1,
});
if (code === 200) {
uni.navigateTo({
url: "/pages/special/confirm_order?cartId=" + data.cartId,
});
} else {
this.$util.showMsg(msg);
}
} catch (err) {
this.$util.showMsg(err.msg);
}
}, true);
},
joinCart() {
// this.$util.checkLogin();
},
//所有插件回调处理事件
changeVal(opt) {
if (typeof opt != "object") opt = {};
var action = opt.action || "";
var value = opt.value || "";
this[action] && this[action](value);
},
// 获取评价列表
getEvaluateList() {
if (this.finished) {
return false;
}
getReplyList({
productId: this.id,
score: this.score,
page: this.page++,
limit: this.limit,
}).then(({ data }) => {
this.evaluateList = this.evaluateList.concat(data);
this.finished = this.limit > data.length;
});
},
// 获取各状态评价数量
getEvaluateStatus() {
getReplyData(this.id).then(({ data }) => {
this.rate = data.star;
this.positive_rate = data.positive_rate;
this.whole = data.whole;
this.praise = data.praise;
this.review = data.review;
this.difference = data.difference;
});
},
// 评价列表状态切换
cateTab(score) {
if (this.score === score) {
return;
}
this.score = score;
this.evaluateList = [];
this.loading = false;
this.finished = false;
this.page = 1;
this.getEvaluateList();
},
handleCreateSharePoster() {
this.poster_image = this.storeInfo.image;
this.createSharePoster();
},
// 获取返佣金额
rebateAmount() {
getRebateAmount(this.id, 2).then(({ data }) => {
this.rebateMoney = Number(data.brokeragePrice);
});
},
rebateAction(value) {
switch (value) {
case "close":
this.rebateMoney = 0;
break;
case "share":
this.handleCreateSharePoster();
break;
}
},
// 获取关联专题
getAssociatedTopics() {
getAssociatedTopics({
page: this.page2,
list: 100,
id: this.id,
}).then(({ data }) => {
data.forEach((item) => {
let path = "/pages/special/details";
let typeName = "图文";
if (item.is_light) {
path = "/pages/special/single_details";
}
if (item.type === 2 || item.light_type === 2) {
typeName = "音频";
} else if (item.type === 3 || item.light_type === 3) {
typeName = "视频";
} else if (item.type === 4) {
typeName = "直播";
} else if (item.type === 5) {
typeName = "专栏";
}
item.path = path + "?id=" + item.id;
item.typeName = typeName;
});
this.specialList = data;
});
},
// 相关讲师
getLecturer() {
getLecturer(this.storeInfo.mer_id).then(({ data }) => {
this.lecturer = data;
});
},
// 点击收藏
collect() {
this.$util.checkLogin(() => {
// collectSpecial(this.id)
// .then(() => {
// this.storeInfo.collect = !this.storeInfo.collect;
// this.$util.showMsg(
// this.storeInfo.collect ? "收藏成功" : "取消收藏成功"
// );
// })
// .catch((err) => {
// console.log(err);
// });
}, true);
},
},
};
</script>
<style scoped lang="scss">
/* 商品详情 */
.goods-detail {
background: #f6f6f6;
padding-bottom: calc(100px + var(--safe-bottom));
}
.goods-detail .swiper-container {
background: #fff;
}
.goods-detail .swiper-container image {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
.goods-detail .detail {
padding: 40rpx 28rpx 40rpx 30rpx;
width: 690rpx;
margin: 20rpx auto 0;
background: #fff;
position: relative;
.share-icon, .collect-icon {
position: absolute;
top: 43rpx;
right: 28rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
font-size: 20rpx;
line-height: 20rpx;
height: 70rpx;
color: #666;
.iconfont.iconshoucang2 {
color: #ff6b00 !important;
}
.iconfont {
font-size: 44rpx;
line-height: 44rpx;
image {
width: 44rpx;
height: 44rpx;
}
}
}
.share-icon {
right: 96rpx;
}
}
.goods-detail .price {
font-size: 0;
}
.goods-detail .now {
font-weight: bold;
font-size: 22rpx;
line-height: 25rpx;
color: #ff6b00;
}
.goods-detail .now text {
font-size: 32rpx;
}
.goods-detail .origin {
font-size: 24rpx;
line-height: 24rpx;
color: #333333;
font-weight: 600;
margin: 18rpx 0 0 0;
text-decoration: line-through;
}
.goods-detail .title {
margin-top: 34rpx;
color: #333333;
font-size: 28rpx;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #0A1547;
line-height: 40rpx;
}
.goods-detail .other {
display: flex;
margin-top: 20rpx;
font-size: 24rpx;
line-height: 33rpx;
text-align: center;
color: #999;
>view:last-child {
margin-left: 40rpx;
}
}
.goods-detail .third {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 54;
display: flex;
align-items: center;
padding-right: 25rpx;
height: 130rpx;
border-top: 1px solid #eeeeee;
background-color: #ffffff;
}
.goods-detail .third .group .btn {
margin-left: 40rpx;
font-size: 24rpx;
line-height: 24rpx;
color: #666;
display: flex;
flex-direction: column;
align-items: center;
.btn-name {
margin-top: 14rpx;
}
}
.goods-detail .third .group image {
width: 44rpx;
height: 44rpx;
vertical-align: middle;
}
.goods-detail .third .submit-btn {
width: 190rpx;
font-size: 30rpx;
color: #ffffff;
font-weight: 600;
height: 80rpx;
line-height: 80rpx;
background: #1D8DFF;
border-radius: 40rpx;
background: linear-gradient(90deg, #FFA800 0%, #FF9C00 100%);
margin-left: auto;
&:last-child {
background: linear-gradient(90deg, #FF6C00 0%, #FF3C00 100%);
margin-left: 11rpx;
}
}
.goods-detail .second {
border-top: 14rpx solid #f5f5f5;
background: #fff;
}
.goods-detail .second .tab {
display: flex;
justify-content: space-around;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.03);
}
.goods-detail .second .tab view {
position: relative;
height: 96rpx;
padding: 0 30rpx;
font-size: 32rpx;
line-height: 96rpx;
color: #999999;
cursor: pointer;
}
.goods-detail .second .tab .on {
font-weight: 600;
color: #23272E;
}
.goods-detail .second .tab .on::after {
content: "";
position: absolute;
bottom: 0;
left: 50%;
z-index: 2;
width: 40rpx;
height: 8rpx;
border-radius: 10rpx;
background-color: #2c8eff;
transform: translateX(-50%);
}
.goods-detail .second .detail image {
width: 100%;
}
.goods-detail .evaluate-section .head {
padding: 30rpx;
border-bottom: 1px solid #f1f1f1;
}
.goods-detail .evaluate-section .score {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #ff6b00;
}
.goods-detail .evaluate-section .score .iconfont {
margin-left: 10rpx;
font-size: 24rpx;
color: #ccc;
}
.goods-detail .evaluate-section .score .iconfont.on {
color: #ff6b00;
}
.goods-detail .evaluate-section .score .iconfont:first-child {
margin-left: 15rpx;
}
.goods-detail .evaluate-section .score view:last-child text {
color: #808080;
}
.goods-detail .evaluate-section .cate {
display: flex;
margin-top: 30rpx;
}
.goods-detail .evaluate-section .cate view {
height: 54rpx;
padding-right: 22rpx;
padding-left: 22rpx;
border-radius: 6rpx;
background-color: #f4f4f4;
font-size: 24rpx;
line-height: 54rpx;
color: #282828;
cursor: pointer;
}
.goods-detail .evaluate-section .cate view view {
margin-left: 16rpx;
}
.goods-detail .evaluate-section .cate view.on {
background-color: #2c8eff;
color: #ffffff;
}
.goods-detail .second .special navigator {
display: flex;
padding: 15rpx 30rpx;
}
.goods-detail .second .special navigator:first-child {
padding-top: 30rpx;
}
.goods-detail .second .special navigator:last-child {
padding-bottom: 30rpx;
}
.goods-detail .second .special .image {
position: relative;
width: 250rpx;
height: 140rpx;
}
.goods-detail .second .special .image image {
width: 100%;
height: 100%;
border-radius: 10rpx;
object-fit: cover;
}
.goods-detail .second .special .image view {
position: absolute;
right: 10rpx;
bottom: 10rpx;
z-index: 2;
height: 34rpx;
padding: 0 11rpx;
border-radius: 3rpx;
background-color: rgba(0, 0, 0, 0.5);
font-size: 22rpx;
line-height: 34rpx;
color: #ffffff;
}
.goods-detail .second .special .text {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
padding-left: 20rpx;
}
.goods-detail .second .special .title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 30rpx;
line-height: 42rpx;
color: #333333;
}
.goods-detail .special .label {
flex: 1;
padding-top: 8rpx;
font-size: 0;
}
.goods-detail .special .label view {
display: inline-block;
height: 38rpx;
padding: 0 12rpx;
border-radius: 4rpx;
background-color: rgba(44, 142, 255, 0.06);
margin-left: 10rpx;
font-size: 20rpx;
line-height: 38rpx;
color: #2c8eff;
}
.goods-detail .special .label view:first-child {
margin-left: 0;
}
.goods-detail .special .price {
font-size: 24rpx;
line-height: 45rpx;
color: #ff6b00;
}
.goods-detail .special .price text {
font-size: 32rpx;
}
.goods-detail .share {
margin-right: 20rpx;
z-index: 2;
width: 54rpx;
height: 54rpx;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.5);
text-align: center;
line-height: 54rpx;
cursor: pointer;
}
.goods-detail .share .iconfont {
font-size: 28rpx;
color: #ffffff;
}
.goods-detail .finished,
.goods-detail .loading {
font-size: 28rpx;
line-height: 100rpx;
text-align: center;
color: #bbb;
}
.goods-detail .fa-spinner {
animation: fa-spin 1s infinite linear;
}
.goods-detail .empty {
margin-top: 50rpx;
margin-bottom: 50rpx;
font-size: 28rpx;
text-align: center;
color: #bbb;
}
.goods-detail .empty image {
display: block;
width: 414rpx;
height: 305rpx;
margin: 0 auto;
}
.poster-canvas {
width: 600px;
height: 960px;
}
</style>