公众号开发(十二)JS-SDK配置及自定义分享
在网页中要使用分享等一系列的微信JS-SDK功能,要先配置好JS-SDK参数。
安装jweixin-module
要使用公众号H5的JS-SDK功能,先要安装jweixin-module
在bctos-weixin-h5根目录下,执行npm初始化(如果你的根目录下已经有package.json文件可省这一步)
npm init
一直默认回车即可
然后正式安装jweixin-module
npm install jweixin-module --save
安装好后在App.vue文件中引入
var wx = require('jweixin-module')
生成JS-SDK配置参数
然后在pages/index/index页面的onReady方法中加入生成JS-SDK配置参数,让页面一加载进来就完成配置
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
uniCloud.callFunction({
name: 'bctos-weixin',
data: {
"action": "getJsApiParams",
"url": location.href.split('#')[0]
},
success(res) {
console.log('getJsApiParams get res', res)
if (res.result.code != 0) {
getApp().error(res.result.message)
} else {
getApp().share(res.result)
}
}
})
一定要注意上面url的参数,一定要填写location.href.split('#')[0]
在这一步最容易出现invalid signature错误,我们也是调试了很久才修改好,如果真出现这个问题,先按官方的建议排查一下
1. 确认签名算法正确,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
2. 确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。
3. 确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。
4. 确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。
5. 确保一定缓存access_token和jsapi_ticket。
6. 确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。
后端云函数实现getJsApiParams方法的代码
async function getApiTicket() {
//先从缓存获取,如果存在就要不重复从微信获取
let key = '' + appId
let ticket = await cache(key)
if (ticket !== false) {
console.log('ticket缓存已存在', ticket);
return ticket
}
let access_token = await getAccessToken()
if (access_token == false) {
return {
code: 1,
message: '获取access_token失败'
}
} else {
console.log('获取access_token成功', access_token);
}
//这里也要使用代理网址请求
let url = 'http://weixin-agent.bctos.cn/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi'
let res = await uniCloud.httpclient.request(url, {
method: 'GET',
data: {}
})
let result = JSON.parse(res.data.toString())
if (typeof(result.ticket) == "undefined") {
console.log('重新获取的Ticket失败了', result)
return false
} else {
console.log('重新获取的Ticket的结果:', result)
await cache(key, result.ticket, result.expires_in)
return result.ticket
}
}
//获取ticket
let ticket = await getApiTicket()
if (ticket == false) {
return {
code: 2,
message: '获取ticket失败'
}
} else {
console.log('获取ticket成功', ticket);
}
let ret = {
jsapi_ticket: ticket,
noncestr: createNonceStr(),
timestamp: createTimestamp(),
url: event.url
};
let string = raw(ret)
console.log('排序后加加密前的字段串', string)
ret.signature = sha1(string);
ret.appId = appId
ret.code = 0
console.log('最终返回的参数', ret)
return ret
注意上面获取ticket的地址,特别是&type=jsapi这个参数,由于微信文档里还有一个获取会员卡ticket的地方,名字都是相同的,URL前面的地址也是一样的,只是后面&type=card不一样。之前不知道,就是这个问题导致我们整整调试了两天,反复按官方上面提到的6点调试了很久,还是报invalid signature报错,所以真的如果报这个错误,要一个字母一个字母都仔细比对才行。
实现自定义分享
成功获取到JS-SDK参数后,就可以在前端使用wx.config进行配置了,下面是配置加自定义分享的内容,如果需要加其它功能,如地理位置等信息,需要有jsApiList里加入对于的接口名
share(jsapi_params) {
let config = {
debug: false,
appId: jsapi_params.appId, // 必填,公众号的唯一标识
timestamp: jsapi_params.timestamp, // 必填,生成签名的时间戳
nonceStr: jsapi_params.noncestr, // 必填,生成签名的随机串
signature: jsapi_params.signature, // 必填,签名,见附录1
jsApiList: [
'updateAppMessageShareData',
'updateTimelineShareData'
]
}
console.log('配置参数', config);
wx.config(config);
wx.error(function(res) {
console.log('JS-SDK配置出问题了', res)
});
wx.ready(function() {
//自定义分享的内容,开发者可以自主修改这部分内容
let shareData1 = {
title: '个体工商户在线注册',
link: "https://caiwu.bctos.cn/index.html?suid=" + uni.getStorageSync('share_uid') +
'#pages/config/detail',
imgUrl: "https://caiwu.bctos.cn/static/share.jpg",
success: function(res) {
console.log('分享设置成功', res);
}
}
let shareData2 = {
...shareData1,
desc: '通过深牛财务线上操作,轻松,快捷办理个体营业执照。'
}
wx.updateAppMessageShareData(shareData2)
wx.updateTimelineShareData(shareData1)
});
},
要看效果不能在浏览器里预览,一定要在微信开发者工具里看,因为浏览器无法调用微信的JS接口。这是在微信开发者工具的公众号网页调试里看到的效果,看到分享设置成功说明js-sdk参数已经配置正常,在手机里分享的话就可以看到自定义的分享内容了

