前言
在小程序日常开发中,有些功能是所有页面都需要使用的,所以就需要对每个page页面进行扩展,在小程序中Page 的作用相当于构造函数, Page 会初始化页面对象然后将配置参数中的属性 merge 到页面对象上。
一、Page页面扩展
1.组件的封装和引用
extend-page.js
const extendPage = Page => {
return object => {
// 登录面板开头
if (!object.data) object.data = {}
object.data.showLoginPanel = false
// method
object.hi = function(name){
console.log(`hi ${name}`);
}
// 派发一个等待处理,需要有代码处理的事件
// 但这个方法没有什么用
object.triggerWaitingEvent = function (type, data = {}){
return new Promise((resolve,reject)=>{
let eventCallback = res => resolve(res)
Object.assign(data, {
eventCallback
})
this.triggerEvent(type, data)
})
}
return Page(object)
}
}
const originalPage = Page
Page = extendPage(originalPage)
app.js中引入扩展页面
require("./lib/extend-page")
2.页面使用
<button bindtap="extendPage" type="primary">请求</button>
<LoginPanel show="{{showLoginPanel}}"></LoginPanel>
async extendPageTest(){
this.hi('weapp')
// 使用request3
let res4 = await wx.wxp.request4({
url: 'http://localhost:3000/user/home',
})
if (res4) console.log('res4', res4)
},
3.效果
二、其他相关封装
1.pop-up组件
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
/**
* 组件的属性列表
*/
properties: {
visible: {
type: Boolean,
value: false
}
},
/**
* 组件的初始数据
*/
data: {},
ready(){
this.triggerEvent('ready')
},
/**
* 组件的方法列表
*/
methods: {
popPreventTouchmove() { },
popPreventTouchmove2() { },
popPreventTouchmove3() { },
cityChange() { },
close() {
this.triggerEvent('close')
},
handleClickMask(e) {
// console.log(e)
if (e.target.dataset.type !== 'unclose') this.close()
}
}
})
<view catchtouchmove="popPreventTouchmove">
<view class="q-pp-mask {{ visible ? 'q-pp-mask-show' : '' }} ptp_exposure" bindtap="handleClickMask" catchtouchmove="popPreventTouchmove">
<view class=" q-pp {{ visible ? 'q-pp-show' : '' }}" catchtouchmove="popPreventTouchmove">
<slot name="content" ></slot>
</view>
</view>
</view>
.q-pp {
position: fixed;
width: 100%;
box-sizing: border-box;
left: 0;
right: 0;
bottom: 0;
background: #f7f7f7;
transform: translate3d(0, 100%, 0);
transform-origin: center;
transition: all 0.2s ease-in-out;
z-index: 900;
visibility: hidden;
}
.q-pp-show {
transform: translate3d(0, 0, 0);
visibility: visible;
}
.q-pp-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 900;
transition: all 0.2s ease-in-out;
opacity: 0;
visibility: hidden;
}
.q-pp-mask-show {
opacity: 1;
visibility: visible;
}
2.LoginPanel组件
<pop-up visible="{{visible}}">
<view slot="content">
<view class="picker-view">
<view class="picker-view__pane">
<text bindtap="close">取消</text>
</view>
<view style="font-size:17px;font-weight:bold;line-height:30px;border-bottom:1px solid #f2f2f2;padding: 0px 0 20px;width: 100%;color:#353535;text-align: center;">微信授权登录</view>
<image style="width: 40px;height: 40px;padding-top: 20px;" src="../../components/custom-tab-bar/component-on.png"></image>
<view style="font-size:17px;font-weight:400;color:#353535;line-height: 40px;">小程序将申请获取以下权限</view>
<view style="font-size:11px;color:#b2b2b2;border-bottom:1px solid #f2f2f2;width: 100%;padding: 0px 0 40px;text-align: center;">● 获得你的公开信息(昵称、头像、地区及性别)</view>
<view style="height:80px;padding-top: 20px;">
<button bindgetuserinfo="login" open-type="getUserInfo" type="primary">登陆</button>
</view>
</view>
</view>
</pop-up>
.picker-view {
width: 100%;
display: flex;
background-color: #fff;
flex-direction: column;
justify-content: center;
align-items: center;
bottom: 0rpx;
left: 0rpx;
}
.picker-item {
line-height: 70rpx;
margin-left: 5rpx;
margin-right: 5rpx;
text-align: center;
}
.picker-view__pane {
height: 100rpx;
width: 100%;
padding: 20rpx 32rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
}
.picker-view__pane text{
color: #00cc88;
font-size: 30rpx;
}
.pick-view__group {
width: 96%;
height: 450rpx;
}
Component({
options: {
multipleSlots: false
},
properties: {
show: {
type: Boolean,
value: false
}
},
observers: {
'show': function (value) {
console.log(value);
this.setData({
visible: value
})
}
},
data: {
visible: false
},
ready() {},
methods: {
close(e) {
this.setData({
visible: false
})
},
async login(e, retryNum = 0) {
let {
userInfo,
encryptedData,
iv
} = e.detail
// 本地token与微信服务器上的session要分别对待
let tokenIsValid = false, sessionIsValid = false
let res0 = await getApp().wxp.checkSession().catch(err=>{
// 清理登陆状态,会触发该错误
// checkSession:fail 系统错误,错误码:-13001,session time out…d relogin
console.log("err",err);
tokenIsValid = false
})
console.log("res0", res0);
if (res0 && res0.errMsg === "checkSession:ok") sessionIsValid = true
let token = wx.getStorageSync('token')
if (token) tokenIsValid = true
if (!tokenIsValid || !sessionIsValid) {
let res1 = await getApp().wxp.login()
let code = res1.code
console.log("code",code);
let res = await getApp().wxp.request({
url: 'http://localhost:3000/user/wexin-login2',
method: 'POST',
header: {
'content-type': 'application/json',
'Authorization': `Bearer ${token || ''}`
},
data: {
code,
userInfo,
encryptedData,
iv,
sessionKeyIsValid:sessionIsValid
}
})
if (res.statusCode == 500){
if (retryNum < 3){
this.login.apply(this, [e, ++retryNum])
}else{
wx.showModal({
title: '登录失败',
content: '请退出小程序,清空记录并重试',
})
}
return
}
// Error: Illegal Buffer at WXBizDataCrypt.decryptData
console.log('登录接口请求成功', res.data)
token = res.data.data.authorizationToken
wx.setStorageSync('token', token)
console.log('authorization', token)
}
getApp().globalData.token = token
wx.showToast({
title: '登陆成功了',
})
this.close()
this.triggerEvent('loginSuccess')
getApp().globalEvent.emit('loginSuccess')
},
login2(e) {
let {
userInfo,
encryptedData,
iv
} = e.detail
// console.log('userInfo', userInfo);
const requestLoginApi = (code) => {
//发起网络请求
wx.request({
url: 'http://localhost:3000/user/wexin-login2',
method: 'POST',
header: {
'content-type': 'application/json'
},
data: {
code: code,
userInfo,
encryptedData,
iv
},
success(res) {
console.log('请求成功', res.data)
let token = res.data.data.authorizationToken
wx.setStorageSync('token', token)
onUserLogin(token)
console.log('authorization', token)
},
fail(err) {
console.log('请求异常', err)
}
})
}
const onUserLogin = (token) => {
getApp().globalData.token = token
wx.showToast({
title: '登陆成功了',
})
this.close()
this.triggerEvent('loginSuccess')
getApp().globalEvent.emit('loginSuccess')
}
const login = () => {
wx.login({
success(res0) {
if (res0.code) {
requestLoginApi(res0.code)
} else {
console.log('登录失败!' + res0.errMsg)
}
}
})
}
wx.checkSession({
success() {
//session_key 未过期,并且在本生命周期一直有效
console.log('在登陆中');
let token = wx.getStorageSync('token')
if (token) {
onUserLogin(token)
} else {
// session会重复,需要处理
login()
}
},
fail() {
// session_key 已经失效,需要重新执行登录流程
login()
}
})
}
}
})
{
"component": true,
"usingComponents": {
"pop-up": "../pop-up/index"
}
}
3.LoginPanel组件
//app.js
require("./lib/extend-page")
// import Event from './lib/event'
import Event from './lib/event2'
import wxp from './lib/wxp'
// getApp().globalEvent
App({
wxp: (wx.wxp = wxp),
globalData: (wx.globalData = {}),
globalEvent: (wx.globalEvent = new Event()),
onLaunch: async function () {
if (!wx.cloud) {
console.error('请使用 2.2.3 或以上的基础库以使用云能力')
} else {
wx.cloud.init({
// env 参数说明:
// env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
// 此处请填入环境 ID, 环境 ID 可打开云控制台查看
// 如不填则使用默认环境(第一个创建的环境)
env: 'default-98491d',
traceUser: true,
})
}
this.globalData = {}
},
import {
promisifyAll
} from 'miniprogram-api-promise';
const wxp = {}
promisifyAll(wx, wxp)
// compatible usage
// wxp.getSystemInfo({success(res) {console.log(res)}})
wxp.request2 = function (args) {
let token = wx.getStorageSync('token')
if (token) {
if (!args.header) args.header = {}
args.header['Authorization'] = `Bearer ${token}`
}
return wxp.request(args).catch(function (reason) {
console.log('reason', reason)
})
}
//
wxp.request3 = function(args){
let token = wx.getStorageSync('token')
if (!token){
return new Promise((resolve, reject)=>{
let pageStack = getCurrentPages()
if (pageStack && pageStack.length > 0) {
let currentPage = pageStack[pageStack.length-1]
currentPage.setData({
showLoginPanel2:true
})
getApp().globalEvent.once("loginSuccess", ()=>{
wxp.request2(args).then(res=>{
resolve(res)
}, err=>{
console.log('err', err);
reject(err)
})
})
}else{
reject('page valid err')
}
})
}
return wxp.request2(args)
}
wxp.request4 = function (args) {
let token = wx.getStorageSync('token')
if (!token) {
let pages = getCurrentPages()
let currentPage = pages[pages.length - 1]
// 展示登陆浮窗
currentPage.setData({
showLoginPanel: true
})
return new Promise((resolve, reject) => {
getApp().globalEvent.once('loginSuccess', function (e) {
wxp.request2(args).then(function (result) {
resolve(result)
}).catch(function (reason) {
console.log('reason', reason);
})
})
})
}
return wxp.request2(args).catch(function (reason) {
console.log('reason', reason);
})
}
export default wxp