
可视化编辑照片书
照片编辑可视化小程序,前端uniapp,后端PHP,一款综合全面的图片编辑软件,支持裁剪、旋转、改尺寸、压缩体积、文字、贴纸、边框等图片编辑功能。软件汇集了大量的模板和装饰素材,采用可视化的设计界面,用户只需导入图片编辑即可制作成书。软件支持快速排版,简单易用,适合制作个性杂志、个性台历、纪念册、个性笔记本等。照片书排版是一款功能强大的照片书制作工具。软件汇集了大量的模板和装饰素材,采用可视化的设计界面,用户只需导入图片编辑即可制作成书。软件支持快速排版,简单易用,适合制作个性杂志、个性台历、纪念册、个性笔记本等。可以打包app,目前已有现成小程序可以体验,欢迎有需要的人士前来询问,V:zpj2220
照片编辑可视化小程序,前端uniapp,后端PHP,一款综合全面的图片编辑软件,支持裁剪、旋转、改尺寸、压缩体积、文字、贴纸、边框等图片编辑功能。软件汇集了大量的模板和装饰素材,采用可视化的设计界面,用户只需导入图片编辑即可制作成书。软件支持快速排版,简单易用,适合制作个性杂志、个性台历、纪念册、个性笔记本等。照片书排版是一款功能强大的照片书制作工具。软件汇集了大量的模板和装饰素材,采用可视化的设计界面,用户只需导入图片编辑即可制作成书。软件支持快速排版,简单易用,适合制作个性杂志、个性台历、纪念册、个性笔记本等。可以打包app,目前已有现成小程序可以体验,欢迎有需要的人士前来询问,V:zpj2220
收起阅读 »
使用uniapp开发ZEBRA(斑马 TC21/TC26)PDA,开启/关闭激光红外,接收扫描数据
公司要为斑马PDA开发app,使用技术是uniapp,需求是当管理后台给PDA分配扫码权限后,PDA才能启动激光红外扫码功能,否则不能启动扫码,最开始的实现思路是当拥有扫码权限,就处理扫描出来的数据,没有扫码权限就不处理扫描出来的数据(注:当没有扫码权限时,按下物理的扫描按键也会出现激光),老板说不行,必须是没有权限不能启动激光红外线,也就是没有权限,按了物理的扫描按键,也不能出现激光。
带着需求在斑马官网找到了对应的API,因此记录一下自己开发过程,方便以后查找,也方便即将遇到同样问题的同学
1、拿出斑马PDA充电、并开机,手指在主屏幕上,向上滑动,找到应用“DataWedge”,如图

