HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【文档】wap2app选项卡样式定制

选项卡 wap2app

以下修改或配置,均在 client_index.html 中进行。

选项卡文字颜色

/*选项卡文字默认颜色*/  
.tab-item {  
  color: #000000;  
}  

/*选项卡文字高亮时颜色*/  
.tab-item.active {  
  color: #FFFF00;  
}

选项卡背景

.tab-inner {  
  background-color: #0000FF;  
  box-shadow: 0 0 1px #FF0000;  
  -webkit-box-shadow: 0 0 1px #FF0000;  
}

底部选项卡是没有边框(border)的,而是使用了阴影(box-shadow)。可以根据需求,修改样式。

修改阴影的颜色

.tab-inner {  
  box-shadow: 0 0 1px #FF0000;  
  -webkit-box-shadow: 0 0 1px #FF0000;  
}

不要阴影,使用边框。

.tab-inner {  
  box-shadow: none;  
  -webkit-box-shadow: none;  
  border-top: 1px solid #0000ff;  
}

关于图标

图标是需要在初始化选项卡的时候就配置的,详细参考 http://ask.dcloud.net.cn/article/12878 中的说明。

new TabBar({  
  list: [{  
      url: "http://hello.wap2app.dcloud.io",  
      text: "首页", //本地路径,也可以是网络路径  
      iconPath: 'http://hello.wap2app.dcloud.io/img/home-outline.png',  
      selectedIconPath: 'http://hello.wap2app.dcloud.io/img/home-outline-highlight.png'  
    },  
    {  
      url: "http://hello.wap2app.dcloud.io/about.html",  
      text: "关于",  
      iconPath: 'http://hello.wap2app.dcloud.io/img/gear-outline.png',  
      selectedIconPath: 'http://hello.wap2app.dcloud.io/img/gear-outline-highlight.png'  
    }  
  ]  
});
继续阅读 »

以下修改或配置,均在 client_index.html 中进行。

选项卡文字颜色

/*选项卡文字默认颜色*/  
.tab-item {  
  color: #000000;  
}  

/*选项卡文字高亮时颜色*/  
.tab-item.active {  
  color: #FFFF00;  
}

选项卡背景

.tab-inner {  
  background-color: #0000FF;  
  box-shadow: 0 0 1px #FF0000;  
  -webkit-box-shadow: 0 0 1px #FF0000;  
}

底部选项卡是没有边框(border)的,而是使用了阴影(box-shadow)。可以根据需求,修改样式。

修改阴影的颜色

.tab-inner {  
  box-shadow: 0 0 1px #FF0000;  
  -webkit-box-shadow: 0 0 1px #FF0000;  
}

不要阴影,使用边框。

.tab-inner {  
  box-shadow: none;  
  -webkit-box-shadow: none;  
  border-top: 1px solid #0000ff;  
}

关于图标

图标是需要在初始化选项卡的时候就配置的,详细参考 http://ask.dcloud.net.cn/article/12878 中的说明。

new TabBar({  
  list: [{  
      url: "http://hello.wap2app.dcloud.io",  
      text: "首页", //本地路径,也可以是网络路径  
      iconPath: 'http://hello.wap2app.dcloud.io/img/home-outline.png',  
      selectedIconPath: 'http://hello.wap2app.dcloud.io/img/home-outline-highlight.png'  
    },  
    {  
      url: "http://hello.wap2app.dcloud.io/about.html",  
      text: "关于",  
      iconPath: 'http://hello.wap2app.dcloud.io/img/gear-outline.png',  
      selectedIconPath: 'http://hello.wap2app.dcloud.io/img/gear-outline-highlight.png'  
    }  
  ]  
});
收起阅读 »

流应用提交指南及如何通过流应用让激活用户翻倍

正式平台 流应用

流应用是一种即点即用的应用。2015年发布,是小程序/快应用等动态App行业的先行者。
流应用默认会在手机桌面创建图标,遇到权限问题会引导用户赋权。
原生App需要经历下载、安装、激活数个环节,100个下载里最终只有40个用户成功启动这个App。
也就是只有40%的激活率。
而流应用即点即用,5秒内完成直接完成下载、激活。
尤其在应用内广告场景中,过去下载安装apk的折损是甚至大于60%,此时尤其需要即点即用的流应用来提升广告效果。

所有HTML5+开发的app,包括wap2app的app,都可以自动转为流应用。
当然需要开发者自己提交和测试。

本文讲解如何在HBuilder中将5+ App、wap2app发布成流应用。

提交到测试平台

在HBuilder(注意HBuilderX暂不支持)中选择需要发布的项目工程,点击顶部菜单“发行 -> 发行为流应用”,会弹出发行界面,如下图:

点击“提交测试”,将应用资源提交到测试平台上,提交成功后,会显示测试二维码,如下图所示:

测试流应用各项业务功能

dcloud.io 下载并安装流应用App,使用流应用App扫描如上的测试二维码,验证应用的各项功能在流应用环境下是否正常。

若有平台差异,建议通过环境判断,做适当修改。

发布到正式平台

在测试平台验证流应用功能正常后,点击“发布为正式”按钮,向正式平台提交。

鉴于监管要求,流应用首次发布正式时,需在线提交一些应用信息,如下图:

信息提交完成后,还需经过平台方的人工审核,审核通过后,会自动发布到正式;开发者再次打开发行流应用菜单,会显示正式平台的二维码(快码),如下图:

若审核失败,会通过邮件通知审核失败的原因(比如用户体验不佳、涉及敏感信息等)。

注意:流应用发布正式平台前,需完成开发者的实名认证,相关流程参考DCloud开发者实名认证流程

继续阅读 »

