1. 前言
在进行小程序开发过程中,涉及一个业务需求:新人领券。
业务需求具体:
- 在首页中添加有3种不同的弹窗: 立即领取A, 现有优惠券列表B,登陆/注册C ,和提示领券的浮窗A
- 为了不影响用户体验,弹窗A/C都要求一周内最多只能弹出3次,且相邻两次弹窗的时间间隔必须超过3小时,弹窗B要求一周内最多只能弹出1次
- 在用户未登录/未领取到目标优惠券之前,将一直显示浮窗
- 此次领取失败时(等到要关闭遮盖层时进行判断是否此次领取成功)要通知首页,进行自动添加小程序收藏提示add-tip,并于5s后自动消失
- 当用户从我的页面登录后再进来首页时,需重新执行一遍领券逻辑。判断是否弹出
2. 实现思路
- 步骤1:初始化页面中各弹窗以及浮窗的显示,(目标:为了不让上一次调用时显示的布局影响到这一次的显示)
- 步骤2:检查是否登陆(已登陆 -> 步骤3, 未登录 -> 步骤4)
- 步骤3:检查该用户是否有领过新人券
- 步骤4:是否弹窗
- 判断弹出什么类型的弹窗(根据步骤1得到的是否登录以及步骤3得到的用户是否领过新人券决定),
- 是否弹出弹窗(根据现在距离上一次弹窗时间以及总弹窗次数判断),
- 弹窗失败回调:显示浮窗(根据是否领过目标优惠券判断)
- 步骤5:检查是否领取成功,通知首页自动添加小程序收藏提示进行显示
3. 页面布局
view.wrapper >
<view class="counpon_fornew_panel" catchtap="emptyFn">
<view class="showPopA" wx:if="{{showPopA}}">
<view class="showPopA" wx:if="{{!showSuccessPopA}}">
弹窗A:已领取
优惠券已放入我的优惠券列表
</view>
<view class="showSuccessPopA" wx:else>
<button bindtap="collectCoupon">点击领取(进行绑定券操作)</button>
</view>
</view>
<view class="showPopB" wx:if="{{showPopB}}">
弹窗B:展示券列表前3张和券总数
<button bindtap="navigateFn" data-action="/pages/web/web?key=coupon">点击查看更多(跳转到官网优惠券列表)</button>
</view>
<view class="showPopC" wx:if="{{showPopC}}">
<button open-type="getUserInfo" class='login_btn' bindgetuserinfo='userlogin'>
弹窗C:点击登录/注册
</button>
</view>
</view>
<view class="showFloatWindowA" wx:if="{{showFloatWindowA}}">
<button bindtap="collectCoupon">浮窗A:点击领取(进行绑定券操作)</button>
</view>
4. 部分代码
4.1 整体逻辑
// 初始化新人领券
initCouponForNewUser: function() {
// 初始化页面中各弹窗以及浮窗的显示
this.initCouponWrapper();
// 检查是否登录
app.globalData.userInfoPromise.then(() => {
console.log('登录成功....');
this.setData({
isLogin: true
});
// 判断是否有领过新人券,如领过,加载用户有效优惠券列表,如未领过,加载新人券具体详情
this.loadCouponUserAvailAble().then(()=>{
// 判断是否弹出新人领券弹窗
this.loadCouponForNewUser();
})
},(err) => {
console.log('登录失败....');
this.setData({
isLogin:false
});
// 获取新人券信息
this.getTargetCouponPromise().then(()=>{
// 判断是否弹出新人领券弹窗
this.loadCouponForNewUser();
})
});
},
// 初始化页面中各弹窗以及浮窗的显示
initCouponWrapper(){
this.setData({
isShowGetCouponWrapper: false,
showPopA: false,
showPopB: false,
showPopC: false,
showFloatWindowA: false,
isShowAddTipWrapper: false
});
},
4.2 判断弹出什么类型的弹窗
由于未登录,未领取,已领取三种状态下展示的弹窗不同,我们这里根据条件作出判断:
-
当有登录且有领过新人券:弹出用户优惠券列表弹窗
-
当有登录但无领过新人券:弹出领新人券弹窗
-
当无登录:弹出登录/注册弹窗
// 是否显示新人领券
loadCouponForNewUser: function(){
this.couponpopA = wx.getStorageSync('M60_COUPONPOP_A');
this.couponpopB = wx.getStorageSync('M60_COUPONPOP_B');
this.couponpopC = wx.getStorageSync('M60_COUPONPOP_C');
this.now = Date.now();
// 有登陆并且领取过
if (this.data.isLogin && this.data.hasCouponForNew) {
this.showCouponPop(this.couponpopB, "B", 1);
return;
}
// 有登陆没有领取过
if (this.data.isLogin && !this.data.hasCouponForNew) {
this.showCouponPop(this.couponpopA, "A", 3);
return;
}
this.showCouponPop(this.couponpopC, "C", 3);
},
4.3 判断是否弹出弹窗
由于需根据上一次弹窗的时间,以及最近一周弹窗的次数,来判断是否弹窗,所以我们每一次弹窗时,就把弹窗时间记录在storage中,以数组的形式保存(方便记录多个数据),弹窗缓存长度不会超过(即<=)时间段内最大弹出次数:
当该弹窗没弹出过:即弹窗缓存长度为0(空字符串长度也为0),设置缓存记录为空数组 -> 弹出,并记录下当前弹出时间
当该弹窗已弹出过:取记录中第一次弹窗时间与当前时间作比较,如若 当前时间- 第一次弹窗时间 > 7 天,就清除缓存记录,设为空数组 -> 弹出, 并记录下当前弹出时间,否则:
当弹窗缓存长度 == 时间段内最大弹出次数:执行弹出失败回调:显示浮窗
当弹窗缓存长度 < 时间段内最大弹出次数:取记录中最后一次弹窗时间与当前时间作比较,如若 当前时间- 最后一次弹窗时间 > 3 小时 -> 弹出, 并记录下当前弹出时间,否则执行弹出失败回调:显示浮窗
showCouponPop: function(couponpopType, strType, maxPopTime) {
let now = this.now;
// 第一次弹窗
if (couponpopType.length == 0) {
couponpopType = [];
this.showCouponPopFn(couponpopType, strType);
return;
}
if (couponpopType.length > maxPopTime) {
return;
}
let firstPop = couponpopType[0];
// 超过7天清除弹窗记录
if (now - firstPop >= 1000 * 60 * 60 * 24 * 7) {
couponpopType = [];
this.showCouponPopFn(couponpopType, strType);
return;
}
// 已经弹出最大允许次数并且没有超过7天
if (couponpopType.length == maxPopTime) {
this.couponPopFail(strType);
return;
}
let lastPop = couponpopType[couponpopType.length - 1];
// 距离最近一次弹窗时间> 3小时
if (now - lastPop >= 1000 * 60 * 60 * 3) {
this.showCouponPopFn(couponpopType, strType);
return;
}
this.couponPopFail(strType);
},
4.4 执行弹出弹窗
显示遮盖层,显示相应的弹窗类型,记录下当前弹窗时间(由于在判断是否弹窗的push到数组中)
/**
*
* @param {Array} couponpopType
* @param {String} strType
*/
showCouponPopFn: function(couponpopType, strType){
let storagePop; // 弹窗对应本地storage_key
let now = this.now; // 当前时间
switch(strType){
case 'A': {
// 先去获取新人券的信息,再显示弹窗
this.setData({
showPopA: true,
showPopC: false
})
storagePop = 'M60_COUPONPOP_A';
break;
}
case 'B': {
// 先去获取有效优惠券列表和总券数,再显示弹窗
if(this.data.couponUserAvailAbleSize > 0){
this.setData({
showPopB: true,
showPopC: false
})
storagePop = 'M60_COUPONPOP_B';
}else{
this.initCouponWrapper();
return;
}
break;
}
case 'C': {
this.setData({
showPopC: true
})
storagePop = 'M60_COUPONPOP_C';
break;
}
}
this.showGetCouponWrapper();
// 记录当前弹窗时间
couponpopType.push(now);
console.log("更改后的弹窗记录时间", couponpopType)
wx.setStorageSync(storagePop, couponpopType);
console.log("更改后的内存",wx.getStorageSync(storagePop))
},
4.5 通知首页显示收藏提示
在领取优惠券方法中添加一个字段:collectSuccess,Boolean类型,判断是否领取成功
当关闭遮盖层时,根据是否领券成功向首页传递不同信息:
默认展示首页的add-tip,当此次领取成功后,不展示首页的add-tip,而是展示遮盖层式的收藏提示:
- 首先根据 collectSuccess 字段判断是否领券成功
- 如若领券成功,再根据当前是否展示优惠券列表判断是此次领券成功还是上次领券成功,(因为当领券成功后在退出小程序前collectSuccess字段一直为true, 此时需判断是此次领券成功还是上次领券成功,如果是上次领券成功 = 即this.data.showPopB 显示优惠券列表为true,否则则为此次领取成功),
- 如若为此次领取成功,显示遮盖层式的收藏提示,不关闭遮盖层(因为从页面结构中可以看到遮盖层式的收藏提示包含在遮盖层中)
- 若为上次领取成功,关闭遮盖层,不显示收藏提示
- 如若领券未成功,关闭遮盖层
- 如若领券成功,再根据当前是否展示优惠券列表判断是此次领券成功还是上次领券成功,(因为当领券成功后在退出小程序前collectSuccess字段一直为true, 此时需判断是此次领券成功还是上次领券成功,如果是上次领券成功 = 即this.data.showPopB 显示优惠券列表为true,否则则为此次领取成功),
- 当不是此次领券成功时,才通知首页开始进行为时5s的自动添加收藏提示的显示
// 关闭遮盖层时,根据是否领券成功向首页传递不同信息
closedGetCouponWrapper() {
if(this.data.collectSuccess){ // 当领券成功后,不展示首页的add-tip,而是展示遮盖层式的收藏提示
// 当遮盖层式的收藏提示没有显示并且不是展示优惠券列表时,显示收藏提示,不关闭弹出层
if(!this.data.isShowAddTipWrapper && !this.data.showPopB){
this.setData({
isShowAddTipWrapper: true,
showPopA: false
})
}else{
// 关闭收藏提示,关闭弹出层
this.setData({
isShowAddTipWrapper: false,
isShowGetCouponWrapper: false
})
wx.showTabBar({});
}
}
// 有领过券了,就不显示浮窗了
else{
this.setData({
isShowGetCouponWrapper: false
})
wx.showTabBar({})
if(!this.data.hasCouponForNew){
this.setData({
showFloatWindowA: true
})
}
this.triggerEvent('showIndexAddTip', true);
}
},
4.6 当用户登录状态改变时,需重新执行一遍是否弹窗逻辑
当用户从我的页面登录后再进来首页时,这里从首页传一个properties:isLogin 字段来判断是否登录,当为登录时,即重新执行一遍业务逻辑,判断是否弹出
properties: {
isLogin:{
type: Boolean,
value: false,
observer(newVal, oldVal){
if(newVal){
this.initCouponForNewUser();
}
}
}
},
5. 总结
- 在决定是否展示弹窗时,记得先清除先前的展示记录
- 可根据具体场景添加编译模式,能更快速定位到错误
如果文章对你有帮助,麻烦点赞哦,一起走花路吧~