记录一下小程序开发工作中遇到的问题,想到什么记什么
1、换行
只有在<text></text>
里才支持回车符换行,<view></view>
里面不支持。
2、动态头部
在自定义界面自定义的头部在下滑的时候会划走,需要自定义一个固定在头部的头部,直接显示不太好,加了点动画,记录一下代码
需要根据滚动做判断,所以需要把页面的代码都放到scroll-view
里面,scroll-view
做父级元素包裹所有代码。
<view>
<scroll-view bindscroll="scroll" scroll-y class="scrollBox" scroll-top="">
<!-- 其他HTML代码 -->
</scroll-view>
</view>
样式,scroll-view
需要屏幕高度,然后进行滚动
.scrollBox{
height: calc(100vh - 120rpx);
width: 100%;
}
js里面需要判断任务栏的高度,和做判断,加动画
Page({
data: {
h: 0, // height
isbig: true, //做节流
isLittle: true, // 滚动节流
scrollTop: 0,
opacity:0,
animationOpacity: null, //动画实例
},
onShow: function () {
let h = app.globalData.navHeight;
let top = this.data.top;
if (top > 50) {
this.setData({ opacity: 1 });
} else {
this.setData({ opacity: 0 });
}
this.setData({ h: h });
},
// 滚动
scroll(e) {
var top = e.detail.scrollTop;
this.setData({ top: top });
if (e.detail.scrollTop > 20) {
this.setData({ isLittle: true });
if (this.data.isbig) {
this.setData({ isbig: false });
var animation = wx.createAnimation({
duration: 650,
timingFunction: 'ease-in'
})
animation.opacity(1).step();
this.setData({ animationOpacity: animation.export() });
}
} else {
this.setData({ isbig: true });
if (this.data.isLittle) {
this.setData({ isLittle: false });
var animation = wx.createAnimation({
duration: 650,
timingFunction: 'ease-out'
})
animation.opacity(0).step();
this.setData({ animationOpacity: animation.export() });
}
}
},
})
3、骨架屏
项目需要加骨架屏,自己动手实现了一个。
html代码
<view wx:if="">
<!-- 其他加载完显示的业务代码 -->
</view>
<!-- 骨架屏 -->
<view wx:if="" class="skeleton">
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
<view>
<view class="text1 "></view>
<view class="content1 "></view>
</view>
</view>
样式及动画
/* 引入app.wxss,抽离了公共样式 */
@import '/app.wxss';
/* 每个页面的样式 */
.skeleton{
width: 100%;
padding: 40rpx;
box-sizing: border-box;
}
.text1{
width: 45%;
height: 40rpx;
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 25%, hsla(0,0%,73%,0.23) 37%,hsla(0,0%,74.5%,.2) 63%);
}
.content1{
width: 100%;
height: 150rpx;
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 25%, hsla(0,0%,73%,0.23) 37%,hsla(0,0%,74.5%,.2) 63%);
margin: 40rpx 0;
}
/* 动画实现的css,抽离到app.js里面了 */
@keyframes mymove{
from {
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 25%, hsla(0,0%,73%,0.23) 37%,hsla(0,0%,74.5%,.2) 63%);
}
5%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 30%, hsla(0,0%,73%,0.23) 42%, hsla(0,0%,74.5%,.2) 68%);
}
10%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 35%, hsla(0,0%,73%,0.23) 47%, hsla(0,0%,74.5%,.2) 73%);
}
15%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 40%, hsla(0,0%,73%,0.23) 52%, hsla(0,0%,74.5%,.2) 78%);
}
20%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 45%,hsla(0,0%,73%,0.23) 57%, hsla(0,0%,74.5%,.2) 83%);
}
25%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 50%,hsla(0,0%,73%,0.23) 62%, hsla(0,0%,74.5%,.2) 88%);
}
30%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 55%,hsla(0,0%,73%,0.23) 67%, hsla(0,0%,74.5%,.2) 93%);
}
35%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 60%,hsla(0,0%,73%,0.23) 72%, hsla(0,0%,74.5%,.2) 98%);
}
40%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 3%,hsla(0,0%,74.5%,.2) 65%,hsla(0,0%,73%,0.23) 77%);
}
45%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 8%,hsla(0,0%,74.5%,.2) 70%,hsla(0,0%,73%,0.23) 82%);
}
50%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 13%,hsla(0,0%,74.5%,.2) 75%,hsla(0,0%,73%,0.23) 87%);
}
55%{
background-image: linear-gradient(90deg, hsla(0,0%,74.5%,.2) 18%,hsla(0,0%,74.5%,.2) 80%,hsla(0,0%,73%,0.23) 92%);
}
60%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 23%, hsla(0,0%,74.5%,.2) 85%,hsla(0,0%,73%,0.23) 97%);
}
65%{
background-image: linear-gradient(90deg,hsla(0,0%,73%,0.23) 2%, hsla(0,0%,74.5%,.2) 28%, hsla(0,0%,74.5%,.2) 90%);
}
70%{
background-image: linear-gradient(90deg,hsla(0,0%,73%,0.23) 7%, hsla(0,0%,74.5%,.2) 33%), hsla(0,0%,74.5%,.2) 95%;
}
75%{
background-image: linear-gradient(90deg,hsla(0,0%,73%,0.23) 12%, hsla(0,0%,74.5%,.2) 38%), hsla(0,0%,74.5%,.2) 5%;
}
80%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 5%,hsla(0,0%,73%,0.23) 17%, hsla(0,0%,74.5%,.2) 43%);
}
85%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 10%,hsla(0,0%,73%,0.23) 22%, hsla(0,0%,74.5%,.2) 48%);
}
90%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 15%,hsla(0,0%,73%,0.23) 27%, hsla(0,0%,74.5%,.2) 53%);
}
95%{
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 20%,hsla(0,0%,73%,0.23) 32%, hsla(0,0%,74.5%,.2) 58%);
}
to {
background-image: linear-gradient(90deg,hsla(0,0%,74.5%,.2) 25%,hsla(0,0%,73%,0.23) 37%, hsla(0,0%,74.5%,.2) 63%);
}
}
.animation{
animation:mymove 1.5s infinite;
-webkit-animation:mymove 1.5s infinite;
}
js里面根据变量判断是否加载完,在接口里面处理
Page({
data:{
isSkeleton: true
},
onShow: function(){
let that = this;
// 加载页面的数据
wx.request({
url: url,
method: "GET",
header: app.header(),
success: function (res) {
if (app.checkRes(res)) {
// 其他业务逻辑,然后取消骨架屏
that.setData({ isSkeleton: false });
}
}
});
},
})
4、设置导航栏右上角角标
wx.setTabBarBadge({
index: 1,
text: 1
})
wx.hideTabBarRedDot({index:1})
index
是索引。
5、检测版本更新
const updateManager = wx.getUpdateManager()
updateManager.onUpdateReady(function () {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
}
}
})
})
6、获取用户信息
在代码里获取用户信息需要在wx.getUserInfo()
的回调里面
wx.getUserInfo({
success(e) {
var userInfo = e.userInfo;
let data: {
name: userInfo.nickName,
avatar: userInfo.avatarUrl,
gender: userInfo.gender
}
}
})
腾讯官方修改了小程序登录、用户信息相关接口调整说明:https://developers.weixin.qq.com/community/develop/doc/000cacfa20ce88df04cb468bc52801
7、服务号通知
点单类小程序用户下单完成需要推送给用户一条通知,比如取餐码之类的内容,需要先设定模板,模板在小程序后台设置:
- 登录小程序后台
- 功能 - 订阅消息
- 选一个模板
设置完后会有一个模板的id,在代码里面传入,当用户点击的时候就会弹出一个授权弹窗,授权完成后想关闭在小程序的右上角设置里面关,关了貌似不会再次弹窗授权弹窗。
wx.requestSubscribeMessage({
tmplIds: ['idxxxxxxx'],
success (res) {
// 业务代码
},
complete (){
// 业务代码,这个功能需要用户同意,也可以拒绝,同意or拒绝都要执行的话放这里
}
})
配置完其他需要后台配合,后台的文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
8、canvas
小程序的canvas同一个id只能的渲染一次,画一次如果wx:if
隐藏掉,再打开无法再次渲染,小程序官方给出的回答是不工作了。
另外小程序想画二维码可以使用:weapp.qrcode.esm.js
。
在https://github.com/LB-nan/cat-Said/blob/master/assets/plugins/weapp.qrcode.esm.js
9、image
判断图片加载是否成功可以使用onload
<image onload="imgOnload" ></image>
10、用户信息展示
用户信息展示不需要调取获取用户信息的微信接口,直接展示就好了。
<view class="listItem flex between">
<view class="title">头像</view>
<view class="con img"><open-data type="userAvatarUrl"></open-data></view>
</view>
<view class="listItem flex between">
<view class="title">性别</view>
<view class="con"><open-data type="userGender" lang="zh_CN"></open-data></view>
</view>
<view class="listItem flex between">
<view class="title">用户名</view>
<view class="con"><open-data type="userNickName"></open-data></view>
</view>
11、小程序的一些要求
现在小程序的各种规则越来越复杂,开发一些感觉会出问题的需求的时候建议先看看这些规范,没啥问题再开发。
官方文档地址:https://developers.weixin.qq.com/community/develop/article/doc/000a62dc6c8a88125129cdc055bc13
12、小程序地图设置导航
需要先腾讯地图开放平台https://lbs.qq.com/
去申请开发者key,申请到之后编辑开启Webservice
服务,小程序输入小程序的APPID
,然后用经纬度调用下面方法,传入2对经纬度以及申请到的开发者key。
getPolyline(){
let that = this;
//网络请求设置
let opt = {
//WebService请求地址,from为起点坐标,to为终点坐标,开发key为必填
url: `https://apis.map.qq.com/ws/direction/v1/walking/?from=${latitude},${longitude}&to=${latitude},${longitude}&key=${'开发者key'}`,
method: 'GET',
dataType: 'json',
success: (res) => {
let ret = res.data
if (ret.status != 0) return; //服务异常处理
let coors = ret.result.routes[0].polyline,
pl = [];
//坐标解压(返回的点串坐标,通过前向差分进行压缩)
let kr = 1000000;
for (let i = 2; i < coors.length; i++) {
coors[i] = Number(coors[i - 2]) + Number(coors[i]) / kr;
}
//将解压后的坐标放入点串数组pl中
for (let i = 0; i < coors.length; i += 2) {
pl.push({
latitude: coors[i],
longitude: coors[i + 1]
})
}
//设置polyline属性,将路线显示出来
let polyline = [{
points: pl,
color: '#1D78F7',
width: 6,
arrowLine: true
}]
that.setData({polyline: polyline})
}
};
wx.request(opt);
}
<map polyline="" ></map>
13、打开微信内置的地图
wx.getLocation({
type: 'wgs84',
success: function (res) {
wx.openLocation({ //使用微信内置地图查看位置。
latitude: Number(cur.latitude),//要去的纬度-地址
longitude: Number(cur.longitude),//要去的经度-地址
name: cur.name, // 地址的名字
address: cur.address // 详细地址
})
}
})
14、设置屏幕亮度
// 获取屏幕亮度
wx.getScreenBrightness({
success: function(e){
that.setData({Brightness: e.value});
}
})
// 这是屏幕亮度
wx.setScreenBrightness({
value: 1,
success: function(){
console.log('设置成功')
}
})
15、设置header
header: function () {
let obj = new Object();
obj = {
'Authorization': "Basic Y3ViZTpjdWJl",
'X-Requested-With': 'XMLHttpRequest',
'content-type': 'application/x-www-form-urlencoded',
'cookie': wx.getStorageSync("sessionid") //读取cookie
}
if (this.globalData.token.access_token != undefined) {
obj.Authorization = "Bearer " + this.globalData.token.access_token
}
return obj;
},
setCookie: function (res) {
wx.setStorageSync("sessionid", res.header["Set-Cookie"]);
},
16、授权手机号
// 绑定手机号
bindgetphonenumber(e) {
let iv = e.detail.iv;
let encryptedData = e.detail.encryptedData;
let socialUserId = app.globalData.socialUserId;
let uniqueId = app.globalData.uniqueId;
app.postBindingWxMobile(encryptedData, iv, socialUserId, uniqueId);
},
// 提交微信绑定
postBindingWxMobile: function (encryptedData, iv, socialUserId, uniqueId) {
let that = this;
wx.login({
success: function (res) {
let code = res.code;
let data = new Object();
data.code = code;
data.encryptedData = encryptedData;
data.iv = iv;
data.socialUserId = socialUserId;
data.uniqueId = uniqueId;
wx.request({
url: that.globalData.severUrl + that.globalData.apiUrl.bindWxMobile,
data: data,
header: that.header(),
method: 'POST',
success: function (res) {
//设置session信息
that.setCookie(res);
if (that.checkRes(res)) {
// 绑定成功的回调
}
},
fail: function (res) {
//console.log(res)
console.log("用户登录失败:" + res.errMsg);
}
})
},
fail: function (res) {
//console.log(res)
// console.log("获取用户手机失败:" + res.errMsg);
}
});
},
17、用户打开小程序
onLaunch: function () {
this.verifyLogin()
},
verifyLogin: function () {
let that = this;
wx.login({
success: function (res) {
// 获取code
let code = res.code
// 通过code请求后台数据 获取登录状态
wx.request({
url: that.globalData.severUrl + that.globalData.apiUrl.loginUrl,
data: {
mobile: "WX@" + code,
client: 'cube-coffee-biz'
},
header: that.header(),
method: 'POST',
success: function (res) {
//设置session信息
that.setCookie(res);
if (res.statusCode == 200) {
// 通过code请求的数据 登录
let token = res.data;
let status = res.data.status;
let socialUserId = res.data.socialUserId;
let uniqueId = res.data.uniqueId;
that.globalData.socialUserId = socialUserId;
that.globalData.uniqueId = uniqueId;
}
},
fail: function (res) {
}
})
}
});
},
18、用户支付流程
用户点击支付
按钮
const app = getApp() // 要在页面头部定义
// 去支付
toPay() {
wx.getNetworkType({
success(res) {
const networkType = res.networkType
if (networkType == 'none') {
wx.showToast({
title: '无网络请检查网络',
icon: 'none',
duration: 2000
})
return;
}
}
})
var that = this;
wx.showModal({
title: '提示',
content: '是否确认支付订单',
success(res) {
if (res.confirm) {
// 这个接口是 获取用户推送授权 用户授权后可以通过后台调用接口给用户发送推送,比如下单完成之类的消息提醒
wx.requestSubscribeMessage({
tmplIds: ['0YTUS-YAFU_zF0uq0sLs3RdJoeKP20AC7YXbYvfUCtA'],
complete: function() {
wx.showLoading({
title: '支付中',
mask: true
})
let obj = {}; // 参数
wx.request({
url: app.globalData.severUrl + app.globalData.apiUrl.orderCreateUrl,
data: obj,
method: "POST",
header: app.header(),
success: function(res) {
if (app.checkRes(res)) {
app.prePay(sn);
}
}
})
}
})
}
}
})
}
prePay
方法
prePay: function(orderSns, jump, orderDetail) {
let that = this;
wx.login({
success: function(res) {
// 获取code
let code = res.code
// 在这个方法里可以进行处理,比如提交后台验证,然后由后台返回支付信息
wx.request({
url: that.globalData.severUrl + that.globalData.apiUrl.payment,
method: "GET",
data: data,
header: header,
success: function(res) {
if (that.checkRes(res)) {
if (res.data.resultCode == 'SUCCESS') {
let timeStamp = res.data.timeStamp;
let nonceStr = res.data.nonceStr;
let packages = res.data.package;
let signType = res.data.signType;
let paySign = res.data.paySign;
let orderSn = res.data.orderSn;
let paymentTransactionSn = res.data.paymentTransactionSn;
let price = res.data.price;
that.weixinPay(timeStamp, nonceStr, packages, signType, paySign, orderSn, paymentTransactionSn, price);
} else if (res.data.resultCode == 'FAIL') {}
}
}
});
}
});
},
提交微信支付
weixinPay: function(timeStamp, nonceStr, packages, signType, paySign, orderSn, paymentTransactionSn, price) {
let that = this;
//判断支付参数是否合法
// 。。。
// 提交请求
wx.requestPayment({
'timeStamp': timeStamp,
'nonceStr': nonceStr,
'package': packages,
'signType': signType,
'paySign': paySign,
'success': function(res) {
if (res.errMsg == 'requestPayment:ok') {
//跳转到支付成功页面 或进行支付成功操作
}
},
'fail': function(res) {
if (res.errMsg == 'requestPayment:fail cancel') {
wx.showToast({
title: "您已取消支付",
mask: true,
icon: 'info',
duration: 2000
});
} else {
}
}
})
},
19、页面间传递数据
wx.navigateTo({
url: '../makeInvoice/makeInvoice',
success: function (res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit('acceptDataFromOpenerPage', { data: obj })
}
})
onLoad: function (options) {
var that = this;
const eventChannel = this.getOpenerEventChannel()
// 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据
eventChannel.on('acceptDataFromOpenerPage', function (data) {
console.log(data);
})
},