2、点击“DataWedge”,启动Profile0(default)文件,并配置Intent输出,禁用Launcher和DWDemo配置文件,如图
3、好了,斑马PDA已经设置好了,现在开始用uniapp做开发了,主要代码如下
var scanObj = {};
//是否开启激光红外线扫描,true开启,false关闭
Vue.prototype.isOpenLaserScan = (isOpen) => {
scanObj.main = plus.android.runtimeMainActivity();//获取activity
var Intent = plus.android.importClass('android.content.Intent');
scanObj.intent = new Intent();
scanObj.intent.setAction("com.symbol.datawedge.api.ACTION");
scanObj.intent.putExtra("com.symbol.datawedge.api.ENABLE_DATAWEDGE", isOpen);
scanObj.main.sendBroadcast(scanObj.intent);
}
//初始化扫描设备
Vue.prototype.initScan = (fun) => {
scanObj.main = plus.android.runtimeMainActivity();//获取activity
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
scanObj.filter = new IntentFilter();
scanObj.filter.addAction("com.dwexample.ACTION"); // 换你的广播动作 com.service.scanner.data(海康威视的PDA)
scanObj.receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver',{
onReceive: function(context, intent) {
plus.android.importClass(intent);
let code = intent.getStringExtra("com.motorolasolutions.emdk.datawedge.data_string");// 换你的广播标签 ScanCode(海康威视的PDA)
if(typeof(fun) === 'function'){
fun(code);
}
}});
}
//启动扫描设备
Vue.prototype.startScan = () => {
try{
scanObj.main.registerReceiver(scanObj.receiver,scanObj.filter);
}catch(e){
//TODO handle the exception
}
}
//停止扫描设备
Vue.prototype.stopScan = () => {
try{
scanObj.main.unregisterReceiver(scanObj.receiver);
}catch(e){
//TODO handle the exception
}
}
4、在生命周期onShow函数中调用扫码,页面调用如下
onShow(){
//监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
let _this = this;
//是否开启激光红外线扫描,默认开启
_this.isOpenLaserScan(true);
//判断是否拥有当前页面的扫码权限,这里是自己业务的权限判断
if (!_this.hasPerm('PutBaseFlour:scan')) {
uni.showModal({
title: '提示',
content: '你没有扫码权限,无法开启激光扫码,请联系管理员!',
showCancel: false
});
setTimeout(() => {
//是否开启激光红外线扫描,没有相应的权限则关闭
_this.isOpenLaserScan(false);
}, 500);
return;
}
_this.initScan((code) => {
//处理自己的业务逻辑
//code为扫码取得的值
});
_this.startScan();
}
5、使用完成后需要销毁,由于app、h5、ios、微信等所支持的生命周期有所不同,建议在以下生命周期中销毁防止没有销毁掉的情况,避免多次进入页面扫码后取得多个值
onHide(){
//监听页面隐藏
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
},
onUnload(){
//监听页面卸载
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
},
onBackPress(){
//监听页面返回
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
},
destroyed:function(){
//页面退出时一定要卸载监听,否则下次进来时会重复,造成扫一次出2个以上的结果
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
}
公司要为斑马PDA开发app,使用技术是uniapp,需求是当管理后台给PDA分配扫码权限后,PDA才能启动激光红外扫码功能,否则不能启动扫码,最开始的实现思路是当拥有扫码权限,就处理扫描出来的数据,没有扫码权限就不处理扫描出来的数据(注:当没有扫码权限时,按下物理的扫描按键也会出现激光),老板说不行,必须是没有权限不能启动激光红外线,也就是没有权限,按了物理的扫描按键,也不能出现激光。
带着需求在斑马官网找到了对应的API,因此记录一下自己开发过程,方便以后查找,也方便即将遇到同样问题的同学
1、拿出斑马PDA充电、并开机,手指在主屏幕上,向上滑动,找到应用“DataWedge”,如图
2、点击“DataWedge”,启动Profile0(default)文件,并配置Intent输出,禁用Launcher和DWDemo配置文件,如图
3、好了,斑马PDA已经设置好了,现在开始用uniapp做开发了,主要代码如下
var scanObj = {};
//是否开启激光红外线扫描,true开启,false关闭
Vue.prototype.isOpenLaserScan = (isOpen) => {
scanObj.main = plus.android.runtimeMainActivity();//获取activity
var Intent = plus.android.importClass('android.content.Intent');
scanObj.intent = new Intent();
scanObj.intent.setAction("com.symbol.datawedge.api.ACTION");
scanObj.intent.putExtra("com.symbol.datawedge.api.ENABLE_DATAWEDGE", isOpen);
scanObj.main.sendBroadcast(scanObj.intent);
}
//初始化扫描设备
Vue.prototype.initScan = (fun) => {
scanObj.main = plus.android.runtimeMainActivity();//获取activity
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
scanObj.filter = new IntentFilter();
scanObj.filter.addAction("com.dwexample.ACTION"); // 换你的广播动作 com.service.scanner.data(海康威视的PDA)
scanObj.receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver',{
onReceive: function(context, intent) {
plus.android.importClass(intent);
let code = intent.getStringExtra("com.motorolasolutions.emdk.datawedge.data_string");// 换你的广播标签 ScanCode(海康威视的PDA)
if(typeof(fun) === 'function'){
fun(code);
}
}});
}
//启动扫描设备
Vue.prototype.startScan = () => {
try{
scanObj.main.registerReceiver(scanObj.receiver,scanObj.filter);
}catch(e){
//TODO handle the exception
}
}
//停止扫描设备
Vue.prototype.stopScan = () => {
try{
scanObj.main.unregisterReceiver(scanObj.receiver);
}catch(e){
//TODO handle the exception
}
}
4、在生命周期onShow函数中调用扫码,页面调用如下
onShow(){
//监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
let _this = this;
//是否开启激光红外线扫描,默认开启
_this.isOpenLaserScan(true);
//判断是否拥有当前页面的扫码权限,这里是自己业务的权限判断
if (!_this.hasPerm('PutBaseFlour:scan')) {
uni.showModal({
title: '提示',
content: '你没有扫码权限,无法开启激光扫码,请联系管理员!',
showCancel: false
});
setTimeout(() => {
//是否开启激光红外线扫描,没有相应的权限则关闭
_this.isOpenLaserScan(false);
}, 500);
return;
}
_this.initScan((code) => {
//处理自己的业务逻辑
//code为扫码取得的值
});
_this.startScan();
}
5、使用完成后需要销毁,由于app、h5、ios、微信等所支持的生命周期有所不同,建议在以下生命周期中销毁防止没有销毁掉的情况,避免多次进入页面扫码后取得多个值
onHide(){
//监听页面隐藏
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
},
onUnload(){
//监听页面卸载
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
},
onBackPress(){
//监听页面返回
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
},
destroyed:function(){
//页面退出时一定要卸载监听,否则下次进来时会重复,造成扫一次出2个以上的结果
if (this.hasPerm('PutBaseFlour:scan')) {
this.stopScan();
}
}

