如何将uniapp开发的小程序移植到公众号H5

发布于 2021-09-27 16:22

背景

    

    下文示例代码需要用到uni-app的 条件编译,关于条件编译的用法可参考这里:https://uniapp.dcloud.io/platform?id=preprocessor

一、修改uniapp项目工程配置

    1、修改manifest.json的h5设置,设置 运行的基础路径 为 /xxxx-mp/,此处的xxxx-mp就是H5在nginx的发布路径;

    2、修改manifest.json的h5设置,设置 地图与定位 的key,该key从 腾讯地图管理后台创建;

二、修改后端API访问方式

// #ifdef MP-WEIXINimport Fly from 'flyio/dist/npm/wx'import serverUrl from './serverUrl.js'const fly = new Fly()// #endif// #ifdef H5let baseURL = ''import Fly from 'flyio/dist/npm/fly'const fly = new Fly()fly.config.baseURL = baseURL// #endif

    在h5的情况下,开发过程中uniapp会加载vue.config.js文件的配置,如果我们在开发过程中要用到测试服务器的接口,可以在vue.config.js文件内配置proxy指向 https://your-dev-server-url.com

module.exports = {  devServer: {    // proxy: 'https://your-product-server-url.com',    proxy: 'http://your-dev-server-url.com',  }}

三、访问微信API

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

    由于我们是在uniapp中使用,可以借助npm包,这里使用的是weixin-js-sdk,只是对jssdk的一层浅包装,可以通过npm安装,支持 CommonJS,便于 browserify, webpack 等直接使用。

npm install weixin-js-sdk

    安装完成之后,在适当的地方引入并初始化,由于H5有多种启动方式,有多个入口,可能需要在多个位置初始化,所以把这个初始化提取为全局方法,放在store里。

import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)import api from '../js/flyiowrapper/api.js'// #ifdef H5import wx from 'weixin-js-sdk'// #endifconst store = new Vuex.Store({  state: {    // #ifdef H5    // jssdk是否已经初始化    wxConfiged: false,    // #endif  },  mutations: {    setWxConfiged(state, configed) {      state.wxConfiged = configed    },  },  actions: {    configWxSdk({      commit,      state    }) {      if (state.wxConfiged) return      let url = window.document.location.href      let urlList = url.split('#')      if (urlList.length > 0) {        url = urlList[0]      }      if (process.env.NODE_ENV == 'development') {        url = 'http://your-product-url.com/xxxx-mp/'      }      console.log('configWxSdk', url)      let merchantCode = uni.getStorageSync('merchantCode')      api.getWxConfig(url, merchantCode).then(res => {        console.log('getWxConfig', res)        wx.config({          debug: false, // 开启调试模式,          appId: res.data.appId, // 必填,企业号的唯一标识,此处填写企业号corpid          nonceStr: res.data.nonceStr, // 必填,生成签名的随机串          signature: res.data.signature, // 必填,签名,见附录1          jsApiList: ['scanQRCode', 'chooseWXPay', 'getLocation',            'openLocation'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2        });        commit('setWxConfiged', true)      })    }  },})export default store

在需要使用的地方引入,比如在主页的onLoad中调用,如下

  import { mapActions } from 'vuex'    export default {    onLoad(){      // #ifdef H5      this.configWxSdk()      // #endif    },    methods:{      // #ifdef H5      ...mapActions(['configWxSdk']),      // #endif    }  } 

    JSSDK配置好之后,就可以开始使用了。

    config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

四、几个要兼容处理的uni API

(1)uni.scanCode()

onScan() {  console.log('onScan')  // #ifdef MP-WEIXIN  this.scanInMp()  // #endif  // #ifdef H5  this.scanInH5()  // #endif},scanInH5() {  let that = this      wx.scanQRCode({    needResult: true,    scanType: ["qrCode"],    success: function(res) {      console.log('scanCode success', res)      // TODO    }  })},scanInMp() {  uni.scanCode({    success: (res) => {      console.log('scanCode success' + res)    }  })},

(2)uni.getLocation()    

    按照官方的说法,这个api是支持H5的,但从实际使用的情况看,支持的不好,我没调成功过,所以还是用条件编译来处理,H5的时候该用weixin-jssdk的wx.getLocation来处理,这个获取的位置精度较高,且速度快。

getPositionAndRefresh(needStopPullDown = false) {  console.log('getPositionAndRefresh')  // #ifdef MP-WEIXIN  this.getPosAndRefresh_Mp()  // #endif  // #ifdef H5  this.getPosAndRefresh_H5()  // #endif},getPosAndRefresh_H5() {  console.log('getPosAndRefresh_H5')  // 因为调试时,在浏览器中,下面的wx.getLocation调用失败  if(process.env.NODE_ENV == 'development'){    this.position = {      latitude: 22.728748,      longitude: 114.05278    }    // TODO    return   }    let that = this  wx.getLocation({    type: 'wgs84',    success(res) {      console.log('wx.getLocation success', res)      that.position = {        latitude: res.latitude,        longitude: res.longitude      }      // TODO    },    fail(res) {      console.log('wx.getLocation fail', res)      uni.showToast({        icon: 'none',        title: '定位失败, ....'      })    }  })},

五、处理好自动登录逻辑

  • 先调用微信的authorize接口

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
  • 系统会自动跳转到redirect_uri/?code=CODE&state=STATE中,这个REDIRECT_URL是业务系统的一个接口,CODE参数是微信服务器给的。

  • 在redirect_uri中拿到了code,请求以下链接获取access_token:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

在这个返回值中就带了openId,格式如下

{  "access_token":"ACCESS_TOKEN",  "expires_in":7200,  "refresh_token":"REFRESH_TOKEN",  "openid":"OPENID",  "scope":"SCOPE" }

有了openId,就可以判定该用户是否存在,要不要自动登录了。

六、微信支付

    weixin-jssdk的说明

   https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

    WeixinJSBridge.invode参照这里:

    https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml

    在项目中代码实现如下:

export function hjPayWrapper(orderInfo, onSuccess, onFail, paramList{  // #ifdef MP-WEIXIN  payByMp(orderInfo, onSuccess, onFail, paramList)  // #endif  // #ifdef H5  // payByWXJSBridge(orderInfo, onSuccess, onFail, paramList)  payByJSSDK(orderInfo, onSuccess, onFail, paramList)  // #endif}export function payByMp(orderInfo, onSuccess, onFail, paramList) {  uni.requestPayment({    provider: 'wxpay',    orderInfo: orderInfo.orderId,    timeStamp: orderInfo.timeStamp,    nonceStr: orderInfo.nonceStr,    package: orderInfo.package,    signType: orderInfo.signType,    paySign: orderInfo.paySign,    success: (payRes) => {      onSuccess(orderInfo, paramList)    },    fail: (payErr) => {      console.log('updatePaidStatus', payErr)      that.$refs.uTips.show({        type: 'warning',        title: `启动支付失败`      })    }  })}// function payByWXJSBridge(orderInfo, onSuccess, onFail, paramList{  console.log('payByWXJSBridge', orderInfo)    WeixinJSBridge.invoke(    'getBrandWCPayRequest', {      'appId': orderInfo.appId,      'nonceStr': orderInfo.nonceStr, // 支付签名随机串,不长于 32 位      'package': orderInfo.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)      'signType': orderInfo.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'      'paySign': orderInfo.paySign, // 支付签名    },    function(res) {      console.log('getBrandWCPayRequest res', res)      if (res.err_msg == "get_brand_wcpay_request:ok") {        // 使用以上方式判断前端返回,微信团队郑重提示:        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。        if(onSuccess) onSuccess(orderInfo, paramList)      }else if(res.err_msg == 'get_brand_wcpay_request:fail'){        console.log('getBrandWCPayRequest err')        if(onFail) onFail(orderInfo, paramList)      }    }  )}function payByJSSDK(orderInfo, onSuccess, onFail, paramList) {  console.log('payByJSSDK', orderInfo)    wx.chooseWXPay({    appId: orderInfo.appId,    timestamp: orderInfo.timeStamp,     nonceStr: orderInfo.nonceStr, // 支付签名随机串,不长于 32 位    package: orderInfo.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)    signType: orderInfo.signType, // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致    paySign: orderInfo.paySign, // 支付签名    success: function (res) {      // 支付成功后的回调函数      if(onSuccess) onSuccess(orderInfo, paramList)    },    fail: function(err){      if(onFail) onFail(orderInfo, paramList)    }  });}

    实现过程中,在payByJSSDK内,由于代码拷贝把timestamp写成timeStamp,结果总是报签名错误,搞得我总是怀疑是不是后端把签名的方式写错了,最终还是自己的锅,切记切记。

https://your-url.com/xxxx-mp/q?m=<mcode>&sn=<nnnn>

    其中:<mcode>是我们给客户分配的唯一标识,<nnnn>是充电桩的序列号。

https://your-url.com/h5/q?m=<mcode>&sn=<nnnn>
https://your-url.com/xxxx-mp/q?sn=<nnnn>

本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。

相关素材