在网页中要使用分享等一系列的微信JS-SDK功能,要先配置好JS-SDK参数。
安装jweixin-module
要使用公众号H5的JS-SDK功能,先要安装jweixin-module
在bctos-weixin-h5根目录下,执行npm初始化(如果你的根目录下已经有package.json文件可省这一步)
npm init
一直默认回车即可
然后正式安装jweixin-module
npm install jweixin-module --save
安装好后在App.vue文件中引入
var wx = require('jweixin-module')
生成JS-SDK配置参数
然后在pages/index/index页面的onReady方法中加入生成JS-SDK配置参数,让页面一加载进来就完成配置
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
uniCloud.callFunction({
name: 'bctos-weixin',
data: {
"action": "getJsApiParams",
"url": location.href.split('#')[0]
},
success(res) {
console.log('getJsApiParams get res', res)
if (res.result.code != 0) {
getApp().error(res.result.message)
} else {
getApp().share(res.result)
}
}
})
一定要注意上面url的参数,一定要填写location.href.split('#')[0]
在这一步最容易出现invalid signature错误,我们也是调试了很久才修改好,如果真出现这个问题,先按官方的建议排查一下
1. 确认签名算法正确,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
2. 确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。
3. 确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。
4. 确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。
5. 确保一定缓存access_token和jsapi_ticket。
6. 确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。
后端云函数实现getJsApiParams方法的代码
async function getApiTicket() {
//先从缓存获取,如果存在就要不重复从微信获取
let key = '' + appId
let ticket = await cache(key)
if (ticket !== false) {
console.log('ticket缓存已存在', ticket);
return ticket
}
let access_token = await getAccessToken()
if (access_token == false) {
return {
code: 1,
message: '获取access_token失败'
}
} else {
console.log('获取access_token成功', access_token);
}
//这里也要使用代理网址请求
let url = 'http://weixin-agent.bctos.cn/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi'
let res = await uniCloud.httpclient.request(url, {
method: 'GET',
data: {}
})
let result = JSON.parse(res.data.toString())
if (typeof(result.ticket) == "undefined") {
console.log('重新获取的Ticket失败了', result)
return false
} else {
console.log('重新获取的Ticket的结果:', result)
await cache(key, result.ticket, result.expires_in)
return result.ticket
}
}
//获取ticket
let ticket = await getApiTicket()
if (ticket == false) {
return {
code: 2,
message: '获取ticket失败'
}
} else {
console.log('获取ticket成功', ticket);
}
let ret = {
jsapi_ticket: ticket,
noncestr: createNonceStr(),
timestamp: createTimestamp(),
url: event.url
};
let string = raw(ret)
console.log('排序后加加密前的字段串', string)
ret.signature = sha1(string);
ret.appId = appId
ret.code = 0
console.log('最终返回的参数', ret)
return ret
注意上面获取ticket的地址,特别是&type=jsapi这个参数,由于微信文档里还有一个获取会员卡ticket的地方,名字都是相同的,URL前面的地址也是一样的,只是后面&type=card不一样。之前不知道,就是这个问题导致我们整整调试了两天,反复按官方上面提到的6点调试了很久,还是报invalid signature报错,所以真的如果报这个错误,要一个字母一个字母都仔细比对才行。
实现自定义分享
成功获取到JS-SDK参数后,就可以在前端使用wx.config进行配置了,下面是配置加自定义分享的内容,如果需要加其它功能,如地理位置等信息,需要有jsApiList里加入对于的接口名
share(jsapi_params) {
let config = {
debug: false,
appId: jsapi_params.appId, // 必填,公众号的唯一标识
timestamp: jsapi_params.timestamp, // 必填,生成签名的时间戳
nonceStr: jsapi_params.noncestr, // 必填,生成签名的随机串
signature: jsapi_params.signature, // 必填,签名,见附录1
jsApiList: [
'updateAppMessageShareData',
'updateTimelineShareData'
]
}
console.log('配置参数', config);
wx.config(config);
wx.error(function(res) {
console.log('JS-SDK配置出问题了', res)
});
wx.ready(function() {
//自定义分享的内容,开发者可以自主修改这部分内容
let shareData1 = {
title: '个体工商户在线注册',
link: "https://caiwu.bctos.cn/index.html?suid=" + uni.getStorageSync('share_uid') +
'#pages/config/detail',
imgUrl: "https://caiwu.bctos.cn/static/share.jpg",
success: function(res) {
console.log('分享设置成功', res);
}
}
let shareData2 = {
...shareData1,
desc: '通过深牛财务线上操作,轻松,快捷办理个体营业执照。'
}
wx.updateAppMessageShareData(shareData2)
wx.updateTimelineShareData(shareData1)
});
},
要看效果不能在浏览器里预览,一定要在微信开发者工具里看,因为浏览器无法调用微信的JS接口。这是在微信开发者工具的公众号网页调试里看到的效果,看到分享设置成功说明js-sdk参数已经配置正常,在手机里分享的话就可以看到自定义的分享内容了