更新最新版本后,uniapp安心打包报错
[Info] 正在制作apk安装包...
[Info] I: Using Apktool 2.4.1 on __UNI__2DB9CA0_cm.apk
[Info] I: Loading resource table...
[Info] I: Decoding AndroidManifest.xml with resources...
[Info] I: Loading resource table from file: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk
[Info] I: Regular manifest package...
[Info] I: Decoding file-resources...
[Info] I: Decoding values */* XMLs...
[Info] I: Copying raw classes.dex file...
[Info] I: Copying raw classes2.dex file...
[Info] I: Copying raw classes3.dex file...
[Info] I: Copying raw assets/39285EFA.dex file...
[Info] I: Copying assets and libs...
[Info] I: Copying unknown files...
[Info] I: Copying original files...
[Info] begin replace files to apk...
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-hdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-hdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-hdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-hdpi/icon.png] success.
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xhdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xhdpi/icon.png] success.
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxhdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxhdpi/icon.png] success.
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxxhdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxxhdpi/icon.png] success.
[Info] begin update files to apk...
[Error] try compile package:0
[Info] I: Using Apktool 2.4.1
[Info] I: Copying C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm classes.dex file...
[Info] I: Copying C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm classes2.dex file...
[Info] I: Copying C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm classes3.dex file...
[Info] I: Checking whether resources has changed...
[Info] I: Building resources...
[Error] W: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml:48: Tag <action> attribute name has invalid character '*'.
[Error] brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\FGHF\AppData\Local\Temp\brut_util_Jar_13841587595912769514.tmp, p, --forced-package-id, 127, --min-sdk-version, 19, --target-sdk-version, 28, --version-code, 32, --version-name, 3.1.0, --no-version-vectors, -F, C:\Users\FGHF\AppData\Local\Temp\APKTOOL640673907049858768.tmp, -e, C:\Users\FGHF\AppData\Local\Temp\APKTOOL11117073117559654397.tmp, -0, arsc, -I, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk, -S, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\res, -M, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml]
[Error] try compile package:1
[Info] I: Using Apktool 2.4.1
[Info] I: Checking whether resources has changed...
[Info] I: Building resources...
[Error] W: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml:48: Tag <action> attribute name has invalid character '*'.
[Error] brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\FGHF\AppData\Local\Temp\brut_util_Jar_1364484411461162108.tmp, p, --forced-package-id, 127, --min-sdk-version, 19, --target-sdk-version, 28, --version-code, 32, --version-name, 3.1.0, --no-version-vectors, -F, C:\Users\FGHF\AppData\Local\Temp\APKTOOL9195127418260236703.tmp, -e, C:\Users\FGHF\AppData\Local\Temp\APKTOOL13100643559112781843.tmp, -0, arsc, -I, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk, -S, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\res, -M, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml]
[Error] try compile package:2
[Info] I: Using Apktool 2.4.1
[Info] I: Checking whether resources has changed...
[Info] I: Building resources...
[Error] W: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml:48: Tag <action> attribute name has invalid character '*'.
[Error] brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\FGHF\AppData\Local\Temp\brut_util_Jar_8421301333268526767.tmp, p, --forced-package-id, 127, --min-sdk-version, 19, --target-sdk-version, 28, --version-code, 32, --version-name, 3.1.0, --no-version-vectors, -F, C:\Users\FGHF\AppData\Local\Temp\APKTOOL15143234046447346603.tmp, -e, C:\Users\FGHF\AppData\Local\Temp\APKTOOL5501540500666506840.tmp, -0, arsc, -I, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk, -S, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\res, -M, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml]
[Error] Apk tool compile package to apk failed
[Error] 制作结果:Failed. Reason:
[Info] 正在制作apk安装包...
[Info] I: Using Apktool 2.4.1 on __UNI__2DB9CA0_cm.apk
[Info] I: Loading resource table...
[Info] I: Decoding AndroidManifest.xml with resources...
[Info] I: Loading resource table from file: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk
[Info] I: Regular manifest package...
[Info] I: Decoding file-resources...
[Info] I: Decoding values */* XMLs...
[Info] I: Copying raw classes.dex file...
[Info] I: Copying raw classes2.dex file...
[Info] I: Copying raw classes3.dex file...
[Info] I: Copying raw assets/39285EFA.dex file...
[Info] I: Copying assets and libs...
[Info] I: Copying unknown files...
[Info] I: Copying original files...
[Info] begin replace files to apk...
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-hdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-hdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-hdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-hdpi/icon.png] success.
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xhdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xhdpi/icon.png] success.
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxhdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxhdpi/icon.png] success.
[Info] begin copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxxhdpi/icon.png]...
[Info] copy file[E:/work_wkz/yg_h5_v2/unpackage/cache/wgt/__UNI__2DB9CA0/.manifest/icon-android-xxxhdpi.png] to [C:/Users/FGHF/AppData/Local/HBuilder X/AndroidPackWork/cache/__UNI__2DB9CA0/packge_cache/__NONE__/__UNI__2DB9CA0_cm/res/drawable-xxxhdpi/icon.png] success.
[Info] begin update files to apk...
[Error] try compile package:0
[Info] I: Using Apktool 2.4.1
[Info] I: Copying C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm classes.dex file...
[Info] I: Copying C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm classes2.dex file...
[Info] I: Copying C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm classes3.dex file...
[Info] I: Checking whether resources has changed...
[Info] I: Building resources...
[Error] W: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml:48: Tag <action> attribute name has invalid character '*'.
[Error] brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\FGHF\AppData\Local\Temp\brut_util_Jar_13841587595912769514.tmp, p, --forced-package-id, 127, --min-sdk-version, 19, --target-sdk-version, 28, --version-code, 32, --version-name, 3.1.0, --no-version-vectors, -F, C:\Users\FGHF\AppData\Local\Temp\APKTOOL640673907049858768.tmp, -e, C:\Users\FGHF\AppData\Local\Temp\APKTOOL11117073117559654397.tmp, -0, arsc, -I, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk, -S, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\res, -M, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml]
[Error] try compile package:1
[Info] I: Using Apktool 2.4.1
[Info] I: Checking whether resources has changed...
[Info] I: Building resources...
[Error] W: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml:48: Tag <action> attribute name has invalid character '*'.
[Error] brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\FGHF\AppData\Local\Temp\brut_util_Jar_1364484411461162108.tmp, p, --forced-package-id, 127, --min-sdk-version, 19, --target-sdk-version, 28, --version-code, 32, --version-name, 3.1.0, --no-version-vectors, -F, C:\Users\FGHF\AppData\Local\Temp\APKTOOL9195127418260236703.tmp, -e, C:\Users\FGHF\AppData\Local\Temp\APKTOOL13100643559112781843.tmp, -0, arsc, -I, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk, -S, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\res, -M, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml]
[Error] try compile package:2
[Info] I: Using Apktool 2.4.1
[Info] I: Checking whether resources has changed...
[Info] I: Building resources...
[Error] W: C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml:48: Tag <action> attribute name has invalid character '*'.
[Error] brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\FGHF\AppData\Local\Temp\brut_util_Jar_8421301333268526767.tmp, p, --forced-package-id, 127, --min-sdk-version, 19, --target-sdk-version, 28, --version-code, 32, --version-name, 3.1.0, --no-version-vectors, -F, C:\Users\FGHF\AppData\Local\Temp\APKTOOL15143234046447346603.tmp, -e, C:\Users\FGHF\AppData\Local\Temp\APKTOOL5501540500666506840.tmp, -0, arsc, -I, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\apktool\1.apk, -S, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\res, -M, C:\Users\FGHF\AppData\Local\HBuilder X\AndroidPackWork\cache\__UNI__2DB9CA0\packge_cache\__NONE__\__UNI__2DB9CA0_cm\AndroidManifest.xml]
[Error] Apk tool compile package to apk failed
[Error] 制作结果:Failed. Reason:
收起阅读 »

