
定时器 编号问题
let end = setInterval(function () { }, 10000);
console.log(end);
for (let i = 1; i <= end; i++) {
clearInterval(i);
}
今天清理定时器的时候,发现一个东西。
放到app.vue里的onLaunch 里,预期返回值应该是 1 的,实际上每次都不一样。
不知道是不是uniapp对定时器进行二次封装了,看文档说返回的是id
而且文档写的也有问题 注意事项中的 无需 应该是 需要

let end = setInterval(function () { }, 10000);
console.log(end);
for (let i = 1; i <= end; i++) {
clearInterval(i);
}
今天清理定时器的时候,发现一个东西。
放到app.vue里的onLaunch 里,预期返回值应该是 1 的,实际上每次都不一样。
不知道是不是uniapp对定时器进行二次封装了,看文档说返回的是id
而且文档写的也有问题 注意事项中的 无需 应该是 需要

发行模式下 发布H5模式 报 API setLocale is not yet implemented
在 main.js 里面 写一段 console.log(uni.setLocale) ,这样就可以了
在 main.js 里面 写一段 console.log(uni.setLocale) ,这样就可以了

uni-push2.0 常见问题
uni-push2.0 常见问题
一、基础问题
1.uni-push2.0 和 uni-push1.0 有什么区别
uni-push1.0:支持 app(Android 和 ios),不支持小程序端、web 端,可以用个推服务端 api 推送
uni-push2.0:支持 app(Android 和 ios)、小程序端、web 端。服务端不支持用个推 api 推送,只能用 dcloud 提供的云函数推送。详见 uni-push 2.0 快速接入指南 。
2.uni-push2.0 应用信息的 MasterSecret 怎么没了
uniCloud 的 服务端(云函数)推送 推送免鉴权,不需要 MasterSecret。详情:服务端推送消息 。
3.uni-push2.0 中 java、php、python 之类的语言怎么用,必须要用云函数吗
必须用云函数。在 uniCloud 的云函数中,加载扩展库 uni-cloud-push
,直接调用相关 API,无需额外的服务端配置。而传统服务器开发者需要把这个 云函数URL化 后变成 http 接口,再由原来的 php 或 java 代码调用这个 http 接口。
4.云函数收费问题
阿里云版 uniCloud 商业版会收费,详情请查看 免费版和商用版
5.自定义铃声
参考文档:https://ext.dcloud.net.cn/plugin?id=7482&from_wecom=1
对于离线的自定义铃声,必须申请自分类权益(小米&华为),oppo、vivo不支持自定义铃声
6.设置角标问题
android、iOS设置角标: https://www.html5plus.org/doc/zh_cn/runtime.html?from_wecom=1#plus.runtime.setBadgeNumber
鸿蒙((HarmonyOS Next))设置角标: uni.setAppBadgeNumber(n: number)
云函数api: https://uniapp.dcloud.net.cn/uniCloud/uni-cloud-push/api.html#uni-cloud-push
7.是否需要云打包?
首次集成push:必须云打包。
修改 android 多厂商参数:必须云打包。
修改 iOS 推送证书:不需要云打包。
8.推送频次限制
推送条数限制:没有限制
单推(to Single)频次限制:没有限制
批量推(to List)频次限制:200万次/天
群推(to APP)频次限制:100次/天,每分钟不能超过5次
9.CID什么情况下会变化
- 若安卓用户超过100天、 iOS用户超过一年,CID 一直没有登录请求(CID 没有与个推服务端建立长链接)。则 CID 会失效,之后再启动会重新生成一个CID;
- 如果应用没有获取sd卡权限,卸载重装/清除缓存,CID会变(CID信息会写入sd卡)。
- 个推Appid参数、应用的包名、苹果Bundleid的修改。
- 苹果手机进行了越狱或系统还原。
二、Android 消息推送问题
安卓厂商离线推送是否要求上架应用商店?
- 目前华为、荣耀、魅族、oppo(测试环境)不要求上架应用商店;vivo 、小米、oppo(正式环境)必须上架应用商店后才可使用离线厂商推送。
安卓消息推送流程说明
- 默认情况下:
- 当CID在线(即app在前台打开运行)时,消息通过个推通道下发到客户端。
- 当CID离线(即app在后台、锁屏、进程关闭)时,有开启对应厂商离线功能的,消息将通过个推侧请求对应厂商侧的服务端,实际的消息是经由厂商服务器下发至客户端。对于没有开启对应厂商功能的,消息将存在个推的离线库中,等待CID在线,再通过个推通道下发到客户端。
- 注意:安卓的消息推送,走了厂商通道的消息就不会再通过个推通道推送至客户端,反之亦是如此,即消息只会推送一次
安卓在线收不到通知
- 手机通知权限是否正常打开,部分安卓8以上的手机通知权限中需要打开【Default】渠道的通知权限。
- 当应用在线时,不会创建“通知栏消息”,此时客户端会立即监听到消息内容。
- 监听到消息内容后,根据业务需要自己判断是否要创建“通知栏消息”,需要就调用创建本地消息API uni.createPushMessage手动创建通知栏消息。
- 服务端执行推送时,传递参数
force_notification:true
,客户端就会自动创建“通知栏消息”(此时你监听不到消息内容),当用户点击通知栏消息后,APP才能监听到消息内容。
安卓离线收不到通知
步骤一:确认CID是否绑定厂商token
在【Uni Push】-【配置管理】-【故障排查】-【 检测CID的状态及信息:】中输入CID查询,看是否会返回厂商token(device token)。
若返回了具体厂商token,请按以下中各厂商部分说明排查。若查询CID未返回token,请看下方步骤二
目前各厂商对消息有额度限制,例如华为单设备单应用每天只能收到 2 条营销类消息,超过后则当天收不到离线。详见 厂商通道限额&QPS说明 ,如果要提升额度,可以参考限额说明文档,向厂商申请系统、私信消息。
-
华为
- 标题长度限制40个字,内容长度限制1024个字。
- emui10的华为手机,检查手机通知权限设置,将【营销通知】的权限也打开,不要默认静默,静默的话是需要下拉通知栏才能看到。
- 手机通知栏消息是否有存满,清除已存的通知栏消息看下新的消息是否能展示。
- 华为【 资讯营销】,一个设备一天只能收到2条离线消息。
-
荣耀
- 荣耀【 资讯营销】,一个设备一天只能收到2条离线消息。
-
VIVO
- 标题长度限制20个字,内容长度限制50个字。
- 检查通知权限,vivo机型默认关闭
- 1个自然日内相同文案的运营消息给同个设备发,vivo会在客户端做去重处理,导致消息不展示
- vivo要求:通知文案中不能带 “包含测试、test字符”、“纯数字”、“纯表情”、“符号”或者“符号+数
字”、“表情+数字”、“表情+符号” - vivo需要上架至应用市场才能使用离线厂商推送。
- vivo【 公信消息】,一个设备一天只能收到2条离线消息。
- 自2025年1月17日之后,通过非官方软件商店下载的应用,无法推送 OPPO 公信消息(建议开发时推送测试消息,参考下方:如何给华为、荣耀、vivo、oppo厂商发送测试消息)
-
小米
- 标题长度限制50个字,内容长度限制128个字。
- 检查手机通知权限设置,小米有不重要通知功能,部分消息可能会存在通知栏不重要通知里
- 服务端推送时,推送代码要加上离线时间设置,不能为空。
- 小米需要上架至应用市场才能使用离线厂商推送。
- 小米【 公信消息】,一个设备一天只能收到5条离线消息。
-
OPPO
- 标题长度限制32个字,内容长度限制200个字。
- 检查手机通知权限是否打开,oppo是默认关闭的,将通知权限下的【Default】通道权限也打开。
- 手机系统时间是否正常
- oppo【 公信消息】,一个设备一天只能收到2条离线消息。
- 自2024年7月29日之后,通过非官方软件商店下载的应用,无法推送 OPPO 公信消息(建议开发时推送测试消息,参考下方:如何给华为、荣耀、vivo、oppo厂商发送测试消息)
-
魅族
- 标题长度限制32个字,内容长度限制100个字。
- 检查消息是否存入了魅族手机右上角【魅族消息盒子】中。
- 清除缓存:手机【系统设置】-【应用管理】-【所有应用】点击右上角【显示系统服务应用】找到【推送服务】和【您自己的 App】,如下图,分别进行“清除数据”,然后重启手机。
步骤二:查询CID未返回token,调试查询厂商code码
-
对应厂商平台上的推送服务状态是否是开启状态。
-
在Dcloud后台【Uni Push】-【厂商推送设置】中保存好厂商参数,并用自有证书提交云打包,且需要打正式包,再获取cid去查询是否有返回。
厂商错误码调试方法如下:
-
需要先安装调试环境,参考安装教程
-
手机连接电脑,手机需开启开发者调试模式,此时不用打开app
-
Windows执行示例:如下图,回车执行:adb logcat | findstr "Assist_" 后打开app
-
Mac执行示例:如下图,回车执行:adb logcat | grep "Assist_" 后打开app
-
此时注意查看返回的厂商Code码,如上图中HW的错误码为907135702,点击下方链接搜索查询官方描述。
-
若未返回任何带有厂商标识的日志,说明厂商推送服务还未置于您app应用内,请重新检查厂商服务开通、配置、云打包相关步骤。
华为(包含荣耀)机型需要额外检查:
- 需要在华为开发者后台配置正确的 sha256 指纹证书
- 云打包用自有证书打正式签名包
- 华为开发者后台包名跟客户端包名需要保持一致
- 厂商推送设置-华为厂商,必须上传agconnect-services.json
- 在华为平台是否开通了华为推送服务
- 保存完参数,需要重新提交云端打包
如何给华为、荣耀、vivo、oppo厂商发送测试消息
Android 厂商有离线推送的额度限制,基本都限制单设备单应用每天只能收到 2 条普通消息;如果要提升额度,可以自己向厂商申请消息分类。详见:厂商通道限额&QPS说明
开发测试阶段额度不足时,可以发送测试消息,目前仅华为、荣耀、vivo、oppo 支持下发测试消息。发送测试消息方式如下:
-
若调用服务端 api 推送,增加options参数:
"options": { "HW": { // 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条,此target_user_type 参数请勿发布至线上。 "/message/android/target_user_type":1 } , "HO": { //值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。 "/android/targetUserType": 1 }, "VV": { //值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。线上请勿使用测试推送 "/pushMode":1 } }
vivo、oppo 发送测试消息,额外需要分别在 vivo 开放平台 、 oppo开放平台 录入测试用户(regid 对应个推cid 绑定的 device token,可以从个推后台的“故障排查”中查询 cid 信息获取)。
华为、荣耀不需要录入测试用户。华为、荣耀、vivo 需要在options内添加测试参数;oppo只需要添加测试设备,不需要options内加参数。
-
若从后台页面推送,填写红框附加参数:
”华为“ 、”/message/android/target_user_type”、”数字“ 、”1“(注意 1 是数字不是字符串)
”荣耀“ 、”/android/targetUserType”、”数字“ 、”1“(注意 1 是数字不是字符串)
”vivo“ 、”/pushMode”、”数字“ 、”1“(注意 1 是数字不是字符串)
三、iOS消息推送问题
iOS消息推送流程说明
- 当CID在线(即app在前台打开运行)时,消息通过个推通道下发到客户端。默认不处理的话是没有任何展示的,透传消息个推只负责传递。
- 当CID离线(即app在后台、锁屏)时,消息将通过个推侧请求对应厂商侧的服务端。
- 注意:与安卓消息流程不同的是,Ios走了厂商离线通道,在消息有效期内,app打开在前台时,透传内容还将通过个推通道传递给客户端; 此时可在客户端透传回调中的参数PushMessage对象中获取aps属性值来判断是否是APNs下发的消息 ,参考推送开发指南
iOS在线收不到消息
当应用在线时,不会创建“通知栏消息”,此时客户端会立即监听到消息内容。
- 监听到消息内容后,根据业务需要自己判断是否要创建“通知栏消息”,需要就调用创建本地消息API uni.createPushMessage手动创建通知栏消息。
- 服务端执行推送时,传递参数
force_notification:true
,客户端就会自动创建“通知栏消息”(此时你监听不到消息内容),当用户点击通知栏消息后,APP才能监听到消息内容。
iOS离线收不到消息
步骤一:确认CID是否绑定苹果devicetoken
在【Uni Push】-【配置管理】-【故障排查】-【 检测CID的状态及信息:】中输入CID查询,看是否会返回苹果devicetoken。
若返回了具体苹果devicetoken,请按以下说明排查,若还未解决,提供CID和任务ID给个推技术支持
注意:返回的devicetoken后面是否带着【developement】字样;若带着,则说明客户端环境是属于苹果的开发环境,苹果推送对开发环境的支持不稳定,建议使用正式环境去进行推送测试。
步骤二:查询CID未返回devicetoken
- 切换手机连接网络,卸载重装试下。
- 使用其他手机安装测试。
- 重新进行云打包。
证书测试一下返回无效、连接报错
- 证书环境问题:通用证书支持开发与正式环境推送;开发证书只支持开发环境推送。
- iOS证书的生成,请参考 证书生成文档。
四、错误提示问题
1、Hbuliderx打包报错,提示包名找不到
查询一下自己上传的service.json文件,和打包时候选择的Android包名是否一致,如果不一致需要修改为一致信息
2、ios新建的证书会报证书环境错误
需要正确上传推送证书
注意:
打包的时候用的开发者证书 后台ios配置需要配的是推送证书
下载ios证书必须是ios的会员,非会员是不能下载的
3、推送时提示:target user is invalid
Unipush推送出现AppidError或者target user is invalid,一般都是您使用的CID,直接通过未进行云打包的自定义基座包获取的,不属于您自己应用,属于Dcloud官方的测试app应用。
需要选择自己的应用,云打包后(自定义基座包或者正式包都行),再获取 cid 。
4、unipush 打包提示应用appid未关联当前服务空间
检查填写的app_id信息,要用unipush 的UNI开头的那个id
5、厂商推送设置配置提示‘验证错误’
- 原因是填写了错误的厂商参数,需要去对应的 厂商平台 核实参数是否一致,且注意填写时不能包含空格。
- 如果确认参数完全一致,可以更换浏览器后再进行填写保存,推荐使用 Google 浏览器。
6、unipush点击运行到基座 毫无反应,弹窗也都没有
点顶部菜单栏帮助-查看运行日志看下:
这种一般都是真机调试版本不对,对应调试的版本和插件版本不一致,当前运行调试的版本没有这个api。检查一下插件的版本和真机版本,更新真机版本解决问题。
7、获取cid提示no push platform报错问题:
没有unipush节点,检查插件工具manifest源码视图里面modules的push配置或者sdkConfigs配置。
8、uniapp在app.vue中接收在线消息时,ios机型点击消息闪退
receive 回调里判断下msg.type是否为空,可能是空指针异常导致闪退。
9、url化后调用提示“应用appId:AppId的unipush服务,未关联当前服务空间
服务空间,unipush那需要关联,项目里也需要关联
10、报错提示 mongo_cell_decision_not_found
注意:扩展库依赖 3 张 opendb 表:opendb-tempdata,opendb-device,uni-id-device。公测版 uniCloud,执行扩展库会自动创建。如果你使用的是 uniCloud 正式版需要自己在 uniCloud 的 web控制台 创建这3张表。
创表路径:
11、获取cid失败,提示:getPushClientId: fail uniPush is not enabled
NVUE项目不行,需要去掉纯nvue选框 https://ask.dcloud.net.cn/question/152548
12、调用报错提示:duplicate message content 或者 push app frequency exceeded
如果推送时不传入 cid,则发送的是全推(给所有用户推)消息。全推每分钟不能超过5次,10分钟内不能推重复消息体 。
通过返回值中的 taskid 几个首字母可以判断是什么推送。RASS:单推、 RASL:批量推、 RASA:全推
请仔细检查云函数是否正确传了 cid
13、后台页面推送时,显示预计人数:0人
原因:没有进行云打包,或者云打包时,未勾选“离线推送”。打的包是小程序 SDK,仅支持发在线透传消息,不会有通知栏展示。
如果是app用户,请把“离线推送”勾选上,云打包再获取cid。 uni-push 2.0 快速接入指南
uni-push2.0 常见问题
一、基础问题
1.uni-push2.0 和 uni-push1.0 有什么区别
uni-push1.0:支持 app(Android 和 ios),不支持小程序端、web 端,可以用个推服务端 api 推送
uni-push2.0:支持 app(Android 和 ios)、小程序端、web 端。服务端不支持用个推 api 推送,只能用 dcloud 提供的云函数推送。详见 uni-push 2.0 快速接入指南 。
2.uni-push2.0 应用信息的 MasterSecret 怎么没了
uniCloud 的 服务端(云函数)推送 推送免鉴权,不需要 MasterSecret。详情:服务端推送消息 。
3.uni-push2.0 中 java、php、python 之类的语言怎么用,必须要用云函数吗
必须用云函数。在 uniCloud 的云函数中,加载扩展库 uni-cloud-push
,直接调用相关 API,无需额外的服务端配置。而传统服务器开发者需要把这个 云函数URL化 后变成 http 接口,再由原来的 php 或 java 代码调用这个 http 接口。
4.云函数收费问题
阿里云版 uniCloud 商业版会收费,详情请查看 免费版和商用版
5.自定义铃声
参考文档:https://ext.dcloud.net.cn/plugin?id=7482&from_wecom=1
对于离线的自定义铃声,必须申请自分类权益(小米&华为),oppo、vivo不支持自定义铃声
6.设置角标问题
android、iOS设置角标: https://www.html5plus.org/doc/zh_cn/runtime.html?from_wecom=1#plus.runtime.setBadgeNumber
鸿蒙((HarmonyOS Next))设置角标: uni.setAppBadgeNumber(n: number)
云函数api: https://uniapp.dcloud.net.cn/uniCloud/uni-cloud-push/api.html#uni-cloud-push
7.是否需要云打包?
首次集成push:必须云打包。
修改 android 多厂商参数:必须云打包。
修改 iOS 推送证书:不需要云打包。
8.推送频次限制
推送条数限制:没有限制
单推(to Single)频次限制:没有限制
批量推(to List)频次限制:200万次/天
群推(to APP)频次限制:100次/天,每分钟不能超过5次
9.CID什么情况下会变化
- 若安卓用户超过100天、 iOS用户超过一年,CID 一直没有登录请求(CID 没有与个推服务端建立长链接)。则 CID 会失效,之后再启动会重新生成一个CID;
- 如果应用没有获取sd卡权限,卸载重装/清除缓存,CID会变(CID信息会写入sd卡)。
- 个推Appid参数、应用的包名、苹果Bundleid的修改。
- 苹果手机进行了越狱或系统还原。
二、Android 消息推送问题
安卓厂商离线推送是否要求上架应用商店?
- 目前华为、荣耀、魅族、oppo(测试环境)不要求上架应用商店;vivo 、小米、oppo(正式环境)必须上架应用商店后才可使用离线厂商推送。
安卓消息推送流程说明
- 默认情况下:
- 当CID在线(即app在前台打开运行)时,消息通过个推通道下发到客户端。
- 当CID离线(即app在后台、锁屏、进程关闭)时,有开启对应厂商离线功能的,消息将通过个推侧请求对应厂商侧的服务端,实际的消息是经由厂商服务器下发至客户端。对于没有开启对应厂商功能的,消息将存在个推的离线库中,等待CID在线,再通过个推通道下发到客户端。
- 注意:安卓的消息推送,走了厂商通道的消息就不会再通过个推通道推送至客户端,反之亦是如此,即消息只会推送一次
安卓在线收不到通知
- 手机通知权限是否正常打开,部分安卓8以上的手机通知权限中需要打开【Default】渠道的通知权限。
- 当应用在线时,不会创建“通知栏消息”,此时客户端会立即监听到消息内容。
- 监听到消息内容后,根据业务需要自己判断是否要创建“通知栏消息”,需要就调用创建本地消息API uni.createPushMessage手动创建通知栏消息。
- 服务端执行推送时,传递参数
force_notification:true
,客户端就会自动创建“通知栏消息”(此时你监听不到消息内容),当用户点击通知栏消息后,APP才能监听到消息内容。
安卓离线收不到通知
步骤一:确认CID是否绑定厂商token
在【Uni Push】-【配置管理】-【故障排查】-【 检测CID的状态及信息:】中输入CID查询,看是否会返回厂商token(device token)。
若返回了具体厂商token,请按以下中各厂商部分说明排查。若查询CID未返回token,请看下方步骤二
目前各厂商对消息有额度限制,例如华为单设备单应用每天只能收到 2 条营销类消息,超过后则当天收不到离线。详见 厂商通道限额&QPS说明 ,如果要提升额度,可以参考限额说明文档,向厂商申请系统、私信消息。
-
华为
- 标题长度限制40个字,内容长度限制1024个字。
- emui10的华为手机,检查手机通知权限设置,将【营销通知】的权限也打开,不要默认静默,静默的话是需要下拉通知栏才能看到。
- 手机通知栏消息是否有存满,清除已存的通知栏消息看下新的消息是否能展示。
- 华为【 资讯营销】,一个设备一天只能收到2条离线消息。
-
荣耀
- 荣耀【 资讯营销】,一个设备一天只能收到2条离线消息。
-
VIVO
- 标题长度限制20个字,内容长度限制50个字。
- 检查通知权限,vivo机型默认关闭
- 1个自然日内相同文案的运营消息给同个设备发,vivo会在客户端做去重处理,导致消息不展示
- vivo要求:通知文案中不能带 “包含测试、test字符”、“纯数字”、“纯表情”、“符号”或者“符号+数
字”、“表情+数字”、“表情+符号” - vivo需要上架至应用市场才能使用离线厂商推送。
- vivo【 公信消息】,一个设备一天只能收到2条离线消息。
- 自2025年1月17日之后,通过非官方软件商店下载的应用,无法推送 OPPO 公信消息(建议开发时推送测试消息,参考下方:如何给华为、荣耀、vivo、oppo厂商发送测试消息)
-
小米
- 标题长度限制50个字,内容长度限制128个字。
- 检查手机通知权限设置,小米有不重要通知功能,部分消息可能会存在通知栏不重要通知里
- 服务端推送时,推送代码要加上离线时间设置,不能为空。
- 小米需要上架至应用市场才能使用离线厂商推送。
- 小米【 公信消息】,一个设备一天只能收到5条离线消息。
-
OPPO
- 标题长度限制32个字,内容长度限制200个字。
- 检查手机通知权限是否打开,oppo是默认关闭的,将通知权限下的【Default】通道权限也打开。
- 手机系统时间是否正常
- oppo【 公信消息】,一个设备一天只能收到2条离线消息。
- 自2024年7月29日之后,通过非官方软件商店下载的应用,无法推送 OPPO 公信消息(建议开发时推送测试消息,参考下方:如何给华为、荣耀、vivo、oppo厂商发送测试消息)
-
魅族
- 标题长度限制32个字,内容长度限制100个字。
- 检查消息是否存入了魅族手机右上角【魅族消息盒子】中。
- 清除缓存:手机【系统设置】-【应用管理】-【所有应用】点击右上角【显示系统服务应用】找到【推送服务】和【您自己的 App】,如下图,分别进行“清除数据”,然后重启手机。
步骤二:查询CID未返回token,调试查询厂商code码
-
对应厂商平台上的推送服务状态是否是开启状态。
-
在Dcloud后台【Uni Push】-【厂商推送设置】中保存好厂商参数,并用自有证书提交云打包,且需要打正式包,再获取cid去查询是否有返回。
厂商错误码调试方法如下:
-
需要先安装调试环境,参考安装教程
-
手机连接电脑,手机需开启开发者调试模式,此时不用打开app
-
Windows执行示例:如下图,回车执行:adb logcat | findstr "Assist_" 后打开app
-
Mac执行示例:如下图,回车执行:adb logcat | grep "Assist_" 后打开app
-
此时注意查看返回的厂商Code码,如上图中HW的错误码为907135702,点击下方链接搜索查询官方描述。
-
若未返回任何带有厂商标识的日志,说明厂商推送服务还未置于您app应用内,请重新检查厂商服务开通、配置、云打包相关步骤。
华为(包含荣耀)机型需要额外检查:
- 需要在华为开发者后台配置正确的 sha256 指纹证书
- 云打包用自有证书打正式签名包
- 华为开发者后台包名跟客户端包名需要保持一致
- 厂商推送设置-华为厂商,必须上传agconnect-services.json
- 在华为平台是否开通了华为推送服务
- 保存完参数,需要重新提交云端打包
如何给华为、荣耀、vivo、oppo厂商发送测试消息
Android 厂商有离线推送的额度限制,基本都限制单设备单应用每天只能收到 2 条普通消息;如果要提升额度,可以自己向厂商申请消息分类。详见:厂商通道限额&QPS说明
开发测试阶段额度不足时,可以发送测试消息,目前仅华为、荣耀、vivo、oppo 支持下发测试消息。发送测试消息方式如下:
-
若调用服务端 api 推送,增加options参数:
"options": { "HW": { // 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条,此target_user_type 参数请勿发布至线上。 "/message/android/target_user_type":1 } , "HO": { //值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。 "/android/targetUserType": 1 }, "VV": { //值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。线上请勿使用测试推送 "/pushMode":1 } }
vivo、oppo 发送测试消息,额外需要分别在 vivo 开放平台 、 oppo开放平台 录入测试用户(regid 对应个推cid 绑定的 device token,可以从个推后台的“故障排查”中查询 cid 信息获取)。
华为、荣耀不需要录入测试用户。华为、荣耀、vivo 需要在options内添加测试参数;oppo只需要添加测试设备,不需要options内加参数。
-
若从后台页面推送,填写红框附加参数:
”华为“ 、”/message/android/target_user_type”、”数字“ 、”1“(注意 1 是数字不是字符串)
”荣耀“ 、”/android/targetUserType”、”数字“ 、”1“(注意 1 是数字不是字符串)
”vivo“ 、”/pushMode”、”数字“ 、”1“(注意 1 是数字不是字符串)
三、iOS消息推送问题
iOS消息推送流程说明
- 当CID在线(即app在前台打开运行)时,消息通过个推通道下发到客户端。默认不处理的话是没有任何展示的,透传消息个推只负责传递。
- 当CID离线(即app在后台、锁屏)时,消息将通过个推侧请求对应厂商侧的服务端。
- 注意:与安卓消息流程不同的是,Ios走了厂商离线通道,在消息有效期内,app打开在前台时,透传内容还将通过个推通道传递给客户端; 此时可在客户端透传回调中的参数PushMessage对象中获取aps属性值来判断是否是APNs下发的消息 ,参考推送开发指南
iOS在线收不到消息
当应用在线时,不会创建“通知栏消息”,此时客户端会立即监听到消息内容。
- 监听到消息内容后,根据业务需要自己判断是否要创建“通知栏消息”,需要就调用创建本地消息API uni.createPushMessage手动创建通知栏消息。
- 服务端执行推送时,传递参数
force_notification:true
,客户端就会自动创建“通知栏消息”(此时你监听不到消息内容),当用户点击通知栏消息后,APP才能监听到消息内容。
iOS离线收不到消息
步骤一:确认CID是否绑定苹果devicetoken
在【Uni Push】-【配置管理】-【故障排查】-【 检测CID的状态及信息:】中输入CID查询,看是否会返回苹果devicetoken。
若返回了具体苹果devicetoken,请按以下说明排查,若还未解决,提供CID和任务ID给个推技术支持
注意:返回的devicetoken后面是否带着【developement】字样;若带着,则说明客户端环境是属于苹果的开发环境,苹果推送对开发环境的支持不稳定,建议使用正式环境去进行推送测试。
步骤二:查询CID未返回devicetoken
- 切换手机连接网络,卸载重装试下。
- 使用其他手机安装测试。
- 重新进行云打包。
证书测试一下返回无效、连接报错
- 证书环境问题:通用证书支持开发与正式环境推送;开发证书只支持开发环境推送。
- iOS证书的生成,请参考 证书生成文档。
四、错误提示问题
1、Hbuliderx打包报错,提示包名找不到
查询一下自己上传的service.json文件,和打包时候选择的Android包名是否一致,如果不一致需要修改为一致信息
2、ios新建的证书会报证书环境错误
需要正确上传推送证书
注意:
打包的时候用的开发者证书 后台ios配置需要配的是推送证书
下载ios证书必须是ios的会员,非会员是不能下载的
3、推送时提示:target user is invalid
Unipush推送出现AppidError或者target user is invalid,一般都是您使用的CID,直接通过未进行云打包的自定义基座包获取的,不属于您自己应用,属于Dcloud官方的测试app应用。
需要选择自己的应用,云打包后(自定义基座包或者正式包都行),再获取 cid 。
4、unipush 打包提示应用appid未关联当前服务空间
检查填写的app_id信息,要用unipush 的UNI开头的那个id
5、厂商推送设置配置提示‘验证错误’
- 原因是填写了错误的厂商参数,需要去对应的 厂商平台 核实参数是否一致,且注意填写时不能包含空格。
- 如果确认参数完全一致,可以更换浏览器后再进行填写保存,推荐使用 Google 浏览器。
6、unipush点击运行到基座 毫无反应,弹窗也都没有
点顶部菜单栏帮助-查看运行日志看下:
这种一般都是真机调试版本不对,对应调试的版本和插件版本不一致,当前运行调试的版本没有这个api。检查一下插件的版本和真机版本,更新真机版本解决问题。
7、获取cid提示no push platform报错问题:
没有unipush节点,检查插件工具manifest源码视图里面modules的push配置或者sdkConfigs配置。
8、uniapp在app.vue中接收在线消息时,ios机型点击消息闪退
receive 回调里判断下msg.type是否为空,可能是空指针异常导致闪退。
9、url化后调用提示“应用appId:AppId的unipush服务,未关联当前服务空间
服务空间,unipush那需要关联,项目里也需要关联
10、报错提示 mongo_cell_decision_not_found
注意:扩展库依赖 3 张 opendb 表:opendb-tempdata,opendb-device,uni-id-device。公测版 uniCloud,执行扩展库会自动创建。如果你使用的是 uniCloud 正式版需要自己在 uniCloud 的 web控制台 创建这3张表。
创表路径:
11、获取cid失败,提示:getPushClientId: fail uniPush is not enabled
NVUE项目不行,需要去掉纯nvue选框 https://ask.dcloud.net.cn/question/152548
12、调用报错提示:duplicate message content 或者 push app frequency exceeded
如果推送时不传入 cid,则发送的是全推(给所有用户推)消息。全推每分钟不能超过5次,10分钟内不能推重复消息体 。
通过返回值中的 taskid 几个首字母可以判断是什么推送。RASS:单推、 RASL:批量推、 RASA:全推
请仔细检查云函数是否正确传了 cid
13、后台页面推送时,显示预计人数:0人
原因:没有进行云打包,或者云打包时,未勾选“离线推送”。打的包是小程序 SDK,仅支持发在线透传消息,不会有通知栏展示。
如果是app用户,请把“离线推送”勾选上,云打包再获取cid。 uni-push 2.0 快速接入指南

放弃 uni_modules 拥抱 npm 吧
就 uniapp 目前的技术力量而言,全面拥抱 npm 才是正道。
虽然 uni_modules 貌似王道,但请考虑技术力量不足造成的用户流失的代价。
全面 CLI,拥抱 npm,增强 vscode 插件。
做大做强,再创辉煌。
就 uniapp 目前的技术力量而言,全面拥抱 npm 才是正道。
虽然 uni_modules 貌似王道,但请考虑技术力量不足造成的用户流失的代价。
全面 CLI,拥抱 npm,增强 vscode 插件。
做大做强,再创辉煌。

iOS APP上架流程(详细)
声明:本文转自http://www.2cto.com/kf/201512/453943.html,侵删。
前言:作为一名iOS开发者,把开发出来的App上传到App Store是必须的。下面就来详细介绍下具体流程。
1.打开苹果开发者中心:https://developer.apple.com
打开后点击:Member Center
如果你的电脑没有保存密码,则会提示你输入开发者帐号和密码,因为我的电脑已经保存了,所以直接进入。
2.点击:Certificates, Identifiers & Profiles
3.点击Devices
一.创建App IDs和Boudle ID
4.点击App IDs,会进入如下界面,点击右上角的 + 号
5.填写App IDs和Boudle ID
6.点击continue
7.点击Submit
8.点击Done
二.创建发布证书(若已创建过发布证书,现在用的是p12文件,可直接跳到第21步)
9.点击Production后,点击 + 号
10.点击App Store and Ad Hoc
11.点击Continue
12.点击Continue
创建本地证书
13.此时返回到桌面,在点开LaunchPad,在其他中找到钥匙串访问,切记不要关闭浏览器
14.打开钥匙串访问,点击电脑左上角的钥匙串访问–证书助理–从证书颁发机构请求证书
15.会出现如下界面,选择存储到磁盘,点击继续
16.选择存储到桌面,存储
17.点击完成
18.你会在桌面上看到下面的文件
19.然后回到浏览器,点击choose File.. 选择创建好的:CertificateSigningRequest.certSigningRequest 文件,点击Generate
<h4 id="20点击download<a href=" http:="" www.2cto.com="" soft"="" target="_blank" class="keylink" style="color: rgb(51, 51, 51); font-family: 宋体; font-size: 14px; line-height: 28px;">下载创建好的发布证书cer后缀的文件然后点击done你创建的发布证书就会存储在帐号中">20.点击Download下载创建好的发布证书(cer后缀的文件),然后点击Done,你创建的发布证书就会存储在帐号中。
注:一般一个开发者帐号创建一个发布证书就够了,如果以后需要在其他电脑上上架App,只需要在钥匙串访问中创建p12文件,把p12文件安装到其他电脑上。这相当于给予了其他电脑发布App的权限。
创建PP文件
21.找到Provisioning Profiles ,点击All,然后点击右上角 + 号
22.选择App Store,点击Continue
23.在App ID 这个选项栏里面找到你刚刚创建的:App IDs(Bundle ID) 类型的套装,点击Continue
24.选择你刚创建的发布证书(或者生成p12文件的那个发布证书),点击Continue
25.在Profile Name栏里输入一个名字(这个是PP文件的名字,可随便输入,在这里我用工程名字,便于分别),然后点击Generate
26.Download生成的PP文件,然后点击Done
在App Store开辟空间
27.回到Member Center,点击iTunes Connect
28.点击我的App
29.点击新建 iOSApp
30.依次按提示填入对应信息,然后点击创建
31.依次把不同尺寸的App截图拉入到对应的里面
32.填入App简介
33.按提示依次输入
34.此时这个构建版本还没有生成,我们先把基本信息填写完毕,然后再进入Xcode中把项目打包发送到过来。
注意:填写完一定要点击右上角的保存。
在Xcode中打包工程
找到你刚刚下载的发布证书(后缀为.cer)或者p12文件,和PP文件,双击,看起来没反应,但是他们已经加入到你的钥匙串中。
35.在Xcode中选择模拟器为iOS Device,按照下图提示操作
36.修改.plist文件,两个.plist文件都要修改
37.然后发送到我的App
38.发送成功后返回到我对App,刷新页面,在构建版本处就会有个 + 号,点击 + 号把发送过来的程序添加上去就行了
39.然后在定价处设置你的App上架后是免费还是收费。
40.回到我的App,点击发布就ok了。
证书我们这边可以借助辅助工具appuploader
Appuploader可以辅助在Windows、linux或mac系统直接申请iOS证书p12,及上传ipa到App Store,最方便在Windows开发上架没有苹果Mac电脑的开发者!配合本教程使用,可以快速掌握如何真机测试及上架!
点击苹果证书按钮
点击新增
输入证书密码,名称
这个密码不是账号密码,而是一个保护证书的密码,是p12文件的密码,此密码设置后没有其他地方可以找到,忘记了只能删除证书重新制作,所以请务必记住密码。还有为了安全起见,密码不要太简单。 证书名称是你为了在证书列表里面便于区别的一个字符,自己好辨识就可以,尽量是是字母和数字之类
选择证书类型
带distribution的是发布类型,带development的是开发类型。
apple类型=ios+mac,所以开发时选择ios app development和apple development 类型都是可以的
选择bundle id
只有部分类型的证书需要选择bundle id,例如推送证书。因为大部分证书是不和app关联的。而是通过描述文件profile文件关联app。
使用appuploader同步服务
如果期望制作好证书后在其他电脑上同样可以下载到这个证书,或者和你同事同步此证书,则需要勾选使用appuploader服务同步。否则您需要手动管理p12文件在不同电脑之间的传输,并且一但创建下载后,无法在其他电脑下载,只能手动复制文件过去。一般情况下,推荐使用appuploader服务同步。
证书类型说明
IOS开发选择apple development或者ios app development 类型 ios 发布选择 apple distribution或者 ios distribution (app store and ad hoc) 开发推送证书选择 apple push notification service ssl (sandbox) 发布推送证书选择 apple push notification service ssl (sandbox & production)
其他证书不是很常用,可以自行百度各种证书说明
声明:本文转自http://www.2cto.com/kf/201512/453943.html,侵删。
前言:作为一名iOS开发者,把开发出来的App上传到App Store是必须的。下面就来详细介绍下具体流程。
1.打开苹果开发者中心:https://developer.apple.com
打开后点击:Member Center
如果你的电脑没有保存密码,则会提示你输入开发者帐号和密码,因为我的电脑已经保存了,所以直接进入。
2.点击:Certificates, Identifiers & Profiles
3.点击Devices
一.创建App IDs和Boudle ID
4.点击App IDs,会进入如下界面,点击右上角的 + 号
5.填写App IDs和Boudle ID
6.点击continue
7.点击Submit
8.点击Done
二.创建发布证书(若已创建过发布证书,现在用的是p12文件,可直接跳到第21步)
9.点击Production后,点击 + 号
10.点击App Store and Ad Hoc
11.点击Continue
12.点击Continue
创建本地证书
13.此时返回到桌面,在点开LaunchPad,在其他中找到钥匙串访问,切记不要关闭浏览器
14.打开钥匙串访问,点击电脑左上角的钥匙串访问–证书助理–从证书颁发机构请求证书
15.会出现如下界面,选择存储到磁盘,点击继续
16.选择存储到桌面,存储
17.点击完成
18.你会在桌面上看到下面的文件
19.然后回到浏览器,点击choose File.. 选择创建好的:CertificateSigningRequest.certSigningRequest 文件,点击Generate
<h4 id="20点击download<a href=" http:="" www.2cto.com="" soft"="" target="_blank" class="keylink" style="color: rgb(51, 51, 51); font-family: 宋体; font-size: 14px; line-height: 28px;">下载创建好的发布证书cer后缀的文件然后点击done你创建的发布证书就会存储在帐号中">20.点击Download下载创建好的发布证书(cer后缀的文件),然后点击Done,你创建的发布证书就会存储在帐号中。
注:一般一个开发者帐号创建一个发布证书就够了,如果以后需要在其他电脑上上架App,只需要在钥匙串访问中创建p12文件,把p12文件安装到其他电脑上。这相当于给予了其他电脑发布App的权限。
创建PP文件
21.找到Provisioning Profiles ,点击All,然后点击右上角 + 号
22.选择App Store,点击Continue
23.在App ID 这个选项栏里面找到你刚刚创建的:App IDs(Bundle ID) 类型的套装,点击Continue
24.选择你刚创建的发布证书(或者生成p12文件的那个发布证书),点击Continue
25.在Profile Name栏里输入一个名字(这个是PP文件的名字,可随便输入,在这里我用工程名字,便于分别),然后点击Generate
26.Download生成的PP文件,然后点击Done
在App Store开辟空间
27.回到Member Center,点击iTunes Connect
28.点击我的App
29.点击新建 iOSApp
30.依次按提示填入对应信息,然后点击创建
31.依次把不同尺寸的App截图拉入到对应的里面
32.填入App简介
33.按提示依次输入
34.此时这个构建版本还没有生成,我们先把基本信息填写完毕,然后再进入Xcode中把项目打包发送到过来。
注意:填写完一定要点击右上角的保存。
在Xcode中打包工程
找到你刚刚下载的发布证书(后缀为.cer)或者p12文件,和PP文件,双击,看起来没反应,但是他们已经加入到你的钥匙串中。
35.在Xcode中选择模拟器为iOS Device,按照下图提示操作
36.修改.plist文件,两个.plist文件都要修改
37.然后发送到我的App
38.发送成功后返回到我对App,刷新页面,在构建版本处就会有个 + 号,点击 + 号把发送过来的程序添加上去就行了
39.然后在定价处设置你的App上架后是免费还是收费。
40.回到我的App,点击发布就ok了。
证书我们这边可以借助辅助工具appuploader
Appuploader可以辅助在Windows、linux或mac系统直接申请iOS证书p12,及上传ipa到App Store,最方便在Windows开发上架没有苹果Mac电脑的开发者!配合本教程使用,可以快速掌握如何真机测试及上架!
点击苹果证书按钮
点击新增
输入证书密码,名称
这个密码不是账号密码,而是一个保护证书的密码,是p12文件的密码,此密码设置后没有其他地方可以找到,忘记了只能删除证书重新制作,所以请务必记住密码。还有为了安全起见,密码不要太简单。 证书名称是你为了在证书列表里面便于区别的一个字符,自己好辨识就可以,尽量是是字母和数字之类
选择证书类型
带distribution的是发布类型,带development的是开发类型。
apple类型=ios+mac,所以开发时选择ios app development和apple development 类型都是可以的
选择bundle id
只有部分类型的证书需要选择bundle id,例如推送证书。因为大部分证书是不和app关联的。而是通过描述文件profile文件关联app。
使用appuploader同步服务
如果期望制作好证书后在其他电脑上同样可以下载到这个证书,或者和你同事同步此证书,则需要勾选使用appuploader服务同步。否则您需要手动管理p12文件在不同电脑之间的传输,并且一但创建下载后,无法在其他电脑下载,只能手动复制文件过去。一般情况下,推荐使用appuploader服务同步。
证书类型说明
IOS开发选择apple development或者ios app development 类型 ios 发布选择 apple distribution或者 ios distribution (app store and ad hoc) 开发推送证书选择 apple push notification service ssl (sandbox) 发布推送证书选择 apple push notification service ssl (sandbox & production)
其他证书不是很常用,可以自行百度各种证书说明

《一步步了解iOS APP上架流程,让你的APP顺利进入App Store的大门》
随着Apple Store越来越成熟,以及越来越多的开发者和公司希望在该平台上投放自己的产品,iOS APP上架成为许多开发者和公司普遍关注的话题。但是,由于苹果App Store的审核政策日益严格,大多数开发者和公司都不太清楚iOS APP上架的具体流程。今天,我们将为您介绍iOS APP上架的具体流程,希望可以帮助您顺利的完成iOS APP的上架。
1、准备App Store账号和必要的资料;
2、登录App Store Connect并注册开发者账号;
3、填写应用信息,包括:应用名称、描述、标签、版本等;
4、上传产品报告文件;
5、上传应用截图和APP文件;
6、提交审核;
7、审核通过后,即可在App Store上架。
证书我们这边可以借助辅助工具appuploader
Appuploader可以辅助在Windows、linux或mac系统直接申请iOS证书p12,及上传ipa到App Store,最方便在Windows开发上架没有苹果Mac电脑的开发者!配合本教程使用,可以快速掌握如何真机测试及上架!
点击苹果证书按钮
点击新增
输入证书密码,名称
这个密码不是账号密码,而是一个保护证书的密码,是p12文件的密码,此密码设置后没有其他地方可以找到,忘记了只能删除证书重新制作,所以请务必记住密码。还有为了安全起见,密码不要太简单。 证书名称是你为了在证书列表里面便于区别的一个字符,自己好辨识就可以,尽量是是字母和数字之类
选择证书类型
带distribution的是发布类型,带development的是开发类型。
apple类型=ios+mac,所以开发时选择ios app development和apple development 类型都是可以的
选择bundle id
只有部分类型的证书需要选择bundle id,例如推送证书。因为大部分证书是不和app关联的。而是通过描述文件profile文件关联app。
使用appuploader同步服务
如果期望制作好证书后在其他电脑上同样可以下载到这个证书,或者和你同事同步此证书,则需要勾选使用appuploader服务同步。否则您需要手动管理p12文件在不同电脑之间的传输,并且一但创建下载后,无法在其他电脑下载,只能手动复制文件过去。一般情况下,推荐使用appuploader服务同步。
证书类型说明
IOS开发选择apple development或者ios app development 类型 ios 发布选择 apple distribution或者 ios distribution (app store and ad hoc) 开发推送证书选择 apple push notification service ssl (sandbox) 发布推送证书选择 apple push notification service ssl (sandbox & production)
其他证书不是很常用,可以自行百度各种证书说明
随着Apple Store越来越成熟,以及越来越多的开发者和公司希望在该平台上投放自己的产品,iOS APP上架成为许多开发者和公司普遍关注的话题。但是,由于苹果App Store的审核政策日益严格,大多数开发者和公司都不太清楚iOS APP上架的具体流程。今天,我们将为您介绍iOS APP上架的具体流程,希望可以帮助您顺利的完成iOS APP的上架。
1、准备App Store账号和必要的资料;
2、登录App Store Connect并注册开发者账号;
3、填写应用信息,包括:应用名称、描述、标签、版本等;
4、上传产品报告文件;
5、上传应用截图和APP文件;
6、提交审核;
7、审核通过后,即可在App Store上架。
证书我们这边可以借助辅助工具appuploader
Appuploader可以辅助在Windows、linux或mac系统直接申请iOS证书p12,及上传ipa到App Store,最方便在Windows开发上架没有苹果Mac电脑的开发者!配合本教程使用,可以快速掌握如何真机测试及上架!
点击苹果证书按钮
点击新增
输入证书密码,名称
这个密码不是账号密码,而是一个保护证书的密码,是p12文件的密码,此密码设置后没有其他地方可以找到,忘记了只能删除证书重新制作,所以请务必记住密码。还有为了安全起见,密码不要太简单。 证书名称是你为了在证书列表里面便于区别的一个字符,自己好辨识就可以,尽量是是字母和数字之类
选择证书类型
带distribution的是发布类型,带development的是开发类型。
apple类型=ios+mac,所以开发时选择ios app development和apple development 类型都是可以的
选择bundle id
只有部分类型的证书需要选择bundle id,例如推送证书。因为大部分证书是不和app关联的。而是通过描述文件profile文件关联app。
使用appuploader同步服务
如果期望制作好证书后在其他电脑上同样可以下载到这个证书,或者和你同事同步此证书,则需要勾选使用appuploader服务同步。否则您需要手动管理p12文件在不同电脑之间的传输,并且一但创建下载后,无法在其他电脑下载,只能手动复制文件过去。一般情况下,推荐使用appuploader服务同步。
证书类型说明
IOS开发选择apple development或者ios app development 类型 ios 发布选择 apple distribution或者 ios distribution (app store and ad hoc) 开发推送证书选择 apple push notification service ssl (sandbox) 发布推送证书选择 apple push notification service ssl (sandbox & production)
其他证书不是很常用,可以自行百度各种证书说明

uni-push 2.0 快速接入指南
uni-push 2.0 快速接入指南
简介
此文档用于帮助开发者快速接入集成 uni-push 2.0 ,若想详细了解可以查看 uni-push 2.0 业务文档 。
名词解释
名词 | 解释 |
---|---|
通知消息 | 指定通知标题和内容后,由个推 SDK 自动处理在系统通知栏中展示通知栏消息,同时响铃或震动提醒用户(响铃和震动受手机系统的设置状态影响)。 |
透传消息 | 即自定义消息,消息体格式客户可以自己定义,如纯文本、json 串等。透传消息个推只传递数据,不做任何处理,客户端接收到透传消息后需要自己去做后续动作处理,如通知栏展示、弹框等。 |
ClientId | 个推业务层中的对外用户标识,用于标识客户端身份,由第三方客户端获取并保存到第三方服务端,是个推 SDK 的唯一识别号,简称 CID、cid。 |
在线推送 | app 在前台打开运行时,通过个推渠道下发消息。 |
离线推送 | app在后台、锁屏、进程关闭时,通过厂商渠道下发消息。若未集成 android 多厂商、未配置 ios 推送证书,则该机型无法使用离线推送。 |
消息下发流程
一、客户端集成
1.1 开通 uni-push 2.0 推送服务
unipush 内部封装好了个推及主流厂商 SDK,开发者在使用前必须开通相关服务。
使用 HBuilder 账号登录 开发者中心 ,创建应用、填写应用信息。
1.2 开通离线厂商推送服务
若未完成开通离线厂商推送,只有在 app 在线时才能收到消息
-
Android 多厂商开通:个推与主流安卓厂商合作融合了厂商推送 SDK,在后台配置 “厂商推送设置” 、并云打包后,可以同时使用 “离线推送”,能提高在安卓厂商设备上的消息到达率。
- 目前华为、荣耀、魅族、oppo(测试环境)不要求上架应用商店;vivo 、小米、oppo(正式环境)必须上架应用商店后才可使用离线厂商推送。
-
iOS 推送证书生成:iOS 支持的推送通知功能,从苹果开发者官网导出证书并配置在后台的 “厂商推送设置” 后,可以同时使用 “离线推送”,能提高在 iOS 设备上的消息到达率。
- iOS 使用推送无需上架 Appstore
-
鸿蒙Next 开通:纯血鸿蒙(HarmonyOS Next)系统的推送,配置鸿蒙Next 参数并进行云打包。
所有的厂商参数都是在下图所示位置配置,配置完成后,需要进行云打包, app 端才会生效:
1.3 云打包
打开 HBuilderX (3.5.1及其以上版本),双击项目中的 “manifest.json” 文件,选择“App 模块配置”,向下找到“Push(消息推送)”,勾选后,点击 “uniPush” 下面配置。
注意:如果是 APP 集成推送,云打包请一定要勾选“离线推送”。如果不勾选“离线推送”,则打的包是小程序 SDK,仅支持发在线透传消息,不会有通知栏展示。
云打包可以选择”正式包“和”自定义基座包“,开发测试建议使用 ”自定义基座包“。
1.4 集成验证
1.4.1 客户端获取 cid
假如我要给“张三”打电话,那就需要知道对方的电话标识,即电话号码是多少。 同理,要给某个客户端推送消息,也需要知道该设备的客户端推送标识。
启动 app 后成功获取 cid 则说明云打包 “在线推送” 成功,支持在线推送。
先跟着示例代码简单体验,详细的uni.getPushClientId API介绍 详情参考 代码示例:
// uni-app客户端获取push客户端标记,代码可以实现在App.vue中
uni.getPushClientId({
success: (res) => {
let push_clientid = res.cid
console.log('客户端推送标识cid:',push_clientid)
},
fail(err) {
console.log(err)
}
})
1.4.2 校验厂商离线推送是否集成成功
输入上方获取的 cid ,查询到对应的 Device Token 则说明云打包 “离线推送” 成功,同时支持离线推送。
如果未查询到 device token,则只能 “在线推送” 。若需要使用 “离线推送” 请重新检查 ”1.2 开通离线厂商推送服务“ 。
二、服务端推送消息
注意:使用 uni-push 2.0,服务端不支持用个推 api 推送,只能用 dcloud 提供的 服务端(云函数)推送 。
2.1 云函数创建
注意:扩展库依赖 3 张 opendb 表:opendb-tempdata
,opendb-device
,uni-id-device
。公测版 uniCloud,执行扩展库会自动创建。如果你使用的是 uniCloud 正式版需要自己在 uniCloud
的 web控制台 创建这3张表。
示例如下:
2.2 云函数执行
在云函数文件目录右键(或按快捷键ctrl + r)-> 上传并运行云函数
,此时你的客户端将收到推送消息(应用关闭时为通知栏消息,在线时代码监听到推送消息)。
云函数中调用uni-cloud-push扩展库的sendMessage方法,向客户端推送消息
// 简单的使用示例
'use strict';
const uniPush = uniCloud.getPushManager({appId:"__UNI__XXXXXX"}) //注意这里需要传入你的应用appId
exports.main = async (event, context) => {
return await uniPush.sendMessage({
"push_clientid": "xxx", //填写上一步在uni-app客户端获取到的客户端推送标识push_clientid
"force_notification":true, //填写true,客户端就会对在线消息自动创建“通知栏消息”。
"title": "通知栏显示的标题",
"content": "通知栏显示的内容",
"payload": {
"text":"体验一下uni-push2.0"
},
"category": {
//HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,要给鸿蒙设备推送时才必传
"harmony":"MARKETING"
},
"options":{
"HW": {
// 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条。此 target_user_type 参数请勿发布至线上。
"/message/android/target_user_type":1
} ,
"HO": {
//值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。
"/android/targetUserType": 1
} ,
"VV": {
//值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。此 pushMode 参数请勿发布至线上。
"/pushMode":1
} ,
"XM": {
//新小米消息分类下,私信公信id都必须要传,否则请求小米厂商接口会被拦截
"/extra.channel_id": "填写小米平台申请的渠道id"
}
}
})
};
若使用vivo测试推送( "/pushMode":1 ),额外需要先在 vivo 开放平台 录入测试用户。华为不需要录入。
在线消息无额度限制。离线推送各厂商的限额(包含 extra.channel_id 如何申请),详见: 厂商通道限额
HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,harmony取值参考 云端通知category取值
如果你希望当应用在线时,也通过“通知栏消息”来提醒用户;可以通过以下两种方式实现:
- 监听到消息内容后,根据业务需要自己判断是否要创建“通知栏消息”,需要就调用创建本地消息API uni.createPushMessage手动创建通知栏消息。
- 服务端执行推送时,传递参数
force_notification:true
,客户端就会自动创建“通知栏消息”(此时你监听不到消息内容),当用户点击通知栏消息后,APP才能监听到消息内容。
先跟着示例代码简单体验,详细的 uniPush.sendMessage API介绍 详情参考
检查确认当前 app 的通知栏权限开启后,则可以开始进行推送测试。
2.2.1 测试在线推送,打开 app 在前台时进行推送
2.2.2 测试离线推送,关闭 app 进程时进行推送
2.2.3 手机成功收到消息展示
恭喜您,如果您已经成功收到 在线 和 离线 的消息展示,那说明已经集成推送成功啦!
2.3 云函数 URL 化 (可选)
在 uniCloud 的云函数中,加载扩展库 uni-cloud-push
,直接调用相关 API,无需额外的服务端配置。
传统服务器开发者(例如:Java、php、python等)需要把这个 云函数URL化 后变成 http 接口,再由代码调用这个 http 接口。
云函数URL化 步骤简介----------->
1,上传步骤云函数代码
2,云函数URL化
3,api 工具测试请求(单推示例)
4,云函数代码示例
// 简单的使用示例
'use strict';
const uniPush = uniCloud.getPushManager({
appId: "你的__UNI__开头的AppId"
})
exports.main = async (event) => {
let obj = JSON.parse(event.body)
const res = await uniPush.sendMessage({
"push_clientid": obj.cids, // 设备id,支持多个以数组的形式指定多个设备,如["cid-1","cid-2"],数组长度不大于1000
"title": obj.title, // 标题
"content": obj.content, // 内容
"payload": obj.payload, // 数据
"category": obj.category, // HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,要给鸿蒙设备推送时才必传
"force_notification": true, //填写true,客户端就会对在线消息自动创建“通知栏消息”,不填写则需要客户端自己处理。
"request_id": obj.request_id ,//请求唯一标识号,10-32位之间;如果request_id重复,会导致消息丢失
"options":obj.options //消息分类,没申请可以不传这个参数
})
return res;
};
注意:如果推送时不传入 cid,则发送的是全推(给所有用户推)消息。全推每分钟不能超过5次,10分钟内不能推重复消息体 。 通过返回值中的 taskid 几个首字母可以判断是什么推送。RASS:单推、 RASL:批量推、 RASA:全推
5,postman请求示例
{
"request_id": "212320028909901111",
"cids": "此处填写自定义打包获取的clientid参数",
"title": "通知标题",
"content": "通知内容 ",
"options": {
"HW": {
// 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条。此 target_user_type 参数请勿发布至线上。
"/message/android/target_user_type": 1
},
"HO": {
//值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。
"/android/targetUserType": 1
},
"VV": {
//值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。此 pushMode 参数请勿发布至线上。
"/pushMode": 1
},
"XM": {
//新小米消息分类下,私信公信id都必须要传,否则请求小米厂商接口会被拦截
"/extra.channel_id": "填写小米平台申请的渠道id"
}
},
"category": {
//HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,要给鸿蒙设备推送时才必传
"harmony":"MARKETING"
},
//payload是点击通知栏消息后,传给客户端click回调的自定义参数
"payload": {
"data1": 1,
"data2": 2
}
}
三、客户端接收消息
3.1 客户端监听推送消息
如果您需要对接收到的消息进行自定义处理,可以再阅读一下本文开头的 “消息下发流程图”。
客户端监听推送消息的代码,需要在收到推送消息之前被执行。所以应当写在应用一启动就会触发的 应用生命周期 onLaunch
中。
示例代码:
//文件路径:项目根目录/App.vue
export default {
onLaunch: function() {
console.log('App Launch')
uni.onPushMessage((res) => {
// 监听通知栏消息的点击
if(res.type == 'click'){
// 如果需要跳转app内指定页面,则自己实现下方的跳转代码。
uni.navigateTo({
//页面路径示例值:/pages/pushinfo/pushinfo
url:'指定页面路径'
})
}
// 监听在线推送消息,若云函数设置了 "force_notification":true,则不会触发此 receive。
if(res.type == 'receive'){
console.log("接收到的消息内容",res.payload);
}
})
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
如果要在receive回调里面,自己创建通知栏消息展示,可以调用 createMessage
先跟着示例代码简单体验,详细的 uni.onPushMessage API介绍 详情参考
四、注意事项
4.1 合规上架
app 若需要上架应用商店,须配置 隐私弹窗 提示用户。
4.2 帮助中心
当您在集成推送服务遇到问题时:
先仔细阅读此接入指南及对应的集成文档,查看是否有遗漏。
阅读 uni-push2.0 常见问题 ,查看是否能解决。
uni-push 2.0 快速接入指南
简介
此文档用于帮助开发者快速接入集成 uni-push 2.0 ,若想详细了解可以查看 uni-push 2.0 业务文档 。
名词解释
名词 | 解释 |
---|---|
通知消息 | 指定通知标题和内容后,由个推 SDK 自动处理在系统通知栏中展示通知栏消息,同时响铃或震动提醒用户(响铃和震动受手机系统的设置状态影响)。 |
透传消息 | 即自定义消息,消息体格式客户可以自己定义,如纯文本、json 串等。透传消息个推只传递数据,不做任何处理,客户端接收到透传消息后需要自己去做后续动作处理,如通知栏展示、弹框等。 |
ClientId | 个推业务层中的对外用户标识,用于标识客户端身份,由第三方客户端获取并保存到第三方服务端,是个推 SDK 的唯一识别号,简称 CID、cid。 |
在线推送 | app 在前台打开运行时,通过个推渠道下发消息。 |
离线推送 | app在后台、锁屏、进程关闭时,通过厂商渠道下发消息。若未集成 android 多厂商、未配置 ios 推送证书,则该机型无法使用离线推送。 |
消息下发流程
一、客户端集成
1.1 开通 uni-push 2.0 推送服务
unipush 内部封装好了个推及主流厂商 SDK,开发者在使用前必须开通相关服务。
使用 HBuilder 账号登录 开发者中心 ,创建应用、填写应用信息。
1.2 开通离线厂商推送服务
若未完成开通离线厂商推送,只有在 app 在线时才能收到消息
-
Android 多厂商开通:个推与主流安卓厂商合作融合了厂商推送 SDK,在后台配置 “厂商推送设置” 、并云打包后,可以同时使用 “离线推送”,能提高在安卓厂商设备上的消息到达率。
- 目前华为、荣耀、魅族、oppo(测试环境)不要求上架应用商店;vivo 、小米、oppo(正式环境)必须上架应用商店后才可使用离线厂商推送。
-
iOS 推送证书生成:iOS 支持的推送通知功能,从苹果开发者官网导出证书并配置在后台的 “厂商推送设置” 后,可以同时使用 “离线推送”,能提高在 iOS 设备上的消息到达率。
- iOS 使用推送无需上架 Appstore
-
鸿蒙Next 开通:纯血鸿蒙(HarmonyOS Next)系统的推送,配置鸿蒙Next 参数并进行云打包。
所有的厂商参数都是在下图所示位置配置,配置完成后,需要进行云打包, app 端才会生效:
1.3 云打包
打开 HBuilderX (3.5.1及其以上版本),双击项目中的 “manifest.json” 文件,选择“App 模块配置”,向下找到“Push(消息推送)”,勾选后,点击 “uniPush” 下面配置。
注意:如果是 APP 集成推送,云打包请一定要勾选“离线推送”。如果不勾选“离线推送”,则打的包是小程序 SDK,仅支持发在线透传消息,不会有通知栏展示。
云打包可以选择”正式包“和”自定义基座包“,开发测试建议使用 ”自定义基座包“。
1.4 集成验证
1.4.1 客户端获取 cid
假如我要给“张三”打电话,那就需要知道对方的电话标识,即电话号码是多少。 同理,要给某个客户端推送消息,也需要知道该设备的客户端推送标识。
启动 app 后成功获取 cid 则说明云打包 “在线推送” 成功,支持在线推送。
先跟着示例代码简单体验,详细的uni.getPushClientId API介绍 详情参考 代码示例:
// uni-app客户端获取push客户端标记,代码可以实现在App.vue中
uni.getPushClientId({
success: (res) => {
let push_clientid = res.cid
console.log('客户端推送标识cid:',push_clientid)
},
fail(err) {
console.log(err)
}
})
1.4.2 校验厂商离线推送是否集成成功
输入上方获取的 cid ,查询到对应的 Device Token 则说明云打包 “离线推送” 成功,同时支持离线推送。
如果未查询到 device token,则只能 “在线推送” 。若需要使用 “离线推送” 请重新检查 ”1.2 开通离线厂商推送服务“ 。
二、服务端推送消息
注意:使用 uni-push 2.0,服务端不支持用个推 api 推送,只能用 dcloud 提供的 服务端(云函数)推送 。
2.1 云函数创建
注意:扩展库依赖 3 张 opendb 表:opendb-tempdata
,opendb-device
,uni-id-device
。公测版 uniCloud,执行扩展库会自动创建。如果你使用的是 uniCloud 正式版需要自己在 uniCloud
的 web控制台 创建这3张表。
示例如下:
2.2 云函数执行
在云函数文件目录右键(或按快捷键ctrl + r)-> 上传并运行云函数
,此时你的客户端将收到推送消息(应用关闭时为通知栏消息,在线时代码监听到推送消息)。
云函数中调用uni-cloud-push扩展库的sendMessage方法,向客户端推送消息
// 简单的使用示例
'use strict';
const uniPush = uniCloud.getPushManager({appId:"__UNI__XXXXXX"}) //注意这里需要传入你的应用appId
exports.main = async (event, context) => {
return await uniPush.sendMessage({
"push_clientid": "xxx", //填写上一步在uni-app客户端获取到的客户端推送标识push_clientid
"force_notification":true, //填写true,客户端就会对在线消息自动创建“通知栏消息”。
"title": "通知栏显示的标题",
"content": "通知栏显示的内容",
"payload": {
"text":"体验一下uni-push2.0"
},
"category": {
//HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,要给鸿蒙设备推送时才必传
"harmony":"MARKETING"
},
"options":{
"HW": {
// 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条。此 target_user_type 参数请勿发布至线上。
"/message/android/target_user_type":1
} ,
"HO": {
//值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。
"/android/targetUserType": 1
} ,
"VV": {
//值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。此 pushMode 参数请勿发布至线上。
"/pushMode":1
} ,
"XM": {
//新小米消息分类下,私信公信id都必须要传,否则请求小米厂商接口会被拦截
"/extra.channel_id": "填写小米平台申请的渠道id"
}
}
})
};
若使用vivo测试推送( "/pushMode":1 ),额外需要先在 vivo 开放平台 录入测试用户。华为不需要录入。
在线消息无额度限制。离线推送各厂商的限额(包含 extra.channel_id 如何申请),详见: 厂商通道限额
HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,harmony取值参考 云端通知category取值
如果你希望当应用在线时,也通过“通知栏消息”来提醒用户;可以通过以下两种方式实现:
- 监听到消息内容后,根据业务需要自己判断是否要创建“通知栏消息”,需要就调用创建本地消息API uni.createPushMessage手动创建通知栏消息。
- 服务端执行推送时,传递参数
force_notification:true
,客户端就会自动创建“通知栏消息”(此时你监听不到消息内容),当用户点击通知栏消息后,APP才能监听到消息内容。
先跟着示例代码简单体验,详细的 uniPush.sendMessage API介绍 详情参考
检查确认当前 app 的通知栏权限开启后,则可以开始进行推送测试。
2.2.1 测试在线推送,打开 app 在前台时进行推送
2.2.2 测试离线推送,关闭 app 进程时进行推送
2.2.3 手机成功收到消息展示
恭喜您,如果您已经成功收到 在线 和 离线 的消息展示,那说明已经集成推送成功啦!
2.3 云函数 URL 化 (可选)
在 uniCloud 的云函数中,加载扩展库 uni-cloud-push
,直接调用相关 API,无需额外的服务端配置。
传统服务器开发者(例如:Java、php、python等)需要把这个 云函数URL化 后变成 http 接口,再由代码调用这个 http 接口。
云函数URL化 步骤简介----------->
1,上传步骤云函数代码
2,云函数URL化
3,api 工具测试请求(单推示例)
4,云函数代码示例
// 简单的使用示例
'use strict';
const uniPush = uniCloud.getPushManager({
appId: "你的__UNI__开头的AppId"
})
exports.main = async (event) => {
let obj = JSON.parse(event.body)
const res = await uniPush.sendMessage({
"push_clientid": obj.cids, // 设备id,支持多个以数组的形式指定多个设备,如["cid-1","cid-2"],数组长度不大于1000
"title": obj.title, // 标题
"content": obj.content, // 内容
"payload": obj.payload, // 数据
"category": obj.category, // HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,要给鸿蒙设备推送时才必传
"force_notification": true, //填写true,客户端就会对在线消息自动创建“通知栏消息”,不填写则需要客户端自己处理。
"request_id": obj.request_id ,//请求唯一标识号,10-32位之间;如果request_id重复,会导致消息丢失
"options":obj.options //消息分类,没申请可以不传这个参数
})
return res;
};
注意:如果推送时不传入 cid,则发送的是全推(给所有用户推)消息。全推每分钟不能超过5次,10分钟内不能推重复消息体 。 通过返回值中的 taskid 几个首字母可以判断是什么推送。RASS:单推、 RASL:批量推、 RASA:全推
5,postman请求示例
{
"request_id": "212320028909901111",
"cids": "此处填写自定义打包获取的clientid参数",
"title": "通知标题",
"content": "通知内容 ",
"options": {
"HW": {
// 值为int 类型。1 表示华为测试消息,华为每个应用每日可发送该测试消息500条。此 target_user_type 参数请勿发布至线上。
"/message/android/target_user_type": 1
},
"HO": {
//值为int 类型。1 表示测试推送,不填默认为0。荣耀每个应用每日可发送该测试消息1000条。此测试参数请勿发布至线上。
"/android/targetUserType": 1
},
"VV": {
//值为int 类型。0 表示正式推送;1 表示测试推送,不填默认为0。此 pushMode 参数请勿发布至线上。
"/pushMode": 1
},
"XM": {
//新小米消息分类下,私信公信id都必须要传,否则请求小米厂商接口会被拦截
"/extra.channel_id": "填写小米平台申请的渠道id"
}
},
"category": {
//HarmonyOS NEXT系统(纯血鸿蒙、非安卓鸿蒙)的消息分类,要给鸿蒙设备推送时才必传
"harmony":"MARKETING"
},
//payload是点击通知栏消息后,传给客户端click回调的自定义参数
"payload": {
"data1": 1,
"data2": 2
}
}
三、客户端接收消息
3.1 客户端监听推送消息
如果您需要对接收到的消息进行自定义处理,可以再阅读一下本文开头的 “消息下发流程图”。
客户端监听推送消息的代码,需要在收到推送消息之前被执行。所以应当写在应用一启动就会触发的 应用生命周期 onLaunch
中。
示例代码:
//文件路径:项目根目录/App.vue
export default {
onLaunch: function() {
console.log('App Launch')
uni.onPushMessage((res) => {
// 监听通知栏消息的点击
if(res.type == 'click'){
// 如果需要跳转app内指定页面,则自己实现下方的跳转代码。
uni.navigateTo({
//页面路径示例值:/pages/pushinfo/pushinfo
url:'指定页面路径'
})
}
// 监听在线推送消息,若云函数设置了 "force_notification":true,则不会触发此 receive。
if(res.type == 'receive'){
console.log("接收到的消息内容",res.payload);
}
})
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
如果要在receive回调里面,自己创建通知栏消息展示,可以调用 createMessage
先跟着示例代码简单体验,详细的 uni.onPushMessage API介绍 详情参考
四、注意事项
4.1 合规上架
app 若需要上架应用商店,须配置 隐私弹窗 提示用户。
4.2 帮助中心
当您在集成推送服务遇到问题时:
先仔细阅读此接入指南及对应的集成文档,查看是否有遗漏。
阅读 uni-push2.0 常见问题 ,查看是否能解决。

wifi打印机打印(二维码,条形码等)
- 打印机,需要插usb(或者微信小程序设置wifi)
- 打印机的默认端口是9100(一般是,具体看购买的打印机)
- wifitool.js是用于连接打印机的
-
此文章是用的CPCL指令,可以根据自己的指令格式修改(var command = tsc.jpPrinter.createNew();)
vue<template> <view> <button @click="printSocket">WiFi打印</button> </view> </template>
js
<script> import tsc from "@/static/libs/tsc.js"; import wifiTool from "@/static/libs/WifiTool.js"; export default { data() { return { ip: "192.168.4.245", port: 9100, str: "yyy", }; }, mounted() { wifiTool.connectWifi("192.168.4.245", 9100); }, methods: { test(str, ip, port) { str = "test"; var Socket = plus.android.importClass("java.net.Socket"); var JavaByte = plus.android.importClass("java.lang.Byte"); var PrintWriter = plus.android.importClass("java.io.PrintWriter"); var BufferedWriter = plus.android.importClass("java.io.BufferedWriter"); var OutputStreamWriter = plus.android.importClass("java.io.OutputStreamWriter"); var BufferedReader = plus.android.importClass("java.io.BufferedReader"); var InputStreamReader = plus.android.importClass("java.io.InputStreamReader"); //测试改良 var StrictMode = plus.android.importClass("android.os.StrictMode"); var Build = plus.android.importClass("android.os.Build"); if (Build.VERSION.SDK_INT > 9) { var policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } var socket = new Socket(ip, port); var outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); var bufferWriter = new BufferedWriter(outputStreamWriter); var out = new PrintWriter(bufferWriter, true); var command = tsc.jpPrinter.createNew(); command.init(0, 200, 200, 210, 1); command.setCls(); command.setQR(30, 40, 2, 6, "123"); // command.setMag(2, 2); // command.setText1(55, 30, 180, 60, "地址: 内蒙"); // command.setMag(2, 2); // command.setText1(55, 6, 180, 120, "重量: 15kg"); command.setPagePrint(); let data = command.getData(); out.println(data); socket.setSoTimeout(3000); socket.close(); }, printSocket() { var command = tsc.jpPrinter.createNew(); command.init(0, 200, 250, 210, 1); command.setCls(); command.setQR(30, 40, 2, 6, "123"); command.setMag(2, 2); command.setText1(55, 30, 180, 60, "地址: 内蒙"); command.setMag(2, 2); command.setText1(55, 6, 180, 120, "重量: 15kg"); command.setPostfeed(32);//打印后走纸 command.setPagePrint(); let data = command.getData(); wifiTool.sendData(data); }, connectWifi(ip, port) { if (plus.os.name == "Android") { // plus.nativeUI.showWaiting("正在打印中。。。"); var Socket = plus.android.importClass("java.net.Socket"); var socket; var outputStream; //解决高低版本兼容 var StrictMode = plus.android.importClass("android.os.StrictMode"); var Build = plus.android.importClass("android.os.Build"); if (Build.VERSION.SDK_INT > 9) { var policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } try { socket = new Socket(ip, port); socket.setKeepAlive(true); outputStream = socket.getOutputStream(); plus.android.importClass(outputStream); // var bytes = plus.android.invoke(str, "getBytes", "gbk"); uni.showToast({ title: "wifi连接成功", }); // plus.nativeUI.closeWaiting(); } catch (e) { console.log("网络连接超时,请重新连接!"); //TODO handle the exception // plus.nativeUI.closeWaiting(); } } }, }, }; </script>
WifiTool.js
```javascript
/**
* 初始化参数
*/
//#ifdef APP-PLUS
var Socket = plus.android.importClass("java.net.Socket");
var socket;
var outputStream;
//解决高低版本兼容
var StrictMode = plus.android.importClass("android.os.StrictMode");
var Build = plus.android.importClass("android.os.Build");
//#endif
/**
* 构造对象
*/
var wifiTool = {
connectWifi(ip, port) {
if (Build.VERSION.SDK_INT > 9) {
var policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
try {
socket = new Socket(ip, port);
socket.setKeepAlive(true);
outputStream = socket.getOutputStream();
plus.android.importClass(outputStream);
// var bytes = plus.android.invoke(str, "getBytes", "gbk");
uni.showToast({
title: 'wifi连接成功',
});
// plus.nativeUI.closeWaiting();
} catch (e) {
console.log("网络连接超时,请重新连接!")
//TODO handle the exception
// plus.nativeUI.closeWaiting();
}
},
sendData(data) {
console.log(outputStream);
outputStream.write(data);
outputStream.flush();
// 关闭socket
// socket.shutdownOutput();
console.log(socket);
}
}
module.exports = wifiTool
tsc.js
/**
* tsc 命令打印工具类
* 2021.04.26 uni-app版本
* @auth boolTrue
*/
var encode = require("./encoding.js");
var jpPrinter = {
createNew: function () {
var jpPrinter = {};
var data = "";
var command = []
var rawCommand = ''
jpPrinter.name = "标签模式";
jpPrinter.init = function (x, y, z, h, q) {
data = "! " + x + " " + y + " " + z + " " + h + " " + q + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.addCommand = function (content) { //将指令转成数组装起
//#ifdef MP-WEIXIN
var code = new encode.TextEncoder(
'gb18030', {
NONSTANDARD_allowLegacyEncoding: true
}).encode(content)
for (var i = 0; i < code.length; ++i) {
command.push(code[i])
}
//#endif
//#ifdef APP-PLUS
let byteCommand = plus.android.invoke(content, 'getBytes', 'gb18030');
for (var i = 0; i < byteCommand.length; ++i) {
command.push(byteCommand[i])
}
//#endif
console.log('command--:', content)
rawCommand += content
}
function intToByte(i) {
// 此处关键 -- android是java平台 byte数值范围是 [-128, 127]
// 因为java平台的byte类型是有符号的 最高位表示符号,所以数值范围固定
// 而图片计算出来的是数值是 0 -255 属于int类型
// 所以把int 转换成byte类型
//#ifdef APP-PLUS
var b = i & 0xFF;
var c = 0;
if (b >= 128) {
c = b % 128;
c = -1 * (128 - c);
} else {
c = b;
}
return c
//#endif
// 而微信小程序不需要,因为小程序api接收的是 无符号8位
//#ifdef MP-WEIXIN
return i
//#endif
}
jpPrinter.setPostfeed = function (length) { //打印之后走纸距离指令
data = "POSTFEED " +length + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setSize = function (pageWidght, pageHeight) { //设置页面大小
data = "SIZE " + pageWidght.toString() + " mm" + "," + pageHeight.toString() + " mm" + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setSpeed = function (printSpeed) { //设置打印机速度
data = "SPEED " + printSpeed.toString() + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setDensity = function (printDensity) { //设置打印机浓度
data = "DENSITY " + printDensity.toString() + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setGap = function (printGap) { //传感器
data = "GAP " + printGap.toString() + " mm\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setCountry = function (country) { //选择国际字符集
/*
001:USA
002:French
003:Latin America
034:Spanish
039:Italian
044:United Kingdom
046:Swedish
047:Norwegian
049:German
*/
data = "COUNTRY " + country + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setCodepage = function (codepage) { //选择国际代码页
/*
8-bit codepage 字符集代表
437:United States
850:Multilingual
852:Slavic
860:Portuguese
863:Canadian/French
865:Nordic
Windows code page
1250:Central Europe
1252:Latin I
1253:Greek
1254:Turkish
以下代码页仅限于 12×24 dot 英数字体
WestEurope:WestEurope
Greek:Greek
Hebrew:Hebrew
EastEurope:EastEurope
Iran:Iran
IranII:IranII
Latvian:Latvian
Arabic:Arabic
Vietnam:Vietnam
Uygur:Uygur
Thai:Thai
1252:Latin I
1257:WPC1257
1251:WPC1251
866:Cyrillic
858:PC858
747:PC747
864:PC864
1001:PC100
*/
data = "CODEPAGE " + codepage + "\r\n";
jpPrinter.addCommand(data)
}
jpPrinter.setCls = function () { //清除打印机缓存
data = "CLS " + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setFeed = function (feed) { //将纸向前推出n
data = "FEED " + feed + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setBackFeed = function (backup) { //将纸向后回拉n
data = "BACKFEED " + backup + "\r\n";
jpPrinter.addCommand(data)
}
jpPrinter.setDirection = function (direction) { //设置打印方向,参考编程手册
data = "DIRECTION " + direction + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setReference = function (x, y) { //设置坐标原点,与打印方向有关
data = "REFERENCE " + x + "," + y + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setFrom = function () { //定位控制指令
data = "FORM \r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setFromfeed = function () { //根据Size进一张标签纸
data = "FORMFEED \r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setHome = function () { //根据Size找到下一张标签纸的位置
data = "HOME \r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setSound = function (level, interval) { //控制蜂鸣器
data = "SOUND " + level + "," + interval + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setLimitfeed = function (limit) { // 检测垂直间距
data = "LIMITFEED " + limit + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setBar = function (x, y, width, height) { //绘制线条
data = "BAR " + x + "," + y + "," + width + "," + height + "\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setBox = function (x_start, y_start, x_end, y_end, thickness) { //绘制方框
data = "BOX " + x_start + "," + y_start + "," + x_end + "," + y_end + "," + thickness + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setErase = function (x_start, y_start, x_width, y_height) { //清除指定区域的数据
data = "ERASE " + x_start + "," + y_start + "," + x_width + "," + y_height + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setReverse = function (x_start, y_start, x_width, y_height) { //将指定的区域反相打印
data = "REVERSE " + x_start + "," + y_start + "," + x_width + "," + y_height + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setText1 = function (font, s, x, y, str) { //打印文字
data = "TEXT " + font + " " + s + " " + x + " " + y + " " + str + "\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setText = function (x, y, font, x_, y_, str) { //打印文字
data = "TEXT " + x + "," + y + ",\"" + font + "\"," + 0 + "," + x_ + "," + y_ + "," + "\"" +
str + "\"\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setMag = function (w,h) { //打印二维码
data = "SETMAG " + w + " " + h + "\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setQR = function (x, y, m, u, content) { //打印二维码
data = "BARCODE QR " + x + " " + y + " M " + m + " U " + u + "\r\n MA," + content + "\r\nENDQR\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setPDF = function (type, x, y,xd,yd,c,s, content) { //打印条形
data = "BARCODE " + type + " " + x + " " + y + " XD " + xd + " YD " + yd + " C " + c + " S " + s + "\r\n"+ content+ "\r\nENDPDF\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setBarCode = function (type, w, r, h, x, y, content) { //打印条形码
data = "BARCODE " + type + " " + w + " " + r + " " + h + " " + x + " " + y + " " + content + "\r\n"
jpPrinter.addCommand(data)
};
// 固定灰度阈值(128以上的都看作白色)
jpPrinter.setBitmap = function (x, y, mode, res) { //添加图片,res为画布参数
var width = parseInt((res.width) / 8 * 8 / 8)
var height = res.height
var imgWidth = res.width
var time = 1;
var temp = res.data.length - width * 32;
var pointList = []
var resultData = []
console.log(width + "--" + height)
data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
jpPrinter.addCommand(data)
//console.log(res.data)
console.log('---以上是原始数据---')
//for循环顺序不要错了,外层遍历高度,内层遍历宽度,因为横向每8个像素点组成一个字节
for (var y = 0; y < height; y++) {
for (var x = 0; x < imgWidth; x++) {
let r = res.data[(y * imgWidth + x) * 4];
let g = res.data[(y * imgWidth + x) * 4 + 1];
let b = res.data[(y * imgWidth + x) * 4 + 2];
let a = res.data[(y * imgWidth + x) * 4 + 3]
//console.log(`当前${y}行${x}列像素,rgba值:(${r},${g},${b},${a})`)
// 像素灰度值
let grayColor = r * 0.299 + g * 0.587 + b * 0.114
//灰度值大于128位
//1不打印, 0打印 (参考:佳博标签打印机编程手册tspl)
if (grayColor > 128) {
pointList.push(1)
} else {
pointList.push(0)
}
}
}
//console.log(pointList)
for (var i = 0; i < pointList.length; i += 8) {
var p = pointList[i] * 128 + pointList[i + 1] * 64 + pointList[i + 2] * 32 + pointList[i +
3] * 16 + pointList[i + 4] * 8 + pointList[i + 5] * 4 + pointList[i + 6] * 2 +
pointList[i + 7]
resultData.push(p)
}
console.log('最终数据:')
//console.log(resultData)
for (var i = 0; i < resultData.length; ++i) {
command.push(intToByte(resultData[i]))
}
}
jpPrinter.setBitmap2 = function (x, y, mode, res) { //添加图片,res为画布参数
var w = res.width
var width = parseInt((res.width + 7) / 8 * 8 / 8)
var height = res.height;
console.log(width + "--" + height)
data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
jpPrinter.addCommand(data)
var r = []
var bits = new Uint8Array(height * width);
for (y = 0; y < height; y++) {
for (x = 0; x < w; x++) {
let r = res.data[(y * w + x) * 4];
let g = res.data[(y * w + x) * 4 + 1];
let b = res.data[(y * w + x) * 4 + 2];
let a = res.data[(y * w + x) * 4 + 3]
var color = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) <<
0);
if ((color & 0xFF) > 128) {
bits[parseInt(y * width + x / 8)] |= (0x80 >> (x % 8));
}
}
}
for (var i = 0; i < bits.length; i++) {
//command.push((~bits[i]) & 0xFF);
command.push(intToByte(bits[i]));
//r.push((~bits[i]) & 0xFF);
}
}
// 平均灰度阈值(先计算平均灰度,然后大于平均灰度的都算作白色)
jpPrinter.setBitmap3 = function (x, y, mode, res) { //添加图片,res为画布参数
var width = parseInt((res.width) / 8 * 8 / 8)
var height = res.height
var imgWidth = res.width
var time = 1;
var temp = res.data.length - width * 32;
var pointList = []
var resultData = []
console.log(width + "--" + height)
data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
jpPrinter.addCommand(data)
//console.log(res.data)
console.log('---以上是原始数据---')
let sumRed = 0,
sumGreen = 0,
sumBlue = 0;
let total = height * imgWidth;
let pix = res.data;
for (var i = 0; i < pix.length; i += 4) {
sumRed += pix[i]
sumGreen += pix[i + 1]
sumBlue += pix[i + 2]
}
let avgRed = parseInt(sumRed / total);
let avgGreen = parseInt(sumGreen / total);
let avgBlue = parseInt(sumBlue / total);
let avgGrayColor = avgRed * 0.299 + avgGreen * 0.587 + avgBlue * 0.114
//for循环顺序不要错了,外层遍历高度,内层遍历宽度,因为横向每8个像素点组成一个字节
for (var y = 0; y < height; y++) {
for (var x = 0; x < imgWidth; x++) {
let r = res.data[(y * imgWidth + x) * 4];
let g = res.data[(y * imgWidth + x) * 4 + 1];
let b = res.data[(y * imgWidth + x) * 4 + 2];
let a = res.data[(y * imgWidth + x) * 4 + 3]
// 像素灰度值
let grayColor = r * 0.299 + g * 0.587 + b * 0.114
//灰度值大于128位
//1不打印, 0打印 (参考:佳博标签打印机编程手册tspl)
if (grayColor > avgGrayColor) {
pointList.push(1)
} else {
pointList.push(0)
}
}
}
//console.log(pointList)
for (var i = 0; i < pointList.length; i += 8) {
var p = pointList[i] * 128 + pointList[i + 1] * 64 + pointList[i + 2] * 32 + pointList[i +
3] * 16 + pointList[i + 4] * 8 + pointList[i + 5] * 4 + pointList[i + 6] * 2 +
pointList[i + 7]
resultData.push(p)
}
console.log('最终数据:')
//console.log(resultData)
for (var i = 0; i < resultData.length; ++i) {
command.push(intToByte(resultData[i]))
}
}
jpPrinter.RawCommand = function (data) {
jpPrinter.addCommand(data)
}
jpPrinter.setPagePrint = function () { //打印页面
// data = "PRINT 1,1\r\n"
data = "PRINT \r\n"
jpPrinter.addCommand(data)
};
//获取打印数据
jpPrinter.getData = function () {
return command;
};
jpPrinter.getRawData = function () {
return rawCommand;
};
jpPrinter.clearCommand = function () {
rawCommand = ''
};
return jpPrinter;
}
};
module.exports.jpPrinter = jpPrinter;
- 打印机,需要插usb(或者微信小程序设置wifi)
- 打印机的默认端口是9100(一般是,具体看购买的打印机)
- wifitool.js是用于连接打印机的
-
此文章是用的CPCL指令,可以根据自己的指令格式修改(var command = tsc.jpPrinter.createNew();)
vue<template> <view> <button @click="printSocket">WiFi打印</button> </view> </template>
js
<script> import tsc from "@/static/libs/tsc.js"; import wifiTool from "@/static/libs/WifiTool.js"; export default { data() { return { ip: "192.168.4.245", port: 9100, str: "yyy", }; }, mounted() { wifiTool.connectWifi("192.168.4.245", 9100); }, methods: { test(str, ip, port) { str = "test"; var Socket = plus.android.importClass("java.net.Socket"); var JavaByte = plus.android.importClass("java.lang.Byte"); var PrintWriter = plus.android.importClass("java.io.PrintWriter"); var BufferedWriter = plus.android.importClass("java.io.BufferedWriter"); var OutputStreamWriter = plus.android.importClass("java.io.OutputStreamWriter"); var BufferedReader = plus.android.importClass("java.io.BufferedReader"); var InputStreamReader = plus.android.importClass("java.io.InputStreamReader"); //测试改良 var StrictMode = plus.android.importClass("android.os.StrictMode"); var Build = plus.android.importClass("android.os.Build"); if (Build.VERSION.SDK_INT > 9) { var policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } var socket = new Socket(ip, port); var outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); var bufferWriter = new BufferedWriter(outputStreamWriter); var out = new PrintWriter(bufferWriter, true); var command = tsc.jpPrinter.createNew(); command.init(0, 200, 200, 210, 1); command.setCls(); command.setQR(30, 40, 2, 6, "123"); // command.setMag(2, 2); // command.setText1(55, 30, 180, 60, "地址: 内蒙"); // command.setMag(2, 2); // command.setText1(55, 6, 180, 120, "重量: 15kg"); command.setPagePrint(); let data = command.getData(); out.println(data); socket.setSoTimeout(3000); socket.close(); }, printSocket() { var command = tsc.jpPrinter.createNew(); command.init(0, 200, 250, 210, 1); command.setCls(); command.setQR(30, 40, 2, 6, "123"); command.setMag(2, 2); command.setText1(55, 30, 180, 60, "地址: 内蒙"); command.setMag(2, 2); command.setText1(55, 6, 180, 120, "重量: 15kg"); command.setPostfeed(32);//打印后走纸 command.setPagePrint(); let data = command.getData(); wifiTool.sendData(data); }, connectWifi(ip, port) { if (plus.os.name == "Android") { // plus.nativeUI.showWaiting("正在打印中。。。"); var Socket = plus.android.importClass("java.net.Socket"); var socket; var outputStream; //解决高低版本兼容 var StrictMode = plus.android.importClass("android.os.StrictMode"); var Build = plus.android.importClass("android.os.Build"); if (Build.VERSION.SDK_INT > 9) { var policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } try { socket = new Socket(ip, port); socket.setKeepAlive(true); outputStream = socket.getOutputStream(); plus.android.importClass(outputStream); // var bytes = plus.android.invoke(str, "getBytes", "gbk"); uni.showToast({ title: "wifi连接成功", }); // plus.nativeUI.closeWaiting(); } catch (e) { console.log("网络连接超时,请重新连接!"); //TODO handle the exception // plus.nativeUI.closeWaiting(); } } }, }, }; </script>
WifiTool.js
```javascript
/**
* 初始化参数
*/
//#ifdef APP-PLUS
var Socket = plus.android.importClass("java.net.Socket");
var socket;
var outputStream;
//解决高低版本兼容
var StrictMode = plus.android.importClass("android.os.StrictMode");
var Build = plus.android.importClass("android.os.Build");
//#endif
/**
* 构造对象
*/
var wifiTool = {
connectWifi(ip, port) {
if (Build.VERSION.SDK_INT > 9) {
var policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
try {
socket = new Socket(ip, port);
socket.setKeepAlive(true);
outputStream = socket.getOutputStream();
plus.android.importClass(outputStream);
// var bytes = plus.android.invoke(str, "getBytes", "gbk");
uni.showToast({
title: 'wifi连接成功',
});
// plus.nativeUI.closeWaiting();
} catch (e) {
console.log("网络连接超时,请重新连接!")
//TODO handle the exception
// plus.nativeUI.closeWaiting();
}
},
sendData(data) {
console.log(outputStream);
outputStream.write(data);
outputStream.flush();
// 关闭socket
// socket.shutdownOutput();
console.log(socket);
}
}
module.exports = wifiTool
tsc.js
/**
* tsc 命令打印工具类
* 2021.04.26 uni-app版本
* @auth boolTrue
*/
var encode = require("./encoding.js");
var jpPrinter = {
createNew: function () {
var jpPrinter = {};
var data = "";
var command = []
var rawCommand = ''
jpPrinter.name = "标签模式";
jpPrinter.init = function (x, y, z, h, q) {
data = "! " + x + " " + y + " " + z + " " + h + " " + q + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.addCommand = function (content) { //将指令转成数组装起
//#ifdef MP-WEIXIN
var code = new encode.TextEncoder(
'gb18030', {
NONSTANDARD_allowLegacyEncoding: true
}).encode(content)
for (var i = 0; i < code.length; ++i) {
command.push(code[i])
}
//#endif
//#ifdef APP-PLUS
let byteCommand = plus.android.invoke(content, 'getBytes', 'gb18030');
for (var i = 0; i < byteCommand.length; ++i) {
command.push(byteCommand[i])
}
//#endif
console.log('command--:', content)
rawCommand += content
}
function intToByte(i) {
// 此处关键 -- android是java平台 byte数值范围是 [-128, 127]
// 因为java平台的byte类型是有符号的 最高位表示符号,所以数值范围固定
// 而图片计算出来的是数值是 0 -255 属于int类型
// 所以把int 转换成byte类型
//#ifdef APP-PLUS
var b = i & 0xFF;
var c = 0;
if (b >= 128) {
c = b % 128;
c = -1 * (128 - c);
} else {
c = b;
}
return c
//#endif
// 而微信小程序不需要,因为小程序api接收的是 无符号8位
//#ifdef MP-WEIXIN
return i
//#endif
}
jpPrinter.setPostfeed = function (length) { //打印之后走纸距离指令
data = "POSTFEED " +length + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setSize = function (pageWidght, pageHeight) { //设置页面大小
data = "SIZE " + pageWidght.toString() + " mm" + "," + pageHeight.toString() + " mm" + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setSpeed = function (printSpeed) { //设置打印机速度
data = "SPEED " + printSpeed.toString() + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setDensity = function (printDensity) { //设置打印机浓度
data = "DENSITY " + printDensity.toString() + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setGap = function (printGap) { //传感器
data = "GAP " + printGap.toString() + " mm\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setCountry = function (country) { //选择国际字符集
/*
001:USA
002:French
003:Latin America
034:Spanish
039:Italian
044:United Kingdom
046:Swedish
047:Norwegian
049:German
*/
data = "COUNTRY " + country + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setCodepage = function (codepage) { //选择国际代码页
/*
8-bit codepage 字符集代表
437:United States
850:Multilingual
852:Slavic
860:Portuguese
863:Canadian/French
865:Nordic
Windows code page
1250:Central Europe
1252:Latin I
1253:Greek
1254:Turkish
以下代码页仅限于 12×24 dot 英数字体
WestEurope:WestEurope
Greek:Greek
Hebrew:Hebrew
EastEurope:EastEurope
Iran:Iran
IranII:IranII
Latvian:Latvian
Arabic:Arabic
Vietnam:Vietnam
Uygur:Uygur
Thai:Thai
1252:Latin I
1257:WPC1257
1251:WPC1251
866:Cyrillic
858:PC858
747:PC747
864:PC864
1001:PC100
*/
data = "CODEPAGE " + codepage + "\r\n";
jpPrinter.addCommand(data)
}
jpPrinter.setCls = function () { //清除打印机缓存
data = "CLS " + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setFeed = function (feed) { //将纸向前推出n
data = "FEED " + feed + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setBackFeed = function (backup) { //将纸向后回拉n
data = "BACKFEED " + backup + "\r\n";
jpPrinter.addCommand(data)
}
jpPrinter.setDirection = function (direction) { //设置打印方向,参考编程手册
data = "DIRECTION " + direction + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setReference = function (x, y) { //设置坐标原点,与打印方向有关
data = "REFERENCE " + x + "," + y + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setFrom = function () { //定位控制指令
data = "FORM \r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setFromfeed = function () { //根据Size进一张标签纸
data = "FORMFEED \r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setHome = function () { //根据Size找到下一张标签纸的位置
data = "HOME \r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setSound = function (level, interval) { //控制蜂鸣器
data = "SOUND " + level + "," + interval + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setLimitfeed = function (limit) { // 检测垂直间距
data = "LIMITFEED " + limit + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setBar = function (x, y, width, height) { //绘制线条
data = "BAR " + x + "," + y + "," + width + "," + height + "\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setBox = function (x_start, y_start, x_end, y_end, thickness) { //绘制方框
data = "BOX " + x_start + "," + y_start + "," + x_end + "," + y_end + "," + thickness + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setErase = function (x_start, y_start, x_width, y_height) { //清除指定区域的数据
data = "ERASE " + x_start + "," + y_start + "," + x_width + "," + y_height + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setReverse = function (x_start, y_start, x_width, y_height) { //将指定的区域反相打印
data = "REVERSE " + x_start + "," + y_start + "," + x_width + "," + y_height + "\r\n";
jpPrinter.addCommand(data)
};
jpPrinter.setText1 = function (font, s, x, y, str) { //打印文字
data = "TEXT " + font + " " + s + " " + x + " " + y + " " + str + "\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setText = function (x, y, font, x_, y_, str) { //打印文字
data = "TEXT " + x + "," + y + ",\"" + font + "\"," + 0 + "," + x_ + "," + y_ + "," + "\"" +
str + "\"\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setMag = function (w,h) { //打印二维码
data = "SETMAG " + w + " " + h + "\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setQR = function (x, y, m, u, content) { //打印二维码
data = "BARCODE QR " + x + " " + y + " M " + m + " U " + u + "\r\n MA," + content + "\r\nENDQR\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setPDF = function (type, x, y,xd,yd,c,s, content) { //打印条形
data = "BARCODE " + type + " " + x + " " + y + " XD " + xd + " YD " + yd + " C " + c + " S " + s + "\r\n"+ content+ "\r\nENDPDF\r\n"
jpPrinter.addCommand(data)
};
jpPrinter.setBarCode = function (type, w, r, h, x, y, content) { //打印条形码
data = "BARCODE " + type + " " + w + " " + r + " " + h + " " + x + " " + y + " " + content + "\r\n"
jpPrinter.addCommand(data)
};
// 固定灰度阈值(128以上的都看作白色)
jpPrinter.setBitmap = function (x, y, mode, res) { //添加图片,res为画布参数
var width = parseInt((res.width) / 8 * 8 / 8)
var height = res.height
var imgWidth = res.width
var time = 1;
var temp = res.data.length - width * 32;
var pointList = []
var resultData = []
console.log(width + "--" + height)
data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
jpPrinter.addCommand(data)
//console.log(res.data)
console.log('---以上是原始数据---')
//for循环顺序不要错了,外层遍历高度,内层遍历宽度,因为横向每8个像素点组成一个字节
for (var y = 0; y < height; y++) {
for (var x = 0; x < imgWidth; x++) {
let r = res.data[(y * imgWidth + x) * 4];
let g = res.data[(y * imgWidth + x) * 4 + 1];
let b = res.data[(y * imgWidth + x) * 4 + 2];
let a = res.data[(y * imgWidth + x) * 4 + 3]
//console.log(`当前${y}行${x}列像素,rgba值:(${r},${g},${b},${a})`)
// 像素灰度值
let grayColor = r * 0.299 + g * 0.587 + b * 0.114
//灰度值大于128位
//1不打印, 0打印 (参考:佳博标签打印机编程手册tspl)
if (grayColor > 128) {
pointList.push(1)
} else {
pointList.push(0)
}
}
}
//console.log(pointList)
for (var i = 0; i < pointList.length; i += 8) {
var p = pointList[i] * 128 + pointList[i + 1] * 64 + pointList[i + 2] * 32 + pointList[i +
3] * 16 + pointList[i + 4] * 8 + pointList[i + 5] * 4 + pointList[i + 6] * 2 +
pointList[i + 7]
resultData.push(p)
}
console.log('最终数据:')
//console.log(resultData)
for (var i = 0; i < resultData.length; ++i) {
command.push(intToByte(resultData[i]))
}
}
jpPrinter.setBitmap2 = function (x, y, mode, res) { //添加图片,res为画布参数
var w = res.width
var width = parseInt((res.width + 7) / 8 * 8 / 8)
var height = res.height;
console.log(width + "--" + height)
data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
jpPrinter.addCommand(data)
var r = []
var bits = new Uint8Array(height * width);
for (y = 0; y < height; y++) {
for (x = 0; x < w; x++) {
let r = res.data[(y * w + x) * 4];
let g = res.data[(y * w + x) * 4 + 1];
let b = res.data[(y * w + x) * 4 + 2];
let a = res.data[(y * w + x) * 4 + 3]
var color = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) <<
0);
if ((color & 0xFF) > 128) {
bits[parseInt(y * width + x / 8)] |= (0x80 >> (x % 8));
}
}
}
for (var i = 0; i < bits.length; i++) {
//command.push((~bits[i]) & 0xFF);
command.push(intToByte(bits[i]));
//r.push((~bits[i]) & 0xFF);
}
}
// 平均灰度阈值(先计算平均灰度,然后大于平均灰度的都算作白色)
jpPrinter.setBitmap3 = function (x, y, mode, res) { //添加图片,res为画布参数
var width = parseInt((res.width) / 8 * 8 / 8)
var height = res.height
var imgWidth = res.width
var time = 1;
var temp = res.data.length - width * 32;
var pointList = []
var resultData = []
console.log(width + "--" + height)
data = "BITMAP " + x + "," + y + "," + width + "," + height + "," + mode + ","
jpPrinter.addCommand(data)
//console.log(res.data)
console.log('---以上是原始数据---')
let sumRed = 0,
sumGreen = 0,
sumBlue = 0;
let total = height * imgWidth;
let pix = res.data;
for (var i = 0; i < pix.length; i += 4) {
sumRed += pix[i]
sumGreen += pix[i + 1]
sumBlue += pix[i + 2]
}
let avgRed = parseInt(sumRed / total);
let avgGreen = parseInt(sumGreen / total);
let avgBlue = parseInt(sumBlue / total);
let avgGrayColor = avgRed * 0.299 + avgGreen * 0.587 + avgBlue * 0.114
//for循环顺序不要错了,外层遍历高度,内层遍历宽度,因为横向每8个像素点组成一个字节
for (var y = 0; y < height; y++) {
for (var x = 0; x < imgWidth; x++) {
let r = res.data[(y * imgWidth + x) * 4];
let g = res.data[(y * imgWidth + x) * 4 + 1];
let b = res.data[(y * imgWidth + x) * 4 + 2];
let a = res.data[(y * imgWidth + x) * 4 + 3]
// 像素灰度值
let grayColor = r * 0.299 + g * 0.587 + b * 0.114
//灰度值大于128位
//1不打印, 0打印 (参考:佳博标签打印机编程手册tspl)
if (grayColor > avgGrayColor) {
pointList.push(1)
} else {
pointList.push(0)
}
}
}
//console.log(pointList)
for (var i = 0; i < pointList.length; i += 8) {
var p = pointList[i] * 128 + pointList[i + 1] * 64 + pointList[i + 2] * 32 + pointList[i +
3] * 16 + pointList[i + 4] * 8 + pointList[i + 5] * 4 + pointList[i + 6] * 2 +
pointList[i + 7]
resultData.push(p)
}
console.log('最终数据:')
//console.log(resultData)
for (var i = 0; i < resultData.length; ++i) {
command.push(intToByte(resultData[i]))
}
}
jpPrinter.RawCommand = function (data) {
jpPrinter.addCommand(data)
}
jpPrinter.setPagePrint = function () { //打印页面
// data = "PRINT 1,1\r\n"
data = "PRINT \r\n"
jpPrinter.addCommand(data)
};
//获取打印数据
jpPrinter.getData = function () {
return command;
};
jpPrinter.getRawData = function () {
return rawCommand;
};
jpPrinter.clearCommand = function () {
rawCommand = ''
};
return jpPrinter;
}
};
module.exports.jpPrinter = jpPrinter;
收起阅读 »