公众号开发(十一)实现access_token获取
要获取access_token必须先完成前面几节的配置:
前端网页托管域名配置:https://bctos.cn/doc/18/1955
公众号域名配置:https://bctos.cn/doc/18/1959
公众号IP白名单配置:https://bctos.cn/doc/18/1963
云函数缓存机制实现:https://bctos.cn/doc/18/1964
确保上面的配置都好了之后,就可以进行access_token的获取了。
由于access_token每天请求的次数有限,必须统一在后端云函数里获取并缓存起来,因此我们的实现代码如下
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
async function getAccessToken() {
//先从缓存获取,如果存在就要不重复从微信获取
let key = '' + appId
let token = await cache(key)
if (token !== false) {
console.log('token缓存已存在', token);
return token
}
//这里要使用代理网址请求
let url = 'http://weixin-agent.bctos.cn/cgi-bin/token?grant_type=client_credential'
url += '&secret=' + appSecret + '&appId=' + appId
let res = await uniCloud.httpclient.request(url, {
method: 'GET',
data: {}
})
let result = JSON.parse(res.data.toString())
if (typeof(result.access_token) == "undefined") {
console.log('重新获取的AccessToken失败了', result)
return false
} else {
console.log('重新获取的AccessToken的结果:', result)
await cache(key, result.access_token, result.expires_in)
return result.access_token
}
}
如果获取失败,可以在uniCloud的WEB后台里的云函数日志中看到微信返回了什么错误信息
要获取access_token必须先完成前面几节的配置:
前端网页托管域名配置:https://bctos.cn/doc/18/1955
公众号域名配置:https://bctos.cn/doc/18/1959
公众号IP白名单配置:https://bctos.cn/doc/18/1963
云函数缓存机制实现:https://bctos.cn/doc/18/1964
确保上面的配置都好了之后,就可以进行access_token的获取了。
由于access_token每天请求的次数有限,必须统一在后端云函数里获取并缓存起来,因此我们的实现代码如下
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
async function getAccessToken() {
//先从缓存获取,如果存在就要不重复从微信获取
let key = '' + appId
let token = await cache(key)
if (token !== false) {
console.log('token缓存已存在', token);
return token
}
//这里要使用代理网址请求
let url = 'http://weixin-agent.bctos.cn/cgi-bin/token?grant_type=client_credential'
url += '&secret=' + appSecret + '&appId=' + appId
let res = await uniCloud.httpclient.request(url, {
method: 'GET',
data: {}
})
let result = JSON.parse(res.data.toString())
if (typeof(result.access_token) == "undefined") {
console.log('重新获取的AccessToken失败了', result)
return false
} else {
console.log('重新获取的AccessToken的结果:', result)
await cache(key, result.access_token, result.expires_in)
return result.access_token
}
}
如果获取失败,可以在uniCloud的WEB后台里的云函数日志中看到微信返回了什么错误信息
收起阅读 »uni-app 获取已链接的蓝牙列表
官方没有找到如何获取已链接的蓝牙列表,使用如下方法实现,节省大家的时间。
getConnetedDevices(){
var main = plus.android.runtimeMainActivity();
var Context = plus.android.importClass("android.content.Context");
var BManager = main.getSystemService(Context.BLUETOOTH_SERVICE);
plus.android.importClass(BManager); //引入相关的method函数
var BAdapter = BManager.getAdapter();
plus.android.importClass(BAdapter);//引入相关的method函数,这样之后才会有isEna
var lists = BAdapter.getBondedDevices();
plus.android.importClass(lists);
var iterator = lists.iterator();
plus.android.importClass(iterator);
while(iterator.hasNext()) {
var d = iterator.next();
plus.android.importClass(d);
console.log("名称:"+d.getName()+",地址:"+d.getAddress());
}
} 官方没有找到如何获取已链接的蓝牙列表,使用如下方法实现,节省大家的时间。
getConnetedDevices(){
var main = plus.android.runtimeMainActivity();
var Context = plus.android.importClass("android.content.Context");
var BManager = main.getSystemService(Context.BLUETOOTH_SERVICE);
plus.android.importClass(BManager); //引入相关的method函数
var BAdapter = BManager.getAdapter();
plus.android.importClass(BAdapter);//引入相关的method函数,这样之后才会有isEna
var lists = BAdapter.getBondedDevices();
plus.android.importClass(lists);
var iterator = lists.iterator();
plus.android.importClass(iterator);
while(iterator.hasNext()) {
var d = iterator.next();
plus.android.importClass(d);
console.log("名称:"+d.getName()+",地址:"+d.getAddress());
}
} 收起阅读 »
公众号开发(十)云函数缓存机制实现
由于云函数每次运行完都会注销,不像其它后端(如PHP)可以使用session或者文件缓存数据。理论上自己部署redis也是用来缓存数据,但redis不可能部署到云函数的内网中,走外网的话性能肯定大打折扣。因此只能使用云数据库来缓存数组,虽然这会增加数据库的访问次数,但也是没办法的事,好在云数据库的性能比较高,不像mysql那样会成为性能瓶颈
数据库就用之前建好的bctos-cache,它只有三个字段:键名(key),缓存内容(value)和过期时间(expired)
其中为了能保存对象数据,所有保存的数据在入库前先经过 JSON.stringify, 出库时会使用 JSON.parse 进行还原。所以不建议直接查询bctos-cache,而是调用云函数处理。
为方便使用,我们直接在APP.vue里封装了cache方法
async function cache(key, value, expired) {
console.log('缓存的参数', {
key,
value,
expired
});
let res = await uniCloud.callFunction({
name: 'bctos-common',
data: {
action: "cache",
key,
value,
expired
}
})
console.log('缓存的结果', res);
if (typeof(res.result.data) == "undefined") {
return res.result
} else {
return res.result.data
}
}
可以这样方便的调用
let app = getApp();
//写入缓存,并且长期有效
app.cache('test','123456')
//写入缓存,而且只缓存300秒,到期自动删除
app.cache('test','123456', 300)
//读取缓存
let res = await app.cache('test')
//删除缓存, value设为null表示删除
app.cache('test', null)
如果是在云函数中使用缓存,建议把上面的cache方法复制到云函数中使用
有了这缓存的功能,后面就可以用来缓存access_token和ticket了
由于云函数每次运行完都会注销,不像其它后端(如PHP)可以使用session或者文件缓存数据。理论上自己部署redis也是用来缓存数据,但redis不可能部署到云函数的内网中,走外网的话性能肯定大打折扣。因此只能使用云数据库来缓存数组,虽然这会增加数据库的访问次数,但也是没办法的事,好在云数据库的性能比较高,不像mysql那样会成为性能瓶颈
数据库就用之前建好的bctos-cache,它只有三个字段:键名(key),缓存内容(value)和过期时间(expired)
其中为了能保存对象数据,所有保存的数据在入库前先经过 JSON.stringify, 出库时会使用 JSON.parse 进行还原。所以不建议直接查询bctos-cache,而是调用云函数处理。
为方便使用,我们直接在APP.vue里封装了cache方法
async function cache(key, value, expired) {
console.log('缓存的参数', {
key,
value,
expired
});
let res = await uniCloud.callFunction({
name: 'bctos-common',
data: {
action: "cache",
key,
value,
expired
}
})
console.log('缓存的结果', res);
if (typeof(res.result.data) == "undefined") {
return res.result
} else {
return res.result.data
}
}
可以这样方便的调用
let app = getApp();
//写入缓存,并且长期有效
app.cache('test','123456')
//写入缓存,而且只缓存300秒,到期自动删除
app.cache('test','123456', 300)
//读取缓存
let res = await app.cache('test')
//删除缓存, value设为null表示删除
app.cache('test', null)
如果是在云函数中使用缓存,建议把上面的cache方法复制到云函数中使用
有了这缓存的功能,后面就可以用来缓存access_token和ticket了
收起阅读 »公众号开发(九)公众号IP白名单配置
之前无论是获取openid还是获取用户信息,都是不需要使用普通接口需要的access_token参数的。但后面的接口就需要用到access_token了,而要获取微信的access_token,是需要配置IP白名单的,但我们的uniCloud云函数是运行在不同的容器中,IP地址是变化的,不是固定的。因此要完全免费实现微信公众号功能,目前看来不大可能。
想要有固定IP地址,方法有三个:
一是使用付费云空间
有钱就是任性,直接购买能提供固定IP的付费云空间就好,这个最简单
二是使用宝塔在自己服务器搭建反向代理
针对上面第二种自己有服务器的情况,并且服务器已经安装了宝塔管理系统,可以按下面流程来增加
增加站点
输入你的域名(以weixin-agent.bctos.cn为例),PHP版本选择静态即可
点网站名进入配置界面
进入反向代理--增加代理
目标URL固定填写https://api.weixin.qq.com,发送域名固定为api.weixin.qq.com,其它内容随意
保存即可生效
然后把自己服务器的外网IP地址配置到公众号平台(mp.weixin.qq.com)的基本配置页面的IP白名单中
然后后面调用微信接口的网址由https://api.weixin.qq.com/xxx修改为代理网址http://weixin-agent.bctos.cn/xxx即可。
如获取access_token的网址:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
那么使用代理的网址是:
http://weixin-agent.bctos.cn/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
注意我没有配置代理域名的证书,因此只能使用http,如果你配置好证书,也可以使用https
三是共享代理
如果你没有自己的服务器,专门买一台服务器作代理的话花钱多而且还浪费资源。这时可以选择我们的共享代理。
共享代理就是我们出一台服务器,已经按上面的方法搭好了反向代理,你只需要付一小笔费用,就可以使用我们的服务器作代理了。
要使用共享代理请加微信(bctos-cn)咨询,付费后我们会给你服务器IP地址和代理网址(不是上面演示的网址),你只需要配置到你的公众号上即可,我们会提供长期的技术支持的哦。
扫码加微信