解决cli项目下配合uniCloud开发时云函数上传下载过于麻烦的问题
一、要解决的问题
uniapp cli vue3 开发小程序时,大多采用的编辑器时vscode 如果此时同时使用unicloud,那么需要两个IDE来回切换,因此制作了一个小工具,原理上是对hbuilder cli 进行封装,从而可以更便捷的上传下载云函数
二、图片介绍
三、使用方法
务必将Hbuilder路径设在在环境变量内,否则会提示版本冲突
下载Releases内最符合您操作系统的版本,将uc.exe 文件放置在您的运行目录下即可使用如下方法进行操作
1. 列举资源信息
uc.exe -l [resource]
如 uc.exe -l cf
表示:列举当前项目中所有云端函数
2. 资源上传[默认覆盖非跳过]
uc.exe -u [resource] [name]
如 uc.exe -u cf test
表示:上传名称为test的云函数,如果云端存在同名函数则覆盖
3. 资源下载[默认覆盖非跳过]
uc.exe -d [resource] [name]
如 uc.exe -d cf test
表示:下载名称为test的云函数,如果本地存在同名函数则覆盖
4. resource的取值如下
resource | 含义 |
---|---|
cf 或 cloudfunction | 云函数 |
cm 或 common | 云函数的公共模块 |
db | 数据集合Schema |
vf | 数据库校验函数 |
ac 或 action | 数据库触发条件 |
sp 或 space | 云空间 |
四、链接下载地址
可以点击链接下载,或者下载附件内的压缩文件使用
github UC
一、要解决的问题
uniapp cli vue3 开发小程序时,大多采用的编辑器时vscode 如果此时同时使用unicloud,那么需要两个IDE来回切换,因此制作了一个小工具,原理上是对hbuilder cli 进行封装,从而可以更便捷的上传下载云函数
二、图片介绍
三、使用方法
务必将Hbuilder路径设在在环境变量内,否则会提示版本冲突
下载Releases内最符合您操作系统的版本,将uc.exe 文件放置在您的运行目录下即可使用如下方法进行操作
1. 列举资源信息
uc.exe -l [resource]
如 uc.exe -l cf
表示:列举当前项目中所有云端函数
2. 资源上传[默认覆盖非跳过]
uc.exe -u [resource] [name]
如 uc.exe -u cf test
表示:上传名称为test的云函数,如果云端存在同名函数则覆盖
3. 资源下载[默认覆盖非跳过]
uc.exe -d [resource] [name]
如 uc.exe -d cf test
表示:下载名称为test的云函数,如果本地存在同名函数则覆盖
4. resource的取值如下
resource | 含义 |
---|---|
cf 或 cloudfunction | 云函数 |
cm 或 common | 云函数的公共模块 |
db | 数据集合Schema |
vf | 数据库校验函数 |
ac 或 action | 数据库触发条件 |
sp 或 space | 云空间 |
四、链接下载地址
可以点击链接下载,或者下载附件内的压缩文件使用
github UC

