解决ios手机企业微信 wx.config is not function, wx.agentConfig is not function的情况
封装 wechatUtils.js
.
import {
getJsSdkSignature,
} from "@/app/api/home.js"
const jsApiList = ['startRecord', 'stopRecord', 'onVoiceRecordEnd', 'translateVoice', 'chooseMessageFile',
'getLocalFileData'
]
class wechatUtils {
constructor() {
this.timer = null
this.isInitedSdk = false //初始化成功
this.isInitSdkError = false //初始化有误
this.isLoading = true //正在初始化
this.isReInit = false //需要重新初始化&js
// this.isReInitJs = false //需要重新初始化(需要加载js)
this.initFn() //注册时就初始化
}
//初始化
initFn(isReInit, callback) {
this.timer = null
this.isInitedSdk = false
this.isInitSdkError = false
this.isLoading = true
this.isReInit = isReInit
this.initJssdkAgent(jsApiList, () => {
// this.startChoose()
this.isLoading = false
this.isInitedSdk = true
callback && callback()
}, () => {
this.isLoading = false
this.isInitSdkError = true
uni.showToast({
icon: "none",
title: "获取企微录音/上传文件权限失败,请重新进入(请升级企微至4.0.20版本以上)",
duration: 4000
})
callback && callback()
})
}
//请求&动态加载js
async createdScript(callback) {
let url = window.location.href.split('#')[0]
console.log('createdScript--url', url);
let res = await getJsSdkSignature({
url
})
let sdkInfo = {}
if (res.code === "000000") {
sdkInfo = res.data
}
console.log('sdkInfo', sdkInfo);
if (!this.isReInit) { //重新初始化&不加载js了
callback && callback(sdkInfo)
return
}
window.wx = null;
const script1 = document.createElement("script");
script1.setAttribute("type", "text/javascript");
script1.setAttribute("referrerpolicy", "origin");
script1.setAttribute("src", "https://res.wx.qq.com/open/js/jweixin-1.2.0.js");
document.head.appendChild(script1);
script1.onload = function() {
const script2 = document.createElement("script");
script2.setAttribute("type", "text/javascript");
script2.setAttribute("referrerpolicy", "origin");
script2.setAttribute("src", "https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js");
document.head.appendChild(script2);
script2.onload = () => {
// 执行wx.config的逻辑
console.log("动态js2的onload成功");
// callback && callback(server, userId, url, cropId)
callback && callback(sdkInfo)
}
script2.onerror = function(err2) {
if (this.isReInit) {
uni.showToast({
icon: "none",
title: "获取企微录音/上传文件权限失败,请重新进入(请升级企微至4.0.20版本以上)",
duration: 4000
})
}
this.isReInit = true
this.isLoading = false
this.isInitSdkError = true
console.log("动态js2的onload失败", err2);
console.log('File Loaded Error2');
};
}
script1.onerror = function(err) {
if (this.isReInit) {
uni.showToast({
icon: "none",
title: "获取企微录音/上传文件权限失败,请重新进入(请升级企微至4.0.20版本以上)",
duration: 4000
})
}
this.isReInit = true
this.isLoading = false
this.isInitSdkError = true
console.log("动态js1的onload失败", err);
console.log('File Loaded Error1');
};
}
//初始化sdk
async initJssdk(jsApiList, callback) {
console.log("uni.getSystemInfoSync().platform: ", uni.getSystemInfoSync().platform);
if (uni.getSystemInfoSync().platform == 'ios') {
let url = window.location.href.split('#')[0]
console.log('ios====url', url);
let res = await getJsSdkSignature({
url
})
let sdkInfo = {}
if (res.code === "000000") {
sdkInfo = res.data
}
this.initConfig(jsApiList, sdkInfo, () => {
callback && callback(sdkInfo)
})
} else {
console.log('this', this);
this.createdScript(sdkInfo => {
console.log('initJssdk===createdScript成功');
window.wx = window.jWeixin;
this.initConfig(jsApiList, sdkInfo, () => {
callback && callback(sdkInfo)
})
})
}
}
//初始化config
initConfig(jsApiList, sdkInfo, callback) {
console.log('initConfig===', wx);
if (!wx || !wx.config) {
if (this.isReInit) {
uni.showToast({
icon: "none",
title: "获取企微录音/上传文件权限失败,请重新进入(请升级企微至4.0.20版本以上)",
duration: 4000
})
}
this.isReInit = true
this.isLoading = false
this.isInitSdkError = true
console.log("wx初始化失败");
return
}
let config = sdkInfo.config
try {
console.log('initConfig==wx.config是什么', wx.config);
wx.config({
beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: config.cropId, // 必填,企业微信的corpID
timestamp: config.timestamp, // 必填,生成签名的时间戳
nonceStr: config.nonceStr, // 必填,生成签名的随机串
signature: config.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法
jsApiList // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来, // 必填,签名,见 附录-JS-SDK使用权限签名算法
// jsApiList: ['startRecord', 'stopRecord', 'onVoiceRecordEnd',
// 'translateVoice'
// ] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
});
wx.ready(function() {
console.log('initConfig===wx.ready');
callback && callback(sdkInfo)
})
wx.error(function(res) {
console.error('initConfig===wx.error', res);
uni.showToast({
title: res,
icon: "none"
});
});
} catch (error) {
console.log(error)
}
}
//初始化AgentConfig
initJssdkAgent(jsApiList, callback, errCallback) {
this.initJssdk(jsApiList, (sdkInfo) => {
console.log('==sdkInfo=', sdkInfo);
let agentConfig = sdkInfo.agentConfig
console.log('agentConfig===', agentConfig);
setTimeout(() => {
wx.agentConfig({
corpid: agentConfig.corpId, // 必填,企业微信的corpid,必须与当前登录的企业一致
agentid: agentConfig.agentId, // 必填,企业微信的应用id (e.g. 1000247)
timestamp: agentConfig.timestamp, // 必填,生成签名的时间戳
nonceStr: agentConfig.nonceStr, // 必填,生成签名的随机串
signature: agentConfig.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
jsApiList, //必填,传入需要使用的接口名称
success: function(res) {
// 回调
console.log("agentConfig==success", res);
if (res.errMsg == "agentConfig:ok") {
// const tempFiles = res.tempFiles
if (res.checkResult) { //判断是否有checkResult
if (!Object.values(res.checkResult).includes(
false)) { //jsApiList中有一个接口权限是false
//ios成功初始化agentConfig
callback && callback(res)
} else {
errCallback && errCallback(res)
}
} else {
//安卓成功初始化agentConfig
callback && callback(res)
}
} else {
errCallback && errCallback(res)
}
},
fail: function(err) {
console.log("agentConfig==fail", err);
if (err.errMsg.indexOf('function not exist') > -1) {
uni.showToast({
title: "版本过低请升级",
icon: "none"
});
}
errCallback && errCallback(err)
}
});
}, 1000)
})
}
//检测是否初始化完成&是否初始成功
checkToast(callback) {
if (this.isLoading) {
uni.showToast({
icon: "none",
title: "获取权限中,请稍后重试"
})
callback && callback(false)
} else {
if (this.isInitSdkError) { //有错误再进行一次初始化
let addJs = this.isReInit ? true : false
this.initFn(addJs, () => {
if (this.isInitSdkError) {
// uni.showToast({
// icon: "none",
// title: "获取企微录音/上传文件权限失败,请重新进入(请升级企微至4.0.20版本以上)",
// })
console.log("checkToast==isInitSdkError");
callback && callback(false)
}
if (this.isInitedSdk) {
console.log("checkToast==isInitedSdk");
callback && callback(true)
}
})
} else if (this.isInitedSdk) {
console.log("checkToast==isInitedSdk");
callback && callback(true)
}
}
return true
}
//选择会话中的文件
chooseMessageFile(callback, errCallback) {
this.checkToast((pass) => {
if (!pass) {
errCallback && errCallback()
return
}
wx.invoke('chooseMessageFile', {
count: 10,
type: 'file',
}, function(res) {
// 这里是回调函数
console.log('invoke成功--chooseMessageFile');
if (res.err_msg == "chooseMessageFile:ok") {
// const tempFiles = res.tempFiles
callback && callback(res)
} else {
errCallback && errCallback(res)
}
});
})
}
//获取文件base64
getLocalFileData(localId, callback, errCallback) {
wx.invoke('getLocalFileData', {
localId, // 文件的localID
}, function(res) {
// 这里是回调函数
if (res.err_msg == "getLocalFileData:ok") {
// var localData = res.localData; // localData是文件的base64数据
callback && callback(res)
} else {
errCallback && errCallback(res)
}
})
}
//开始录音
startRecord(callback, errCallback) {
let that = this
this.checkToast((pass) => {
if (!pass) {
errCallback && errCallback()
return
}
wx.startRecord({
success: function() {
that.timer = setInterval(() => {
that.time++
}, 1000)
callback && callback()
// that.onVoiceRecordEnd()
},
fail: function() {
// 开始录音失败
},
cancel: function() {
// 用户拒绝授权录音
}
});
})
}
//停止录音
stopRecord(callback, errCallback) {
this.checkToast((pass) => {
if (!pass) {
errCallback && errCallback()
return
}
//停止录音接口
wx.stopRecord({
success: function(res) {
// alert(res.localId);
// let localId = res.localId;
callback && callback(res)
}
})
})
}
//监听到录音停止
onVoiceRecordEnd(callback, errCallback) {
let that = this
//停止录音接口
wx.onVoiceRecordEnd({
complete: function(res) {
// 60秒停止录音
// that.localId = res.localId
// this.translate()
clearInterval(that.timer);
callback && callback(res)
}
})
}
//录音转文字
translateVoice(localId, callback, errCallback) {
//停止录音接口
wx.translateVoice({
localId: localId, // 需要识别的音频的本地Id,由录音相关接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function(res) {
// alert(res.localId);
// let localId = res.localId;
callback && callback(res)
},
fail: (err) => {
errCallback && errCallback(err)
}
})
}
}
export default wechatUtils
App.vue
进行注册
async onLaunch(options) {
uni.console.log("App onLaunch", options);
// uni.log2P(navigator.userAgent)
let ua = "" //设备信息
// #ifdef H5
if (options?.query?.isWxwork) {
this.$store.state.app.isWxwork = true
await this.checkLogin() //企微获取登录信息&接口获取token和用户信息
ua = navigator.userAgent
if (ua.indexOf("WindowsWechat") < 0) { //电脑企微不使用sdk相关
this.$store.state.app.isWxworkPc = false
//初始化全局企微sdk
uni.wechatUtil = new wechatUtil();
}else{
this.$store.state.app.isWxworkPc = true
}
}
// #endif
`````````
},
template.h5.html
引入官方sdk的js
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon" type="image/x-icon" href="https://cdn.uviewui.com/uview/common/favicon.ico">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>
项目标题
</title>
<!--引入企微sdk-->
<script src="https://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>
<style>
::-webkit-scrollbar{
display: none;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
})
</script>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.css" />
</head>
<body>
<!-- 该文件为 H5 平台的模板 HTML,并非应用入口。 -->
<!-- 请勿在此文件编写页面代码或直接运行此文件。 -->
<!-- 详见文档:https://uniapp.dcloud.io/collocation/manifest?id=h5-template -->
<noscript>
<strong>本站点必须要开启JavaScript才能运行</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script>
/*BAIDU_STAT*/
</script>
</body>
</html>
voice.vue
页面调用
//开启录音接口
startRecord() {
this.isPause = false
uni.wechatUtil.startRecord(() => {
this.$emit('change', true)
this.isTranslateErr = false
this.recordEnd()
}, (err) => {
this.isTranslateErr = true
console.log("startRecord--err", err);
})
},
//停止录音接口
stopRecord() {
this.isPause = true
uni.wechatUtil.stopRecord((res) => {
let localId = res.localId;
this.translateVoice(localId)
}, (err) => {
this.isTranslateErr = true
console.log("stopRecord--err", err);
})
},
//监听 60秒停止录音
recordEnd() {
uni.wechatUtil.onVoiceRecordEnd((res) => {
uni.console.log("onVoiceRecordEnd", res);
if (this.value) {
this.isPause = true
let localId = res.localId;
this.translateVoice(localId)
}
})
},
//录音文字识别
translateVoice(localId) {
uni.wechatUtil.translateVoice(localId, (res) => {
let result = res.translateResult;
//去掉最后一个句号
result = result.substring(0, result.length - 1);
this.$emit('onTranslated', result)
}, (err) => {
this.isTranslateErr = true
console.log("translateVoice--err", err);
})
},
从会话选择文件
部分代码
let dataList = await this.chooseMessageFile()
chooseMessageFile() {
return new Promise((resolve, reject) => {
uni.wechatUtil.chooseMessageFile((res) => {
let pass = this.checkRight(res)
if (!pass) {
resolve([])
return
}
// this.tempFiles = res.tempFiles
console.log("res.tempFiles", res.tempFiles)
this.getLocalFileData(res.tempFiles, (dataList) => {
resolve(dataList)
})
})
})
},
getLocalFileData(tempFiles, callback) {
console.log("this.tempFiles===", tempFiles)
let dataList = JSON.parse(JSON.stringify(tempFiles))
let promiseList = []
for (let i = 0; i < dataList.length; i++) {
promiseList.push(
new Promise((resolve, reject) => {
uni.wechatUtil.getLocalFileData(dataList[i].localId, (res) => {
var localData = res.localData
dataList[i].localData = res.localData
resolve()
// console.log("res.localData",res.localData)
uni.showToast({
icon: "none",
title: "成功拿到base64"
})
}, err => {
reject()
console.log("getLocalFileData,err", err)
})
})
)
}
Promise.all(promiseList).then(() => {
callback && callback(dataList)
})
// console.log("dataList===", dataList)
// callback && callback(dataList)
},
uni.uploadFile
上传部分代码
let filePath = this.dataURLtoFile(’拿到的文件base64‘, ’文件名‘)
dataURLtoFile(dataurl, fileName) {
// 将base64转为Unicode规则编码,切记需要将头部文件类型做截取
let bstr = atob(dataurl.substring(dataurl.indexOf(',') + 1), "UTF-8"),
n = bstr.length,
u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n) // 转换编码后才可以使用charCodeAt 找到Unicode编码
}
return new File([u8arr], fileName, {
type: "UTF-8"
})
},
uni.uploadFile({
url: data.host, //仅为示例,非真实的接口地址
filePath: URL.createObjectURL(filePath),
name: 'file',
formData: {
key: fileMsg.obj_name,
policy: data.policy,
OSSAccessKeyId: data.accessid,
signature: data.signature,
callback: data.callback
},
说明
- 在
initJssdk
函数里,安卓需要动态加载js,ios不需要(ios也动态加载会出现undefined和not a function的情况) - 避免在调用方法时,每个地方都要写判断是否初始化完成&是否初始化成功(并且不成功会重新进行初始化),在调用每个sdk能力方法前 做了前置判断,即
checkToast
函数。 - 选择会话文件功能(官方只支持手机企业微信,暂不支持PC),获取到文件base64之后,我们是上传到oss,
uni.uploadFile
方法中的filePath
需要将文件base64转换成File
格式。
参考引用
https://developer.work.weixin.qq.com/document/path/95027
https://ask.dcloud.net.cn/article/39636
https://developers.weixin.qq.com/community/enterprisewechat/doc/0000ae13ec8f30a5238c55d3a5bc00
https://blog.csdn.net/rxh13543515695/article/details/120671328
https://www.cnblogs.com/lizhao123/p/16693819.html