之前无论是获取openid还是获取用户信息,都是不需要使用普通接口需要的access_token参数的。但后面的接口就需要用到access_token了,而要获取微信的access_token,是需要配置IP白名单的,但我们的uniCloud云函数是运行在不同的容器中,IP地址是变化的,不是固定的。因此要完全免费实现微信公众号功能,目前看来不大可能。
想要有固定IP地址,方法有三个:
一是使用付费云空间
有钱就是任性,直接购买能提供固定IP的付费云空间就好,这个最简单
二是使用宝塔在自己服务器搭建反向代理
针对上面第二种自己有服务器的情况,并且服务器已经安装了宝塔管理系统,可以按下面流程来增加
增加站点
输入你的域名(以weixin-agent.bctos.cn为例),PHP版本选择静态即可
点网站名进入配置界面
进入反向代理--增加代理
目标URL固定填写https://api.weixin.qq.com,发送域名固定为api.weixin.qq.com,其它内容随意
保存即可生效
然后把自己服务器的外网IP地址配置到公众号平台(mp.weixin.qq.com)的基本配置页面的IP白名单中
然后后面调用微信接口的网址由https://api.weixin.qq.com/xxx修改为代理网址http://weixin-agent.bctos.cn/xxx即可。
如获取access_token的网址:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
那么使用代理的网址是:
http://weixin-agent.bctos.cn/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
注意我没有配置代理域名的证书,因此只能使用http,如果你配置好证书,也可以使用https
三是共享代理
如果你没有自己的服务器,专门买一台服务器作代理的话花钱多而且还浪费资源。这时可以选择我们的共享代理。
共享代理就是我们出一台服务器,已经按上面的方法搭好了反向代理,你只需要付一小笔费用,就可以使用我们的服务器作代理了。
要使用共享代理请加微信(bctos-cn)咨询,付费后我们会给你服务器IP地址和代理网址(不是上面演示的网址),你只需要配置到你的公众号上即可,我们会提供长期的技术支持的哦。
扫码加微信

