HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

Google Play上传应用反馈Unoptimized APK问题的说明

GooglePlay aab

目前使用HBuilder|HBuilderX云端打包提交Google应用市场(Google Play)会反馈以下邮件提示:

Unoptimized APK  
Warning:  

This APK results in unused code and resources being sent to users. Your app could be smaller if you used the Android App Bundle. By not optimizing your app for device configurations, your app is larger to download and install on users' devices than it needs to be. Larger apps see lower install success rates and take up storage on users' devices.  

Resolution:  

Use the Android App Bundle to automatically optimize for device configurations, or manage it yourself with multiple APKs.

以上信息是告诉开发者上传新的Android App Bundle文件格式(aab),以前是apk格式。Android App Bundle是Android 引入新的 App 动态化框架,借助Split Apk完成动态加载,使用aab动态下发方式,可以大幅度减少应用体积。
详情参考Google 官方文档:https://developer.android.com/guide/app-bundle/

目前只是警告信息,仍然可以继续提交apk格式包,暂时还没有准确时间要求必须上传aab格式包

HBuilder|HBuilderX云端打包目前仅支持生成apk包,如果想要生成aab格式包,暂时可以使用App离线SDK本地离线打包。

  • 更新Android Studio到最新版本
  • 按照Android离线打包教程配置工程,调试确保功能正常
  • 选择菜单 Build -> Generate Signed Bundle / APK ...
  • 勾选 “Android App Bundle”,点击“Next”
  • 配置证书信息,点击“Next”
  • 配置保存aab文件的目标路径,选择release,点击“Finish”

完成后会在目标路径生成aab文件。
aab文件只能用于提交Google Play,不能作为HBuilderX的基座,也不能直接安装到手机

我们会持续跟踪Google Play上传应用的政策要求,及时更新,确保符合Google应用市场的要求

继续阅读 »

目前使用HBuilder|HBuilderX云端打包提交Google应用市场(Google Play)会反馈以下邮件提示:

Unoptimized APK  
Warning:  

This APK results in unused code and resources being sent to users. Your app could be smaller if you used the Android App Bundle. By not optimizing your app for device configurations, your app is larger to download and install on users' devices than it needs to be. Larger apps see lower install success rates and take up storage on users' devices.  

Resolution:  

Use the Android App Bundle to automatically optimize for device configurations, or manage it yourself with multiple APKs.

以上信息是告诉开发者上传新的Android App Bundle文件格式(aab),以前是apk格式。Android App Bundle是Android 引入新的 App 动态化框架,借助Split Apk完成动态加载,使用aab动态下发方式,可以大幅度减少应用体积。
详情参考Google 官方文档:https://developer.android.com/guide/app-bundle/

目前只是警告信息,仍然可以继续提交apk格式包,暂时还没有准确时间要求必须上传aab格式包

HBuilder|HBuilderX云端打包目前仅支持生成apk包,如果想要生成aab格式包,暂时可以使用App离线SDK本地离线打包。

  • 更新Android Studio到最新版本
  • 按照Android离线打包教程配置工程,调试确保功能正常
  • 选择菜单 Build -> Generate Signed Bundle / APK ...
  • 勾选 “Android App Bundle”,点击“Next”
  • 配置证书信息,点击“Next”
  • 配置保存aab文件的目标路径,选择release,点击“Finish”

完成后会在目标路径生成aab文件。
aab文件只能用于提交Google Play,不能作为HBuilderX的基座,也不能直接安装到手机

我们会持续跟踪Google Play上传应用的政策要求,及时更新,确保符合Google应用市场的要求

收起阅读 »

ios上架需注意的20条,避免被拒!

iOS iOS打包

iOS APP上架审核比较严格!

这里整理了iOS上架需要注意的事项,上架前尽量避免,争取一次提交审核成功,被拒打回重新修改提交审核会耗费很多时间!

ios app上架App Store详细教程分享

1、应用内包含检查更新功能iOS 应用的版本更新必须通过 App Store 进行,自身 App 内不能包含提示更新功能。从2015年3月起,所有包含检查更新功能的 App 都会被拒绝上架。

2、使用第三方登录时未做安装检测接入第三方登录要检测是否安装了第三方客户端,未安装时不要显示对应按钮。2015年9月之前,通常可以采用判断未安装则隐藏登录按钮的方式。但目前隐藏按钮的方式也可能被审核拒绝,QQ 和微博提供了 web 登录的方式,如果判断未安装,需要允许用户使用 webview 的登录方式。苹果在条款中有声明不允许 iOS 应用的正常使用需要依赖另外一个 App。

3、采集设备IDFA但应用没有广告功能从2014年2月起,Apple 开始拒绝采集 IDFA (identifier for advertising) 却未集成任何广告服务的应用进入 App Store。如果 App 本身没有广告,ASO100.com 建议可以在审核的时候显示一个 Banner 广告,并且放在比较明显的位置,审核通过后关掉即可。

4、含UGC却未提供用户协议及举报功能如果你的 App 内有发帖等UGC(用户产生内容)功能,必须提供用户协议,并留有内容举报功能,否则就会被审核拒绝。

5、上传时没有使用真实的应用截图应用程序的名称、描述、截图或者预览与应用的内容和功能不相关将会被拒绝。有 App 因为应用截图使用的是自己设计的插画而被审核拒绝。

6、应用必须使用邀请码才能注册使用苹果要求应用不能限制只有部分用户可以使用。

7、应用内出现第三方移动平台的名字或图标一直以来,苹果都不允许iOS开发者在进行软件描述时提到 Android 版本,而自从2015年4月起,在 App 内、截图等任何地方提到安卓、Android 的文字、图标、系统界面都会被拒。曾经有电商 App,因为出现了售卖三星安卓手机而被拒。。。

8、应用内涉及奖励,未声明与苹果无关App 里有实物奖励的话,不能使用苹果产品(例如 iPhone 、iPad 等)作为奖品。另外一定要声明“奖励由本公司提供,与苹果官方无关”。

9、没有提供恢复内购的方法增加一个“恢复购买记录”的按钮即可。

10、未注册时不能使用与账号无关的功能对于资讯等 App,在没有进行与用户信息相关的操作时,却强行让用户登录,甚至不登录就无法看到任何内容,有可能会被拒绝。

11、iPhone 应用在 iPad 上不能正常显示iPhone程序必须不经修改就能以iPhone分辨率和2倍iPhone 3GS的分辨率在iPad上运行。即使你的App 只为 iPhone 用户提供,在 iPad 上也必须能够正常显示,否则审核会被拒绝。

12、侵犯第三方版权对于视频、音乐、图书类的应用很容易因为这一条而被拒。另外 ASO100.com 建议应用内最好不要出现第三方的商标,例如运营商的Logo、影视公司的 Logo 等。13、应用截图、名称、描述等出现不雅词汇在应用截图、名称、描述等任何地方出现例如诸如 牛逼、绿茶婊、无节操、逗比 等词汇,都会被苹果审核拒绝。

14、应用出现 beta版、测试版字样不要过度谦虚地在启动画面或者应用名称上加上”beta”字样,苹果不允许测试版产品上架。

15、注册缺少隐私政策如果应用包含注册功能,注册页面必须提供隐私说明协议按钮或者链接。另外在 iTunes connect 提交新版本的时候,Privacy Policy URL 必须要填写。

16、应用出现崩溃、加载失败等 bug审核期间出现崩溃会导致审核被拒。ASO100.com 建议,在审核期间务必保证服务器稳定,避免审核人员审核时出现内容加载失败的情况,导致被拒。

17、应用描述、截图和应用功能不符如果应用的描述或截图介绍的功能在审核期间没有体现,则会被拒绝,如果介绍文案不够详细也会有一定概率被拒。

18、应用包含应用推荐功能除特殊情况,苹果明令禁止应用内推荐其他APP。

19、应用包含不正确的诊断功能如果你的应用中,包含不真实的系统检测或优化功能,苹果会认为这项功能有误导用户的嫌疑,审核时会被拒绝。

20、应用提交的新版本与上一版差异过大如果你提交的新版本应用与上一版相比,功能上变化过大,比如将游戏升级为工具类应用,或在新版本中完全改掉前一版产品的功能,则会被苹果拒绝。

继续阅读 »

iOS APP上架审核比较严格!

这里整理了iOS上架需要注意的事项,上架前尽量避免,争取一次提交审核成功,被拒打回重新修改提交审核会耗费很多时间!

ios app上架App Store详细教程分享

1、应用内包含检查更新功能iOS 应用的版本更新必须通过 App Store 进行,自身 App 内不能包含提示更新功能。从2015年3月起,所有包含检查更新功能的 App 都会被拒绝上架。

2、使用第三方登录时未做安装检测接入第三方登录要检测是否安装了第三方客户端,未安装时不要显示对应按钮。2015年9月之前,通常可以采用判断未安装则隐藏登录按钮的方式。但目前隐藏按钮的方式也可能被审核拒绝,QQ 和微博提供了 web 登录的方式,如果判断未安装,需要允许用户使用 webview 的登录方式。苹果在条款中有声明不允许 iOS 应用的正常使用需要依赖另外一个 App。

3、采集设备IDFA但应用没有广告功能从2014年2月起,Apple 开始拒绝采集 IDFA (identifier for advertising) 却未集成任何广告服务的应用进入 App Store。如果 App 本身没有广告,ASO100.com 建议可以在审核的时候显示一个 Banner 广告,并且放在比较明显的位置,审核通过后关掉即可。

