TOC
最近在工作中需要在微信小程序中加入一套任务系统,某些操作将会触发一个任务弹窗
整个子任务会比较分散,如果使用小程序的自定义组件系统,可能会导致过于过于偏离 DRY 原则
的代码,在经过一些调研以及和后端的协调之后,最终决定使用一套基于 Request 中间件拦截
+ template WXML 模板
共同组成的方案,由于微信小程序的限制(无法直接操作 DOM) ,所以不可避免的会拥有轻微的侵入性,不过好在这个代价应该还算是可以接受的。
任务弹窗模板部分
首先需要建立我们的任务弹窗模板
/template/missionDialog/index.wxml
<!-- 任务完成消息提示弹窗 -->
<template name="mission">
<view wx:if="{{showMission}}" class="mission-dialog-mask">
<view class="mission-dialog-box">
<image src="/images/ic-mission-bg.png" class="bg" />
<view class="inner">
<view class="top">
<view class="title">— 恭喜您 —</view>
<view class="text">{{missionInfo.title}}</view>
<view class="text">{{missionInfo.desc}}</view>
</view>
<view class="style">
<image src="/images/ic-mission-light.png" class="light" />
<image src="/images/ic-mission-ic.png" class="ic" />
</view>
<view class="btns {{missionInfo.showKnow ? 'double' : ''}}">
<view bind:tap="_handleTapMissionClose" class="btn close">关闭</view>
<view bind:tap="_handleTapMissionKnow" class="btn know" wx:if="{{missionInfo.showKnow}}">
我知道了
</view>
</view>
</view>
</view>
</view>
</template>
我们采用 showMission
+ missionInfo
对象来共同控制整个弹窗。这两个属性将会在所有需要启动该弹窗的页面实例上进行控制。
引入 Template 模板
接下来在所有需要启动任务弹窗的页面中嵌入引用
<import src="/template/missionDialog/index.wxml"></import>
<template is="mission" data="{{showMission:showMission,missionInfo:missionInfo}}" />
修改请求中间件
我们的约定是一但某个操作完成了一个目标任务,后端就会在请求结果里面的 task 字段中嵌入任务信息。比如说任务名称,任务奖励。所以我们能够在中间件中统一处理。
return function(appId, url, data, successCallback) {
const token = userInfo.getToken()
wx.showLoading({
title: '处理中'
})
wx.request({
method: method,
dataType: 'json',
responseType: 'text',
header: {
token: token,
'content-type': 'application/json'
},
// 该正则调试时使用
url: /mock/.test(host) ? `${host}${url}` : `${host}/${appId}${url}`,
data: data,
success: function(res) {
// xxx 某些业务
// 先触发请求回调 该干嘛干嘛
successCallback(res.data)
// 如果完成了某个任务 弹出任务弹窗
setTimeout(() => {
const pages = getCurrentPages()
let context = pages[pages.length - 1]
if (res.data.tasks && res.data.tasks.length > 0) {
let taskInfo = res.data.tasks[0]
if (!context) {
return console.log(`请给${url}添加上下文`)
}
let title =
taskInfo.type === '10'
? `完成${taskInfo.title}任务`
: `完成新手任务`
let desc =
taskInfo.type === '10'
? `影响力+${taskInfo.influenceScore}`
: `${taskInfo.title}`
context._handleTapMissionClose = () => {
context.setData({
showMission: false
})
// 点击按钮 A 之后 xxx
}
context._handleTapMissionKnow = () => {
context.setData({
showMission: false
})
// 点击按钮 B 之后 xxx
}
// 显示弹窗
context.setData({
showMission: true,
missionInfo: {
title: title,
desc: desc,
showKnow: true
}
})
}
}, 800)
},
fail: function(res) {
console.log(res)
},
complete: function(res) {
if (res.statusCode != 200) {
console.log(res)
}
}
})
}
}
有两点值得注意
- 我们可以通过 currentPages 获取页面上下文 从而 setData 来控制弹窗
- setTimeout 的主要目的是为了避免某些操作可能会切换页面,做一定的延迟处理
总结
总的来说结果还是比较让人满意的,在付出较低的代价的情况下,做到了具有一定扩展性的技术方案
事实上嵌入模板的操作通过手写一个打包器来进行所有页面的注入,但是因为整个项目体量并不是很大,所以还是没有这么做,不过这里还是希望微信能够放开一些打包部分的自定义能力。