流应用是一种即点即用的应用。2015年发布,是小程序/快应用等动态App行业的先行者。
流应用默认会在手机桌面创建图标,遇到权限问题会引导用户赋权。
原生App需要经历下载、安装、激活数个环节,100个下载里最终只有40个用户成功启动这个App。
也就是只有40%的激活率。
而流应用即点即用,5秒内完成直接完成下载、激活。
尤其在应用内广告场景中,过去下载安装apk的折损是甚至大于60%,此时尤其需要即点即用的流应用来提升广告效果。

所有HTML5+开发的app,包括wap2app的app,都可以自动转为流应用。
当然需要开发者自己提交和测试。

本文讲解如何在HBuilder中将5+ App、wap2app发布成流应用。

提交到测试平台

在HBuilder(注意HBuilderX暂不支持)中选择需要发布的项目工程,点击顶部菜单“发行 -> 发行为流应用”,会弹出发行界面,如下图:

点击“提交测试”,将应用资源提交到测试平台上,提交成功后,会显示测试二维码,如下图所示:

测试流应用各项业务功能

dcloud.io 下载并安装流应用App,使用流应用App扫描如上的测试二维码,验证应用的各项功能在流应用环境下是否正常。

若有平台差异,建议通过环境判断,做适当修改。

发布到正式平台

在测试平台验证流应用功能正常后,点击“发布为正式”按钮,向正式平台提交。

鉴于监管要求,流应用首次发布正式时,需在线提交一些应用信息,如下图:

信息提交完成后,还需经过平台方的人工审核,审核通过后,会自动发布到正式;开发者再次打开发行流应用菜单,会显示正式平台的二维码(快码),如下图:

若审核失败,会通过邮件通知审核失败的原因(比如用户体验不佳、涉及敏感信息等)。

注意:流应用发布正式平台前,需完成开发者的实名认证,相关流程参考DCloud开发者实名认证流程

收起阅读 »

打开系统应用,如日历,要打开对应的权限

日历

如下图,如果你想打开手机的内置应用,有些是需要添加权限的,之前调式都可以打开日历,打包后,都不可以打开日历,查看才发现需要添加权限,,

如下图,如果你想打开手机的内置应用,有些是需要添加权限的,之前调式都可以打开日历,打包后,都不可以打开日历,查看才发现需要添加权限,,

plus.geolocation.getCurrentPosition || plus.maps.Map.calculateDistance

5+文档中距离计算是按照百度坐标来进行计算的;而定位获取到的坐标是国测局坐标(火星坐标,比如高德地图在用),需要转换为百度
坐标;具体转换方法由于简书已发布文章就不在重复编辑了,可参考https://www.jianshu.com/p/b1e9edf61cd8

继续阅读 »

5+文档中距离计算是按照百度坐标来进行计算的;而定位获取到的坐标是国测局坐标(火星坐标,比如高德地图在用),需要转换为百度
坐标;具体转换方法由于简书已发布文章就不在重复编辑了,可参考https://www.jianshu.com/p/b1e9edf61cd8

收起阅读 »

html5如何利用百度地图快速的定位经纬度

定位 地图