4、含UGC却未提供用户协议及举报功能如果你的 App 内有发帖等UGC(用户产生内容)功能,必须提供用户协议,并留有内容举报功能,否则就会被审核拒绝。

5、上传时没有使用真实的应用截图应用程序的名称、描述、截图或者预览与应用的内容和功能不相关将会被拒绝。有 App 因为应用截图使用的是自己设计的插画而被审核拒绝。

6、应用必须使用邀请码才能注册使用苹果要求应用不能限制只有部分用户可以使用。

7、应用内出现第三方移动平台的名字或图标一直以来,苹果都不允许iOS开发者在进行软件描述时提到 Android 版本,而自从2015年4月起,在 App 内、截图等任何地方提到安卓、Android 的文字、图标、系统界面都会被拒。曾经有电商 App,因为出现了售卖三星安卓手机而被拒。。。

8、应用内涉及奖励,未声明与苹果无关App 里有实物奖励的话,不能使用苹果产品(例如 iPhone 、iPad 等)作为奖品。另外一定要声明“奖励由本公司提供,与苹果官方无关”。

9、没有提供恢复内购的方法增加一个“恢复购买记录”的按钮即可。

10、未注册时不能使用与账号无关的功能对于资讯等 App,在没有进行与用户信息相关的操作时,却强行让用户登录,甚至不登录就无法看到任何内容,有可能会被拒绝。

11、iPhone 应用在 iPad 上不能正常显示iPhone程序必须不经修改就能以iPhone分辨率和2倍iPhone 3GS的分辨率在iPad上运行。即使你的App 只为 iPhone 用户提供,在 iPad 上也必须能够正常显示,否则审核会被拒绝。

12、侵犯第三方版权对于视频、音乐、图书类的应用很容易因为这一条而被拒。另外 ASO100.com 建议应用内最好不要出现第三方的商标,例如运营商的Logo、影视公司的 Logo 等。13、应用截图、名称、描述等出现不雅词汇在应用截图、名称、描述等任何地方出现例如诸如 牛逼、绿茶婊、无节操、逗比 等词汇,都会被苹果审核拒绝。

14、应用出现 beta版、测试版字样不要过度谦虚地在启动画面或者应用名称上加上”beta”字样,苹果不允许测试版产品上架。

15、注册缺少隐私政策如果应用包含注册功能,注册页面必须提供隐私说明协议按钮或者链接。另外在 iTunes connect 提交新版本的时候,Privacy Policy URL 必须要填写。

16、应用出现崩溃、加载失败等 bug审核期间出现崩溃会导致审核被拒。ASO100.com 建议,在审核期间务必保证服务器稳定,避免审核人员审核时出现内容加载失败的情况,导致被拒。

17、应用描述、截图和应用功能不符如果应用的描述或截图介绍的功能在审核期间没有体现,则会被拒绝,如果介绍文案不够详细也会有一定概率被拒。

18、应用包含应用推荐功能除特殊情况,苹果明令禁止应用内推荐其他APP。

19、应用包含不正确的诊断功能如果你的应用中,包含不真实的系统检测或优化功能,苹果会认为这项功能有误导用户的嫌疑,审核时会被拒绝。

20、应用提交的新版本与上一版差异过大如果你提交的新版本应用与上一版相比,功能上变化过大,比如将游戏升级为工具类应用,或在新版本中完全改掉前一版产品的功能,则会被苹果拒绝。

收起阅读 »

关于wap2app默认的原生导航条无法删除!

翻遍全站文档,各种建议,就是没有能成功的,琢磨十几个小时终于搞定了 可以全站去除原生导航条,需要的私信或者Q我291447654

翻遍全站文档,各种建议,就是没有能成功的,琢磨十几个小时终于搞定了 可以全站去除原生导航条,需要的私信或者Q我291447654

uni-app开发多端之钉钉小程序

钉钉小程序

经常有开发者咨询uni-app是否支持钉钉小程序

答案当然是支持!

本文通过将hello uni-app发布到钉钉小程序,演示如何使用uni-app开发钉钉小程序

扩展钉钉小程序平台

创建hello uni-app项目后,在项目根目录下新增或修改package.json,在根节点下新增如下扩展配置:

"uni-app": {  
    "scripts": {  
        "mp-dingtalk": {   
            "title":"钉钉小程序",   
            "env": {   
                "UNI_PLATFORM": "mp-alipay"   
            },  
            "define": {   
                "MP-DINGTALK": true   
            }  
        }  
    }  
}

运行到模拟器

HBuilderX会根据package.json的扩展配置,在运行菜单下,生成钉钉小程序菜单,如下图:

点击钉钉小程序菜单,开始编译,编译成功后,会自动打开支付宝开发者工具,展开左侧钉钉菜单,选择第三方个人应用,如下图:

然后点击右上角打开项目,选择编译目录(类似:%Project_root%/unpackage/dist/dev/mp-alipay/,注意:钉钉小程序复用支付宝小程序的编译目录),在接下来的界面中,注意修改项目名称,并特别注意修改项目类型为“钉钉 - 第三方个人应用”

项目导入后,支付宝开发者工具会进行编译载入,并在右侧模拟器上显示,如下:

在右侧模拟器上可以点击各页面进行测试。

发行到钉钉开放平台

同样,HBuilderX会根据package.json的扩展配置,在发行 -> 自定义发行菜单下,生成钉钉小程序菜单,如下图:

点击钉钉小程序菜单,开始编译,编译成功后,会自动打开支付宝开发者工具,展开左侧钉钉菜单,选择第三方个人应用,如下图:

然后点击右上角打开项目,选择编译目录(类似:%Project_root%/unpackage/dist/build/mp-alipay/,注意发行时是build目录,不是dev目录),在接下来的界面中,注意修改项目名称,并特别注意修改项目类型为“钉钉 - 第三方个人应用”

项目导入后,支付宝开发者工具会进行编译载入,并在右侧模拟器上显示,如下:

上传需登录后才能操作,使用手机端的钉钉App,扫码登录,

登录成功后,左上角选择关联的小程序(需提前在钉钉开放平台创建小程序),如下图:

点击支付宝开发者工具右上角的“上传”按钮,将代码上传到钉钉开放平台,上传成功后,即可在钉钉开放平台中进行版本发布,可设置为预览版,或直接提交审核:

注意:钉钉小程序的审核发布,需首先完成“钉钉组织认证”和“服务商认证”,提醒开发者提早完成认证,否则无法审核通过。

API调用

钉钉大部分api都是uni通用的API,hello uni-app是可以直接在钉钉里跑的。
如果你需要调用钉钉的专有API,和其他端开发也一样,在钉钉端可直接调用dd的API,当然要跨端的话,需要写在条件编译里。

继续阅读 »

经常有开发者咨询uni-app是否支持钉钉小程序

答案当然是支持!

本文通过将hello uni-app发布到钉钉小程序,演示如何使用uni-app开发钉钉小程序

扩展钉钉小程序平台

创建hello uni-app项目后,在项目根目录下新增或修改package.json,在根节点下新增如下扩展配置:

"uni-app": {  
    "scripts": {  
        "mp-dingtalk": {   
            "title":"钉钉小程序",   
            "env": {   
                "UNI_PLATFORM": "mp-alipay"   
            },  
            "define": {   
                "MP-DINGTALK": true   
            }  
        }  
    }  
}

运行到模拟器

HBuilderX会根据package.json的扩展配置,在运行菜单下,生成钉钉小程序菜单,如下图:

点击钉钉小程序菜单,开始编译,编译成功后,会自动打开支付宝开发者工具,展开左侧钉钉菜单,选择第三方个人应用,如下图:

然后点击右上角打开项目,选择编译目录(类似:%Project_root%/unpackage/dist/dev/mp-alipay/,注意:钉钉小程序复用支付宝小程序的编译目录),在接下来的界面中,注意修改项目名称,并特别注意修改项目类型为“钉钉 - 第三方个人应用”

项目导入后,支付宝开发者工具会进行编译载入,并在右侧模拟器上显示,如下:

在右侧模拟器上可以点击各页面进行测试。

发行到钉钉开放平台

同样,HBuilderX会根据package.json的扩展配置,在发行 -> 自定义发行菜单下,生成钉钉小程序菜单,如下图:

点击钉钉小程序菜单,开始编译,编译成功后,会自动打开支付宝开发者工具,展开左侧钉钉菜单,选择第三方个人应用,如下图:

然后点击右上角打开项目,选择编译目录(类似:%Project_root%/unpackage/dist/build/mp-alipay/,注意发行时是build目录,不是dev目录),在接下来的界面中,注意修改项目名称,并特别注意修改项目类型为“钉钉 - 第三方个人应用”

项目导入后,支付宝开发者工具会进行编译载入,并在右侧模拟器上显示,如下:

上传需登录后才能操作,使用手机端的钉钉App,扫码登录,

登录成功后,左上角选择关联的小程序(需提前在钉钉开放平台创建小程序),如下图:

点击支付宝开发者工具右上角的“上传”按钮,将代码上传到钉钉开放平台,上传成功后,即可在钉钉开放平台中进行版本发布,可设置为预览版,或直接提交审核:

