小程序记录

2021/02/25 微信小程序

记录一下小程序开发工作中遇到的问题,想到什么记什么

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、服务号通知

点单类小程序用户下单完成需要推送给用户一条通知,比如取餐码之类的内容,需要先设定模板,模板在小程序后台设置:

  1. 登录小程序后台
  2. 功能 - 订阅消息
  3. 选一个模板

设置完后会有一个模板的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);
  })
},

Search

    Table of Contents