公众号开发(八)从微信中获取用户昵称头像
上一节我们使用openid完成了注册和登录,但uni-id里的用户信息都是空的,这一节我们将实现在微信中获取用户昵称头像。
类似获取openid流程,获取用户信息也是需要跳转页面的,并且还需要用户确认授权才能获取到信息
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
代码已加详细说明
async getWeixinUserInfo() {
//如果缓存存在,则直接返回
let user = uni.getStorageSync('wx-user')
if (user) return user
//本地无法跳转,不能获取user
if (window.location.href.indexOf('localhost') != -1) return false;
//先取uid,保存获取用户信息之前已经通过openid完成注册登录
let uid = await this.getUid()
if (!uid) return false;
//先读取公众号配置
let config = await this.bctosCommon('config')
let appid = config.result.appId
let secret = config.result.appSecret
//获取微信跳转带回来的参数
let option = this.getGetParam()
if (typeof(option.state) == "undefined" || option.state != 'bctos') { //第一步:用户同意授权,获取code
let redirect_uri = encodeURIComponent(location.href.split('#')[0])
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/' +
`authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=bctos#wechat_redirect`
} else if (typeof(option.state) != "undefined" && option.state == 'bctos') {
//第二步:通过code换取网页授权access_token
if (!typeof(option.code) || option.code == '') {
this.error('获取code失败')
} else {
//前端不能跨域请求微信服务器,只能通过云函数代请求, 获取openid和access_token
let res = await this.bctosCommon('jsonp', {
url: 'https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code' +
`&appid=${appid}&secret=${secret}&code=${option.code}`
})
let data = res.result.data
if (typeof(data) != "object" || typeof(data.openid) == "undefined") {
this.error(JSON.stringify(data))
} else {
uni.setStorageSync('openid', data.openid)
//获取用户信息
res = await this.bctosCommon('jsonp', {
url: `https://api.weixin.qq.com/sns/userinfo?access_token=${data.access_token}&openid=${data.openid}&lang=zh_CN`
})
if (typeof(res.result.data) != "object" || typeof(res.result.data.openid) == "undefined") {
this.error(JSON.stringify(res.result.data))
} else {
//获取的结果缓存在前端
uni.setStorageSync('wx-user', res.result.data)
//获取的结果保存到uni-id的用户表中
this.bctosCommon('saveUserInfo', {
uid,
...res.result.data
})
return res.result.data
}
}
}
}
return false
}
虽然它调用它后端云函数jsonp,但它只是一个代获取远程信息的作用,代码如下
if (!event.method) {
event.method = 'GET'
}
if (!event.data) {
event.data = {}
}
if (!event.dataType) {
event.dataType = 'json'
}
res = await uniCloud.httpclient.request(event.url, {
method: event.method,
data: event.data,
dataType: event.dataType
})
因为如果直接在前端使用uni.request请求的话,会报跨域错误,只能使用后端uniCloud.httpclient.request方法来请求。
得到结果后保存到uni-id的代码如下
let save = {
uid: event.uid,
wx_openid: event.openid,
nickname: event.nickname,
gender: event.sex,
province: event.province,
city: event.city,
country: event.country,
avatar: event.headimgurl
}
if (typeof(event.unionid) != "undefined") save.wx_unionid = event.unionid
res = await uniID.updateUser(save)
其中国家(country),省(province),市(city)这三个字段有uni-id的用户表结构中是没有定义的,但不影响数据的保存
上一节我们使用openid完成了注册和登录,但uni-id里的用户信息都是空的,这一节我们将实现在微信中获取用户昵称头像。
类似获取openid流程,获取用户信息也是需要跳转页面的,并且还需要用户确认授权才能获取到信息
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
代码已加详细说明
async getWeixinUserInfo() {
//如果缓存存在,则直接返回
let user = uni.getStorageSync('wx-user')
if (user) return user
//本地无法跳转,不能获取user
if (window.location.href.indexOf('localhost') != -1) return false;
//先取uid,保存获取用户信息之前已经通过openid完成注册登录
let uid = await this.getUid()
if (!uid) return false;
//先读取公众号配置
let config = await this.bctosCommon('config')
let appid = config.result.appId
let secret = config.result.appSecret
//获取微信跳转带回来的参数
let option = this.getGetParam()
if (typeof(option.state) == "undefined" || option.state != 'bctos') { //第一步:用户同意授权,获取code
let redirect_uri = encodeURIComponent(location.href.split('#')[0])
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/' +
`authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=bctos#wechat_redirect`
} else if (typeof(option.state) != "undefined" && option.state == 'bctos') {
//第二步:通过code换取网页授权access_token
if (!typeof(option.code) || option.code == '') {
this.error('获取code失败')
} else {
//前端不能跨域请求微信服务器,只能通过云函数代请求, 获取openid和access_token
let res = await this.bctosCommon('jsonp', {
url: 'https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code' +
`&appid=${appid}&secret=${secret}&code=${option.code}`
})
let data = res.result.data
if (typeof(data) != "object" || typeof(data.openid) == "undefined") {
this.error(JSON.stringify(data))
} else {
uni.setStorageSync('openid', data.openid)
//获取用户信息
res = await this.bctosCommon('jsonp', {
url: `https://api.weixin.qq.com/sns/userinfo?access_token=${data.access_token}&openid=${data.openid}&lang=zh_CN`
})
if (typeof(res.result.data) != "object" || typeof(res.result.data.openid) == "undefined") {
this.error(JSON.stringify(res.result.data))
} else {
//获取的结果缓存在前端
uni.setStorageSync('wx-user', res.result.data)
//获取的结果保存到uni-id的用户表中
this.bctosCommon('saveUserInfo', {
uid,
...res.result.data
})
return res.result.data
}
}
}
}
return false
}
虽然它调用它后端云函数jsonp,但它只是一个代获取远程信息的作用,代码如下
if (!event.method) {
event.method = 'GET'
}
if (!event.data) {
event.data = {}
}
if (!event.dataType) {
event.dataType = 'json'
}
res = await uniCloud.httpclient.request(event.url, {
method: event.method,
data: event.data,
dataType: event.dataType
})
因为如果直接在前端使用uni.request请求的话,会报跨域错误,只能使用后端uniCloud.httpclient.request方法来请求。
得到结果后保存到uni-id的代码如下
let save = {
uid: event.uid,
wx_openid: event.openid,
nickname: event.nickname,
gender: event.sex,
province: event.province,
city: event.city,
country: event.country,
avatar: event.headimgurl
}
if (typeof(event.unionid) != "undefined") save.wx_unionid = event.unionid
res = await uniID.updateUser(save)
其中国家(country),省(province),市(city)这三个字段有uni-id的用户表结构中是没有定义的,但不影响数据的保存
收起阅读 »公众号开发(七)uni-id自动注册和登录
在上一节我们得到openid之后,就可以在uni-id绑定用户,实现注册和登录功能了。
前端比较简单,只需要把openid提交给后端,由后端操作uni-id实现注册和登录,得到用户ID后缓存起来供后面的使用
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
async getUid() {
//如果缓存存在,则直接返回
let user = uniCloud.getCurrentUserInfo()
console.log('user', user);
if (user.uid) return user.uid
//如果openid不存在,自动先去获取openid
let openid = await this.getOpenid()
console.log('openid', openid);
if (!openid) return false
//后端使用openid实现自动注册和登录功能
let res = await this.bctosCommon('loginOrRegister', {
openid
})
console.log('get res', res)
if (res.result.code === 0) {
//返回成功,缓存uni-id相关信息
uni.setStorageSync('uni_id_token', res.result.token)
uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
uni.setStorageSync('uid', res.result.uid)
return res.result.uid
} else {
this.error(res.result.message)
return false
}
}
后面的实现代码,直接使用openid作为用户名和密码进行注册,当然这个开发者也可以加点其它信息让密码复杂些。
let param = {
username: event.openid,
password: event.openid
}
res = await uniID.login({
...param,
queryField: ['username']
})
if (res.code == 10101 || res.code == 10002) {
console.log('need register')
param.wx_openid = event.openid
if (typeof(event.unionid) != "undefined") param.wx_unionid = event.unionid
res = await uniID.register(param)
}
它先使用uniID.login登录,如果错误提示码为10101或10002表示用户不存在,就先使用uniID.register完成注册和登录
这就是openid与用户ID实现绑定的逻辑,比较简单。
在上一节我们得到openid之后,就可以在uni-id绑定用户,实现注册和登录功能了。
前端比较简单,只需要把openid提交给后端,由后端操作uni-id实现注册和登录,得到用户ID后缓存起来供后面的使用
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
async getUid() {
//如果缓存存在,则直接返回
let user = uniCloud.getCurrentUserInfo()
console.log('user', user);
if (user.uid) return user.uid
//如果openid不存在,自动先去获取openid
let openid = await this.getOpenid()
console.log('openid', openid);
if (!openid) return false
//后端使用openid实现自动注册和登录功能
let res = await this.bctosCommon('loginOrRegister', {
openid
})
console.log('get res', res)
if (res.result.code === 0) {
//返回成功,缓存uni-id相关信息
uni.setStorageSync('uni_id_token', res.result.token)
uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
uni.setStorageSync('uid', res.result.uid)
return res.result.uid
} else {
this.error(res.result.message)
return false
}
}
后面的实现代码,直接使用openid作为用户名和密码进行注册,当然这个开发者也可以加点其它信息让密码复杂些。
let param = {
username: event.openid,
password: event.openid
}
res = await uniID.login({
...param,
queryField: ['username']
})
if (res.code == 10101 || res.code == 10002) {
console.log('need register')
param.wx_openid = event.openid
if (typeof(event.unionid) != "undefined") param.wx_unionid = event.unionid
res = await uniID.register(param)
}
它先使用uniID.login登录,如果错误提示码为10101或10002表示用户不存在,就先使用uniID.register完成注册和登录
这就是openid与用户ID实现绑定的逻辑,比较简单。
收起阅读 »公众号开发(六)获取微信公众号的用户openid
从现在开始正式进入公众号H5开发,首先需要先获取用户openid。
由于获取openid需要先跳转到微信然后带参数跳转回来,因此只能在前端实现openid的获取。
实现获取openid代码
可能以后很多页面都需要获取openid,因此把获取方法直接写在根目录的App.vue文件的methods里
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
代码已经加了详细的说明
methods: {
async getOpenid() {
//如果缓存存在,则直接返回
let openid = uni.getStorageSync('openid')
if (openid) return openid
//本地无法跳转,不能获取openid
if (window.location.href.indexOf('localhost') != -1) return false;
//先读取公众号配置
let config = await this.bctosCommon('config')
let appid = config.result.appId
let secret = config.result.appSecret
//获取微信跳转带回来的参数
let option = this.getGetParam()
if (typeof(option.state) == "undefined" || option.state != 'bctos') {
//第一步:用户同意授权,获取code
let redirect_uri = encodeURIComponent(location.href.split('#')[0])
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/'
`authorize?appid=${appid} 从现在开始正式进入公众号H5开发,首先需要先获取用户openid。
由于获取openid需要先跳转到微信然后带参数跳转回来,因此只能在前端实现openid的获取。
实现获取openid代码
可能以后很多页面都需要获取openid,因此把获取方法直接写在根目录的App.vue文件的methods里
完整的代码请在官方插件市场下载:https://ext.dcloud.net.cn/plugin?id=4829
代码已经加了详细的说明
methods: {
async getOpenid() {
//如果缓存存在,则直接返回
let openid = uni.getStorageSync('openid')
if (openid) return openid
//本地无法跳转,不能获取openid
if (window.location.href.indexOf('localhost') != -1) return false;
//先读取公众号配置
let config = await this.bctosCommon('config')
let appid = config.result.appId
let secret = config.result.appSecret
//获取微信跳转带回来的参数
let option = this.getGetParam()
if (typeof(option.state) == "undefined" || option.state != 'bctos') {
//第一步:用户同意授权,获取code
let redirect_uri = encodeURIComponent(location.href.split('#')[0])
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/'
`authorize?appid=${appid} 收起阅读 »
【app+sockjs】app下,stompjs不支持uniapp环境?
在uniapp下,用浏览器和h5调试,使用sockjs+stompjs没有任何问题。但是app下,无法使用stompjs,貌似不兼容。只好用sockjs,然后模拟stompjs的订阅,发送消息给websocket了。
在uniapp下,用浏览器和h5调试,使用sockjs+stompjs没有任何问题。但是app下,无法使用stompjs,貌似不兼容。只好用sockjs,然后模拟stompjs的订阅,发送消息给websocket了。
uniapp 蓝牙打印机 打印图片、二维码、条码、文字等 最新解决方案【兼容 小程序、android App】无偿分享出
参考连接:
使用native.js 在uni app中 连接经典蓝牙 任意打印图片,摆脱ble低功耗的低速传输
便携式蓝牙打印,研究了很长时间,终于打印出来了,现在分享经验,省的少走弯路
热敏打印机编程 ESC/POS指令 【尤其这篇对图片处理讲解的非常好】
两个页面:
使用native.js连接蓝牙的demo -- 快速打印(只兼容app)
使用BLE低功耗蓝牙的demo -- 低速打印(可兼容小程序)
努力研究了三天,熬夜两个通宵终于打印出来了
【如果你们打印出现偏差,请检查图片的宽度是不是8的倍数,如果不是就用画图调整一下】
其他的问题请 私信我
demo地址 gitee开源中国:BluetoothPrinter 欢迎star
参考连接:
使用native.js 在uni app中 连接经典蓝牙 任意打印图片,摆脱ble低功耗的低速传输
便携式蓝牙打印,研究了很长时间,终于打印出来了,现在分享经验,省的少走弯路
热敏打印机编程 ESC/POS指令 【尤其这篇对图片处理讲解的非常好】
两个页面:
使用native.js连接蓝牙的demo -- 快速打印(只兼容app)
使用BLE低功耗蓝牙的demo -- 低速打印(可兼容小程序)
努力研究了三天,熬夜两个通宵终于打印出来了
【如果你们打印出现偏差,请检查图片的宽度是不是8的倍数,如果不是就用画图调整一下】
其他的问题请 私信我
demo地址 gitee开源中国:BluetoothPrinter 欢迎star
收起阅读 »公众号开发(五)公众号域名配置及参数配置
公众号平台配置
登录公众号平台(https://mp.weixin.qq.com/)
在公众号设置--功能设置页面,先设置好业务,JS,网页三个地方的安全域名。
在测试号设置安全域名(正式公众号设置的地方在: 公众号设置--功能设置 里):
设置网页授权域名
确认保存
公众号参数配置到uni-config-center
由于公众号的参数(如appid,appsecret等)都是比较机密的信息,直接写到前端uniapp里不大安全,可以像上一节增加uni-id配置一样增加公众号配置到uni-config-center
在uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center目录下创建bctos-weixin-h5目录,然后在其下面创建config.json文档,要根据自己公众号的实际的参数填写
{
"appId": "公众号appid",
"appSecret": "公众号密钥",
"mchId": "微信支付商户ID",
"mchKey": "微信支付API密钥",
"notify_url": "微信支付回调地址"
}
增加读取配置的云函数
由于前端uniapp无法直接读取uni-config-center里的配置,需要一个云函数帮我们实现参数的读取。
在uniCloud/cloudfunctions目录上右键新键云函数,函数名为:bctos-config-center,在自动生成的index.js文件写入以下代码
'use strict';
const createConfig = require('uni-config-center')
exports.main = async (event, context) => {
//event为客户端上传的参数
console.log('event : ', event)
if (typeof(event.pluginId) == "undefined") event.pluginId = 'bctos-weixin-h5';
const uniIdConfig = createConfig({
pluginId: event.pluginId
})
if (typeof(event.key) == "undefined" || event.key == '') {
console.log('获取全部配置');
return uniIdConfig.config();
} else {
console.log('获取指定配置');
return uniIdConfig.config(event.key);
}
};
由于这个云函数使用了uni-config-center,因此需要右键bctos-config-center目录管理公共模块依赖
之前由于对云函数理解不深,以为直接引用就可以,其实每个云函数在运行都是在相互独立的环境中,因此资源是不互相共用的,云函数每次引用别的资源时,都需要重新执行管理公共模块依赖以便更新依赖。
测试前端是否能获取到配置
打开pages/index/index文件,直接onLoad方法里加入测试代码看看能不能取到配置数据
onLoad() {
uniCloud.callFunction({
name: "bctos-config-center",
data: {}
}).then(res => {
console.log('config', res);
})
},
在HBuilder X的调试里选择连接本地云函数,因为云函数和配置还没上传云端,所以要选择本地调试
然后运行项目到内置浏览器看看
运行起来后我们可以看到配置信息
说明我们已经能正常读取公众号配置信息
公众号平台配置
登录公众号平台(https://mp.weixin.qq.com/)
在公众号设置--功能设置页面,先设置好业务,JS,网页三个地方的安全域名。
在测试号设置安全域名(正式公众号设置的地方在: 公众号设置--功能设置 里):
设置网页授权域名
确认保存
公众号参数配置到uni-config-center
由于公众号的参数(如appid,appsecret等)都是比较机密的信息,直接写到前端uniapp里不大安全,可以像上一节增加uni-id配置一样增加公众号配置到uni-config-center
在uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center目录下创建bctos-weixin-h5目录,然后在其下面创建config.json文档,要根据自己公众号的实际的参数填写
{
"appId": "公众号appid",
"appSecret": "公众号密钥",
"mchId": "微信支付商户ID",
"mchKey": "微信支付API密钥",
"notify_url": "微信支付回调地址"
}
增加读取配置的云函数
由于前端uniapp无法直接读取uni-config-center里的配置,需要一个云函数帮我们实现参数的读取。
在uniCloud/cloudfunctions目录上右键新键云函数,函数名为:bctos-config-center,在自动生成的index.js文件写入以下代码
'use strict';
const createConfig = require('uni-config-center')
exports.main = async (event, context) => {
//event为客户端上传的参数
console.log('event : ', event)
if (typeof(event.pluginId) == "undefined") event.pluginId = 'bctos-weixin-h5';
const uniIdConfig = createConfig({
pluginId: event.pluginId
})
if (typeof(event.key) == "undefined" || event.key == '') {
console.log('获取全部配置');
return uniIdConfig.config();
} else {
console.log('获取指定配置');
return uniIdConfig.config(event.key);
}
};
由于这个云函数使用了uni-config-center,因此需要右键bctos-config-center目录管理公共模块依赖
之前由于对云函数理解不深,以为直接引用就可以,其实每个云函数在运行都是在相互独立的环境中,因此资源是不互相共用的,云函数每次引用别的资源时,都需要重新执行管理公共模块依赖以便更新依赖。
测试前端是否能获取到配置
打开pages/index/index文件,直接onLoad方法里加入测试代码看看能不能取到配置数据
onLoad() {
uniCloud.callFunction({
name: "bctos-config-center",
data: {}
}).then(res => {
console.log('config', res);
})
},
在HBuilder X的调试里选择连接本地云函数,因为云函数和配置还没上传云端,所以要选择本地调试
然后运行项目到内置浏览器看看
运行起来后我们可以看到配置信息
说明我们已经能正常读取公众号配置信息
收起阅读 »