注意:钉钉小程序的审核发布,需首先完成“钉钉组织认证”和“服务商认证”,提醒开发者提早完成认证,否则无法审核通过。

API调用

钉钉大部分api都是uni通用的API,hello uni-app是可以直接在钉钉里跑的。
如果你需要调用钉钉的专有API,和其他端开发也一样,在钉钉端可直接调用dd的API,当然要跨端的话,需要写在条件编译里。

收起阅读 »

繁星计划之阿里小程序征文活动火热进行中~大疆无人机、Switch游戏机、FILCO机械键盘等你来拿!

uni_app 支付宝小程序

_

为了鼓励开发者们能够分享支付宝小程序技术心得、开发实战经验,现支付宝、阿里标准团队、阿里云、蚂蚁智能科技CodeLab联合启动“繁星计划阿里小程序征文活动”。支付宝开发者社区是支付宝开发者之间进行技术交流、讨论与学习的社区,服务于广大的支付宝小程序的开发者。阿里云为小程序开发者提供资源与生态商机。

参与人群:
企业&个人开发者、大学生

内容:
开发者学习、开发、设计支付宝小程序&使用阿里小程序云过程中的实战经验总结或小故事;

可参考内容:
(1)支付宝小程序开发

  • 开发、上架经验总结分享;
  • 疑难问题解决;
  • 自定义组件、插件等;
  • 性能优化实践;
  • 调试、问题定位;
  • 测试体系、自动化测试;
  • 前后端开发联调;
  • 小程序运行原理探究;
  • 可视化小程序开发;

(2)阿里云小程序云服务开发经验分享
(3)支付宝小程序能力的接入经验分享与应用场景探索;

时间节点:8月15日——10月20日;

形式:形式不限,可以是自己录制的一段视频讲解、一段改编的歌词/顺口溜、一个开源DEMO及开发介绍、文章、还可以是信息图、漫画等多种形式。内容专业、详实、有趣易懂。

征文要求

  • 内容不违反法律法规,规章及规范性文件,符合支付宝&云栖社区内容规定与其他适用的协议、规则或规范要求;
  • 内容必须和支付宝小程序&阿里小程序云相关;
  • 内容必须为原创,严禁抄袭或洗稿;
  • 内容有创意、清晰易懂、信息详实准确、有深度;
  • 投稿篇数不限,但雷同内容不能重复投稿;
  • 内容可以授权支付宝通过官方渠道进行发布与宣传。

参加流程:在大赛期间将文章内容发布在聚能聊“繁星计划之阿里小程序征文活动”话题下即可。

评选规则

初审:内容发表后,支付宝小程序官方会根据文章质量评选出入围的作品,入围作品将被云栖社区和支付宝开发者社区发表推荐。

终审:为了保证活动的公平公正,主办方会邀请5位支付宝专家担任活动的终审评委,给所有入围的作品评分,评选出最后的获奖作品。