关于动态取消checkbox选中状态代码实现~~~~~~
记住文档的一句话:checkbox的checked只做默认选中状态功能
业务逻辑是,在列表中选择学生,但是选中人数有限定,当选中事件触发后,判断当前选中人数是否超过限定人数,如果有,则将新选中的学生取消选中
核心逻辑是:
记录前几次选中的ID,找出最新选中的下标,然后将列表list清空,并根据前几次选中情况重新赋值新变量存数组,并将前几次选中的学生赋值为选中状态,在页面渲染完成后,在赋值给list
相当于对list的指针做了修改,达到页面同步更新的目的
<template>
<view class="cflex content">
<view class="w100 p25 cflex aifs w100 info-box jcsb">
<view>{{info.title}}</view>
<view class="rflex jcfs info-status">
<view>状态:{{info.statusName}}</view>
<view class="split">|</view>
<view>可投票:{{info.voteNum}}人</view>
</view>
</view>
<view class="line"></view>
<view class="p30 w100">
<checkbox-group @change="checkStudent">
<template v-for="item in list">
<label>
<view class="w100 score-item p30 cflex jcsa aifs" :key="item.studentId">
<view class="list-title rflex jcsb">
<text>{{item.studentName}}</text>
<checkbox :value="item.studentId.toString()" color="#fff" :checked="item.checked"/>
</view>
<view class="rflex aic w100">
<view>姓名:{{item.studentName}}</view>
<view class="split">|</view>
<view>性别:{{item.sexName}}</view>
</view>
</view>
</label>
</template>
</checkbox-group>
</view>
<view class="w100 cflex">
<view class="submit-btn cflex">
<text>继续学习</text>
</view>
</view>
</view>
</template>
<script>
import $cache from '@/common/js/cache.js';
import $studentTheory from '@/common/js/import/studentTheory.js';
export default {
onShow() {
let courseInfo = $cache.getCourseCache();
if (courseInfo) {
this.courseInfo = courseInfo;
let evaluationCache = $cache.getEvaluationCache();
if (evaluationCache) {
this.info = evaluationCache;
this.getDetail();
} else {
this.$common.backTips('未找到互评信息');
}
} else {
this.$common.backTips('未找到课程信息');
}
},
data() {
return {
info: {},
courseInfo: {},
list: [],
submitData: {
checkList: []
}
}
},
methods: {
getDetail() {
let statusConfig = {
1: '未开始',
2: '互评中',
3: '已结束'
};
this.$common.loading();
$studentTheory.evaluationDetail({courseEvaluationId: this.info.courseEvaluationId}, (res) => {
if (res.code == 200) {
res.data.statusName = statusConfig[res.data.status] != null ? statusConfig[res.data.status] : '';
this.info = Object.assign({}, this.info, res.data);
this.$common.getDict('sex', (dict) => {
$studentTheory.evaluationClassStudent({}, (res) => {
this.$common.loading(false);
if (res.code == 200) {
this.$each(res.data, (item) => {
item.checked = false;
item.sexName = dict[item.sex] ? dict[item.sex].dictValue : '';
});
this.list = res.data;
} else {
this.$common.backTips(res.msg);
}
});
});
} else {
this.$common.backTips(res.msg);
}
});
},
checkStudent(val) {
let valArr = val.detail.value;
//取出新增项下标
let addIndex = -1;
if (valArr.length > this.submitData.checkList.length) {
this.$each(valArr, (id, index) => {
if (!this.submitData.checkList.includes(id)) {
addIndex = index;
return false;
}
});
}
let num = valArr.length;
if (num > this.info.voteNum) {
let lastIndex = valArr.length - 1;
valArr.splice(addIndex, 1); //删除新增项
this.$common.tips('最多只能选择 ' + this.info.voteNum + ' 人');
let tmpArr = Object.assign([], this.list);
this.$each(tmpArr, (item, index) => {
item.checked = false;
if (valArr.includes(item.studentId.toString())) {
item.checked = true;
}
});
this.list = [];
this.$nextTick(() => {
this.list = tmpArr;
});
}
this.submitData.checkList = valArr;
}
}
}
</script>
记住文档的一句话:checkbox的checked只做默认选中状态功能
业务逻辑是,在列表中选择学生,但是选中人数有限定,当选中事件触发后,判断当前选中人数是否超过限定人数,如果有,则将新选中的学生取消选中
核心逻辑是:
记录前几次选中的ID,找出最新选中的下标,然后将列表list清空,并根据前几次选中情况重新赋值新变量存数组,并将前几次选中的学生赋值为选中状态,在页面渲染完成后,在赋值给list
相当于对list的指针做了修改,达到页面同步更新的目的
<template>
<view class="cflex content">
<view class="w100 p25 cflex aifs w100 info-box jcsb">
<view>{{info.title}}</view>
<view class="rflex jcfs info-status">
<view>状态:{{info.statusName}}</view>
<view class="split">|</view>
<view>可投票:{{info.voteNum}}人</view>
</view>
</view>
<view class="line"></view>
<view class="p30 w100">
<checkbox-group @change="checkStudent">
<template v-for="item in list">
<label>
<view class="w100 score-item p30 cflex jcsa aifs" :key="item.studentId">
<view class="list-title rflex jcsb">
<text>{{item.studentName}}</text>
<checkbox :value="item.studentId.toString()" color="#fff" :checked="item.checked"/>
</view>
<view class="rflex aic w100">
<view>姓名:{{item.studentName}}</view>
<view class="split">|</view>
<view>性别:{{item.sexName}}</view>
</view>
</view>
</label>
</template>
</checkbox-group>
</view>
<view class="w100 cflex">
<view class="submit-btn cflex">
<text>继续学习</text>
</view>
</view>
</view>
</template>
<script>
import $cache from '@/common/js/cache.js';
import $studentTheory from '@/common/js/import/studentTheory.js';
export default {
onShow() {
let courseInfo = $cache.getCourseCache();
if (courseInfo) {
this.courseInfo = courseInfo;
let evaluationCache = $cache.getEvaluationCache();
if (evaluationCache) {
this.info = evaluationCache;
this.getDetail();
} else {
this.$common.backTips('未找到互评信息');
}
} else {
this.$common.backTips('未找到课程信息');
}
},
data() {
return {
info: {},
courseInfo: {},
list: [],
submitData: {
checkList: []
}
}
},
methods: {
getDetail() {
let statusConfig = {
1: '未开始',
2: '互评中',
3: '已结束'
};
this.$common.loading();
$studentTheory.evaluationDetail({courseEvaluationId: this.info.courseEvaluationId}, (res) => {
if (res.code == 200) {
res.data.statusName = statusConfig[res.data.status] != null ? statusConfig[res.data.status] : '';
this.info = Object.assign({}, this.info, res.data);
this.$common.getDict('sex', (dict) => {
$studentTheory.evaluationClassStudent({}, (res) => {
this.$common.loading(false);
if (res.code == 200) {
this.$each(res.data, (item) => {
item.checked = false;
item.sexName = dict[item.sex] ? dict[item.sex].dictValue : '';
});
this.list = res.data;
} else {
this.$common.backTips(res.msg);
}
});
});
} else {
this.$common.backTips(res.msg);
}
});
},
checkStudent(val) {
let valArr = val.detail.value;
//取出新增项下标
let addIndex = -1;
if (valArr.length > this.submitData.checkList.length) {
this.$each(valArr, (id, index) => {
if (!this.submitData.checkList.includes(id)) {
addIndex = index;
return false;
}
});
}
let num = valArr.length;
if (num > this.info.voteNum) {
let lastIndex = valArr.length - 1;
valArr.splice(addIndex, 1); //删除新增项
this.$common.tips('最多只能选择 ' + this.info.voteNum + ' 人');
let tmpArr = Object.assign([], this.list);
this.$each(tmpArr, (item, index) => {
item.checked = false;
if (valArr.includes(item.studentId.toString())) {
item.checked = true;
}
});
this.list = [];
this.$nextTick(() => {
this.list = tmpArr;
});
}
this.submitData.checkList = valArr;
}
}
}
</script>
收起阅读 »