定位功能(Geolocation)是HTML5的新特性,因此只有在支持HTML5的现代浏览器上运行,特别是手持设备如iphone,地理定位更加精确。首先我们要检测用户设备浏览器是否支持地理定位,如果支持则获取地理信息。注意这个特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的,所以我们在访问该应用时会提示是否允许地理定位,我们当然选择允许即可。
function getLocation(){ 
if (navigator.geolocation){ 
navigator.geolocation.getCurrentPosition(showPosition,showError); 
}else{ 
alert("浏览器不支持地理定位。"); 


上面的代码可以知道,如果用户设备支持地理定位,则运行 getCurrentPosition() 方法。如果getCurrentPosition()运行成功,则向参数showPosition中规定的函数返回一个coordinates对象,getCurrentPosition() 方法的第二个参数showError用于处理错误,它规定当获取用户位置失败时运行的函数。
我们先来看函数showError(),它规定获取用户地理位置失败时的一些错误代码处理方式:
function showError(error){ 
switch(error.code) { 
case error.PERMISSION_DENIED: 
alert("定位失败,用户拒绝请求地理定位"); 
break; 
case error.POSITION_UNAVAILABLE: 
alert("定位失败,位置信息是不可用"); 
break; 
case error.TIMEOUT: 
alert("定位失败,请求获取用户位置超时"); 
break; 
case error.UNKNOWN_ERROR: 
alert("定位失败,定位系统失效"); 
break; 


我们再来看函数showPosition(),调用coords的latitude和longitude即可获取到用户的纬度和经度。
function showPosition(position){ 
var lat = position.coords.latitude; //纬度 
var lag = position.coords.longitude; //经度 
alert('纬度:'+lat+',经度:'+lag); 

利用百度地图
function showPosition(position){ 
var latlon = position.coords.latitude+','+position.coords.longitude; 

//baidu 
var url = "<a href="http://api.map.baidu.com/geocoder/v2/?ak=C93b5178d7a8ebdb830b9b557abce78b&callback=renderReverse&location="+latlon+"&output=json&pois=0">http://api.map.baidu.com/geocoder/v2/?ak=C93b5178d7a8ebdb830b9b557abce78b&callback=renderReverse&location="+latlon+"&output=json&pois=0</a>"; 
$.ajax({ 
type: "GET", 
dataType: "jsonp", 
url: url, 
beforeSend: function(){ 
$("#baidu_geo").html('正在定位...'); 
}, 
success: function (json) { 
if(json.status==0){ 
$("#baidu_geo").html(json.result.formatted_address); 

}, 
error: function (XMLHttpRequest, textStatus, errorThrown) { 
$("#baidu_geo").html(latlon+"地址位置获取失败"); 

}); 
}); 
  好了现在大家已经知道如何利用百度地图快速定位经纬度了,如果还存在不理解的地方,可以留言咨询我们。本文由专业的微信小程序开发公司燚轩科技整理发布,如需转载请注明出处!

继续阅读 »

定位功能(Geolocation)是HTML5的新特性,因此只有在支持HTML5的现代浏览器上运行,特别是手持设备如iphone,地理定位更加精确。首先我们要检测用户设备浏览器是否支持地理定位,如果支持则获取地理信息。注意这个特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的,所以我们在访问该应用时会提示是否允许地理定位,我们当然选择允许即可。
function getLocation(){ 
if (navigator.geolocation){ 
navigator.geolocation.getCurrentPosition(showPosition,showError); 
}else{ 
alert("浏览器不支持地理定位。"); 


上面的代码可以知道,如果用户设备支持地理定位,则运行 getCurrentPosition() 方法。如果getCurrentPosition()运行成功,则向参数showPosition中规定的函数返回一个coordinates对象,getCurrentPosition() 方法的第二个参数showError用于处理错误,它规定当获取用户位置失败时运行的函数。
我们先来看函数showError(),它规定获取用户地理位置失败时的一些错误代码处理方式:
function showError(error){ 
switch(error.code) { 
case error.PERMISSION_DENIED: 
alert("定位失败,用户拒绝请求地理定位"); 
break; 
case error.POSITION_UNAVAILABLE: 
alert("定位失败,位置信息是不可用"); 
break; 
case error.TIMEOUT: 
alert("定位失败,请求获取用户位置超时"); 
break; 
case error.UNKNOWN_ERROR: 
alert("定位失败,定位系统失效"); 
break; 


我们再来看函数showPosition(),调用coords的latitude和longitude即可获取到用户的纬度和经度。
function showPosition(position){ 
var lat = position.coords.latitude; //纬度 
var lag = position.coords.longitude; //经度 
alert('纬度:'+lat+',经度:'+lag); 

利用百度地图
function showPosition(position){ 
var latlon = position.coords.latitude+','+position.coords.longitude; 

//baidu 
var url = "<a href="http://api.map.baidu.com/geocoder/v2/?ak=C93b5178d7a8ebdb830b9b557abce78b&callback=renderReverse&location="+latlon+"&output=json&pois=0">http://api.map.baidu.com/geocoder/v2/?ak=C93b5178d7a8ebdb830b9b557abce78b&callback=renderReverse&location="+latlon+"&output=json&pois=0</a>"; 
$.ajax({ 
type: "GET", 
dataType: "jsonp", 
url: url, 
beforeSend: function(){ 
$("#baidu_geo").html('正在定位...'); 
}, 
success: function (json) { 
if(json.status==0){ 
$("#baidu_geo").html(json.result.formatted_address); 

}, 
error: function (XMLHttpRequest, textStatus, errorThrown) { 
$("#baidu_geo").html(latlon+"地址位置获取失败"); 

}); 
}); 
  好了现在大家已经知道如何利用百度地图快速定位经纬度了,如果还存在不理解的地方,可以留言咨询我们。本文由专业的微信小程序开发公司燚轩科技整理发布,如需转载请注明出处!

收起阅读 »

mui.openWindow()打不开页面

开发时遇到mui.openWindow()打不开页面,看了很多资料都没有解决。比如说把代码放在mui.plusready内部执行,也不能解决问题。
在需要打开的页面中使用console.log()输出一段文本,结果控制台没有看到,说明页面并没有加载(注意:我没有使用预加载页面)。最后检查发现问题在于一个<buttom>标签,标签放在表单内部。(以下原因是我的猜想)buttom标签放在表单内部,点击的时候,页面有刷新的效果,而且有请求后台,说明这个按钮被默认处理为表单提交按钮,所以点击按钮的时候就不响应自定义点击事件。解决办法是给<button>标签添加 type="button"属性。 问题解决。

继续阅读 »

开发时遇到mui.openWindow()打不开页面,看了很多资料都没有解决。比如说把代码放在mui.plusready内部执行,也不能解决问题。
在需要打开的页面中使用console.log()输出一段文本,结果控制台没有看到,说明页面并没有加载(注意:我没有使用预加载页面)。最后检查发现问题在于一个<buttom>标签,标签放在表单内部。(以下原因是我的猜想)buttom标签放在表单内部,点击的时候,页面有刷新的效果,而且有请求后台,说明这个按钮被默认处理为表单提交按钮,所以点击按钮的时候就不响应自定义点击事件。解决办法是给<button>标签添加 type="button"属性。 问题解决。

收起阅读 »

分享一个简单的淘宝、京东,采集商品宝贝的图片例子

HTML5+

简单的淘宝、京东,采集商品宝贝的图片例子,
作用: 用于快速采集商品宝贝图片,用于发布微信朋友圈宣传推广商品,和收集商品等等.

主要是把JS 注入到,要采集图片的网站,  
appendJsFile("_www/js/mui.min.js");  
appendJsFile("_www/js/jsimg.js");  

在底部留下按键,通过 evalJS , 操作要采集的网站  

案例在下面,可以下载体验下

继续阅读 »

简单的淘宝、京东,采集商品宝贝的图片例子,
作用: 用于快速采集商品宝贝图片,用于发布微信朋友圈宣传推广商品,和收集商品等等.

主要是把JS 注入到,要采集图片的网站,  
appendJsFile("_www/js/mui.min.js");  
appendJsFile("_www/js/jsimg.js");  

在底部留下按键,通过 evalJS , 操作要采集的网站  

案例在下面,可以下载体验下

收起阅读 »

【购买】app商城B2C,核心功能完整,带支付,后台php或net都行

B2C商城(APP的)有做过的吗,核心功能完整无Bug的,
带支付功能,后台php或者.net的都行,可以代价私聊,QQ:705722325。

我相信这个系统应该有成熟的作品的,拿出来,我购买,让H5APP发扬光大,

无需售后,我们有技术团队,

当然,如果有需要公安局治安大队的阵地管控、特行检查等app的也可以联系我。

继续阅读 »

B2C商城(APP的)有做过的吗,核心功能完整无Bug的,
带支付功能,后台php或者.net的都行,可以代价私聊,QQ:705722325。

我相信这个系统应该有成熟的作品的,拿出来,我购买,让H5APP发扬光大,

无需售后,我们有技术团队,

当然,如果有需要公安局治安大队的阵地管控、特行检查等app的也可以联系我。

收起阅读 »

用mui要在CSS里面加 *{touch-action: none;}

更新MUI

用mui要在CSS里面加 *{touch-action: none;} 否则tap事件,开发工具会提示
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

继续阅读 »

用mui要在CSS里面加 *{touch-action: none;} 否则tap事件,开发工具会提示
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

收起阅读 »

MUI使用个推推送流程分析

推送

原文链接(看的格式可能舒服点)

需求:app通知栏收到推送,点击通知栏推送可以进入到不同的详情页


1、监听方法分析

  • receive方法:
    当APP在前台的时候,可以监听接收透传消息,APP不在前台的时候无法监听
    plus.push.addEventListener("receive", function (msg) {  
    }
    • click方法:
      1、iOS APP处于后台,透传消息会变成通知提醒,此时点击通知触发
      2、点击本地设置的推送到通知栏的通知
plus.push.addEventListener("click", function (msg) {  
}

2、技术分析

通知栏通知:

  • iOS
    在个推中,iOS 只有透传,没有通知。当APP不在前台时,透传只在通知栏提醒,点击走click方法处理业务;
  • Android
    APP透传消息都不会出现在通知栏,有两种处理方式:
    1、个推发送通知(但是点击通知只能打开APP、打开链接、下载应用)
    2、收到透传消息后,本地生成通知到通知栏(仅限于APP处于前台,receive方法中处理)

透传消息:

  • APP处于前台才可接收到,APP处于后台无法接收。且接收透传消息时,不可以直接做跳转页面等业务处理,业务处理跳转等需在点击通知栏后进行相应处理。

3、工欲善其事必先利其器,先看官方常见问题:

image.png

4、逻辑处理:

iOS

  • APP处于前台:

    服务器端推送透传消息后,直接在receive方法监听到:
    iOS APP处于前台.png

但是这不是我们要的。我们需要的是用户收到消息后在通知栏出现,而不是直接在receive方法中做业务处理。那么我们可以看下上面官方常见问题第三点,我们需要在消息中心创建一个本地消息:

plus.push.createMessage( str, "LocalMSG", options );

通过点击通知中心的这个本地消息,进入app实现click方法监听处理业务。
那么问题来了,经过测试我们发现,在创建本地消息的时候(receive方法中),APP一定是处于前台的,那么创建了本地通知以后,我们的receive方法一定会监听到,这样不就进入到一个死循环了么?

receive监听 -> 创建本地通知 -> receive再监听到本地通知 -> 再创建本地通知。。。

子子孙孙无穷尽也。

那么我们可以根据参考常见问题的第三点:用户在创建IOS本地消息是可以在“payload”节点添加特殊标记对消息进行区分。
这玩意是在创建消息的时候,官方模板自带的一个参数。在个推后台iOS透传的时候,有一个:image.png
这个其实只是就是把payload参数的值变成你的透传消息json,那么本地创建通知的时候这个值是什么呢?我们看上面创建本地通知的时候,第二个参数LocalMSG,这个就是我们自定义在本地推送的时候的payload参数的值。所以,在receive方法中,我们根据payload参数的值是不是我们设定的LocalMSG就可以判断监听到的推送是不是本地创建的了。

  • APP处于后台:

    当APP处于后台,receive方法监听不到消息,此时消息走消息中心推送,点击后进入APP内,走click方法处理业务,满足需求。
    iOS APP处于后台.png

Android

根据常见问题第二点,我们知道在处理Android消息的时候,需要使用标准格式的透传json:

{title:"通知标题",content:"通知内容",payload:"通知去干嘛这里可以自定义"}

这样,透传消息就不会走receive方法,而是像我们需求那样实现所有通知进入通知中心,点击通知中心走click方法处理。如果不按照此格式来,消息不会走消息中心,而是直接走监听的receive方法(APP在前台时,不在前台时,透传消息甚至监听不到)。

Android APP消息流程梳理.png

需要注意的一点是:

Android透传格式{"title":"通知1","content":"通知内容","payload":"{type:\"notify\",payload:\"通知去干嘛这里可以自定义\"}"}如果想要传输自定义json,赋值给payload,但是里面的json必须是转义过后的,且任何和官方规定格式在同一层的字段都是无效的,例如: {"id":"123","title":"通知1","content":"通知内容","payload":"{type:\"notify\",ids:\"456\"}"}
这里面第一个id:"123"在实际接收到的时候并不会存在。

5、总结

其实扯了一大堆,简而言之就是一句话:

Android推送的时候要注意后台返回的json满足官方格式即可,iOS推送要注意当APP在前台的时候,在receive方法监听的时候判断是否是本地通知。

下面是我的部分关键代码,alart()是测试阶段为了看到效果加的:

        //获取推送通知  
        plus.push.addEventListener("receive", function (msg) {  
            alert("接收到的透传推送 " + JSON.stringify(msg));  
            if (plus.os.name == "iOS") {  
                switch (msg.payload) {  
                    case "LocalMSG":  
                        alert("本地创建消息的 " + JSON.stringify(msg));  
                        break;  
                    default:  
                        //测试本地推送  
                        createLocalPushMsg(msg);  
                        break;  
                }  
            }  
        }, false);
        //点击推送进来  
        plus.push.addEventListener("click", function (msg) {  
            // 判断是从本地创建还是离线推送的消息  
            alert("点击处理消息 " + JSON.stringify(msg));  
        }, false);
        //创建本地消息  
        function createLocalPushMsg(msg) {  
            var options = {cover: false};  
            plus.push.createMessage(msg.content, "LocalMSG", options);  
            if (plus.os.name == "iOS") {  
                outLine('*如果无法创建消息,请到"设置"->"通知"中配置应用在通知中心显示!');  
            }  
        }
本文参考的官方文档附上
继续阅读 »

原文链接(看的格式可能舒服点)

需求:app通知栏收到推送,点击通知栏推送可以进入到不同的详情页


1、监听方法分析

  • receive方法:
    当APP在前台的时候,可以监听接收透传消息,APP不在前台的时候无法监听
    plus.push.addEventListener("receive", function (msg) {  
    }
    • click方法:
      1、iOS APP处于后台,透传消息会变成通知提醒,此时点击通知触发
      2、点击本地设置的推送到通知栏的通知
plus.push.addEventListener("click", function (msg) {  
}

2、技术分析

通知栏通知:

  • iOS
    在个推中,iOS 只有透传,没有通知。当APP不在前台时,透传只在通知栏提醒,点击走click方法处理业务;
  • Android
    APP透传消息都不会出现在通知栏,有两种处理方式:
    1、个推发送通知(但是点击通知只能打开APP、打开链接、下载应用)
    2、收到透传消息后,本地生成通知到通知栏(仅限于APP处于前台,receive方法中处理)

透传消息:

  • APP处于前台才可接收到,APP处于后台无法接收。且接收透传消息时,不可以直接做跳转页面等业务处理,业务处理跳转等需在点击通知栏后进行相应处理。

3、工欲善其事必先利其器,先看官方常见问题:

image.png

4、逻辑处理:

iOS

  • APP处于前台:

    服务器端推送透传消息后,直接在receive方法监听到:
    iOS APP处于前台.png

但是这不是我们要的。我们需要的是用户收到消息后在通知栏出现,而不是直接在receive方法中做业务处理。那么我们可以看下上面官方常见问题第三点,我们需要在消息中心创建一个本地消息:

plus.push.createMessage( str, "LocalMSG", options );

通过点击通知中心的这个本地消息,进入app实现click方法监听处理业务。
那么问题来了,经过测试我们发现,在创建本地消息的时候(receive方法中),APP一定是处于前台的,那么创建了本地通知以后,我们的receive方法一定会监听到,这样不就进入到一个死循环了么?

receive监听 -> 创建本地通知 -> receive再监听到本地通知 -> 再创建本地通知。。。

子子孙孙无穷尽也。

那么我们可以根据参考常见问题的第三点:用户在创建IOS本地消息是可以在“payload”节点添加特殊标记对消息进行区分。
这玩意是在创建消息的时候,官方模板自带的一个参数。在个推后台iOS透传的时候,有一个:image.png
这个其实只是就是把payload参数的值变成你的透传消息json,那么本地创建通知的时候这个值是什么呢?我们看上面创建本地通知的时候,第二个参数LocalMSG,这个就是我们自定义在本地推送的时候的payload参数的值。所以,在receive方法中,我们根据payload参数的值是不是我们设定的LocalMSG就可以判断监听到的推送是不是本地创建的了。

  • APP处于后台:

    当APP处于后台,receive方法监听不到消息,此时消息走消息中心推送,点击后进入APP内,走click方法处理业务,满足需求。
    iOS APP处于后台.png

Android

根据常见问题第二点,我们知道在处理Android消息的时候,需要使用标准格式的透传json:

{title:"通知标题",content:"通知内容",payload:"通知去干嘛这里可以自定义"}

这样,透传消息就不会走receive方法,而是像我们需求那样实现所有通知进入通知中心,点击通知中心走click方法处理。如果不按照此格式来,消息不会走消息中心,而是直接走监听的receive方法(APP在前台时,不在前台时,透传消息甚至监听不到)。

Android APP消息流程梳理.png

需要注意的一点是:

Android透传格式{"title":"通知1","content":"通知内容","payload":"{type:\"notify\",payload:\"通知去干嘛这里可以自定义\"}"}如果想要传输自定义json,赋值给payload,但是里面的json必须是转义过后的,且任何和官方规定格式在同一层的字段都是无效的,例如: {"id":"123","title":"通知1","content":"通知内容","payload":"{type:\"notify\",ids:\"456\"}"}
这里面第一个id:"123"在实际接收到的时候并不会存在。

5、总结

其实扯了一大堆,简而言之就是一句话:

Android推送的时候要注意后台返回的json满足官方格式即可,iOS推送要注意当APP在前台的时候,在receive方法监听的时候判断是否是本地通知。

下面是我的部分关键代码,alart()是测试阶段为了看到效果加的:

        //获取推送通知  
        plus.push.addEventListener("receive", function (msg) {  
            alert("接收到的透传推送 " + JSON.stringify(msg));  
            if (plus.os.name == "iOS") {  
                switch (msg.payload) {  
                    case "LocalMSG":  
                        alert("本地创建消息的 " + JSON.stringify(msg));  
                        break;  
                    default:  
                        //测试本地推送  
                        createLocalPushMsg(msg);  
                        break;  
                }  
            }  
        }, false);
        //点击推送进来  
        plus.push.addEventListener("click", function (msg) {  
            // 判断是从本地创建还是离线推送的消息  
            alert("点击处理消息 " + JSON.stringify(msg));  
        }, false);
        //创建本地消息  
        function createLocalPushMsg(msg) {  
            var options = {cover: false};  
            plus.push.createMessage(msg.content, "LocalMSG", options);  
            if (plus.os.name == "iOS") {  
                outLine('*如果无法创建消息,请到"设置"->"通知"中配置应用在通知中心显示!');  
            }  
        }
本文参考的官方文档附上
收起阅读 »

花了半天时间大概写了份 mui 的 d.ts ,mark一下

mui.min.d.ts

interface Subpages {  
  url: string  
  id: string  
  styles?: object  
  extras?: object  
}  

interface PreloadPages extends Subpages {  
  subpages?: Array<Subpages>  
}  

interface PullRefreshBase {  
  height?: string | number  
  auto?: boolean  
  contentrefresh?: string  
  callback: any  
}  
interface PullRefreshUp extends PullRefreshBase {  
  contentnomore?: string  
}  
interface pullRefreshDown extends PullRefreshBase {  
  style: 'circle'  
  color?: string  
  range?: string | number  
  offset?: string | number  
  contentdown?: string  
  contentover?: string  
}  
interface PullRefresh {  
  container: string  
  down?: pullRefreshDown  
  up?: PullRefreshUp  
}  

interface OffCanvas {  
  /** 显示 */  
  show (): void  
  /** 隐藏 */  
  close (): void  
  /** 切换 */  
  toggle (): void  
  /** 判断是否为显示状态 */  
  isShown (direction?: string): boolean  
}  

interface Numbox {  
  /** 获取当前值 */  
  getValue (): string  
  /** 动态设置新值 */  
  setValue (val: number): void  
  /** 更新选项 */  
  setOption (option: 'min' | 'step' | 'max', val: number): void  
}  

declare namespace mui {  
  /** 初始化 */  
  function init (options?: {  
    wipeBack?: boolean  
    swipe?: boolean  
    subpages?: Array<Subpages>  
    preloadPages?: Array<PreloadPages>  
    pullRefresh?: PullRefresh  
    gestureConfig?: {  
      tap?: boolean  
      doubletap?: boolean  
      longtap?: boolean  
      hold?: boolean  
      release?: boolean  
      swipeleft?: boolean  
      swiperight?: boolean  
      swipeup?: boolean  
      swipedown?: boolean  
      dragstart?: boolean  
      drag?: boolean  
      dragend?: boolean  
    }  
    keyEventBind?: {  
      backbutton?: boolean  
      menubutton?: boolean  
    }  
    beforeback?: Function  
    statusBarBackground?: string  
    preloadLimit?: number  
  }): void  
  /** 打开新窗口 */  
  function openWindow (options: {  
    url: string  
    id: string  
    styles?: object  
    extras?: object  
    createNew?: boolean  
    show?: {  
      autoShow?: boolean  
      aniShow?: object  
      duration?: number  
    }  
    waiting?: {  
      autoShow?: boolean  
      title?: string  
      options?: object  
    }  
  }): void  
  /** 自动消失消息提示框 */  
  function toast (message: string, options?: {  
    duration?: number | 'long' | 'short'  
    type?: 'div'  
  }): void  
  /** 警告框 */  
  function alert (message: string, title?: string, btnValue?: string, callback?: any, type?: 'div'): void  
  /** 确认框 */  
  function confirm (message: string, title?: string, btnValue?: string, callback?: any, type?: 'div'): void  
  /** 输入对话框 */  
  function prompt (message: string, title?: string, btnValue?: string, callback?: any, type?: 'div'): void  
  /** 触发 Dom 事件 */  
  function trigger (element: HTMLElement, event: string, data?: any): void  
  /** 触发自定义事件 */  
  function fire (target: any, event: string, data?: any): void  
  /** 遍历 */  
  function each (obj: any, callback: any): void  
  /** 合并对象 */  
  function extend (deep: boolean, target: any, ...object: Array<any>): void  
  /** 封装 setTimeOut */  
  function later (func: any, delay: number, context?: any): void  
  /** 滚动窗口屏幕到指定位置 */  
  function scrollTo (ypos: number, duration: number, callback?: any): void  
  /** 判断当前运行环境 */  
  let os: object & os  
  interface os {  
    /** 是否在 5+ 环境内 */  
    plus: boolean | undefined  
    /** 是否是流应用 */  
    stream: boolean | undefined  
    /** 是否为 IOS */  
    ios: boolean | undefined  
    /** 是否 iphone */  
    iphone: boolean | undefined  
    /** 是否 ipad */  
    ipad: boolean | undefined  
    /** 是否 android */  
    android: boolean | undefined  
    /** 是否 android 的 chrome 环境 */  
    isBadAndroid: boolean | undefined  
    /** 版本号 */  
    version: string  
  }  
  /** plusReady */  
  function plusReady (callback: any): void  
  /** 关闭最后一次弹出的对话框(H5模式) */  
  function closePopup (): void  
  /** 关闭所有对话框(H5模式) */  
  function closePopups (): void  
  /** 轮播 */  
  function slider (options: any): void  
  /** 创建遮罩 */  
  function createMask (callback: any): Mask  
  interface Mask {  
    /** 显示遮罩 */  
    show (): void  
    /** 关闭遮罩 */  
    close (): void  
  }  
  /** 选择器组件 picker */  
  class PopPicker {  
    constructor (options: { layer?: number, buttons?: Array<any>})  
    setData (options: Array<any>): void  
    pickers: Array<Picker>  
    getSelectedItem (): Array<Picker>  
    show (callback: any): void  
    hide (): void  
    dispose (): void  
  }  
  /** 日期选择器 DtPicker */  
  class DtPicker {  
    constructor (options?: any)  
    getSelectedItems (): any  
    show (callback?: any): void  
    hide (): void  
    dispose (): void  
  }  
  /** 打开新页面 */  
  function open (...options: Array<any>): any  
  /** 当前页面 */  
  function currentWebview (...options: Array<any>): any  
  /** 关闭窗口 */  
  function back (...options: Array<any>): any  
  /** 重写返回逻辑 */  
  function backFunction (...options: Array<any>): any  
  /** 双击退出应用 */  
  function backDouble (...options: Array<any>): any  
  /** 双击进入后台 */  
  function backTast (...options: Array<any>): any  
  /** 预加载 */  
  function preload (...options: Array<any>): any  
  /** Ajax */  
  function ajax (url?: string, settings?: {  
    async?: boolean  
    crossDomain?: boolean  
    data?: any  
    dataType?: 'xml' | 'html' | 'script' | 'json' | 'text'  
    error?: any  
    success?: any  
    timeout?: number  
    type?: 'GET' | 'POST'  
    headers?: any  
    processData?: boolean  
  }): void  
  /** Ajax post */  
  function post (url: string, data?: any, success?: any, dataType?: 'xml' | 'html' | 'script' | 'json' | 'text'): void  
  function get (...options: Array<any>): any  
  function getJSON (...options: Array<any>): any  
}  

interface Picker {  
  setSelectedValue (value: string, duration?: number, callback?: any): void  
  setSelectedIndex (index: number, duration?: number, callback?: any): void  
}  

interface Mui {  
  /** 绑定 Dom 事件 */  
  on (event: string, selector: string, callback: any): void  
  /** 取消事件绑定 */  
  off (event?: string, selector?: string): void  
  /** 遍历 */  
  each (callback: any): void  
  /** 弹出菜单 */  
  popover (status: 'show' | 'hide' | 'toggle', anchor?: HTMLElement | Element): void  
  /** 聚焦 input */  
  input (): void  
  /** 初始化数字输入框 numbox */  
  numbox (): Numbox  
  /** 侧滑菜单 */  
  offCanvas (active?: 'show' | 'close' | 'toggle'): OffCanvas  
  /** 滚动条 */  
  progressbar (options?: any): Progressbar  
  /** 透明标题栏 */  
  transparent (options: any): void  
  /** 区域滚动 */  
  scroll(options?: {  
    scrollY?: boolean  
    scrollX?: boolean  
    startX?: number  
    startY?: number  
    indicators?: boolean  
    deceleration?: number  
    bounce?: boolean  
  }): Scroll  
  /** 初始化 switch */  
  ['switch'] (): void  
}  

interface Scroll {  
  scrollTo (xpos: number, ypos: number, duration?: number): void  
  scrollToBottom (duration?: number): void  
}  
interface Progressbar {  
  show (): void  
  hide (): void  
  setProgress(param: any): void  
}  

/** Mui Dom 选择器 */  
declare function mui (dom: string): Mui & Array<HTMLElement>  
继续阅读 »

mui.min.d.ts

interface Subpages {  
  url: string  
  id: string  
  styles?: object  
  extras?: object  
}  

interface PreloadPages extends Subpages {  
  subpages?: Array<Subpages>  
}  

interface PullRefreshBase {  
  height?: string | number  
  auto?: boolean  
  contentrefresh?: string  
  callback: any  
}  
interface PullRefreshUp extends PullRefreshBase {  
  contentnomore?: string  
}  
interface pullRefreshDown extends PullRefreshBase {  
  style: 'circle'  
  color?: string  
  range?: string | number  
  offset?: string | number  
  contentdown?: string  
  contentover?: string  
}  
interface PullRefresh {  
  container: string  
  down?: pullRefreshDown  
  up?: PullRefreshUp  
}  

interface OffCanvas {  
  /** 显示 */  
  show (): void  
  /** 隐藏 */  
  close (): void  
  /** 切换 */  
  toggle (): void  
  /** 判断是否为显示状态 */  
  isShown (direction?: string): boolean  
}  

interface Numbox {  
  /** 获取当前值 */  
  getValue (): string  
  /** 动态设置新值 */  
  setValue (val: number): void  
  /** 更新选项 */  
  setOption (option: 'min' | 'step' | 'max', val: number): void  
}  

declare namespace mui {  
  /** 初始化 */  
  function init (options?: {  
    wipeBack?: boolean  
    swipe?: boolean  
    subpages?: Array<Subpages>  
    preloadPages?: Array<PreloadPages>  
    pullRefresh?: PullRefresh  
    gestureConfig?: {  
      tap?: boolean  
      doubletap?: boolean  
      longtap?: boolean  
      hold?: boolean  
      release?: boolean  
      swipeleft?: boolean  
      swiperight?: boolean  
      swipeup?: boolean  
      swipedown?: boolean  
      dragstart?: boolean  
      drag?: boolean  
      dragend?: boolean  
    }  
    keyEventBind?: {  
      backbutton?: boolean  
      menubutton?: boolean  
    }  
    beforeback?: Function  
    statusBarBackground?: string  
    preloadLimit?: number  
  }): void  
  /** 打开新窗口 */  
  function openWindow (options: {  
    url: string  
    id: string  
    styles?: object  
    extras?: object  
    createNew?: boolean  
    show?: {  
      autoShow?: boolean  
      aniShow?: object  
      duration?: number  
    }  
    waiting?: {  
      autoShow?: boolean  
      title?: string  
      options?: object  
    }  
  }): void  
  /** 自动消失消息提示框 */  
  function toast (message: string, options?: {  
    duration?: number | 'long' | 'short'  
    type?: 'div'  
  }): void  
  /** 警告框 */  
  function alert (message: string, title?: string, btnValue?: string, callback?: any, type?: 'div'): void  
  /** 确认框 */  
  function confirm (message: string, title?: string, btnValue?: string, callback?: any, type?: 'div'): void  
  /** 输入对话框 */  
  function prompt (message: string, title?: string, btnValue?: string, callback?: any, type?: 'div'): void  
  /** 触发 Dom 事件 */  
  function trigger (element: HTMLElement, event: string, data?: any): void  
  /** 触发自定义事件 */  
  function fire (target: any, event: string, data?: any): void  
  /** 遍历 */  
  function each (obj: any, callback: any): void  
  /** 合并对象 */  
  function extend (deep: boolean, target: any, ...object: Array<any>): void  
  /** 封装 setTimeOut */  
  function later (func: any, delay: number, context?: any): void  
  /** 滚动窗口屏幕到指定位置 */  
  function scrollTo (ypos: number, duration: number, callback?: any): void  
  /** 判断当前运行环境 */  
  let os: object & os  
  interface os {  
    /** 是否在 5+ 环境内 */  
    plus: boolean | undefined  
    /** 是否是流应用 */  
    stream: boolean | undefined  
    /** 是否为 IOS */  
    ios: boolean | undefined  
    /** 是否 iphone */  
    iphone: boolean | undefined  
    /** 是否 ipad */  
    ipad: boolean | undefined  
    /** 是否 android */  
    android: boolean | undefined  
    /** 是否 android 的 chrome 环境 */  
    isBadAndroid: boolean | undefined  
    /** 版本号 */  
    version: string  
  }  
  /** plusReady */  
  function plusReady (callback: any): void  
  /** 关闭最后一次弹出的对话框(H5模式) */  
  function closePopup (): void  
  /** 关闭所有对话框(H5模式) */  
  function closePopups (): void  
  /** 轮播 */  
  function slider (options: any): void  
  /** 创建遮罩 */  
  function createMask (callback: any): Mask  
  interface Mask {  
    /** 显示遮罩 */  
    show (): void  
    /** 关闭遮罩 */  
    close (): void  
  }  
  /** 选择器组件 picker */  
  class PopPicker {  
    constructor (options: { layer?: number, buttons?: Array<any>})  
    setData (options: Array<any>): void  
    pickers: Array<Picker>  
    getSelectedItem (): Array<Picker>  
    show (callback: any): void  
    hide (): void  
    dispose (): void  
  }  
  /** 日期选择器 DtPicker */  
  class DtPicker {  
    constructor (options?: any)  
    getSelectedItems (): any  
    show (callback?: any): void  
    hide (): void  
    dispose (): void  
  }  
  /** 打开新页面 */  
  function open (...options: Array<any>): any  
  /** 当前页面 */  
  function currentWebview (...options: Array<any>): any  
  /** 关闭窗口 */  
  function back (...options: Array<any>): any  
  /** 重写返回逻辑 */  
  function backFunction (...options: Array<any>): any  
  /** 双击退出应用 */  
  function backDouble (...options: Array<any>): any  
  /** 双击进入后台 */  
  function backTast (...options: Array<any>): any  
  /** 预加载 */  
  function preload (...options: Array<any>): any  
  /** Ajax */  
  function ajax (url?: string, settings?: {  
    async?: boolean  
    crossDomain?: boolean  
    data?: any  
    dataType?: 'xml' | 'html' | 'script' | 'json' | 'text'  
    error?: any  
    success?: any  
    timeout?: number  
    type?: 'GET' | 'POST'  
    headers?: any  
    processData?: boolean  
  }): void  
  /** Ajax post */  
  function post (url: string, data?: any, success?: any, dataType?: 'xml' | 'html' | 'script' | 'json' | 'text'): void  
  function get (...options: Array<any>): any  
  function getJSON (...options: Array<any>): any  
}  

interface Picker {  
  setSelectedValue (value: string, duration?: number, callback?: any): void  
  setSelectedIndex (index: number, duration?: number, callback?: any): void  
}  

interface Mui {  
  /** 绑定 Dom 事件 */  
  on (event: string, selector: string, callback: any): void  
  /** 取消事件绑定 */  
  off (event?: string, selector?: string): void  
  /** 遍历 */  
  each (callback: any): void  
  /** 弹出菜单 */  
  popover (status: 'show' | 'hide' | 'toggle', anchor?: HTMLElement | Element): void  
  /** 聚焦 input */  
  input (): void  
  /** 初始化数字输入框 numbox */  
  numbox (): Numbox  
  /** 侧滑菜单 */  
  offCanvas (active?: 'show' | 'close' | 'toggle'): OffCanvas  
  /** 滚动条 */  
  progressbar (options?: any): Progressbar  
  /** 透明标题栏 */  
  transparent (options: any): void  
  /** 区域滚动 */  
  scroll(options?: {  
    scrollY?: boolean  
    scrollX?: boolean  
    startX?: number  
    startY?: number  
    indicators?: boolean  
    deceleration?: number  
    bounce?: boolean  
  }): Scroll  
  /** 初始化 switch */  
  ['switch'] (): void  
}  

interface Scroll {  
  scrollTo (xpos: number, ypos: number, duration?: number): void  
  scrollToBottom (duration?: number): void  
}  
interface Progressbar {  
  show (): void  
  hide (): void  
  setProgress(param: any): void  
}  

/** Mui Dom 选择器 */  
declare function mui (dom: string): Mui & Array<HTMLElement>  
收起阅读 »