Files
lucky_shop/pages_promotion/point/detail.vue
ZF sun f794a2ae27 refactor: 统一替换登录跳转逻辑为通用方法
将项目内多处手动调用$refs.login.open的登录跳转逻辑,统一替换为新增的redirectTologin通用方法,简化代码并提升维护性,同时统一处理了延迟和nextTick调用逻辑。
2026-06-27 16:52:25 +08:00

526 lines
18 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>
<view :style="themeColor">
<view scroll-y="true" class="goods-detail" :class="isIphoneX ? 'active' : ''">
<view class="goods-container">
<view class="goods-media">
<!-- #ifdef H5 -->
<cover-view class="share">
<view class="share_left"><text class="iconfont icon-back_light" @click="$util.goBack()"></text>
</view>
</cover-view>
<!-- #endif -->
<view class="goods-img show">
<swiper class="swiper">
<swiper-item :item-id="'goods_id_' + pointInfo.type">
<view class="item" v-if="pointInfo.type == 2">
<image class="adv-pic"
:src="pointInfo.image ? $util.img(pointInfo.image) : $util.img('public/uniapp/point/coupon.png')"
@error="imageError($util.img('public/uniapp/point/coupon.png'))"
mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="item" v-else-if="pointInfo.type == 3">
<image class="adv-pic"
:src="pointInfo.image ? $util.img(pointInfo.image) : $util.img('public/uniapp/point/hongbao.png')"
@error="imageError($util.img('public/uniapp/point/hongbao.png'))"
mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="item" v-else>
<image class="adv-pic" :src="$util.img(pointInfo.image)" @error="imageError()"
mode="aspectFit" show-menu-by-longpress></image>
</view>
</swiper-item>
</swiper>
</view>
</view>
<view class="group-wrap padding-top">
<view class="goods-module-wrap">
<text class="price-symbol ">{{ pointInfo.point }}{{ $lang('detail.pointsSuffix') }}</text>
<template v-if="pointInfo.exchange_price != '0.00' && pointInfo.pay_type > 0">
<text class="price-symbol ">+{{ $lang('common.currencySymbol') }}{{ pointInfo.exchange_price
}}</text>
</template>
<view class="market-price-wrap" v-if="pointInfo.price">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
<text class="money">{{ pointInfo.price }}</text>
</view>
<view class="follow-and-share"
v-if="pointInfo.stock_show == 1 || pointInfo.stock_show == undefined">
<text class="color-tip" v-if="pointInfo.pointCoupon != 1">{{ $lang('detail.stockLabel') }}{{ pointInfo.stock }}{{
pointInfo.stock >= 0 ?
pointInfo.unit : '' }}</text>
<text class="color-tip" v-else>{{ $lang('detail.stockLabel') }}{{ $lang('detail.unlimitedStock') }}{{ pointInfo.stock >= 0 ? pointInfo.unit : '' }}</text>
</view>
</view>
<view class="goods-module-wrap info">
<text class="sku-name-wrap" v-if="pointInfo.type == 1">{{ isEnEnv ? pointInfo.en_goods_name :
pointInfo.goods_name
}}</text>
<text class="sku-name-wrap" v-else>{{ pointInfo.name }}</text>
</view>
<view class="coupon-desc">
<view v-if="pointInfo.balance && pointInfo.balance > 0" class="color-tip">{{ $lang('detail.containsBalance', { balance: pointInfo.balance }) }}</view>
<view v-if="pointInfo.coupon_type == 'random'" class="color-tip">{{ $lang('detail.noThresholdCoupon') }}</view>
<view v-if="pointInfo.coupon_type == 'reward'" class="color-tip">{{ $lang('detail.fullReduction', { atLeast: pointInfo.at_least, money: pointInfo.money }) }}</view>
<view v-if="pointInfo.coupon_type == 'discount'" class="color-tip">
{{ $lang('detail.fullAmount', { atLeast: pointInfo.at_least }) }}
<text>{{ pointInfo.discount }}{{ $lang('detail.discountSuffix') }}</text>
</view>
<view v-if="pointInfo.coupon_type == 'discount'" class="color-tip">{{ $lang('detail.maxDiscount', { limit: pointInfo.discount_limit }) }}</view>
<view class="time color-tip" v-if="pointInfo.coupon_type">
{{ pointInfo.validity_type == 1 ? $lang('detail.validFromReceive', { days: pointInfo.fixed_term }) :
$lang('detail.validUntil', { time: $util.timeStampTurnTime(pointInfo.end_time) }) }}
</view>
</view>
</view>
<!-- 添加二维码 start -->
<view class="detail-community" v-if="pointInfo.qr_data && pointInfo.qr_data.qr_state == 1">
<view class="community-box">
<image :src="$util.img('public/uniapp/goods/detail_erweiImage.png')" mode="aspectFill" show-menu-by-longpress></image>
<view class="community-content">
<view class="community-title">{{ pointInfo.qr_data.qr_name }}</view>
<view class="community-txt">{{ pointInfo.qr_data.community_describe }}</view>
</view>
</view>
<view class="community-btn" @click="onCommunity()">{{ $lang('detail.addCommunity') }}</view>
</view>
<!-- 促销 -->
<view class="community-model" @touchmove.prevent.stop @click.stop="onCloseCommunity()"
v-show="isCommunity">
<view class="community-model-content" @click.stop>
<view class="community-model-content-radius">
<view>{{ $lang('detail.addCommunityTitle') }}</view>
</view>
<view class="community-model-content-draw" v-if="pointInfo.qr_data && pointInfo.qr_data.qr_img">
<image :src="pointInfo.qr_data.qr_img != '' && pointInfo.qr_data.qr_state == 1
? $util.img(pointInfo.qr_data.qr_img)
: $util.img('public/uniapp/goods/detail_erweiImage.png')
" mode="aspectFill" show-menu-by-longpress />
</view>
<view class="community-model-content-text">{{ $lang('detail.addCommunityTip') }}</view>
</view>
<view class="community-model-close" @click.stop="onCloseCommunity()"><text
class="iconfont icon-close"></text>
</view>
</view>
<!-- 添加二维码 end -->
<block v-if="pointInfo.type == 1">
<view class="newdetail margin-bottom">
<!-- 已选规格 -->
<view class="item selected-sku-spec" v-if="pointInfo.sku_spec_format" @click="exchange">
<view class="label">{{ $lang('detail.selectSpec') }}</view>
<view class="box">
<text v-for="(item, index) in pointInfo.sku_spec_format" :key="index">{{ item.spec_name
}}/{{
item.spec_value_name }}</text>
</view>
<text class="iconfont icon-right"></text>
</view>
<view class="item goods-attribute" @click="openAttributePopup()"
v-if="pointInfo.goods_attr_format && pointInfo.goods_attr_format.length > 0">
<view class="label">{{ $lang('detail.attribute') }}</view>
<view class="box">
<text v-for="(item, index) in pointInfo.goods_attr_format" :key="index"
v-if="index < 2">{{
item.attr_name }}: {{ item.attr_value_name }}</text>
</view>
<text class="iconfont icon-right"></text>
</view>
</view>
<!-- 商品属性 -->
<view @touchmove.prevent.stop>
<uni-popup ref="attributePopup" type="bottom">
<view class="goods-attribute-popup-layer popup-layer">
<view class="head-wrap" @click="closeAttributePopup()">
<text>{{ $lang('detail.goodsAttribute') }}</text>
<text class="iconfont icon-close"></text>
</view>
<scroll-view scroll-y class="goods-attribute-body">
<view class="item" v-for="(item, index) in pointInfo.goods_attr_format"
:key="index">
<text class="attr-name">{{ item.attr_name }}</text>
<text class="value-name">{{ item.attr_value_name }}</text>
</view>
</scroll-view>
<view class="button-box"><button type="primary"
@click="closeAttributePopup()">{{ $lang('detail.confirm') }}</button></view>
</view>
</uni-popup>
</view>
<!-- 商品服务 -->
<view @touchmove.prevent.stop>
<uni-popup ref="merchantsServicePopup" type="bottom">
<view class="goods-merchants-service-popup-layer popup-layer">
<view class="head-wrap" @click="closeMerchantsServicePopup()">
<text>{{ $lang('detail.goodsService') }}</text>
<text class="iconfont icon-close"></text>
</view>
<scroll-view scroll-y>
<view class="item" :class="{ 'empty-desc': !item.desc }"
v-for="(item, index) in pointInfo.goods_service" :key="index">
<view class="iconfont icon-dui "></view>
<view class="info-wrap">
<text class="title">{{ item.service_name }}</text>
<text class="describe" v-if="item.desc">{{ item.desc }}</text>
</view>
</view>
</scroll-view>
<view class="button-box"><button type="primary"
@click="closeMerchantsServicePopup()">{{ $lang('detail.confirm') }}</button></view>
</view>
</uni-popup>
</view>
</block>
<!-- 详情 -->
<view class="goods-detail-tab">
<view class="detail-tab">
<view class="tab-item">{{ $lang('detail.exchangeDetail') }}</view>
</view>
<view class="detail-content">
<view class="detail-content-item">
<view class="goods-details" v-if="pointInfo.content"><rich-text
:nodes="pointInfo.content"></rich-text>
</view>
<view class="goods-details active" v-else>{{ $lang('detail.noExchangeDetail') }}</view>
</view>
</view>
</view>
<!-- SKU选择 -->
<ns-goods-sku v-if="pointInfo.id" ref="goodsSku" @refresh="refreshGoodsSkuDetail"
:goods-detail="pointInfo" :goods-id="pointInfo.goods_id" :max-buy="pointInfo.max_buy"
:min-buy="pointInfo.min_buy" source="point"></ns-goods-sku>
</view>
</view>
<view class="detail-swap" @click="exchange()" :class="{ 'position-bottom': isIphoneX }" v-if="!isLogin"><button
type="primary">{{ $lang('detail.loginToExchange') }}</button></view>
<block v-else>
<view class="detail-swap" :class="{ 'position-bottom': isIphoneX }" v-if="pointInfo.stock == 0"><button
disabled>{{ $lang('detail.insufficientStock') }}</button></view>
<view class="detail-swap" :class="{ 'position-bottom': isIphoneX }" v-else-if="enough"><button
disabled>{{ $lang('detail.insufficientPoints') }}</button></view>
<view class="detail-swap" @click="exchange()" :class="{ 'position-bottom': isIphoneX }" v-else><button
type="primary" hover-class="none">{{ $lang('detail.exchange') }}</button></view>
</block>
<loading-cover ref="loadingCover"></loading-cover>
<ns-login ref="login"></ns-login>
<to-top v-if="showTop" @toTop="scrollToTopNative()"></to-top>
<!-- 隐私弹窗 -->
<privacy-popup ref="privacyPopup"></privacy-popup>
</view>
</template>
<script>
import htmlParser from '@/common/js/html-parser';
import scroll from '@/common/js/scroll-view.js';
export default {
data() {
return {
id: 0,
pointInfo: {
image: '',
pointCoupon: 0
},
isIphoneX: false, //判断手机是否是iphoneX以上
isLogin: false,
memberPoint: 0,
isCommunity: false, //社群弹窗
//分享时详情所用图片
shareImg: ''
};
},
mixins: [scroll],
onLoad(options) {
//小程序分享接收source_member
if (options.source_member) {
uni.setStorageSync('source_member', options.source_member);
}
// 小程序扫码进入,接收source_member
if (options.scene) {
var sceneParams = decodeURIComponent(options.scene);
sceneParams = sceneParams.split('&');
if (sceneParams.length) {
sceneParams.forEach(item => {
if (item.indexOf('sku_id') != -1) this.skuId = item.split('-')[1];
if (item.indexOf('m') != -1) uni.setStorageSync('source_member', item.split('-')[1]);
if (item.indexOf('is_test') != -1) uni.setStorageSync('is_test', 1);
});
}
}
this.isIphoneX = this.$util.uniappIsIPhoneX();
if (options.id) {
this.id = options.id;
this.getPointInfo();
} else {
this.$util.redirectTo('/pages_promotion/point/list', {}, 'redirectTo');
}
},
onShow() {
//记录分享关系
if (this.storeToken && uni.getStorageSync('source_member')) {
this.$util.onSourceMember(uni.getStorageSync('source_member'));
}
},
/**
* 转发分享
*/
onShareAppMessage(res) {
var title = '';
var imageUrl = '';
switch (this.pointInfo.type) {
case 1: //商品
title = this.pointInfo.sku_name;
imageUrl = this.pointInfo.sku_image;
break;
case 2: //优惠券
case 3: //红包
title = this.pointInfo.name;
imageUrl = this.pointInfo.image;
break;
}
title = this.$lang('detail.shareTitle', { point: this.pointInfo.point, title: title });
imageUrl = this.$util.img(imageUrl);
var route = this.$util.getCurrentShareRoute(this.memberInfo ? this.memberInfo.member_id : 0);
var path = route.path;
return {
title: title,
path: path,
imageUrl: imageUrl,
success: res => { },
fail: res => { }
};
},
// 分享到微信朋友圈
// #ifdef MP-WEIXIN
onShareTimeline() {
var title = '';
var imageUrl = '';
switch (this.pointInfo.type) {
case 1: //商品
title = this.pointInfo.sku_name;
imageUrl = this.pointInfo.sku_image;
break;
case 2: //优惠券
case 3: //红包
title = this.pointInfo.name;
imageUrl = this.pointInfo.image;
break;
}
title = this.$lang('detail.shareTitle', { point: this.pointInfo.point, title: title });
imageUrl = this.$util.img(imageUrl);
var route = this.$util.getCurrentShareRoute(this.memberInfo ? this.memberInfo.member_id : 0);
var query = route.query;
return {
title: title,
query: query,
imageUrl: imageUrl
};
},
// #endif
computed: {
enough() {
return parseInt(this.pointInfo.point) > parseInt(this.memberPoint);
}
},
watch: {
storeToken: function (nVal, oVal) {
if (nVal) {
this.isLogin = true;
this.getPointInfo();
}
}
},
methods: {
//获取个人积分信息
getAccountInfo(e, f) {
if (this.storeToken) {
this.$api.sendRequest({
url: '/api/memberaccount/info',
data: {
account_type: 'point'
},
success: res => {
if (res.code == 0 && res.data) {
this.isLogin = true;
this.memberPoint = res.data.point;
let point_num = Math.floor(parseInt(res.data.point) / f);
this.Max = e >= point_num ? point_num : e;
} else {
this.$util.showToast({
title: res.message
});
}
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
}
});
} else {
this.isLogin = false;
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
}
},
//获取详情
getPointInfo() {
this.$api.sendRequest({
url: '/pointexchange/api/goods/detail',
data: {
id: this.id
},
success: res => {
if (res.code >= 0 && res.data.length !== 0) {
this.pointInfo = res.data;
//获取分享图片
if (this.pointInfo.image) {
this.shareImg = this.$util.img(this.pointInfo.image);
} else {
this.shareImg = this.$util.img('public/uniapp/blindbox/default.png');
}
this.$langConfig.title(this.pointInfo.name);
let save = this.pointInfo.type == 2 ? this.pointInfo.count : this.pointInfo.stock;
if (this.pointInfo.type == 2 && this.pointInfo.stock == -1) {
this.pointInfo.pointCoupon = 1;
}
if (this.pointInfo.type == 1) {
this.pointInfo.image = this.pointInfo['sku_image'];
// 当前商品SKU规格
if (this.pointInfo.sku_spec_format) this.pointInfo.sku_spec_format = JSON.parse(this.pointInfo.sku_spec_format);
// 商品属性
if (this.pointInfo.goods_attr_format) {
let goods_attr_format = JSON.parse(this.pointInfo.goods_attr_format);
this.pointInfo.goods_attr_format = this.$util.unique(goods_attr_format, 'attr_id');
for (var i = 0; i < this.pointInfo.goods_attr_format.length; i++) {
for (var j = 0; j < goods_attr_format.length; j++) {
if (
this.pointInfo.goods_attr_format[i].attr_id == goods_attr_format[j].attr_id &&
this.pointInfo.goods_attr_format[i].attr_value_id != goods_attr_format[j].attr_value_id
) {
this.pointInfo.goods_attr_format[i].attr_value_name += '、' + goods_attr_format[j].attr_value_name;
}
}
}
}
if (this.pointInfo.goods_spec_format) this.pointInfo.goods_spec_format = JSON.parse(this.pointInfo.goods_spec_format);
}
this.pointInfo.unit = this.pointInfo.unit || this.$lang('detail.unitPiece');
// 商品详情
if (this.pointInfo.content) this.pointInfo.content = htmlParser(this.pointInfo.content);
this.getAccountInfo(save, this.pointInfo.point);
} else {
this.$util.showToast({
title: res.message
});
setTimeout(() => {
this.$util.redirectTo('/pages_promotion/point/list', {}, 'redirectTo');
}, 1000);
}
}
});
},
// 立即购买
exchange() {
if (!this.storeToken) {
this.redirectTologin('/pages_promotion/point/detail?id=' + this.id);
return;
}
this.$refs.goodsSku.show('point');
},
/**
* 刷新商品详情数据
* @param {Object} pointInfo
*/
refreshGoodsSkuDetail(pointInfo) {
Object.assign(this.pointInfo, pointInfo);
this.pointInfo.unit = this.pointInfo.unit || this.$lang('detail.unitPiece');
},
//服务
openMerchantsServicePopup() {
this.$refs.merchantsServicePopup.open();
},
closeMerchantsServicePopup() {
this.$refs.merchantsServicePopup.close();
},
//属性
openAttributePopup() {
this.$refs.attributePopup.open();
},
closeAttributePopup() {
this.$refs.attributePopup.close();
},
imageError() {
this.pointInfo.image = this.$util.getDefaultImage().goods;
this.$forceUpdate();
},
//添加福利群
onCommunity() {
this.isCommunity = true;
},
onCloseCommunity() {
this.isCommunity = false;
}
}
};
</script>
<style lang="scss">
@import '@/common/css/goods_detail.scss';
.group-wrap .goods-module-wrap.info {
padding-bottom: 10rpx;
}
.group-wrap {
padding-bottom: 20rpx;
}
.detail-swap {
box-sizing: border-box;
background: #ffffff;
position: fixed;
left: 0;
bottom: 0rpx;
width: 100%;
padding: 20rpx 0;
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom)) !important;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom)) !important;
button {
height: 80rpx;
line-height: 80rpx;
border-radius: 10rpx;
&[type='primary'] {
background-color: var(--goods-btn-color);
}
}
}
</style>
<style scoped>
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
max-height: unset !important;
}
</style>