评审标准: 内容创意(30%)、技术干货或运营思路(30%)、内容形式(如视频品质、文章排版、图片设计质量等,占比20%)、人气[浏览量、收藏与评论、点赞数(包括但不限于云栖社区和支付宝开发者社区) (视频可发布在抖音平台等,相应的浏览量可作为评选)(20%)

奖励:
报名参加活动的,均可以得到支付宝小程序技术专家1对1的辅导。

专业开发者组:
一等奖 1名 DJI大疆御mavic air无人机一台

image

二等奖 5名 Switch游戏机一台

image

三等奖 20名 FILCO机械键盘斐尔可104键圣手二代

image

(1)获奖内容将会在支付宝开发者中心(生活号+小程序)中发布宣传,在云栖社区,支付包开发者社区进行宣传;

(2)部分有创意内容有机会在支付宝、阿里云的官方阵地进行宣传;

大学生组:
一等奖 1名 DJI大疆御mavic air无人机一台
二等奖 5名 Switch游戏机一台
三等奖 20名 FILCO机械键盘斐尔可104键圣手二代

20名鼓励奖,颁发纪念品与获奖证书,同时我们也将走入北京大学、清华大学做活动宣传与小程序培训。

支付宝开发者中心:
https://developers.alipay.com/developmentAccess/developmentAccess.htm

支付宝开放能力 https://docs.alipay.com/mini/introduce/pay

阿里小程序云 https://www.aliyun.com/product/miniappdev

支付宝小程序开发文档:https://docs.alipay.com/mini/introduce

支付宝开发者官方万人钉钉群:23390832

活动举办方:支付宝、阿里标准团队、阿里云、蚂蚁智能科技


优秀文章推荐:

作者:斯文的鸡蛋
《支付宝小程序项目实战》分享5个可以真实运行的支付宝小程序应用
作者:吴胜斌
原生JS开发的轻量、无依赖的日历组件

继续阅读 »

_

为了鼓励开发者们能够分享支付宝小程序技术心得、开发实战经验,现支付宝、阿里标准团队、阿里云、蚂蚁智能科技CodeLab联合启动“繁星计划阿里小程序征文活动”。支付宝开发者社区是支付宝开发者之间进行技术交流、讨论与学习的社区,服务于广大的支付宝小程序的开发者。阿里云为小程序开发者提供资源与生态商机。

参与人群:
企业&个人开发者、大学生

内容:
开发者学习、开发、设计支付宝小程序&使用阿里小程序云过程中的实战经验总结或小故事;

可参考内容:
(1)支付宝小程序开发

  • 开发、上架经验总结分享;
  • 疑难问题解决;
  • 自定义组件、插件等;
  • 性能优化实践;
  • 调试、问题定位;
  • 测试体系、自动化测试;
  • 前后端开发联调;
  • 小程序运行原理探究;
  • 可视化小程序开发;

(2)阿里云小程序云服务开发经验分享
(3)支付宝小程序能力的接入经验分享与应用场景探索;

时间节点:8月15日——10月20日;

形式:形式不限,可以是自己录制的一段视频讲解、一段改编的歌词/顺口溜、一个开源DEMO及开发介绍、文章、还可以是信息图、漫画等多种形式。内容专业、详实、有趣易懂。

征文要求

  • 内容不违反法律法规,规章及规范性文件,符合支付宝&云栖社区内容规定与其他适用的协议、规则或规范要求;
  • 内容必须和支付宝小程序&阿里小程序云相关;
  • 内容必须为原创,严禁抄袭或洗稿;
  • 内容有创意、清晰易懂、信息详实准确、有深度;
  • 投稿篇数不限,但雷同内容不能重复投稿;
  • 内容可以授权支付宝通过官方渠道进行发布与宣传。

参加流程:在大赛期间将文章内容发布在聚能聊“繁星计划之阿里小程序征文活动”话题下即可。

评选规则

初审:内容发表后,支付宝小程序官方会根据文章质量评选出入围的作品,入围作品将被云栖社区和支付宝开发者社区发表推荐。

终审:为了保证活动的公平公正,主办方会邀请5位支付宝专家担任活动的终审评委,给所有入围的作品评分,评选出最后的获奖作品。

评审标准: 内容创意(30%)、技术干货或运营思路(30%)、内容形式(如视频品质、文章排版、图片设计质量等,占比20%)、人气[浏览量、收藏与评论、点赞数(包括但不限于云栖社区和支付宝开发者社区) (视频可发布在抖音平台等,相应的浏览量可作为评选)(20%)

奖励:
报名参加活动的,均可以得到支付宝小程序技术专家1对1的辅导。

专业开发者组:
一等奖 1名 DJI大疆御mavic air无人机一台

image

二等奖 5名 Switch游戏机一台

image

三等奖 20名 FILCO机械键盘斐尔可104键圣手二代

image

(1)获奖内容将会在支付宝开发者中心(生活号+小程序)中发布宣传,在云栖社区,支付包开发者社区进行宣传;

(2)部分有创意内容有机会在支付宝、阿里云的官方阵地进行宣传;

大学生组:
一等奖 1名 DJI大疆御mavic air无人机一台
二等奖 5名 Switch游戏机一台
三等奖 20名 FILCO机械键盘斐尔可104键圣手二代

20名鼓励奖,颁发纪念品与获奖证书,同时我们也将走入北京大学、清华大学做活动宣传与小程序培训。

支付宝开发者中心:
https://developers.alipay.com/developmentAccess/developmentAccess.htm

支付宝开放能力 https://docs.alipay.com/mini/introduce/pay

阿里小程序云 https://www.aliyun.com/product/miniappdev

支付宝小程序开发文档:https://docs.alipay.com/mini/introduce

支付宝开发者官方万人钉钉群:23390832

活动举办方:支付宝、阿里标准团队、阿里云、蚂蚁智能科技


优秀文章推荐:

作者:斯文的鸡蛋
《支付宝小程序项目实战》分享5个可以真实运行的支付宝小程序应用
作者:吴胜斌
原生JS开发的轻量、无依赖的日历组件

收起阅读 »

关于高德地图打包APP之后无法定位的问题

1.首先查看是否高德KEY失效
2.在查看配置是否和官方文档相同

  1. 其次查看是否申请的是的Android的key;注意打包APP一定要使用Android的Key,否则打包之后定位无效

1.首先查看是否高德KEY失效
2.在查看配置是否和官方文档相同

  1. 其次查看是否申请的是的Android的key;注意打包APP一定要使用Android的Key,否则打包之后定位无效

tabbar

移动APP

请问uni-app里面的tabbar这个能够去动态的改变数量吗?现在有这个需求但是自己写的又有点卡顿,所有官方大大们有没有什么解决方案。

请问uni-app里面的tabbar这个能够去动态的改变数量吗?现在有这个需求但是自己写的又有点卡顿,所有官方大大们有没有什么解决方案。

微信小程序语音识别

想着既然自己踩了几天坑解决了问题,那就花点时间整理给有需要的人,这样社区有用的帖子才会越来越多。

一 问题背景
微信小程序端需要解决语音识别的功能。
二 思路
当时的思路有一个,就是使用小程序的录音和上传api,将录音文件上传到后台,后台调用第三方语音接口将识别的内容再返回给小程序,这样实现我觉得应该也可以。但是百度发现了小程序的团队开发的第三方插件,试了一下可以用。
三 实现方法
1登陆小程序管理后台,设置->第三方设置(拖到下方插件管理)->添加插件->搜索“同声传译”


2在manifest.json文件中,小程序节点下添加插件。

"mp-weixin" : {  
        /* 小程序特有相关 */  
        "appid" : "wx59f6366a9db5ced5",  
        "setting" : {  
            "urlCheck" : true  
        },  
        "usingComponents" : true,  
        "plugins" : {  
            "WechatSI" : {  
                "version" : "0.3.1",  
                "provider" : "wx069ba97219f66d99"  
            }  
        }  
    },

3在功能页面中使用就可以了

<template>  
    <view>  
        <!-- #ifndef H5 -->  
        <cu-custom bgColor="bg-gradual-green">  
            <block slot="content">语音识别</block>  
        </cu-custom>  
        <!-- #endif -->  

        <view class="voicepad">  
            {{voiceState}}  
        </view>  
        <button class="cu-btn  bg-green voicebtn " @touchstart="touchStart" @touchend="touchEnd"><text class="cuIcon-voicefill"></text></button>  
    </view>  
</template>  

<script>  
    var plugin = requirePlugin("WechatSI")  
    let manager = plugin.getRecordRecognitionManager();  

    export default {  
        data() {  
            return {  
                voiceState: "你可以这样说..."  
            }  
        },  
        onShow() {  

        },    
        onLoad() {  
            this.initRecord();  
        },  
        methods: {  
            touchStart: function() {  
                // uni.vibrateLong({  
                //  success:function(){  
                //  }  
                // });  
                        manager.start({  
                            duration: 60000,  
                            lang: "zh_CN"  
                        });  
            },  
            touchEnd: function() {  
                uni.showToast()  
                manager.stop();  
            },  
            /**  
             * 初始化语音识别回调  
             * 绑定语音播放开始事件  
             */  
            initRecord: function() {  
                manager.onStart = function(res) {  
                    this.voiceState ="onStart:"+ res.msg+"正在录音"  
                };  
                //有新的识别内容返回,则会调用此事件  
                manager.onRecognize = (res) => {  
                    this.voiceState = "onRecognize"+res.result;  
                }  

                // 识别结束事件  
                manager.onStop = (res) => {  

                    this.voiceState ="onStop"+ res.result;  
                }  

                // 识别错误事件  
                manager.onError = (res) => {  

                    this.voiceState = "onError"+res.msg;  

                }  
            },  
        }  
    }  
</script>  

<style>  
.voicebtn{height:130upx;display: block;width:130upx;line-height:130upx;border-radius: 65upx;font-size: 50upx;position: absolute;top:1060upx;left:310upx;}  
.voicepad{height: 250upx;width:680upx;background: #fff;margin: 30upx auto;border-radius: 8upx;color: #CCCCCC;padding:20upx;font-size: 35upx;}  
</style>  
继续阅读 »

想着既然自己踩了几天坑解决了问题,那就花点时间整理给有需要的人,这样社区有用的帖子才会越来越多。

一 问题背景
微信小程序端需要解决语音识别的功能。
二 思路
当时的思路有一个,就是使用小程序的录音和上传api,将录音文件上传到后台,后台调用第三方语音接口将识别的内容再返回给小程序,这样实现我觉得应该也可以。但是百度发现了小程序的团队开发的第三方插件,试了一下可以用。
三 实现方法
1登陆小程序管理后台,设置->第三方设置(拖到下方插件管理)->添加插件->搜索“同声传译”


2在manifest.json文件中,小程序节点下添加插件。

"mp-weixin" : {  
        /* 小程序特有相关 */  
        "appid" : "wx59f6366a9db5ced5",  
        "setting" : {  
            "urlCheck" : true  
        },  
        "usingComponents" : true,  
        "plugins" : {  
            "WechatSI" : {  
                "version" : "0.3.1",  
                "provider" : "wx069ba97219f66d99"  
            }  
        }  
    },

3在功能页面中使用就可以了

<template>  
    <view>  
        <!-- #ifndef H5 -->  
        <cu-custom bgColor="bg-gradual-green">  
            <block slot="content">语音识别</block>  
        </cu-custom>  
        <!-- #endif -->  

        <view class="voicepad">  
            {{voiceState}}  
        </view>  
        <button class="cu-btn  bg-green voicebtn " @touchstart="touchStart" @touchend="touchEnd"><text class="cuIcon-voicefill"></text></button>  
    </view>  
</template>  

<script>  
    var plugin = requirePlugin("WechatSI")  
    let manager = plugin.getRecordRecognitionManager();  

    export default {  
        data() {  
            return {  
                voiceState: "你可以这样说..."  
            }  
        },  
        onShow() {  

        },    
        onLoad() {  
            this.initRecord();  
        },  
        methods: {  
            touchStart: function() {  
                // uni.vibrateLong({  
                //  success:function(){  
                //  }  
                // });  
                        manager.start({  
                            duration: 60000,  
                            lang: "zh_CN"  
                        });  
            },  
            touchEnd: function() {  
                uni.showToast()  
                manager.stop();  
            },  
            /**  
             * 初始化语音识别回调  
             * 绑定语音播放开始事件  
             */  
            initRecord: function() {  
                manager.onStart = function(res) {  
                    this.voiceState ="onStart:"+ res.msg+"正在录音"  
                };  
                //有新的识别内容返回,则会调用此事件  
                manager.onRecognize = (res) => {  
                    this.voiceState = "onRecognize"+res.result;  
                }  

                // 识别结束事件  
                manager.onStop = (res) => {  

                    this.voiceState ="onStop"+ res.result;  
                }  

                // 识别错误事件  
                manager.onError = (res) => {  

                    this.voiceState = "onError"+res.msg;  

                }  
            },  
        }  
    }  
</script>  

<style>  
.voicebtn{height:130upx;display: block;width:130upx;line-height:130upx;border-radius: 65upx;font-size: 50upx;position: absolute;top:1060upx;left:310upx;}  
.voicepad{height: 250upx;width:680upx;background: #fff;margin: 30upx auto;border-radius: 8upx;color: #CCCCCC;padding:20upx;font-size: 35upx;}  
</style>  
收起阅读 »

Appstore审核反馈废弃UIWebview APIs问题的说明

itme90809 WKWebview uiwebview Appstore iOS

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-ios-uiwebview

iOS有UIWebview和WKWebview两种webview。从iOS13开始苹果将UIWebview列为过期API。

2020年4月起App Store将不再接受使用UIWebView的新App上架、2020年12月起将不再接受使用UIWebView的App更新。

从HBuilderX 2.2.5起,iOS上默认均已经是WKWebview,除非开发者手动在代码中指定要用UIWebview,否则实际渲染的页面都是在WKWebview里渲染的。
不过,虽然实际页面是WKWebview渲染的,但App底层引擎源码里仍然有UIWebview的可选引用。Appstore的机审会发现二进制代码中包括对UIWebview的引用,从而引发告警。
从HBuilderX 2.6.6起,uiWebview从基础引擎中移除,变成可选模块(manifest里选择)。机审也没有提示了。

老HBuilder和HBuilderX 2.2.5之前的版本,App端策略如下:

  • 5+ APP(含wap2app)
    默认为UIWebview。
  • uni-app
    vue页面中web-view组件默认使用UIWebview,nvue页面中web-view组件使用WKWebview。

HBuilderX 2.2.5+版本已将iOS上所有webview的默认内核由UIWebview调整为WKWebview。

<a id="uiwebview"/>
HBuilderX 2.6.6+版本已将iOS中所有UIWebview代码从基础引擎中摘除,独立为UIWebview模块,如继续使用UIWebview则需在manifest中勾选使用UIWebview模块

配置使用UIWebview模块

打开项目的manifest.json文件,在“App模块配置”项中勾选“iOS UIWebview”:

配置后需提交云端打包生效

5+App(含wap2app)如何切换iOS默认使用UIWebview或WKWebview内核?

HBuilderX 2.2.5以前的版本,iOS上webview的默认为UIWebview,HBuilderX2.2.5及以后的版本默认改为WKWebview。
如果要修改默认值,可在manifest.json中配置。
在manifest.json文件源码视图中设置plus -> kernel -> ios 的值为 "WKWebview"或"UIWebview":

    "plus": {  
        "kernel": {  
            "ios": "UIWebview"    //或者 "WKWebview"  
        },  
        // ...  
    }

uni-app 如何配置web-view组件默认使用UIWebview或WKWebview内核?

HBuilderX 2.2.5以前的版本,iOS上vue页面中web-view组件或调用5+ API创建的Webview窗口默认为UIWebview,HBuilderX2.2.5及以后的版本默认改为WKWebview。
如果要修改默认值,可在manifest.json中配置。
在manifest.json文件源码视图中设置 app-plus -> kernel -> ios 的值为 "WKWebview"或"UIWebview":

    "app-plus": {  
        "kernel": {  
            "ios": "UIWebview"    //或者 "WKWebview"  
        },  
        // ...  
    }

nvue页面中的web-view组件强制使用WKWebview,不可配置

如何使用5+ API(plus.webview.create)创建Webview窗口时指定使用UIWebview或WKWebview内核?

创建Webvie窗口时可通过kernel属性指定内核,如下:

// 通过kernel属性指定Webview的内核  
var w = plus.webview.create('https://xxx.xxx.xxx', 'id', {  
    'kernel': 'UIWebview'       //或者'WKWebview'  
});

更多规范参考5+ API的 WebviewStyles

使用WKWebview的影响

使用WKWebview替换UIWebview将会影响以下功能:

  • 更严格的跨域访问限制
    WKWebview认为本地html通过js访问网络及本地文件都算跨域访问(这种情况UIWebview不是跨域),跨域时访问网络资源使用5+ API(plus.net)来替换xmlhttp等传统ajax写法;ajax也不能访问本地文件,需使用5+ API(plus.io)读取本地文件,后者有个单独文章可参考:https://ask.dcloud.net.cn/article/36858
    使用exif.js等三方库可能涉及跨目录的本地图片下载请求,图像方向获取和旋转Plus有专门的API,无需使用js库做。
  • 由于WKWebview不支持跨域访问,标准的xhr或jq的ajax,都无法跨域。mui框架中网络请求判断为跨域访问会自动调用5+ API(plus.net),如果在mui.plusReady触发前调用,因为5+ API没有准备好会报“Script error.filename:lineno:0”错误,这时必须保证mui的网络请求在mui.plusReady后调用,或者直接改用plus.net写法。
  • WKWebview下canvas也有跨域问题,比如canvas.toDataURL。
    如果canvas使用网络图像遇到跨域问题,需要服务端设置图像的响应头:Access-Control-Allow-Origin
    如果canvas使用本地图像遇到跨域问题,可以使用plus接口将图像转换为base64再使用,相关插件:https://ext.dcloud.net.cn/plugin?id=123
  • iOS手机内存不足时,如果是UIWebview的应用,系统会整体回收这个App,现象是在重新打开已打开过的App时App整体重启。而WKWebview则是单个页面回收,这带来的坏处就是内存不足时,会单个页面白屏。详见https://ask.dcloud.net.cn/article/35913。uni-app不涉及此问题,如果是5+App,方式1是在manifest切回UIWebview,暂时UIWebview还可以上架,只是会收到警告。方式2时监听白屏事件,自行恢复页面:https://ask.dcloud.net.cn/article/36540
  • iOS8、9上的WKWebview不支持websql,iOS10恢复支持
  • 不支持plus.navigator.setCookie
  • 不支持webview的overrideresource方法
  • wk第一次渲染速度略慢于uiwebview;
  • 由于资源拦截的API overrideresource 无法再使用,5+ APP(含wap2app)项目中,云打包时的js原生混淆功能会失效。如果要使用js原生混淆必须使用UIWebview。uni-app有单独的原生js加密方案,因为uni-app的js不运行在webview里,而是在独立的jscore里,所以不受影响。

但WKWebview的好处是:节省内存;滚动时懒加载的图片也可以实时渲染,而uiwebview在滚动停止后懒加载的图片才能显示。

如果同时在一个app里使用ui和wk两种webview,注意2种webview之间的cookie、localstorage、session不共享,但plus.storage是共享的。

uni-app中Webview的使用注意

uni-app的js运行在独立的jscore中,而不是Webview中,不存在跨域问题。
uni-app的渲染层,在iOS下是强制wkwebview。如果你编写了renderjs代码,在渲染层执行js,则同样会遇到跨域问题。此时尽量把与跨域相关的操作放到普通的js逻辑层操作。
除了渲染层,还有一个web-view组件的问题要注意:

  • uni-app的vue页面的web-view组件,从HBuilderX 2.2.5+起是WKWebview,之前版本默认是UIWebview
  • uni-app的非自定义组件模式的js逻辑层,在HBuilderX 2.2.5之前是UIWebview。升级到HBuilderX2.3+后可能导致网络跨域问题,fail{"statusCode":0,"errMsg":"request:fail abort"}。不过非自定义组件已于2019年11月1日起停止支持。

如果需要调整uni-app下web-view组件的渲染内核设置,将manifest.json源码视图的app-plus -> kernel -> ios 的值设为 UIWebview。

uni-app的nvue页面问题

nvue页面不使用webview渲染,但其中的web-view组件说明如下。

  • nvue的weex 组件模式
    weex模式下的web-view组件是weex自己实现的,它目前仍然使用UIWebview。官方会追踪weex的升级。
  • nvue的uni-app组件模式
    web-view组件使用WKWebview。不可修改为uiWebview。

三方SDK中UIWebview的使用

目前如下SDK中仍然使用了UIWebview,不管是5+App还是uni-app。

  • DCloud开屏广告
    HBuilderX 2.2.5版之前,点击广告打开的内置网页仍然使用UIWebview加载
    HBuilderX 2.2.5+版本已调整改为WKWebview。
  • 支付宝
    HBuilderX 2.6.10版本之前,支付宝SDK为15.5.7版本,包含了UIWebview
    HBuilderX 2.6.10+版本已更新支付宝SDK为15.7.4,没有使用UIWebview
  • 微信登录、分享、支付
    HBuilderX 2.6.6+版本已更新微信SDK为1.8.6.2版本,没有使用UIWebview。
    注意微信登录支付的SDK升级后,会强制要求通用链接。另见文档:https://ask.dcloud.net.cn/article/36445
  • 微博登录、分享
    HBuilderX 2.6.10版本之前,微博SDK为3.2.5版本,包含了UIWebview
    HBuilderX 2.6.10+版本已更新微博SDK为3.2.7版本,没有使用UIWebview
  • QQ登录、分享
    HBuilderX 2.3.4+版本已更新QQSDK为3.3.6,没有使用UIWebview。
  • 小米登录
    小米官方SDK中使用了UIWebview,如果提交appstore建议不要使用小米登录

5+App开发者建议直接升级为uni-app,一劳永逸,不会有跨域、白屏和无法加密等各种问题。

<a id="lixianuiwebview">离线打包配置UIWebview模块 </a>

如果开发者需要在离线打包工程中使用UIWebview功能,需要在自己的离线工程中配置UIWebview模块

注意事项

如果配置正确后,提交Appstore,仍然被提示含有UIWebview,那么请检查你的app是否使用了其他原生插件。
一般或者是配置错误,或者是三方原生插件造成。

继续阅读 »

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-ios-uiwebview

iOS有UIWebview和WKWebview两种webview。从iOS13开始苹果将UIWebview列为过期API。

2020年4月起App Store将不再接受使用UIWebView的新App上架、2020年12月起将不再接受使用UIWebView的App更新。

从HBuilderX 2.2.5起,iOS上默认均已经是WKWebview,除非开发者手动在代码中指定要用UIWebview,否则实际渲染的页面都是在WKWebview里渲染的。
不过,虽然实际页面是WKWebview渲染的,但App底层引擎源码里仍然有UIWebview的可选引用。Appstore的机审会发现二进制代码中包括对UIWebview的引用,从而引发告警。
从HBuilderX 2.6.6起,uiWebview从基础引擎中移除,变成可选模块(manifest里选择)。机审也没有提示了。

老HBuilder和HBuilderX 2.2.5之前的版本,App端策略如下:

  • 5+ APP(含wap2app)
    默认为UIWebview。
  • uni-app
    vue页面中web-view组件默认使用UIWebview,nvue页面中web-view组件使用WKWebview。

HBuilderX 2.2.5+版本已将iOS上所有webview的默认内核由UIWebview调整为WKWebview。

<a id="uiwebview"/>
HBuilderX 2.6.6+版本已将iOS中所有UIWebview代码从基础引擎中摘除,独立为UIWebview模块,如继续使用UIWebview则需在manifest中勾选使用UIWebview模块

配置使用UIWebview模块

打开项目的manifest.json文件,在“App模块配置”项中勾选“iOS UIWebview”:

配置后需提交云端打包生效

5+App(含wap2app)如何切换iOS默认使用UIWebview或WKWebview内核?

HBuilderX 2.2.5以前的版本,iOS上webview的默认为UIWebview,HBuilderX2.2.5及以后的版本默认改为WKWebview。
如果要修改默认值,可在manifest.json中配置。
在manifest.json文件源码视图中设置plus -> kernel -> ios 的值为 "WKWebview"或"UIWebview":

    "plus": {  
        "kernel": {  
            "ios": "UIWebview"    //或者 "WKWebview"  
        },  
        // ...  
    }

uni-app 如何配置web-view组件默认使用UIWebview或WKWebview内核?

HBuilderX 2.2.5以前的版本,iOS上vue页面中web-view组件或调用5+ API创建的Webview窗口默认为UIWebview,HBuilderX2.2.5及以后的版本默认改为WKWebview。
如果要修改默认值,可在manifest.json中配置。
在manifest.json文件源码视图中设置 app-plus -> kernel -> ios 的值为 "WKWebview"或"UIWebview":

    "app-plus": {  
        "kernel": {  
            "ios": "UIWebview"    //或者 "WKWebview"  
        },  
        // ...  
    }

nvue页面中的web-view组件强制使用WKWebview,不可配置

如何使用5+ API(plus.webview.create)创建Webview窗口时指定使用UIWebview或WKWebview内核?

创建Webvie窗口时可通过kernel属性指定内核,如下:

// 通过kernel属性指定Webview的内核  
var w = plus.webview.create('https://xxx.xxx.xxx', 'id', {  
    'kernel': 'UIWebview'       //或者'WKWebview'  
});

更多规范参考5+ API的 WebviewStyles

使用WKWebview的影响

使用WKWebview替换UIWebview将会影响以下功能:

  • 更严格的跨域访问限制
    WKWebview认为本地html通过js访问网络及本地文件都算跨域访问(这种情况UIWebview不是跨域),跨域时访问网络资源使用5+ API(plus.net)来替换xmlhttp等传统ajax写法;ajax也不能访问本地文件,需使用5+ API(plus.io)读取本地文件,后者有个单独文章可参考:https://ask.dcloud.net.cn/article/36858
    使用exif.js等三方库可能涉及跨目录的本地图片下载请求,图像方向获取和旋转Plus有专门的API,无需使用js库做。
  • 由于WKWebview不支持跨域访问,标准的xhr或jq的ajax,都无法跨域。mui框架中网络请求判断为跨域访问会自动调用5+ API(plus.net),如果在mui.plusReady触发前调用,因为5+ API没有准备好会报“Script error.filename:lineno:0”错误,这时必须保证mui的网络请求在mui.plusReady后调用,或者直接改用plus.net写法。
  • WKWebview下canvas也有跨域问题,比如canvas.toDataURL。
    如果canvas使用网络图像遇到跨域问题,需要服务端设置图像的响应头:Access-Control-Allow-Origin
    如果canvas使用本地图像遇到跨域问题,可以使用plus接口将图像转换为base64再使用,相关插件:https://ext.dcloud.net.cn/plugin?id=123
  • iOS手机内存不足时,如果是UIWebview的应用,系统会整体回收这个App,现象是在重新打开已打开过的App时App整体重启。而WKWebview则是单个页面回收,这带来的坏处就是内存不足时,会单个页面白屏。详见https://ask.dcloud.net.cn/article/35913。uni-app不涉及此问题,如果是5+App,方式1是在manifest切回UIWebview,暂时UIWebview还可以上架,只是会收到警告。方式2时监听白屏事件,自行恢复页面:https://ask.dcloud.net.cn/article/36540
  • iOS8、9上的WKWebview不支持websql,iOS10恢复支持
  • 不支持plus.navigator.setCookie
  • 不支持webview的overrideresource方法
  • wk第一次渲染速度略慢于uiwebview;
  • 由于资源拦截的API overrideresource 无法再使用,5+ APP(含wap2app)项目中,云打包时的js原生混淆功能会失效。如果要使用js原生混淆必须使用UIWebview。uni-app有单独的原生js加密方案,因为uni-app的js不运行在webview里,而是在独立的jscore里,所以不受影响。

但WKWebview的好处是:节省内存;滚动时懒加载的图片也可以实时渲染,而uiwebview在滚动停止后懒加载的图片才能显示。

如果同时在一个app里使用ui和wk两种webview,注意2种webview之间的cookie、localstorage、session不共享,但plus.storage是共享的。

uni-app中Webview的使用注意

uni-app的js运行在独立的jscore中,而不是Webview中,不存在跨域问题。
uni-app的渲染层,在iOS下是强制wkwebview。如果你编写了renderjs代码,在渲染层执行js,则同样会遇到跨域问题。此时尽量把与跨域相关的操作放到普通的js逻辑层操作。
除了渲染层,还有一个web-view组件的问题要注意:

  • uni-app的vue页面的web-view组件,从HBuilderX 2.2.5+起是WKWebview,之前版本默认是UIWebview
  • uni-app的非自定义组件模式的js逻辑层,在HBuilderX 2.2.5之前是UIWebview。升级到HBuilderX2.3+后可能导致网络跨域问题,fail{"statusCode":0,"errMsg":"request:fail abort"}。不过非自定义组件已于2019年11月1日起停止支持。

如果需要调整uni-app下web-view组件的渲染内核设置,将manifest.json源码视图的app-plus -> kernel -> ios 的值设为 UIWebview。

uni-app的nvue页面问题

nvue页面不使用webview渲染,但其中的web-view组件说明如下。

  • nvue的weex 组件模式
    weex模式下的web-view组件是weex自己实现的,它目前仍然使用UIWebview。官方会追踪weex的升级。
  • nvue的uni-app组件模式
    web-view组件使用WKWebview。不可修改为uiWebview。

三方SDK中UIWebview的使用

目前如下SDK中仍然使用了UIWebview,不管是5+App还是uni-app。

  • DCloud开屏广告
    HBuilderX 2.2.5版之前,点击广告打开的内置网页仍然使用UIWebview加载
    HBuilderX 2.2.5+版本已调整改为WKWebview。
  • 支付宝
    HBuilderX 2.6.10版本之前,支付宝SDK为15.5.7版本,包含了UIWebview
    HBuilderX 2.6.10+版本已更新支付宝SDK为15.7.4,没有使用UIWebview
  • 微信登录、分享、支付
    HBuilderX 2.6.6+版本已更新微信SDK为1.8.6.2版本,没有使用UIWebview。
    注意微信登录支付的SDK升级后,会强制要求通用链接。另见文档:https://ask.dcloud.net.cn/article/36445
  • 微博登录、分享
    HBuilderX 2.6.10版本之前,微博SDK为3.2.5版本,包含了UIWebview
    HBuilderX 2.6.10+版本已更新微博SDK为3.2.7版本,没有使用UIWebview
  • QQ登录、分享
    HBuilderX 2.3.4+版本已更新QQSDK为3.3.6,没有使用UIWebview。
  • 小米登录
    小米官方SDK中使用了UIWebview,如果提交appstore建议不要使用小米登录

5+App开发者建议直接升级为uni-app,一劳永逸,不会有跨域、白屏和无法加密等各种问题。

<a id="lixianuiwebview">离线打包配置UIWebview模块 </a>

如果开发者需要在离线打包工程中使用UIWebview功能,需要在自己的离线工程中配置UIWebview模块

注意事项

如果配置正确后,提交Appstore,仍然被提示含有UIWebview,那么请检查你的app是否使用了其他原生插件。
一般或者是配置错误,或者是三方原生插件造成。

收起阅读 »

uni-app 利用native调用Android NFC 读取RFID标签的UID

uuid NFC

manifest.json 权限:
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
"<uses-permission android:name=\"android.permission.NFC\"/>",
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
nfc.vue代码如下,完全可运行:

<template>  
    <view>  
        <view class="uni-padding-wrap">  
            NFC  
            <view class="uni-common-mt" style="background:#FFF; padding:20upx;">  
                <text>  
                    UID:{{UID}}  
                    {{tip}}  
                </text>  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    var NfcAdapter;  
    export default {  
        data() {  
            return {  
                title: 'redNFC',  
                UID: '',  
                msg: '',  
                tip: '',  
            }  
        },  
        onLoad() {  
            console.log("onLoad");  

        },  
        onShow() {  
            console.log("onShow");  
            this.NFCInit();  
        },  
        onHide() {  
            console.log("onHide");  
            this.NFCReadUID();  
        },  
        methods: {  
            NFCInit() {  
                try {  
                    var main = plus.android.runtimeMainActivity();  
                    //console.log(main);  
                    var Intent = plus.android.importClass('android.content.Intent');  
                    // console.log(Intent);  
                    var Activity = plus.android.importClass('android.app.Activity');  
                    //console.log(Activity);  
                    var PendingIntent = plus.android.importClass('android.app.PendingIntent');  
                    // console.log(PendingIntent);                    
                    var IntentFilter = plus.android.importClass('android.content.IntentFilter');  
                    // console.log(IntentFilter);  
                    // var Uri = plus.android.importClass('android.net.Uri');                     
                    // var Bundle = plus.android.importClass('android.os.Bundle');                    
                    // var Handler = plus.android.importClass('android.os.Handler');  
                    //console.log(Handler);  
                    NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');  
                    //console.log(NfcAdapter);  
                    var _nfcAdapter = NfcAdapter.getDefaultAdapter(main)  
                    // console.log(_nfcAdapter);  

                    var ndef = new IntentFilter("android.nfc.action.NDEF_DISCOVERED"); //NfcAdapter.ACTION_NDEF_DISCOVERED  
                    // console.log(ndef);  
                    var tag = new IntentFilter("android.nfc.action.TAG_DISCOVERED"); //NfcAdapter.ACTION_TECH_DISCOVERED  
                    // console.log(tag);  
                    var tech = new IntentFilter("android.nfc.action.TECH_DISCOVERED");  
                    // console.log(tech);  
                    var intentFiltersArray = [ndef, tag, tech];  

                    var techListsArray = [  
                        ["android.nfc.tech.Ndef"],  
                        ["android.nfc.tech.IsoDep"],  
                        ["android.nfc.tech.NfcA"],  
                        ["android.nfc.tech.NfcB"],  
                        ["android.nfc.tech.NfcF"],  
                        ["android.nfc.tech.Nfcf"],  
                        ["android.nfc.tech.NfcV"],  
                        ["android.nfc.tech.NdefFormatable"],  
                        ["android.nfc.tech.MifareClassi"],  
                        ["android.nfc.tech.MifareUltralight"]  
                    ];  

                    var _intent = new Intent(main, main.getClass());  
                    // console.log(_intent);  
                    _intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);  

                    var pendingIntent = PendingIntent.getActivity(main, 0, _intent, 0);  
                    // console.log(pendingIntent);  

                    if (_nfcAdapter == null) {  
                        this.tip = '本设备不支持NFC!';  
                    } else if (_nfcAdapter.isEnabled() == false) {  
                        this.tip = 'NFC功能未打开!';  
                    } else {  
                        this.tip = 'NFC正常';  
                        _nfcAdapter.enableForegroundDispatch(main, pendingIntent, IntentFilter, techListsArray);  
                    }  
                } catch (e) {  
                    //TODO handle the exception  
                }  
            },  
            NFCReadUID() {  
                var main = plus.android.runtimeMainActivity();  
                var _intent = main.getIntent();  
                var _action = _intent.getAction();  
                // console.log("action type:" + _action);  

                if (NfcAdapter.ACTION_NDEF_DISCOVERED == _action || NfcAdapter.ACTION_TAG_DISCOVERED == _action || NfcAdapter.ACTION_TECH_DISCOVERED ==  
                    _action) {  
                    var Tag = plus.android.importClass('android.nfc.Tag');  
                    var tagFromIntent = _intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
                    var uid = _intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);  
                    console.log(uid);  
                    this.UID = this.Bytes2HexString(uid);  
                    //console.log(this.UID);  
                }  
            },  
            //将byte[] 转为Hex,  
            Bytes2HexString(arrBytes) {  
                var str = "";  
                for (var i = 0; i < arrBytes.length; i++) {  
                    var tmp;  
                    var num = arrBytes[i];  
                    if (num < 0) {  
                        //Java中数值是以补码的形式存在的,应用程序展示的十进制是补码对应真值。补码的存在主要为了简化计算机底层的运算,将减法运算直接当加法来做  
                        tmp = (255 + num + 1).toString(16);  
                    } else {  
                        tmp = num.toString(16);  
                    }  
                    if (tmp.length == 1) {  
                        tmp = "0" + tmp;  
                    }  
                    str += tmp;  
                }  
                return str;  
            }  
        }  
    }  
</script>  

<style>  

</style>  
继续阅读 »

manifest.json 权限:
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
"<uses-permission android:name=\"android.permission.NFC\"/>",
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
nfc.vue代码如下,完全可运行:

<template>  
    <view>  
        <view class="uni-padding-wrap">  
            NFC  
            <view class="uni-common-mt" style="background:#FFF; padding:20upx;">  
                <text>  
                    UID:{{UID}}  
                    {{tip}}  
                </text>  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    var NfcAdapter;  
    export default {  
        data() {  
            return {  
                title: 'redNFC',  
                UID: '',  
                msg: '',  
                tip: '',  
            }  
        },  
        onLoad() {  
            console.log("onLoad");  

        },  
        onShow() {  
            console.log("onShow");  
            this.NFCInit();  
        },  
        onHide() {  
            console.log("onHide");  
            this.NFCReadUID();  
        },  
        methods: {  
            NFCInit() {  
                try {  
                    var main = plus.android.runtimeMainActivity();  
                    //console.log(main);  
                    var Intent = plus.android.importClass('android.content.Intent');  
                    // console.log(Intent);  
                    var Activity = plus.android.importClass('android.app.Activity');  
                    //console.log(Activity);  
                    var PendingIntent = plus.android.importClass('android.app.PendingIntent');  
                    // console.log(PendingIntent);                    
                    var IntentFilter = plus.android.importClass('android.content.IntentFilter');  
                    // console.log(IntentFilter);  
                    // var Uri = plus.android.importClass('android.net.Uri');                     
                    // var Bundle = plus.android.importClass('android.os.Bundle');                    
                    // var Handler = plus.android.importClass('android.os.Handler');  
                    //console.log(Handler);  
                    NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');  
                    //console.log(NfcAdapter);  
                    var _nfcAdapter = NfcAdapter.getDefaultAdapter(main)  
                    // console.log(_nfcAdapter);  

                    var ndef = new IntentFilter("android.nfc.action.NDEF_DISCOVERED"); //NfcAdapter.ACTION_NDEF_DISCOVERED  
                    // console.log(ndef);  
                    var tag = new IntentFilter("android.nfc.action.TAG_DISCOVERED"); //NfcAdapter.ACTION_TECH_DISCOVERED  
                    // console.log(tag);  
                    var tech = new IntentFilter("android.nfc.action.TECH_DISCOVERED");  
                    // console.log(tech);  
                    var intentFiltersArray = [ndef, tag, tech];  

                    var techListsArray = [  
                        ["android.nfc.tech.Ndef"],  
                        ["android.nfc.tech.IsoDep"],  
                        ["android.nfc.tech.NfcA"],  
                        ["android.nfc.tech.NfcB"],  
                        ["android.nfc.tech.NfcF"],  
                        ["android.nfc.tech.Nfcf"],  
                        ["android.nfc.tech.NfcV"],  
                        ["android.nfc.tech.NdefFormatable"],  
                        ["android.nfc.tech.MifareClassi"],  
                        ["android.nfc.tech.MifareUltralight"]  
                    ];  

                    var _intent = new Intent(main, main.getClass());  
                    // console.log(_intent);  
                    _intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);  

                    var pendingIntent = PendingIntent.getActivity(main, 0, _intent, 0);  
                    // console.log(pendingIntent);  

                    if (_nfcAdapter == null) {  
                        this.tip = '本设备不支持NFC!';  
                    } else if (_nfcAdapter.isEnabled() == false) {  
                        this.tip = 'NFC功能未打开!';  
                    } else {  
                        this.tip = 'NFC正常';  
                        _nfcAdapter.enableForegroundDispatch(main, pendingIntent, IntentFilter, techListsArray);  
                    }  
                } catch (e) {  
                    //TODO handle the exception  
                }  
            },  
            NFCReadUID() {  
                var main = plus.android.runtimeMainActivity();  
                var _intent = main.getIntent();  
                var _action = _intent.getAction();  
                // console.log("action type:" + _action);  

                if (NfcAdapter.ACTION_NDEF_DISCOVERED == _action || NfcAdapter.ACTION_TAG_DISCOVERED == _action || NfcAdapter.ACTION_TECH_DISCOVERED ==  
                    _action) {  
                    var Tag = plus.android.importClass('android.nfc.Tag');  
                    var tagFromIntent = _intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
                    var uid = _intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);  
                    console.log(uid);  
                    this.UID = this.Bytes2HexString(uid);  
                    //console.log(this.UID);  
                }  
            },  
            //将byte[] 转为Hex,  
            Bytes2HexString(arrBytes) {  
                var str = "";  
                for (var i = 0; i < arrBytes.length; i++) {  
                    var tmp;  
                    var num = arrBytes[i];  
                    if (num < 0) {  
                        //Java中数值是以补码的形式存在的,应用程序展示的十进制是补码对应真值。补码的存在主要为了简化计算机底层的运算,将减法运算直接当加法来做  
                        tmp = (255 + num + 1).toString(16);  
                    } else {  
                        tmp = num.toString(16);  
                    }  
                    if (tmp.length == 1) {  
                        tmp = "0" + tmp;  
                    }  
                    str += tmp;  
                }  
                return str;  
            }  
        }  
    }  
