uniapp h5调用企业微信JS-SDK实现录音/语音识别功能、从会话选择文件功能

  • Post author:
  • Post category:uniapp




解决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



版权声明:本文为weixin_45282357原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。