有关uniapp的苹果应用内支付调起代码编写经验分享
官方文档在这里:苹果应用内支付
官方文档有一部分说的比较模糊不太详细,报错也是如此。因此这里说一下方便大家排雷避坑
-
申请流程
- 要有苹果开发者账号这个就不说了
- 要在appstoreconnect的应用的功能下面管理你的 App 内购项目,亲测添加后把信息填写完整并保存,只要状态为准备提交即可!
- 把你添加的商品的产品 ID记下来一会要用到(下文称pid)
- 你需要到苹果开发者中心证书管理生成一个iOS App Development类型的证书并生成p12文件和Profile页面生成mobileprovision文件,具体生成教程很多就不一一列举了。生成后打包个自定义基座就可以写代码了。
注意!上述过程一个都不能漏
-
代码编写流程
代码编写文档放在文章开头了,这里说一下调起顺序:- 使用uni.getProvider获取id为appleiap的channel,下文称该channel为支付通道。例:
async getIAPChannel() { return await new Promise((recv, recj) => { uni.getProvider({ service: 'payment', success: res => { const iapChannel = res.providers.find(channel => { return channel.id === 'appleiap'; }); if (iapChannel) recv(iapChannel); recj(new Error('当前环境不支持使用IAP支付!')); } }); }); }
- 使用通道的requestProduct方法获取pid对应的商品信息。例:
let orderDetail = await new Promise((recv, recj) => { channel.requestProduct([pid], recv, recj); });
注意!!!这一步不能省略,否则会无法调起支付!!!
- 使用uni.requestPayment调起支付。例:
let transaction = await new Promise((recv, recj) => { uni.requestPayment({ provider: 'appleiap', orderInfo: { productid: pid, username: 'ORDERID' + orderid, quantity: 1, manualFinishTransaction: true }, success: t => { recv(t); }, fail: e => { recj(new Error('支付调起失败:Errcode ' + e.errCode + ',' + e.errMsg)); } }); });
- 使用uni.getProvider获取id为appleiap的channel,下文称该channel为支付通道。例:
-
掉单处理
官方文档中说明部分与实际demo代码不一致。这里说一下流程:- 使用uni.getProvider获取id为appleiap的channel,下文称该channel为支付通道。例同代码编写流程
- 使用支付通道的restoreCompletedTransactions方法获取可能掉单的订单列表(支付时一定要设置manualFinishTransaction为true否则这里就找不到了)。例:
let customOrderList = await new Promise((recv, recj) => { channel.restoreCompletedTransactions({}, recv, e => { //第一个参数必传,否则会导致app闪退! recj(new Error('获取未完成订单列表失败:Errcode ' + e.errCode + ',' + e.errMsg)); }); });
- 处理掉单,掉单分几种情况,官方文档有介绍:
const IapTransactionState = { purchasing: "0", // 【应用商店正在处理的交易】A transaction that is being processed by the App Store. purchased: "1", // 【一个成功处理的交易】A successfully processed transaction. failed: "2", // 【一个失败的交易】A failed transaction. restored: "3", // 【已经购买过该商品】A transaction that restores content previously purchased by the user. deferred: "4" // 【在队列中,但其最终状态为等待外部操作(如“请求购买”)的交易】A transaction that is in the queue, but its final status is pending external action such as Ask to Buy. };
我只需要处理failed和purchased两种状态的交易即可,具体根据业务需求决定。例:
for (let i = 0; i < customOrderList.length; i++) { if (customOrderList[i].transactionState == 2) { //关闭失败订单 for (let b = 0; b < 5; b++) try { await new Promise((recv, recj) => { channel.finishTransaction(transaction, recv, recj); }); break; } catch (e) { console.log(e); } } else await this.verifyTransactionReceipt(customOrderList[i]); //上报已经成功但服务器没有处理的订单(把transactionReceipt属性值发给服务端就行了),接收到服务器成功响应后再关闭订单。 }
官方文档在这里:苹果应用内支付
官方文档有一部分说的比较模糊不太详细,报错也是如此。因此这里说一下方便大家排雷避坑
-
申请流程
- 要有苹果开发者账号这个就不说了
- 要在appstoreconnect的应用的功能下面管理你的 App 内购项目,亲测添加后把信息填写完整并保存,只要状态为准备提交即可!
- 把你添加的商品的产品 ID记下来一会要用到(下文称pid)
- 你需要到苹果开发者中心证书管理生成一个iOS App Development类型的证书并生成p12文件和Profile页面生成mobileprovision文件,具体生成教程很多就不一一列举了。生成后打包个自定义基座就可以写代码了。
注意!上述过程一个都不能漏
-
代码编写流程
代码编写文档放在文章开头了,这里说一下调起顺序:- 使用uni.getProvider获取id为appleiap的channel,下文称该channel为支付通道。例:
async getIAPChannel() { return await new Promise((recv, recj) => { uni.getProvider({ service: 'payment', success: res => { const iapChannel = res.providers.find(channel => { return channel.id === 'appleiap'; }); if (iapChannel) recv(iapChannel); recj(new Error('当前环境不支持使用IAP支付!')); } }); }); }
- 使用通道的requestProduct方法获取pid对应的商品信息。例:
let orderDetail = await new Promise((recv, recj) => { channel.requestProduct([pid], recv, recj); });
注意!!!这一步不能省略,否则会无法调起支付!!!
- 使用uni.requestPayment调起支付。例:
let transaction = await new Promise((recv, recj) => { uni.requestPayment({ provider: 'appleiap', orderInfo: { productid: pid, username: 'ORDERID' + orderid, quantity: 1, manualFinishTransaction: true }, success: t => { recv(t); }, fail: e => { recj(new Error('支付调起失败:Errcode ' + e.errCode + ',' + e.errMsg)); } }); });
- 使用uni.getProvider获取id为appleiap的channel,下文称该channel为支付通道。例:
-
掉单处理
官方文档中说明部分与实际demo代码不一致。这里说一下流程:- 使用uni.getProvider获取id为appleiap的channel,下文称该channel为支付通道。例同代码编写流程
- 使用支付通道的restoreCompletedTransactions方法获取可能掉单的订单列表(支付时一定要设置manualFinishTransaction为true否则这里就找不到了)。例:
let customOrderList = await new Promise((recv, recj) => { channel.restoreCompletedTransactions({}, recv, e => { //第一个参数必传,否则会导致app闪退! recj(new Error('获取未完成订单列表失败:Errcode ' + e.errCode + ',' + e.errMsg)); }); });
- 处理掉单,掉单分几种情况,官方文档有介绍:
const IapTransactionState = { purchasing: "0", // 【应用商店正在处理的交易】A transaction that is being processed by the App Store. purchased: "1", // 【一个成功处理的交易】A successfully processed transaction. failed: "2", // 【一个失败的交易】A failed transaction. restored: "3", // 【已经购买过该商品】A transaction that restores content previously purchased by the user. deferred: "4" // 【在队列中,但其最终状态为等待外部操作(如“请求购买”)的交易】A transaction that is in the queue, but its final status is pending external action such as Ask to Buy. };
我只需要处理failed和purchased两种状态的交易即可,具体根据业务需求决定。例:
for (let i = 0; i < customOrderList.length; i++) { if (customOrderList[i].transactionState == 2) { //关闭失败订单 for (let b = 0; b < 5; b++) try { await new Promise((recv, recj) => { channel.finishTransaction(transaction, recv, recj); }); break; } catch (e) { console.log(e); } } else await this.verifyTransactionReceipt(customOrderList[i]); //上报已经成功但服务器没有处理的订单(把transactionReceipt属性值发给服务端就行了),接收到服务器成功响应后再关闭订单。 }

希望能把打包功能和编辑器功能分开 或者专门出一个打包的工具
希望能把打包功能和编辑器功能分开 或者专门出一个打包的工具 不要每次更新 hbuilderx 就会要更新整包
希望能把打包功能和编辑器功能分开 或者专门出一个打包的工具 不要每次更新 hbuilderx 就会要更新整包

关于iOS真机无法运行标准基座的公告
HBuilderX中自带的标准真机运行基座,使用DCloud向苹果申请的企业开发者证书签名,根据苹果开发者企业计划许可协议要求,使用企业开发者证书签名的App只允许企业员工内部使用,不允许企业外部人员安装使用。
因收到苹果公司警告,目前开发者已无法在iOS真机设备使用标准运行基座。(Mac电脑中的iOS模拟器中还可以继续使用标准基座,它不限制企业证书使用)
在HBuilderX 3.6.1及更低版本:
-
iOS真机运行时会提示以下错误:
-
已经安装基座的iOS真机设备运行时会提示以下错误:
HBuilder3.6.2起,错误提示已经改善。
解决方案
方案一
如果要在真机设备使用,开发者需要自己向苹果申请Development证书,重签标准基座(需HBuilderX 3.7+),或使用自己的证书打包自定义基座,参考:https://uniapp.dcloud.net.cn/tutorial/run/run-app.html
如何申请开发(Development)证书和描述文件,请参考:https://ask.dcloud.net.cn/article/152
方案二
在Mac电脑中安装XCode,使用iOS模拟器真机运行,参考:iOS设备选择-使用iOS模拟器
此限制不影响其他范围,不影响使用开发者自己的证书打包发布应用。
HBuilderX中自带的标准真机运行基座,使用DCloud向苹果申请的企业开发者证书签名,根据苹果开发者企业计划许可协议要求,使用企业开发者证书签名的App只允许企业员工内部使用,不允许企业外部人员安装使用。
因收到苹果公司警告,目前开发者已无法在iOS真机设备使用标准运行基座。(Mac电脑中的iOS模拟器中还可以继续使用标准基座,它不限制企业证书使用)
在HBuilderX 3.6.1及更低版本:
-
iOS真机运行时会提示以下错误:
-
已经安装基座的iOS真机设备运行时会提示以下错误:
HBuilder3.6.2起,错误提示已经改善。
解决方案
方案一
如果要在真机设备使用,开发者需要自己向苹果申请Development证书,重签标准基座(需HBuilderX 3.7+),或使用自己的证书打包自定义基座,参考:https://uniapp.dcloud.net.cn/tutorial/run/run-app.html
如何申请开发(Development)证书和描述文件,请参考:https://ask.dcloud.net.cn/article/152
方案二
在Mac电脑中安装XCode,使用iOS模拟器真机运行,参考:iOS设备选择-使用iOS模拟器
此限制不影响其他范围,不影响使用开发者自己的证书打包发布应用。
收起阅读 »
uniapp 与webview传参 uni.webview.1.5.4.js
版本
uni.webview.1.5.4.js 版本 1.5.4
https://uniapp.dcloud.net.cn/component/web-view.html#
https://gitee.com/dcloud/uni-app/raw/dev/dist/uni.webview.1.5.4.js
传参:
vue中。监听。
可以写在app.vue中。需要用的地方监听。
// #ifdef APP-PLUS
// app 用于监听 验证码webview 事件
plus.globalEvent.addEventListener('plusMessage', (msg) => {
const result = msg.data.args.data;
if(result.name == 'postMessage'){
console.log('postMessage', msg)
uni.$emit('webviewCode', msg);
}
})
webview中。
// 待触发 `UniAppJSBridgeReady` 事件后,即可调用 uni 的 API。 document.addEventListener('UniAppJSBridgeReady', function() { uni.webView.getEnv(function(res) { console.log('当前环境:' + JSON.stringify(res)); if (res.h5) { //$('body').css('background-color', 'transparent'); // h5 h5Ready(); } else if (res.plus) { // 通知到APP // 此处理是关键, 传参内容入在 对象的data属性中。 uni.postMessage({data: {status: 1}}); //if(window.plus){ // plusReady(); //}else{ // document.addEventListener('plusready', plusReady, false); // } } }); });
sdk原码
版本
uni.webview.1.5.4.js 版本 1.5.4
https://uniapp.dcloud.net.cn/component/web-view.html#
https://gitee.com/dcloud/uni-app/raw/dev/dist/uni.webview.1.5.4.js
传参:
vue中。监听。
可以写在app.vue中。需要用的地方监听。
// #ifdef APP-PLUS
// app 用于监听 验证码webview 事件
plus.globalEvent.addEventListener('plusMessage', (msg) => {
const result = msg.data.args.data;
if(result.name == 'postMessage'){
console.log('postMessage', msg)
uni.$emit('webviewCode', msg);
}
})
webview中。
// 待触发 `UniAppJSBridgeReady` 事件后,即可调用 uni 的 API。 document.addEventListener('UniAppJSBridgeReady', function() { uni.webView.getEnv(function(res) { console.log('当前环境:' + JSON.stringify(res)); if (res.h5) { //$('body').css('background-color', 'transparent'); // h5 h5Ready(); } else if (res.plus) { // 通知到APP // 此处理是关键, 传参内容入在 对象的data属性中。 uni.postMessage({data: {status: 1}}); //if(window.plus){ // plusReady(); //}else{ // document.addEventListener('plusready', plusReady, false); // } } }); });
sdk原码
收起阅读 »
app-vue + html2canvas + renderjs 页面保存图片模糊问题分享
不废话,上代码
<view id="inviter_hb" class="bg-img">
<img class="bg" :src="bgImg" mode="scaleToFill"></img>
<view class="qr-box">
<img class="qr-img" :src="qrImg" mode="scaleToFill"></img>
</view>
<text class="inviter-code">邀请码:<text>{{inviteCode}}</text></text>
</view>
以下是renderjs 代码,可以在此操作dom
<script module="html2canvas" lang="renderjs">
// import html2canvas from '@/common/html2canvas.min.js'
export default {
mounted() {
if (!window.html2canvas) {
// 动态引入较大类库避免影响页面展示
const script = document.createElement('script')
// view 层的页面运行在 www 根目录,其相对路径相对于 www 计算
script.src = 'static/js/html2canvas.min.js'
document.head.appendChild(script)
}
},
methods: {
createInviter(event, ownerInstance) {
ownerInstance.callMethod('showLoading')
const inviterDom = document.getElementById("inviter_hb")
html2canvas(inviterDom, {
width: inviterDom.scrollWidth,
height: inviterDom.scrollHeight,
allowTaint: true,
useCORS: true,
scale: 3,
dpi: 300
}).then(canvas => {
ownerInstance.callMethod('renderFinish', canvas.toDataURL('image/jpeg', 1.0))
}).catch(error => {
console.log(error)
ownerInstance.callMethod('hideLoading')
})
}
}
}
</script>
重要:页面中图片不能用background-image设置,也不能用image标签设置,需要用img,否则生成的图片还是模糊
不废话,上代码
<view id="inviter_hb" class="bg-img">
<img class="bg" :src="bgImg" mode="scaleToFill"></img>
<view class="qr-box">
<img class="qr-img" :src="qrImg" mode="scaleToFill"></img>
</view>
<text class="inviter-code">邀请码:<text>{{inviteCode}}</text></text>
</view>
以下是renderjs 代码,可以在此操作dom
<script module="html2canvas" lang="renderjs">
// import html2canvas from '@/common/html2canvas.min.js'
export default {
mounted() {
if (!window.html2canvas) {
// 动态引入较大类库避免影响页面展示
const script = document.createElement('script')
// view 层的页面运行在 www 根目录,其相对路径相对于 www 计算
script.src = 'static/js/html2canvas.min.js'
document.head.appendChild(script)
}
},
methods: {
createInviter(event, ownerInstance) {
ownerInstance.callMethod('showLoading')
const inviterDom = document.getElementById("inviter_hb")
html2canvas(inviterDom, {
width: inviterDom.scrollWidth,
height: inviterDom.scrollHeight,
allowTaint: true,
useCORS: true,
scale: 3,
dpi: 300
}).then(canvas => {
ownerInstance.callMethod('renderFinish', canvas.toDataURL('image/jpeg', 1.0))
}).catch(error => {
console.log(error)
ownerInstance.callMethod('hideLoading')
})
}
}
}
</script>
重要:页面中图片不能用background-image设置,也不能用image标签设置,需要用img,否则生成的图片还是模糊
收起阅读 »
做了一个uni-app版的vconsole,可以查看network。并且支持在每个页面自动插入自定义组件
微信小程序在开发环境是自带vconsole的,但是不支持network面板,要看网络请求还需要抓包,所以我自己做了一个调试工具,支持network面板。
另外,小程序是没有开放根节点的,因此在开发的过程中解决了在各个页面自动插入组件的问题,发现有些人也是有这个场景的,因此把这个功能也暴露了出来。
直接使用可以直接看npm包的说明:uni-devtool
原理说明可以看这里:定制一个小程序版的vconsole
微信小程序在开发环境是自带vconsole的,但是不支持network面板,要看网络请求还需要抓包,所以我自己做了一个调试工具,支持network面板。
另外,小程序是没有开放根节点的,因此在开发的过程中解决了在各个页面自动插入组件的问题,发现有些人也是有这个场景的,因此把这个功能也暴露了出来。
直接使用可以直接看npm包的说明:uni-devtool
原理说明可以看这里:定制一个小程序版的vconsole