</script>  

<style>  

</style>  
收起阅读 »

Android Studio离线打包 替换新版本过程记录

uniapp离线打包

1.更新HBuilderX至最新版本,本地打包项目目录
2.下载官方最新SDK
3.打开AndroidStudio 导入SDK中 HBuilder-Hello目录(可自行修改名称)

  1. 替换app -> assets-> apps 目录下的文件包为自己的项目包
  2. 修改app -> assets-> data -> dcloud_control.xml文件 appid => 项目ID appver = "1.0.0"
  3. 修改AndroidMenifest.xml文件
  4. 修改build.grandle
  5. 修改 icon splash push 等图片
继续阅读 »

1.更新HBuilderX至最新版本,本地打包项目目录
2.下载官方最新SDK
3.打开AndroidStudio 导入SDK中 HBuilder-Hello目录(可自行修改名称)

  1. 替换app -> assets-> apps 目录下的文件包为自己的项目包
  2. 修改app -> assets-> data -> dcloud_control.xml文件 appid => 项目ID appver = "1.0.0"
  3. 修改AndroidMenifest.xml文件
  4. 修改build.grandle
  5. 修改 icon splash push 等图片
收起阅读 »

ajax请求的上拉加载更多案例

ajax mui
页面用的是jsp
<div class="mui-off-canvas-wrap mui-draggable">  
        <div class="mui-inner-wrap">  
            <!-- <header class="mui-bar mui-bar-nav">   
                <h1 class="mui-title"></h1>  
            </header> -->  
            <div id="pullrefresh" class="mui-content mui-scroll-wrapper">  
                <div class="mui-scroll">  
                    <ul class="mui-table-view">  
                        <li class="mui-table-view-cell" style="text-align: center;" id="total" value="${total/100}">  
                            合计:¥${total/100 }  
                        </li>  
                    </ul>  
                    <ul class="mui-table-view" id="ulId">  
                        <c:forEach items="${checks}" var="check">  
                            <li class="mui-table-view-cell" id="list${check.id }" value="${check.id}" onclick="detail(${check.id})">  
                                <p class="mui-badge">¥${check.money/100}</p> ${check.remark}  
                                <p class="mui-ellipsis">车牌号:${check.carId}</p>  
                                <p class='mui-ellipsis' id="createDate"><fmt:formatDate value="${check.create_date}" pattern="yyyy-MM-dd HH:mm:ss" /></p>  
                            </li>  
                        </c:forEach>  
                    </ul>  
                </div>  
            </div>  
        </div>  
    </div>  
    <script src="<%=basePath%>open/js/mui.min.js"></script>  
    <script type="text/javascript">  
            mui.init({  
              pullRefresh : {  
                container : "#pullrefresh",//待刷新区域标识,querySelector能定位的css选择器均可,比如:id、.class等  
                up : {  
                  contentrefresh : "正在加载...",//可选,正在加载状态时,上拉加载控件上显示的标题内容  
                  callback : getCheckInfo //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据;  
                }  
              }  
            });  
            var page = 1;  

            // 上拉加载操作  
            function getCheckInfo() {  
                page++;  
                mui.ajax('url?token=<%=token%>&page=' + page, {  
                        dataType: 'json', //服务器返回json格式数据  
                        type: 'POST', //HTTP请求类型  
                        timeout: 10000, //超时时间设置为10秒;  
                        headers: {  
                            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'  
                        },  
                        success: function(data) {  
                            setTimeout(function() {  
                                mui('#pullrefresh').pullRefresh().endPullupToRefresh((data.data.length==0)); // 参数为true代表没有更多数据了。  
                                var table = document.body.querySelector('#ulId');  
                                var cells = document.body.querySelectorAll('.mui-table-view-cell');  
                                var result = data.data;  
                                for (var i = 0; i < result.length; i++) {  
                                    var li = document.createElement('li');  
                                    li.className = 'mui-table-view-cell';  
                                    li.id = 'list' + result[i].id;  
                                    li.value = result[i].id;  
                                    li.innerHTML = '<p class="mui-badge">¥' + result[i].money/100 + '</p>' +  result[i].remark + '<p class="mui-ellipsis">车牌号:' + result[i].carId + '</p><p class="mui-ellipsis" id="createDate">' + result[i].create_date + '</p>';  
                                    table.appendChild(li);  
                                }  
                            },1500);  
                        },  
                        error: function(xhr, type, errorThrown) {  
                            console.log(errorThrown);  
                        }  
                    });  
            }  
            mui.init({  
              gestureConfig:{  
               tap: true, //默认为true  
               doubletap: true, //默认为false  
               longtap: true, //默认为false  
               swipe: true, //默认为true  
               drag: true, //默认为true  
               hold:false,//默认为false,不监听  
               release:false//默认为false,不监听  
              }  
            });  
            mui("#ulId").on("longtap","li",function(list) {  
                // 逻辑代码,例如跳转详情页、ajax  
                    mui.confirm( "是否删除该记录?", "提示", ["是", "否"], function(e) {  
                    if (e.index == 0) {  
                    if (list.path[1].value == undefined) {  
                        delCheck(list.path[0].value);  
                    } else {  
                        delCheck(list.path[1].value);  
                    }  

                    }  
                });  
            });  

            // 执行删除操作  
            function delCheck(id) {  
                mui.ajax('url?token=<%=token%>&id=' + id, {  
                    dataType: 'json', //服务器返回json格式数据  
                    type: 'POST', //HTTP请求类型  
                    timeout: 10000, //超时时间设置为10秒;  
                    headers: {  
                        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'  
                    },  
                    success: function(data) {  
                        var total = data.data/100;  
                        if (parseInt(total, 10) === total) {  
                            total = total + '.0';  
                        }  
                        document.getElementById("list" + id).remove();  
                        mui.alert("删除成功!", "提示", "确定");  
                        document.getElementById("total").innerHTML = "合计:¥" + total;  
                        document.getElementById("total").value = total;  
                    },  
                    error: function(xhr, type, errorThrown) {  
                        console.log(errorThrown);  
                    }  
                });  
            };  
        </script>
