前言
- 安卓端可以使用,苹果和PC端使用不了,这是个困扰了我很久的问题,在全网查找的时候发现,这个15年就有人陆续提出了这个问题,但是一直没有解决方案,企业微信侧也没有给出明确的解释,直到我在腾讯拉了企微的开发过来,才发现就是个10年都没去优化的陷阱。。。
- 解决方案见下方步骤四
背景
- 企业微信审批和dcops工单平台的互联
- 在企业微信的审批应用的一些审批模版里添加一个控件,这个控件可以获取到dcops的工单号
实现方案
查阅文档和页面
-
在企微审批应用后台模版里添加一个 多选或者单选的控件,这个控件的"选项来源"选择"关联外部选项",就会出现一个填外部选项页面地址的输入框
- 我们需要获取到用户身份,用该用户身份登录和划定可选工单号的范围,就需要完成 带登录态跳转移动端页面
- 根据文档 构造网页授权链接
- 注意必须使用urlencode对链接进行处理 然后再赋值给redirect_uri,urlencode是指对URL 中的某些字符,如空格、问号、等号等,转换为特定编码格式
- 注意必须使用urlencode对链接进行处理 然后再赋值给redirect_uri,urlencode是指对URL 中的某些字符,如空格、问号、等号等,转换为特定编码格式
-
查看 审批控件中的外部选项接口文档 发现调用该接口需要先完成两件事情
- 先调用 wx,agentConfig配置
- 获取到这个控件添加到url里的key,下面调用接口时会用到
配置实现
一、引入js
<!-- <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> -->
<script src="https://res.wx.qq.com/wwopen/js/jsapi/jweixin-1.0.0.js"></script>
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
二、通过config接口注入权限验证配置
window.wx.config({
beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug: true, // 在测试没问题之后,关闭debug模式,防止弹窗
appId: 'xxxx', // 必填,企业微信的corpID
timestamp: this.timestamp, // 必填,生成签名的时间戳
nonceStr: 'xxxx', // 必填,生成签名的随机串
signature: this.signatrue, // 必填,签名,见 附录-JS-SDK使用权限签名算法
jsApiList: ['agentConfig','saveApprovalSelectedItems','getApprovalSelectedItems'], // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
});
wx.error((e) => {
e && alert(JSON.stringify(e));
});
!!#ff0000 FAQ!!
生成签名
-
完成wx.config之前,需要先去生成签名signature。文档JS-SDK使用权限签名算法
-
必需的noncestr、timestamp必须和上一步wx.config里填写的nonceStr、timestamp一模一样。
-
url不包含#之后的部分,可使用
window.location.href.split('#')[0]
处理 -
拼接好之后必须用sha1加密
-
完成生成签名signature之前,需要先去生成临时票据,获取企业的jsapi_ticket
- jsapi_ticket的有效期为7200秒,api调用次数也非常有限,需做缓存处理
三、通过agentConfig接口注入应用(审批应用)权限验证配置
window.wx.ready((e) => {
window.wx.agentConfig({
beta: true,
corpid: 'xxx', // 必填,企业微信的corpid,必须与当前登录的企业一致
agentid: 'xxx', // 必填,企业微信的应用id
timestamp: this.timestamp, // 必填,生成签名的时间戳
nonceStr: 'xxx', // 必填,生成签名的随机串
signature: this.appsignature,// 必填,签名,见附录1
jsApiList: ['selectExternalContact', 'saveApprovalSelectedItems','getApprovalSelectedItems'], //必填
success: function(res) {
console.log('应用权限获取成功', res);
},
fail: function(res) {
console.log('应用权限获取失败', res);
}
});
});
!!#ff0000 FAQ!!
调用失败wx.agentConfig is not a function
- 按照企业微信官方文档 引入JS文件 引入的
http://res.wx.qq.com/open/js/jweixin-1.2.0.js
,在调用wx.agentConfig时,会报错Uncaught TypeError: wx.agentConfig is not a function
, - 所以 第一步 的引用链接改为
https://res.wx.qq.com/wwopen/js/jsapi/jweixin-1.0.0.js
,即可调用所有jssdk接口
签名无效invalid signature
- agentConfig与config的签名算法完全一样,但是jsapi_ticket的获取方法不一样,需要单独获取
- 并且获取access_token的也需要用对应应用的secret,参考官方文档
提示需使用应用可信域名
-
“审批应用“这种基础应用是不能配置可信域名的,需要使用第三方自建应用,在网页授权及JS-SDK里配置上redirect_uri的域名或ip,否则会报错permission denied
-
并且在审批应用的“可调用接口的应用”里添加上该第三方自建应用,即可正确调用
-
四、通过wx.invoke接口保存和查询选中的数据
window.wx.invoke('saveApprovalSelectedItems', {
"key": mykey, // 字符串,从 URL 中获取到的 key
"selectedData": selectedData,// 字符串,选中的选项格式化为 JSON 字符串,格式见
},(res) => {
if (res.err_msg === 'saveApprovalSelectedItems:ok') {
console.log('保存结果成功', res);
console.log('保存请求参数', {
"key": mykey,
"selectedData": selectedData,
});
} else {
console.log('保存结果失败', res);
}
});
!!#ff0000 FAQ!!
"确定"按钮不亮,这里应该是企微端控制的
- 确保selectedData 必须是字符串,否则 按钮是灰色的
选中一次数据之后,再次进入选择别的数据,该控件的数据不能被替换掉?
- 两次的key不相同,这里先写一个固定的key测试即可
- 使用固定的key就可以替换了,说明key是和控件绑定的
苹果/iphone端和PC端的“确定”按钮怎么都不会亮,安卓的可以正常使用
-
企微对安卓端的调用没有那么严格,key正确与否、与企微携带过来的key是否相同,都不影响安卓端正常使用
-
苹果/iphone端和PC端的限制比较严格,key必须要填写,而且是企微跳转动态添加过来的。
-
那么问题就来了,如果我用这种
https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE&agentid=AGENTID#wechat_redirect
,带身份验证跳转的形式来构造网页授权链接,审批应用添加的两个参数key和selectorType,应该是添加到了末尾,必然会丢失。 -
这就意味着 身份授权和key不能同时获取到。这是企微一直没有优化的地方,从15年开始就有人提出这个问题了。有以下两种解决方案
- 方案一:不使用企微提供的构造网页授权链接,直接跳转第三方页面。如果第三方网站可以开放直接跳转进入,或者设置白名单,并且不需要使用审批提单人来区分数据(比如我希望提加班审批单的人只能看到他自己的第三方数据,我就需要获取到当前提单人/操作人)。显然不是很好的方案
- 方案二:经测试key必须添加到
redirect_uri
里,并且urlencode,才能正确跳转和获取到key。-
那么我们就可以先直接跳转到第三方页面(比如:https:xxx/test),
-
key和selectorType会被添加到url末尾。拦截页面获取到key之后,添加到
redirect_uri
里
-
再通过"构造网页授权链接"获取身份验证,来完成既获取身份授权又获取到key,相当于跳转了三次。至此PC端和苹果端可以正常使用了。
实现代码如下,XXX根据实际情况填写:
-
// 拦截企微跳转链接,先获取到key再重新跳转
if (this.$route.query.path === 'test') {
try {
// 拦截没有身份授权code的
if (!this.$route.query.code) {
const origin = 'http://XXXX.com'
// 从URL里获取到key
let url = `${origin}/loading?path=test&key=${this.$route.query.key}`;
// 构造网页授权链接并跳转
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=XXX&redirect_uri=${encodeURIComponent(
url
)}&response_type=code&scope=snsapi_base&state=XXX&agentid=XXX#wechat_redirect`;
return;
}
} catch (error) {
console.log(error);
}
}
五、既然key是控件id,而且是动态添加的,那么用接口拉取企微的审批模版数据,是不是就不能用固定控件id解析数据?
- 经多单测试,虽然key确实是控件id,而且是动态添加的,但是审批模版的控件id没有变化,用接口拉取企微的审批模版数据,可以用固定控件id解析数据。
其他尝试
WECOM-JSSDK
- **WECOM-JSSDK**提供了Typescript类型支持、npm引入等新能力
- 也是先权限注入再调用接口。
ww.register({ corpId: '', // 必填,当前用户企业所属企业ID agentId: '', // 必填,当前应用的AgentID jsApiList: ['getExternalContact'], // 必填,需要使用的JSAPI列表 getConfigSignature, // 必填,根据url生成企业签名的回调函数 getAgentConfigSignature // 必填,根据url生成应用签名的回调函数 }) async function getConfigSignature(url) { // 根据 url 生成企业签名 // 生成方法参考 https://developer.work.weixin.qq.com/document/14924 return { timestamp, nonceStr, signature } } async function getAgentConfigSignature(url) { // 根据 url 生成应用签名,生成方法同上,但需要使用应用的 jsapi_ticket return { timestamp, nonceStr, signature } }
FAQ
wx.invoke用不了
- 需要使用ww.invoke,但是并没有官方文档说明,应该和wx的用法一样
本地调试可以使用ww.getSignature,传入JSAPI_TICKET即可,虽然会报错,但是不影响使用
- 用法:开发者快速接入-生成tSignature
- 报错: