企微H5 应用的一些坑

904 阅读4分钟

需要使用企微 wx.config 以及 wx.agentConfig 的项目,需要另外引入JS,不能用 npm i wx-js-sdk --save 

如果执行了npm 命令,请执行卸载指令 npm uninstall weixin-sdk-js --save ,然后在vue项目中的index.html页面中引入官网相关sdk-js的js

如果只需要 wx.config 可以直接使用 npm i wx-js-sdk --save

以下是引入JS:

<script src=``"//res.wx.qq.com/open/js/jweixin-1.2.0.js"``></script>

<script src=``"https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"``></script>

并且,引入JS需要放到<head>中

(参考网址:www.cnblogs.com/cndjl/p/141…)

企业微信api 官网:work.weixin.qq.com/api/doc/900…

→需要应用OAuth2.0网页授权功能、以及调用JS-SDK,必须使用可信域名。可信域名在企业微信管理后台设置。需要添加验证文件至服务器。

(私域管理后台地址:work.weixin.qq.com/wework_admi…

注意:调用JS-SDK、跳转小程序的可信域名,不能带有端口号。带了的话会验证不通过。(因此需要运维配置域名)

企业微信截图_20210111163103.png

1. 出现 wx.agentConfig is undefinded 报错

  • 原因:agentConfig 的注入是等到页面加载完成才注入的, 因此 wx.agentConfig 不能写在 beforeEach (路由前卫)中,否则在IOS系统中会报错,安卓以及 window 暂未该问题

  • 解决方法:创建一个起始页,专门用于请求 相关微信校验方法(wx.config、wx.ready、wx.agentConfig、wx.invoke)。

参考网址:www.sanshu.cn/a/12150.htm…

企业微信截图_20210115163232.png

2.wx.config 配置

  • 注意:jsApiList 必须写齐全,不然调用接口会没权限导致报错
wx.config({

    beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题

    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

    appId: getKey('appid'), // 必填,公众号的唯一标识

    timestamp: data.timestamp, // 必填,生成签名的时间戳

    nonceStr: data.nonceStr, // 必填,生成签名的随机串    

    signature: data.signature, // 必填,签名,见附录1

    jsApiList: jsApiList // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

})

3.wx.agentConfig 配置


wx.agentConfig({

    corpid: getKey('appid'), // 必填,企业微信的corpid,必须与当前登录的企业一致

    agentid: 1000011, // 必填,企业微信的应用id (e.g. 1000247)

    timestamp: agentData.timestamp, // 必填,生成签名的时间戳

    nonceStr: agentData.nonceStr, // 必填,生成签名的随机串

    signature: agentData.signature,// 必填,签名,见附录-JS-SDK使用权限签名算法

    jsApiList: jsApiList, //必填

    success: function(res) {},

    fail: function(err) {

       if (res.errMsg.indexOf('function not exist') > -1) {

         alert('版本过低请升级')

       }

    }
})


特别注意:

image2021-1-11 16_40_8.png

agentConfig 与 config 的签名算法完全一样,但是jsapi_ticket的获取方法不一样。(后端注意)

因此,config 与 agentConfig 的 nonceStr、signature 是不一样的。

参考网址:juejin.cn/post/684490…(企业微信自建应用踩坑指南)

4.完整代码

新建login.vue 文件,为初始化页面,页面空白,只做登录校验

<template>

  <view class="content"> </view>

</template>

<script>

import axios from 'axios'

import { getKey, agentSign, getJsApiList, isMac } from '@/utils/index'

export default {

  data () {

    return {

      wx: undefined,

      isIos: false

    }

  },

  


  async onLoad () {

    var u = navigator.userAgent

    var ua = u.toLowerCase()

    console.log('/iphone|ipad|ipod/', ua)

    console.log('wx', wx)

    console.log('window.jWeixin', window.jWeixin)

    // 检查session

    uni.showLoading({

      mask: true,

      title: '加载中...',

      duration: 100000

    })

    this.$service.checkQwSession().then(res => {

      console.log('checkQwSession==' + JSON.stringify(res))

      // 检查session通过

      if (res.code === 200) {

        this.beforeEach()

      }

    })

  },

  onShow () {},

  computed: {

     wxMethod () {

      return isMac() ? wx : window.jWeixin

     }

  },

  methods: {

    // 跳转前验证

    beforeEach () {

      const _this = this

      // getContext 获取不同的入口,getCurExternalContact 获得当前外部联系人userId, getCurExternalChat 获取当前客户群的群ID

      const jsApiList = getJsApiList()

      window.entryUrl = window.location.href

      if (jsApiList) {

        const uri = '/fwapi/'

        axios

          .get('/fwapi/ccqw/api/util/corp/js/api/ticket', {

            headers: {

              corpId: getKey('corpId')

            },

            params: {

              url: window.entryUrl

            }

          })

          .then(res => {

            const env = process.env.NODE_ENV

            const data = res.data.data

            console.log('签名信息', data)

            if (env === 'production') {

              // 进行 wx.config 校验

              _this.wxConfig(data, jsApiList)

            } else {

              // 本机开发测试配置, 不需要进行 wx.config 校验

              this.nativeInit()

            }

          })

          .catch(res => {})

      }

    },

  


    // 验证微信接口

    wxConfig (data, jsApiList) {

      console.log('进入wxConfig 函数', wx, window.jWeixin)

      const _this = this

      const wxMethod = this.wxMethod

        wxMethod.config({

          beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题

          debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

          appId: getKey('corpId'), // 必填,公众号的唯一标识

          timestamp: data.timestamp, // 必填,生成签名的时间戳

          nonceStr: data.nonceStr, // 必填,生成签名的随机串

          signature: data.signature, // 必填,签名,见附录1

          jsApiList: jsApiList // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

        })

       wxMethod.error(function (res) {

         // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。

          console.log(' 失败', res)

        })

        wxMethod.ready(function () {

          agentSign(() => {

            console.log('从agentSign 返回,准备进入 getContext 方法')

            wxMethod.invoke('getContext', {}, function (res) {

              // var entry = '入口'

              if (res.err_msg == 'getContext:ok') {

                uni.setStorageSync('sysList', res.entry) //返回进入H5页面的入口类型,目前有normal、contact_profile、single_chat_tools、group_chat_tools

                _this.initpage()

                // entry = res.entry //返回进入H5页面的入口类型,目前有normal、contact_profile、single_chat_tools、group_chat_tools

              } else {

                //错误处理

                return

              }

            })

          })

        })

   

    },

    // 本机开发测试配置

    nativeInit () {

      uni.navigateTo({

        url: `/material`

      })

    },

    // 跳转页面

    initpage () {

      uni.navigateTo({

        url: `/material`

      })

    }

  }

}

</script>

  


<style></style>

新建一个utils/index.js 文件


import axios from 'axios'

const ENV = !!(process.env.VUE_APP_TITLE === 'production')

  


// this.entry = uni.getStorageSync('sysList') || ''

// let entry = uni.getStorageSync('sysList') || ''

const keyOptions = {

  corpId: uni.getStorageSync('corpId') || 'ww446eead17e5f5a4c',

  agentId: uni.getStorageSync('agentId'),

  accessid: ENV ? 'skyscrm' : '9001',

  signKey: ENV ? 'y6BcpY46HNB4jeshAksWYxf4nJZr9TtK' : 'MYETSFT22RSJkEIF',

  // gwApiUrl: ENV ? '' : 'https://beta-api-gw.coocaa.com/',

}

const jsApiList = [

  'getContext',

  'getCurExternalContact',

  'getCurExternalChat',

  'sendChatMessage'

]

export function getJsApiList() {

  return jsApiList

}

export function isMac () {

  return /macintosh|mac os x/i.test(navigator.userAgent)

}

// agentConfig 签名

export function agentSign(callback) {

  return new Promise((resole, reject) => {

    axios

      .get('/fwapi/ccqw/api/util/corp/agent/js/api/ticket', {

        headers: {

          corpId: getKey('corpId')

        },

        params: {

          url: window.location.href

        }

      })

      .then(agentRes => {

        const agentData = agentRes.data.data

        console.log('签名信息', agentData)

        console.log('agentId', getKey('agentId'))

        console.log('corpId', getKey('corpId'))

        console.log('进入agentConfig 函数222', wx, window.jWeixin)

        let agentConfig = isMac() ?  wx.agentConfig : window.jWeixin.agentConfig

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

        agentConfig({

          // 进入微信客服消息界面

          corpid: getKey('corpId'), // 必填,企业微信的corpid,必须与当前登录的企业一致

          agentid: getKey('agentId'), // 必填,企业微信的应用id (e.g. 1000247)

          timestamp: agentData.timestamp, // 必填,生成签名的时间戳

          nonceStr: agentData.nonceStr, // 必填,生成签名的随机串

          signature: agentData.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法

          jsApiList: jsApiList, //必填

          success: function (data) {

            console.log('agentConfig 成功')

            resole(true)

            callback()

            console.log('agentConfig 成功1')

          },

          fail: function (res) {

            reject(false)

            console.log('agentConfig 失败')

            if (res.errMsg.indexOf('function not exist') > -1) {

              alert('版本过低请升级')

            }

          }

        })

      })

      .catch(e => {

      })

  })

  


}

export function getKey(value) {

  return keyOptions[value]

}