继续阅读 »
页面用的是jsp
<div class="mui-off-canvas-wrap mui-draggable">  
        <div class="mui-inner-wrap">  
            <!-- <header class="mui-bar mui-bar-nav">   
                <h1 class="mui-title"></h1>  
            </header> -->  
            <div id="pullrefresh" class="mui-content mui-scroll-wrapper">  
                <div class="mui-scroll">  
                    <ul class="mui-table-view">  
                        <li class="mui-table-view-cell" style="text-align: center;" id="total" value="${total/100}">  
                            合计:¥${total/100 }  
                        </li>  
                    </ul>  
                    <ul class="mui-table-view" id="ulId">  
                        <c:forEach items="${checks}" var="check">  
                            <li class="mui-table-view-cell" id="list${check.id }" value="${check.id}" onclick="detail(${check.id})">  
                                <p class="mui-badge">¥${check.money/100}</p> ${check.remark}  
                                <p class="mui-ellipsis">车牌号:${check.carId}</p>  
                                <p class='mui-ellipsis' id="createDate"><fmt:formatDate value="${check.create_date}" pattern="yyyy-MM-dd HH:mm:ss" /></p>  
                            </li>  
                        </c:forEach>  
                    </ul>  
                </div>  
            </div>  
        </div>  
    </div>  
    <script src="<%=basePath%>open/js/mui.min.js"></script>  
    <script type="text/javascript">  
            mui.init({  
              pullRefresh : {  
                container : "#pullrefresh",//待刷新区域标识,querySelector能定位的css选择器均可,比如:id、.class等  
                up : {  
                  contentrefresh : "正在加载...",//可选,正在加载状态时,上拉加载控件上显示的标题内容  
                  callback : getCheckInfo //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据;  
                }  
              }  
            });  
            var page = 1;  

            // 上拉加载操作  
            function getCheckInfo() {  
                page++;  
                mui.ajax('url?token=<%=token%>&page=' + page, {  
                        dataType: 'json', //服务器返回json格式数据  
                        type: 'POST', //HTTP请求类型  
                        timeout: 10000, //超时时间设置为10秒;  
                        headers: {  
                            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'  
                        },  
                        success: function(data) {  
                            setTimeout(function() {  
                                mui('#pullrefresh').pullRefresh().endPullupToRefresh((data.data.length==0)); // 参数为true代表没有更多数据了。  
                                var table = document.body.querySelector('#ulId');  
                                var cells = document.body.querySelectorAll('.mui-table-view-cell');  
                                var result = data.data;  
                                for (var i = 0; i < result.length; i++) {  
                                    var li = document.createElement('li');  
                                    li.className = 'mui-table-view-cell';  
                                    li.id = 'list' + result[i].id;  
                                    li.value = result[i].id;  
                                    li.innerHTML = '<p class="mui-badge">¥' + result[i].money/100 + '</p>' +  result[i].remark + '<p class="mui-ellipsis">车牌号:' + result[i].carId + '</p><p class="mui-ellipsis" id="createDate">' + result[i].create_date + '</p>';  
                                    table.appendChild(li);  
                                }  
                            },1500);  
                        },  
                        error: function(xhr, type, errorThrown) {  
                            console.log(errorThrown);  
                        }  
                    });  
            }  
            mui.init({  
              gestureConfig:{  
               tap: true, //默认为true  
               doubletap: true, //默认为false  
               longtap: true, //默认为false  
               swipe: true, //默认为true  
               drag: true, //默认为true  
               hold:false,//默认为false,不监听  
               release:false//默认为false,不监听  
              }  
            });  
            mui("#ulId").on("longtap","li",function(list) {  
                // 逻辑代码,例如跳转详情页、ajax  
                    mui.confirm( "是否删除该记录?", "提示", ["是", "否"], function(e) {  
                    if (e.index == 0) {  
                    if (list.path[1].value == undefined) {  
                        delCheck(list.path[0].value);  
                    } else {  
                        delCheck(list.path[1].value);  
                    }  

                    }  
                });  
            });  

            // 执行删除操作  
            function delCheck(id) {  
                mui.ajax('url?token=<%=token%>&id=' + id, {  
                    dataType: 'json', //服务器返回json格式数据  
                    type: 'POST', //HTTP请求类型  
                    timeout: 10000, //超时时间设置为10秒;  
                    headers: {  
                        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'  
                    },  
                    success: function(data) {  
                        var total = data.data/100;  
                        if (parseInt(total, 10) === total) {  
                            total = total + '.0';  
                        }  
                        document.getElementById("list" + id).remove();  
                        mui.alert("删除成功!", "提示", "确定");  
                        document.getElementById("total").innerHTML = "合计:¥" + total;  
                        document.getElementById("total").value = total;  
                    },  
                    error: function(xhr, type, errorThrown) {  
                        console.log(errorThrown);  
                    }  
                });  
            };  
        </script>
收起阅读 »