HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

分享图片压缩上传demo,可以选择一张或多张图片也可以拍摄照片

图片裁剪 图片压缩

2016-08-05更新:
下方的代码是比较OLD的了,是通过js进行图片的剪切 旋转 再生成,效率较低。
后来又整合了一个利用native.js本地接口的压缩代码 ,链接在这
。页面中有详细的说明,需要的童鞋们可以参考以下。

代码整合了
1.多串君

  1. 伟子
    两个人的demo,
    其中resize原来的filereader在5 中更改为plus.io.FileReader()方不报错。
    如有错误,请不吝批评指正。

更新日志:
2015-05-09 1020450921@qq.com
1.修复了ios下无法获取图片宽高的问题:
ios下不在img.onload中是获取不到文件对象的。

  1. 新增了最大宽度(高度)的判别,按比例压缩。
    具体代码如下:

    
    <!DOCTYPE html>  
    <html>  
    
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title></title>  
        <link href="http://ask.dcloud.net.cn/../../../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            body {  
                background-color: #efeff4;  
            }  
            .mui-content {} .mui-content a {  
                color: #8F8F94;  
            }  
            .mui-content a.active {  
                color: #007aff;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .btn_1 {  
                position: absolute;  
                bottom: 100px;  
                left: 10px;  
                right: 10px;  
            }  
            .btn_2 {  
                position: absolute;  
                bottom: 20px;  
                left: 10px;  
                right: 10px;  
            }  
            .mui-btn-block {  
                width: 90%;  
                margin: 0 auto;  
            }  
            body {  
                overflow: hidden;  
            }  
            .showimg {  
                margin: 20px 10px auto 10px;  
                text-align: center;  
            }  
        </style>  
    </head>  
    
    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">上传身份证照片</h1>  
            <a class="mui-pull-right mui-icon mui-icon-upload" onclick="imgupgrade()"></a>  
        </header>  
        <!--  
    作者:1020450921@qq.com  
    时间:2015-04-24  
    描述:参考 http://ask.dcloud.net.cn/question/2089  
    -->  
        <div class="mui-content">  
            <div class="showimg">  
    
            </div>  
            <button type="button" class="mui-btn mui-btn-primary mui-btn-block  btn_1" onclick="galleryImgs()">从相册中选择图片</button>  
            <br>  
            <button type="button" class="mui-btn mui-btn-success mui-btn-block btn_2" onclick="cameraimages()">拍照</button>  
        </div>  
    </body>  
    <script src="../../../js/mui.min.js"></script>  
    <script src="../../../js/binaryajax.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/exif.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/canvasResize.js" type="text/javascript" charset="utf-8"></script>  
    
    <script type="text/javascript">  
        mui.init();  
        mui.plusReady(function() {})  
    
               //上传单张图片  
        function galleryImg() {  
            //每次拍摄或选择图片前清空数组对象  
            f1.splice(0, f1.length);  
            document.getElementsByClassName("showimg")[0].innerHTML = null;  
            // 从相册中选择图片  
            mui.toast("从相册中选择一张图片");  
            plus.gallery.pick(function(path) {  
                showImg(path);  
            }, function(e) {  
                mui.toast("取消选择图片");  
            }, {  
                filter: "image",  
                multiple: false  
            });  
        }  
    
        function galleryImgs() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                // 从相册中选择图片  
                mui.toast("从相册中选择不超过两张图片");  
                plus.gallery.pick(function(e) {  
                    if (e.files.length != 2) {  
                        mui.toast('请选择身份证正面和背面照片共两张');  
                        return false;  
                    }  
                    for (var i in e.files) {  
                        showImg(e.files[i]);  
                    }  
                }, function(e) {  
                    mui.toast("取消选择图片");  
                }, {  
                    filter: "image",  
                    multiple: true  
                });  
            }  
            // 拍照添加文件  
    
        function cameraimages() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                var cmr = plus.camera.getCamera();  
                cmr.captureImage(function(p) {  
                    plus.io.resolveLocalFileSystemURL(p, function(entry) {  
                        var localurl = entry.toLocalURL(); //把拍照的目录路径,变成本地url路径,例如file:///........之类的。  
                        showImg(localurl);  
                    });  
                }, function(e) {  
                    mui.toast("很抱歉,获取失败 "   e);  
                });  
            }  
            // 全局数组对象,添加文件,用于压缩上传使用  
        var f1 = new Array();  

function showImg(url) {
// 兼容以“file:”开头的情况
if (0 != url.toString().indexOf("file://")) {
url = "file://" + url;
}
var div = document.getElementsByClassName("showimg")[0];
var img = new Image();
img.src = url; // 传过来的图片路径在这里用。
img.onclick = function() {
plus.runtime.openFile(url);
};
img.onload = function() {
var tmph = img.height;
var tmpw = img.width;
var isHengTu = tmpw > tmph;
var max = Math.max(tmpw, tmph);
var min = Math.min(tmpw, tmph);
var bili = min / max;
if (max > 1200) {
max = 1200;
min = Math.floor(bili * max);
}
tmph = isHengTu ? min : max;
tmpw = isHengTu ? max : min;
img.style.border = "1px solid rgb(200,199,204)";
img.style.margin = "10px";
img.style.width = "150px";
img.style.height = "150px";
img.onload = null;
plus.io.resolveLocalFileSystemURL(url, function(entry) {
entry.file(function(file) {
console.log(file.size + '--' + file.name);
canvasResize(file, {
width: tmpw,
height: tmph,
crop: false,
quality: 50, //压缩质量
rotate: 0,
callback: function(data, width, height) {
f1.push(data);
img.src = data;
div.appendChild(img);
plus.nativeUI.closeWaiting();
}
});
});
},
function(e) {
plus.nativeUI.closeWaiting();
console.log(e.message);
});
};
};

    function imgupgrade() {  
            mui.toast('后台联调时启用上传功能');  
            return;  
            var wt = plus.nativeUI.showWaiting();  
            var url = '后台地址';  
            var dataType = 'json';  
            //发送数据    
            var data = {  
                files1: f1 //base64数据          
            };  
            mui.post(url, data, success, dataType);  
        }  
        //成功响应的回调函数  
    var success = function(response) {  
        plus.nativeUI.closeWaiting();  
        if (response != null) {  
            alert("上传成功");  
        }  
    }  
</script>  

</html>



伟子的js代码我放在了附件中。
继续阅读 »

2016-08-05更新:
下方的代码是比较OLD的了,是通过js进行图片的剪切 旋转 再生成,效率较低。
后来又整合了一个利用native.js本地接口的压缩代码 ,链接在这
。页面中有详细的说明,需要的童鞋们可以参考以下。

代码整合了
1.多串君

  1. 伟子
    两个人的demo,
    其中resize原来的filereader在5 中更改为plus.io.FileReader()方不报错。
    如有错误,请不吝批评指正。

更新日志:
2015-05-09 1020450921@qq.com
1.修复了ios下无法获取图片宽高的问题:
ios下不在img.onload中是获取不到文件对象的。

  1. 新增了最大宽度(高度)的判别,按比例压缩。
    具体代码如下:

    
    <!DOCTYPE html>  
    <html>  
    
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title></title>  
        <link href="http://ask.dcloud.net.cn/../../../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            body {  
                background-color: #efeff4;  
            }  
            .mui-content {} .mui-content a {  
                color: #8F8F94;  
            }  
            .mui-content a.active {  
                color: #007aff;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .btn_1 {  
                position: absolute;  
                bottom: 100px;  
                left: 10px;  
                right: 10px;  
            }  
            .btn_2 {  
                position: absolute;  
                bottom: 20px;  
                left: 10px;  
                right: 10px;  
            }  
            .mui-btn-block {  
                width: 90%;  
                margin: 0 auto;  
            }  
            body {  
                overflow: hidden;  
            }  
            .showimg {  
                margin: 20px 10px auto 10px;  
                text-align: center;  
            }  
        </style>  
    </head>  
    
    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">上传身份证照片</h1>  
            <a class="mui-pull-right mui-icon mui-icon-upload" onclick="imgupgrade()"></a>  
        </header>  
        <!--  
    作者:1020450921@qq.com  
    时间:2015-04-24  
    描述:参考 http://ask.dcloud.net.cn/question/2089  
    -->  
        <div class="mui-content">  
            <div class="showimg">  
    
            </div>  
            <button type="button" class="mui-btn mui-btn-primary mui-btn-block  btn_1" onclick="galleryImgs()">从相册中选择图片</button>  
            <br>  
            <button type="button" class="mui-btn mui-btn-success mui-btn-block btn_2" onclick="cameraimages()">拍照</button>  
        </div>  
    </body>  
    <script src="../../../js/mui.min.js"></script>  
    <script src="../../../js/binaryajax.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/exif.js" type="text/javascript" charset="utf-8"></script>  
    <script src="../../../js/canvasResize.js" type="text/javascript" charset="utf-8"></script>  
    
    <script type="text/javascript">  
        mui.init();  
        mui.plusReady(function() {})  
    
               //上传单张图片  
        function galleryImg() {  
            //每次拍摄或选择图片前清空数组对象  
            f1.splice(0, f1.length);  
            document.getElementsByClassName("showimg")[0].innerHTML = null;  
            // 从相册中选择图片  
            mui.toast("从相册中选择一张图片");  
            plus.gallery.pick(function(path) {  
                showImg(path);  
            }, function(e) {  
                mui.toast("取消选择图片");  
            }, {  
                filter: "image",  
                multiple: false  
            });  
        }  
    
        function galleryImgs() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                // 从相册中选择图片  
                mui.toast("从相册中选择不超过两张图片");  
                plus.gallery.pick(function(e) {  
                    if (e.files.length != 2) {  
                        mui.toast('请选择身份证正面和背面照片共两张');  
                        return false;  
                    }  
                    for (var i in e.files) {  
                        showImg(e.files[i]);  
                    }  
                }, function(e) {  
                    mui.toast("取消选择图片");  
                }, {  
                    filter: "image",  
                    multiple: true  
                });  
            }  
            // 拍照添加文件  
    
        function cameraimages() {  
                //每次拍摄或选择图片前清空数组对象  
                f1.splice(0, f1.length);  
                document.getElementsByClassName("showimg")[0].innerHTML = null;  
                var cmr = plus.camera.getCamera();  
                cmr.captureImage(function(p) {  
                    plus.io.resolveLocalFileSystemURL(p, function(entry) {  
                        var localurl = entry.toLocalURL(); //把拍照的目录路径,变成本地url路径,例如file:///........之类的。  
                        showImg(localurl);  
                    });  
                }, function(e) {  
                    mui.toast("很抱歉,获取失败 "   e);  
                });  
            }  
            // 全局数组对象,添加文件,用于压缩上传使用  
        var f1 = new Array();  

function showImg(url) {
// 兼容以“file:”开头的情况
if (0 != url.toString().indexOf("file://")) {
url = "file://" + url;
}
var div = document.getElementsByClassName("showimg")[0];
var img = new Image();
img.src = url; // 传过来的图片路径在这里用。
img.onclick = function() {
plus.runtime.openFile(url);
};
img.onload = function() {
var tmph = img.height;
var tmpw = img.width;
var isHengTu = tmpw > tmph;
var max = Math.max(tmpw, tmph);
var min = Math.min(tmpw, tmph);
var bili = min / max;
if (max > 1200) {
max = 1200;
min = Math.floor(bili * max);
}
tmph = isHengTu ? min : max;
tmpw = isHengTu ? max : min;
img.style.border = "1px solid rgb(200,199,204)";
img.style.margin = "10px";
img.style.width = "150px";
img.style.height = "150px";
img.onload = null;
plus.io.resolveLocalFileSystemURL(url, function(entry) {
entry.file(function(file) {
console.log(file.size + '--' + file.name);
canvasResize(file, {
width: tmpw,
height: tmph,
crop: false,
quality: 50, //压缩质量
rotate: 0,
callback: function(data, width, height) {
f1.push(data);
img.src = data;
div.appendChild(img);
plus.nativeUI.closeWaiting();
}
});
});
},
function(e) {
plus.nativeUI.closeWaiting();
console.log(e.message);
});
};
};

    function imgupgrade() {  
            mui.toast('后台联调时启用上传功能');  
            return;  
            var wt = plus.nativeUI.showWaiting();  
            var url = '后台地址';  
            var dataType = 'json';  
            //发送数据    
            var data = {  
                files1: f1 //base64数据          
            };  
            mui.post(url, data, success, dataType);  
        }  
        //成功响应的回调函数  
    var success = function(response) {  
        plus.nativeUI.closeWaiting();  
        if (response != null) {  
            alert("上传成功");  
        }  
    }  
</script>  

</html>



伟子的js代码我放在了附件中。
收起阅读 »

App资源在线差量升级更新

update wgt wgtu 升级 差量升级

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

5+应用可使用以下方式进行升级

本文重点介绍5+应用资源差量升级,相对App资源独立升级,因为只需要下载更新的资源文件,所以升级包体积更小,升级速度会更快。
注意:需HBuilder5.7.0以上版本才有此功能

生成移动App资源差量升级包

顾名思义,差量升级包是针对某个历史版本到新版本的差量,所以对于升级服务器来讲需要保留所有历史版本,并且分别生成每个历史版本到新版本的差量升级包。
目前HBuilder IDE并未提供版本控制系统,所以需要按照以下规则手动生成差量升级包:

  • 差量升级包文件格式为wgtu
    在HTML5+移动App支持的差量升级包文件后缀名必须是wgtu,它是标准的zip压缩文件,可以使用任何zip工具对wgtu目录进行压缩,并修改文件后缀名称为wgtu。
  • wgtu目录结构
名称 类型 说明
www 目录 保存需要更新的数据文件,其下的目录结构与应用的目录结构一致,其下必须包含manifest.json文件,否则认为更新文件非法,不执行升级操作
update.xml 文件 保存应用升级操作的配置信息,参考update.xml文件格式
  • update.xml文件格式(注意:文件名必须全部小写)
    update.xml为标准的xml文件格式,定义wgtu文件升级配置信息,其文件格式如下:
元素名称 父元素 类型 描述 必须
wgtu 根节点
appid wgtu 属性 升级应用的appid
basis wgtu 节点 基础应用信息
version basis 属性 基础应用的版本信息(旧版本的版本号
remove wgtu 节点 升级需要删除的文件/目录列表
item remove 节点 需要删除的项
path item 属性 需要删除的项的路径,相对于应用根目录,如“image/a.png”表示image目录下的a.png文件。如果是目录则以‘/’结尾,如“res/img/”表示res目录下的img目录,删除时包括下的所有内容。
  • wgtu示例
    应用升级前的版本1.0,其应用目录结构为:

    应用升级后的版本1.1,其应用目录结构为:

    差量升级包wgtu文件的目录结构为(不包括最外层wgtu目录):

    应用升级更新文件manifest.json、index.html,新增detail.html、image/new.png,删除image/icon5.png。
    其中update.xml中的内容为:
    <?xml version="1.0" encoding="utf-8" standalone="no"?>  
    <wgtu appid="TEST" >  
    <basis version="1.0" />  
    <remove>  
    <item path="image/icon5.png" />  
    </remove>  
    </wgtu>

应用中差量更新资源

下载wgtu文件并更新:

    var url='http://demo.dcloud.net.cn/helloh5/update/HelloH5.wgtu';  
    plus.nativeUI.showWaiting("升级中...");  
    var dtask = plus.downloader.createDownload( url, {method:"GET"}, function(d,status){  
        if ( status == 200 ) {   
            console.log( "Download wgtu success: " + d.filename );  
            plus.runtime.install(d.filename,{},function(){  
                plus.nativeUI.closeWaiting();  
                plus.nativeUI.alert("Update wgtu success, restart now!",function(){  
                    plus.runtime.restart();  
                });  
            },function(e){  
                plus.nativeUI.closeWaiting();  
                alert("Update wgtu failed: "+e.message);  
            });  
        } else {  
            plus.nativeUI.closeWaiting();  
             alert( "Download wgtu failed: " + status );   
        }   
    } );  
    dtask.addEventListener('statechanged',function(d,status){  
        console.log("statechanged: "+d.state);  
    });  
    dtask.start();

注意

  1. wgtu包中的update.xml文件名称必须全部小写,并且和www在同一级目录中
  2. wgtu包中www目录下必须包含manifest.json文件,并且里面不能包含注释(HBuilder中默认带注释,需要手动删除所有注释)
  3. update.xml中的appid值是应用的AppID(如“H5F6AE111”),不是程序的包名(如“io.dcloud.H5F6AE1111”)
  4. wgtu包必须是使用zip格式压缩的文件(不能使用如rar等其它压缩格式)

App store应用更新说明
应用资源更新肯定是违反apple政策的,但目前看起来它也不管。你在官网案例那里下载Appstore版本的那些app,大多启动后都会提示更新,反正也都没下架。如果你不是很大的公司,apple不会理睬你。如果你是大公司,建议不要做整体更新,每次更新几个页面,也不要提示更新后需要重启,这样会安全点。

感谢 @大裤子 反馈建议更新补充文档,wgtu更新经验分享:http://ask.dcloud.net.cn/question/6353

继续阅读 »

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

5+应用可使用以下方式进行升级

本文重点介绍5+应用资源差量升级,相对App资源独立升级,因为只需要下载更新的资源文件,所以升级包体积更小,升级速度会更快。
注意:需HBuilder5.7.0以上版本才有此功能

生成移动App资源差量升级包

顾名思义,差量升级包是针对某个历史版本到新版本的差量,所以对于升级服务器来讲需要保留所有历史版本,并且分别生成每个历史版本到新版本的差量升级包。
目前HBuilder IDE并未提供版本控制系统,所以需要按照以下规则手动生成差量升级包:

  • 差量升级包文件格式为wgtu
    在HTML5+移动App支持的差量升级包文件后缀名必须是wgtu,它是标准的zip压缩文件,可以使用任何zip工具对wgtu目录进行压缩,并修改文件后缀名称为wgtu。
  • wgtu目录结构
名称 类型 说明
www 目录 保存需要更新的数据文件,其下的目录结构与应用的目录结构一致,其下必须包含manifest.json文件,否则认为更新文件非法,不执行升级操作
update.xml 文件 保存应用升级操作的配置信息,参考update.xml文件格式
  • update.xml文件格式(注意:文件名必须全部小写)
    update.xml为标准的xml文件格式,定义wgtu文件升级配置信息,其文件格式如下:
元素名称 父元素 类型 描述 必须
wgtu 根节点
appid wgtu 属性 升级应用的appid
basis wgtu 节点 基础应用信息
version basis 属性 基础应用的版本信息(旧版本的版本号
remove wgtu 节点 升级需要删除的文件/目录列表
item remove 节点 需要删除的项
path item 属性 需要删除的项的路径,相对于应用根目录,如“image/a.png”表示image目录下的a.png文件。如果是目录则以‘/’结尾,如“res/img/”表示res目录下的img目录,删除时包括下的所有内容。
  • wgtu示例
    应用升级前的版本1.0,其应用目录结构为:

    应用升级后的版本1.1,其应用目录结构为:

    差量升级包wgtu文件的目录结构为(不包括最外层wgtu目录):

    应用升级更新文件manifest.json、index.html,新增detail.html、image/new.png,删除image/icon5.png。
    其中update.xml中的内容为:
    <?xml version="1.0" encoding="utf-8" standalone="no"?>  
    <wgtu appid="TEST" >  
    <basis version="1.0" />  
    <remove>  
    <item path="image/icon5.png" />  
    </remove>  
    </wgtu>

应用中差量更新资源

下载wgtu文件并更新:

    var url='http://demo.dcloud.net.cn/helloh5/update/HelloH5.wgtu';  
    plus.nativeUI.showWaiting("升级中...");  
    var dtask = plus.downloader.createDownload( url, {method:"GET"}, function(d,status){  
        if ( status == 200 ) {   
            console.log( "Download wgtu success: " + d.filename );  
            plus.runtime.install(d.filename,{},function(){  
                plus.nativeUI.closeWaiting();  
                plus.nativeUI.alert("Update wgtu success, restart now!",function(){  
                    plus.runtime.restart();  
                });  
            },function(e){  
                plus.nativeUI.closeWaiting();  
                alert("Update wgtu failed: "+e.message);  
            });  
        } else {  
            plus.nativeUI.closeWaiting();  
             alert( "Download wgtu failed: " + status );   
        }   
    } );  
    dtask.addEventListener('statechanged',function(d,status){  
        console.log("statechanged: "+d.state);  
    });  
    dtask.start();

注意

  1. wgtu包中的update.xml文件名称必须全部小写,并且和www在同一级目录中
  2. wgtu包中www目录下必须包含manifest.json文件,并且里面不能包含注释(HBuilder中默认带注释,需要手动删除所有注释)
  3. update.xml中的appid值是应用的AppID(如“H5F6AE111”),不是程序的包名(如“io.dcloud.H5F6AE1111”)
  4. wgtu包必须是使用zip格式压缩的文件(不能使用如rar等其它压缩格式)

App store应用更新说明
应用资源更新肯定是违反apple政策的,但目前看起来它也不管。你在官网案例那里下载Appstore版本的那些app,大多启动后都会提示更新,反正也都没下架。如果你不是很大的公司,apple不会理睬你。如果你是大公司,建议不要做整体更新,每次更新几个页面,也不要提示更新后需要重启,这样会安全点。

感谢 @大裤子 反馈建议更新补充文档,wgtu更新经验分享:http://ask.dcloud.net.cn/question/6353

收起阅读 »

segment分段选择页面根据传入的querystring 激活相应的分段

具体需求描述是这样的:父页面有两个链接需要导向segment页并根据传入的querystring激活不同的段落。
使用原始的url带参的方式,没有使用自定义扩展参数的方式,不过建议使用自定义扩展参数的方式。

mui.openwindow({  
     extras:{  
      'x_id':'10'//自定义扩展参数,可以用来处理页面间传值  
    }  
})

在B页面这样去获取

var self=plus.webview.currentWebview();  
var id=self.x_id;

具体的实现代码如下:
父页面html,打开链接依然使用mui.openwindow.

<ul>  
                    <li><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item1mobile" style="color:rgb(0, 0, 0) ;">商家XX</a></li>  
                    <li>20</li>  
                </ul>  
                <ul>  
                    <li ><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item2mobile" style="color:rgb(0, 0, 0) ;">个人XX</a></li>  
                    <li>10</li>  
                </ul>

子页面:

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title>我的XX</title>  
        <link href="http://ask.dcloud.net.cn/../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            .mui-segmented-control .mui-control-item {  
                line-height: 28px;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .gray_color {  
                color: #8f8f94;  
                font-size: 0.8em;  
            }  
        </style>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我的XX</h1>  
        </header>  
        <div class="mui-content">  
            <div class="mui-content-padded tab_title" style="margin: 0.65em 3em;">  
                <div id="segmentedControl" class="mui-segmented-control">  
                    <a class="mui-control-item mui-active" href="#item1mobile">  

                        商家XX  
                    </a>  
                    <a class="mui-control-item" href="#item2mobile">  

                        个人XX  

                    </a>  
                </div>  
            </div>  

            <div id="item1mobile" class="mui-control-content mui-active">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月19日XXXXX<span class='mui-pull-right gray_color'>3月18号</span>  
                                <p class='mui-ellipsis'>您在日历XXX中设定的XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月15日XXX<span class='mui-pull-right gray_color'>3月14号</span>  
                                <p class='mui-ellipsis'>您在XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月11日XXX<span class='mui-pull-right gray_color'>3月10号</span>  
                                <p class='mui-ellipsis'>您在日历XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
            <div id="item2mobile" class="mui-control-content">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月12号</span>  
                                <p class='mui-ellipsis'>xxxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月10号</span>  
                                <p class='mui-ellipsis'>xxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
        </div>  
        <script src="../js/mui.min.js"></script>  
        <script type="text/javascript" charset="utf-8">  
            mui.init();  
            mui.plusReady(function() {  
                               //我使用plus,有时候会报undefined错误  
                console.log(plus.webview.currentWebview().id)  
            })  
             mui.ready(function() {  
                var urlx = this.document.URL.toString();  
                var modelx = "item1mobile";  
                if (urlx.lastIndexOf('?') != -1) {  
                    modelx = urlx.substr(urlx.lastIndexOf('?')   1, urlx.length).split('=')[1];  
                }  
                var ar = document.getElementById("segmentedControl").querySelectorAll(".mui-control-item");  
                var reg = new RegExp('(\\s|^)'   'mui-active'   '(\\s|$)');  
                var item1 = document.getElementById("item1mobile");  
                var item2 = document.getElementById("item2mobile");  
                //重置  
                ar[0].className = ar[0].className.replace(reg, '');  
                ar[1].className = ar[1].className.replace(reg, '');  
                item1.className = item1.className.replace(reg, '');  
                item1.className = item2.className.replace(reg, '');  
                if (modelx == "item1mobile") {  
                    ar[0].className  = " mui-active";  
                    ar[1].className = ar[1].className.replace(reg, '');  
                    item1.className  = " mui-active";  
                    item2.className = item2.className.replace(reg, '');  
                } else {  
                    ar[0].className = ar[0].className.replace(reg, '');  
                    ar[1].className  = " mui-active";  
                    item2.className  = " mui-active";  
                    item1.className = item1.className.replace(reg, '');  
                }  
            });  
        </script>  
    </body>  

</html>
继续阅读 »

具体需求描述是这样的:父页面有两个链接需要导向segment页并根据传入的querystring激活不同的段落。
使用原始的url带参的方式,没有使用自定义扩展参数的方式,不过建议使用自定义扩展参数的方式。

mui.openwindow({  
     extras:{  
      'x_id':'10'//自定义扩展参数,可以用来处理页面间传值  
    }  
})

在B页面这样去获取

var self=plus.webview.currentWebview();  
var id=self.x_id;

具体的实现代码如下:
父页面html,打开链接依然使用mui.openwindow.

<ul>  
                    <li><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item1mobile" style="color:rgb(0, 0, 0) ;">商家XX</a></li>  
                    <li>20</li>  
                </ul>  
                <ul>  
                    <li ><a class="mui-inline" href="http://ask.dcloud.net.cn/myxx.html?model=item2mobile" style="color:rgb(0, 0, 0) ;">个人XX</a></li>  
                    <li>10</li>  
                </ul>

子页面:

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title>我的XX</title>  
        <link href="http://ask.dcloud.net.cn/../css/mui.min.css" rel="stylesheet" />  
        <style type="text/css">  
            .mui-segmented-control .mui-control-item {  
                line-height: 28px;  
            }  
            .mui-title {  
                font-family: simhei;  
            }  
            .gray_color {  
                color: #8f8f94;  
                font-size: 0.8em;  
            }  
        </style>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">我的XX</h1>  
        </header>  
        <div class="mui-content">  
            <div class="mui-content-padded tab_title" style="margin: 0.65em 3em;">  
                <div id="segmentedControl" class="mui-segmented-control">  
                    <a class="mui-control-item mui-active" href="#item1mobile">  

                        商家XX  
                    </a>  
                    <a class="mui-control-item" href="#item2mobile">  

                        个人XX  

                    </a>  
                </div>  
            </div>  

            <div id="item1mobile" class="mui-control-content mui-active">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月19日XXXXX<span class='mui-pull-right gray_color'>3月18号</span>  
                                <p class='mui-ellipsis'>您在日历XXX中设定的XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月15日XXX<span class='mui-pull-right gray_color'>3月14号</span>  
                                <p class='mui-ellipsis'>您在XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                3月11日XXX<span class='mui-pull-right gray_color'>3月10号</span>  
                                <p class='mui-ellipsis'>您在日历XXX即将到来</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
            <div id="item2mobile" class="mui-control-content">  
                <ul class="mui-table-view">  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月12号</span>  
                                <p class='mui-ellipsis'>xxxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                    <li class="mui-table-view-cell mui-media">  
                        <a href="javascript:void(0);">  
                            <img class="mui-media-object mui-pull-left" src="../images/content.jpg">  
                            <div class="mui-media-body">  
                                订单消息<span class='mui-pull-right gray_color'>4月10号</span>  
                                <p class='mui-ellipsis'>xxxxxxx</p>  
                            </div>  
                        </a>  
                    </li>  
                </ul>  
            </div>  
        </div>  
        <script src="../js/mui.min.js"></script>  
        <script type="text/javascript" charset="utf-8">  
            mui.init();  
            mui.plusReady(function() {  
                               //我使用plus,有时候会报undefined错误  
                console.log(plus.webview.currentWebview().id)  
            })  
             mui.ready(function() {  
                var urlx = this.document.URL.toString();  
                var modelx = "item1mobile";  
                if (urlx.lastIndexOf('?') != -1) {  
                    modelx = urlx.substr(urlx.lastIndexOf('?')   1, urlx.length).split('=')[1];  
                }  
                var ar = document.getElementById("segmentedControl").querySelectorAll(".mui-control-item");  
                var reg = new RegExp('(\\s|^)'   'mui-active'   '(\\s|$)');  
                var item1 = document.getElementById("item1mobile");  
                var item2 = document.getElementById("item2mobile");  
                //重置  
                ar[0].className = ar[0].className.replace(reg, '');  
                ar[1].className = ar[1].className.replace(reg, '');  
                item1.className = item1.className.replace(reg, '');  
                item1.className = item2.className.replace(reg, '');  
                if (modelx == "item1mobile") {  
                    ar[0].className  = " mui-active";  
                    ar[1].className = ar[1].className.replace(reg, '');  
                    item1.className  = " mui-active";  
                    item2.className = item2.className.replace(reg, '');  
                } else {  
                    ar[0].className = ar[0].className.replace(reg, '');  
                    ar[1].className  = " mui-active";  
                    item2.className  = " mui-active";  
                    item1.className = item1.className.replace(reg, '');  
                }  
            });  
        </script>  
    </body>  

</html>
收起阅读 »

C#配置支付宝信息

支付宝 Payment

参考支付宝官方的demo配置的方法,亲测可行.

        public object GetPayInfo(string _amount)//_amount:付款金额  
        {  
            string orderInfo = OrderInfo(_amount);  
            // 对订单做RSA 签名  
            string sign = RSAFromPkcs8.sign(orderInfo, Config.Private_key, Config.Input_charset); //支付宝提供的Config.cs  
            //仅需对sign做URL编码  
            sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);  
            string payInfo = orderInfo + "&sign=\"" + sign + "\"&"  
                + getSignType();  
            return payInfo;  
        }  
        public string OrderInfo(string price)  
        {              
            Dictionary<string, string> payinfo = new Dictionary<string, string>();  
            payinfo.Add("service", "\"mobile.securitypay.pay\"");  
            payinfo.Add("partner", "\"" + Config.Partner + "\"");  
            payinfo.Add("seller_id", "\"" + Config.SELLER + "\"");  
            payinfo.Add("out_trade_no", "\"" + DateTime.Now.ToString("yyyyMMddHHmmssfff")+ "\"");  
            payinfo.Add("subject", "\"****\"");  
            payinfo.Add("body", "\"*****\"");  
            payinfo.Add("total_fee", "\"" + price.ToString() + "\"");  
            payinfo.Add("notify_url", "\"***********"");  
            payinfo.Add("payment_type", "\"1\"");  
            payinfo.Add("_input_charset", "\"UTF-8\"");  
            payinfo.Add("it_b_pay", "\"30m\"");              
            return Core.CreateLinkString(payinfo);  
        }  
         public String getSignType()  
        {  
            return "sign_type=\"RSA\"";  
        }

HB中的方法

                   function topay(rate) {  
                if (w) {  
                    return;  
                }  
                //检查是否请求订单中  
                w = plus.nativeUI.showWaiting();  
                mui.getJSON("http://************/api/Pay", {  
                    _amount: rate  
                }, function(data) {  
                    w.close();  
                    w = null;  
                    if (data) {  
                        plus.payment.request(pays["alipay"], data, function(result) {                             
                                        plus.nativeUI.alert("支付成功:感谢您的支持。", function() {  
                                            mui.back();  
                                        }, "****");                                   
                        }, function(error) {  
                            if (error.code == 62001) {  
                                mui.toast("您放弃支付.");  
                                mui.back();  
                            } else {  
                                plus.nativeUI.alert("支付失败", null, "请前往会员中心再次对该订单尝试支付,支付失败:" + error.code);  
                            }  
                        });  
                    }  
                });  
            }
继续阅读 »

参考支付宝官方的demo配置的方法,亲测可行.

        public object GetPayInfo(string _amount)//_amount:付款金额  
        {  
            string orderInfo = OrderInfo(_amount);  
            // 对订单做RSA 签名  
            string sign = RSAFromPkcs8.sign(orderInfo, Config.Private_key, Config.Input_charset); //支付宝提供的Config.cs  
            //仅需对sign做URL编码  
            sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);  
            string payInfo = orderInfo + "&sign=\"" + sign + "\"&"  
                + getSignType();  
            return payInfo;  
        }  
        public string OrderInfo(string price)  
        {              
            Dictionary<string, string> payinfo = new Dictionary<string, string>();  
            payinfo.Add("service", "\"mobile.securitypay.pay\"");  
            payinfo.Add("partner", "\"" + Config.Partner + "\"");  
            payinfo.Add("seller_id", "\"" + Config.SELLER + "\"");  
            payinfo.Add("out_trade_no", "\"" + DateTime.Now.ToString("yyyyMMddHHmmssfff")+ "\"");  
            payinfo.Add("subject", "\"****\"");  
            payinfo.Add("body", "\"*****\"");  
            payinfo.Add("total_fee", "\"" + price.ToString() + "\"");  
            payinfo.Add("notify_url", "\"***********"");  
            payinfo.Add("payment_type", "\"1\"");  
            payinfo.Add("_input_charset", "\"UTF-8\"");  
            payinfo.Add("it_b_pay", "\"30m\"");              
            return Core.CreateLinkString(payinfo);  
        }  
         public String getSignType()  
        {  
            return "sign_type=\"RSA\"";  
        }

HB中的方法

                   function topay(rate) {  
                if (w) {  
                    return;  
                }  
                //检查是否请求订单中  
                w = plus.nativeUI.showWaiting();  
                mui.getJSON("http://************/api/Pay", {  
                    _amount: rate  
                }, function(data) {  
                    w.close();  
                    w = null;  
                    if (data) {  
                        plus.payment.request(pays["alipay"], data, function(result) {                             
                                        plus.nativeUI.alert("支付成功:感谢您的支持。", function() {  
                                            mui.back();  
                                        }, "****");                                   
                        }, function(error) {  
                            if (error.code == 62001) {  
                                mui.toast("您放弃支付.");  
                                mui.back();  
                            } else {  
                                plus.nativeUI.alert("支付失败", null, "请前往会员中心再次对该订单尝试支付,支付失败:" + error.code);  
                            }  
                        });  
                    }  
                });  
            }
收起阅读 »

这几天对HBuilder使用的感想

mui

HBuilder,刚推出来过后不久,我就开始尝鲜,使用的时候让我感觉很是惊艳,自动提示的让我很满足,什么link 、script、meta标签,刷刷就出来了,这个必须点赞。后来继续尝试WEB 、APP开发,发现能上传自动帮我打包,感觉很轻松,因为我尝试过配置Phongap的环境,一个android,就有些麻烦,更何况还有IOS的。
接着,我用HBuilder来编写PC端的网页,也把这个工具推荐给了我同事们使用,大家都觉得挺好的。
过了一段时间,今年,领导让给写一个APP,我说用原生来开发吧(我这时已经会Android开发了),领导say no,用HTML 5开发,要兼容IOS、Android、WP、微信(微信是一个超级APP,可以算是一个小平台了),另外我给你安排一个专家,帮你做好微信支付、二维码扫描等等功能,我说不用,我知道一个工具,并集成了UI、还有很多开发好的功能,领导很满意,让我先开发一个DEMO出来。
清明节3天假,我第一天就写的差不多了,迅速写好了DEMO大部分工作,也在我的IOS手机上调试完毕,完成度大概80%,遗留了一些优化的工作(不要小瞧这部分),很顺利,很开心。于是在我的Android(小米2s)机器上,我第二天继续调试,我擦!,怎么各种各样的情况出现了,首先领导很重视的二维码扫描功能,我在这部Android机上调了半天,硬是识别不出来,后来是我手不小心抖了下,才发现只有小抖几下才识别的出来。这个我在问答部分也提了,得到官方回复,他们的小米2上是正常的,我不知道会不会与“S”有关系,在二维码扫描方面,扫描速度还是有点不行~~
接着我继续界面的跳转,我比较郁闷,原生Anroid有singleTop、singleTask模式的,我就想让webview重复利用下,例如 首页,我肯定不想让它重复打开吧。查下API,找到了plus.webview.show 方法,不行,IOS的显示不出来,找到了webview.show方法(我想这个两个方法实现逻辑是一样的),结果IOS都无法显示出来,Android到挺正常的,继续查openClosed,IOS的还是不好使,偶那个神哪!在问答区,我发出了提问,官方回复使用zindex,OK,果然能显示出来!但是接着我再首页打开新的页面,mygod,出了个圈圈,就不见踪影,明白了,新打开的界面没有继承打开者zindex属性,得,我只好重新下openWindow方法,将currentWebview,的zindex赋值给要打开的页面,结果还是不行,我想明白了我要取的是topWebview的zindex, 只好查API,没有获取topWebView的方法,得,将show()方法也重写下,并自己记录下topWebview,IOS正常了,达到我要的效果了。
在Android上来试试,Y的,Android倒不干了,使用webview.getStyle.zindex,发现,额,程序刚开始运行的时候得到undifined,而IOS得到的是0。算了算了,给重写的函数加两个判断分支,如果是android,执行原来的方法。呼,脑子终于清静了。
四天的时间,其实还算是挺快的。但是挺有点烦人的。webview的show方法,官方说下一版修复。恩,我也觉得应该将这方面的栈结果顺序维护好。我使用过plus.webview.all()函数,好像如果用show方法,在被show的之后的webview 貌似没被清空。
时间紧迫,我还没对这些缺陷做准确的验证,再提出来。
因此,希望官方提供一个专门的缺陷报告填写页过来,问答的方式 不是感觉很严谨呵。另外,mui的js源码也阅读了下,额,API不够用,当然可以自己写,但是官方的理解肯定深刻一些。5+部分的可否给一些原理图神马的。HB整体的发展路线图、蓝图,也能给一个,让大家也讨论下,让小伙伴们都参加进来,给予意见吧。
我还是挺喜欢HB的,但是这些或多或少的问题能得到解决。

继续阅读 »

HBuilder,刚推出来过后不久,我就开始尝鲜,使用的时候让我感觉很是惊艳,自动提示的让我很满足,什么link 、script、meta标签,刷刷就出来了,这个必须点赞。后来继续尝试WEB 、APP开发,发现能上传自动帮我打包,感觉很轻松,因为我尝试过配置Phongap的环境,一个android,就有些麻烦,更何况还有IOS的。
接着,我用HBuilder来编写PC端的网页,也把这个工具推荐给了我同事们使用,大家都觉得挺好的。
过了一段时间,今年,领导让给写一个APP,我说用原生来开发吧(我这时已经会Android开发了),领导say no,用HTML 5开发,要兼容IOS、Android、WP、微信(微信是一个超级APP,可以算是一个小平台了),另外我给你安排一个专家,帮你做好微信支付、二维码扫描等等功能,我说不用,我知道一个工具,并集成了UI、还有很多开发好的功能,领导很满意,让我先开发一个DEMO出来。
清明节3天假,我第一天就写的差不多了,迅速写好了DEMO大部分工作,也在我的IOS手机上调试完毕,完成度大概80%,遗留了一些优化的工作(不要小瞧这部分),很顺利,很开心。于是在我的Android(小米2s)机器上,我第二天继续调试,我擦!,怎么各种各样的情况出现了,首先领导很重视的二维码扫描功能,我在这部Android机上调了半天,硬是识别不出来,后来是我手不小心抖了下,才发现只有小抖几下才识别的出来。这个我在问答部分也提了,得到官方回复,他们的小米2上是正常的,我不知道会不会与“S”有关系,在二维码扫描方面,扫描速度还是有点不行~~
接着我继续界面的跳转,我比较郁闷,原生Anroid有singleTop、singleTask模式的,我就想让webview重复利用下,例如 首页,我肯定不想让它重复打开吧。查下API,找到了plus.webview.show 方法,不行,IOS的显示不出来,找到了webview.show方法(我想这个两个方法实现逻辑是一样的),结果IOS都无法显示出来,Android到挺正常的,继续查openClosed,IOS的还是不好使,偶那个神哪!在问答区,我发出了提问,官方回复使用zindex,OK,果然能显示出来!但是接着我再首页打开新的页面,mygod,出了个圈圈,就不见踪影,明白了,新打开的界面没有继承打开者zindex属性,得,我只好重新下openWindow方法,将currentWebview,的zindex赋值给要打开的页面,结果还是不行,我想明白了我要取的是topWebview的zindex, 只好查API,没有获取topWebView的方法,得,将show()方法也重写下,并自己记录下topWebview,IOS正常了,达到我要的效果了。
在Android上来试试,Y的,Android倒不干了,使用webview.getStyle.zindex,发现,额,程序刚开始运行的时候得到undifined,而IOS得到的是0。算了算了,给重写的函数加两个判断分支,如果是android,执行原来的方法。呼,脑子终于清静了。
四天的时间,其实还算是挺快的。但是挺有点烦人的。webview的show方法,官方说下一版修复。恩,我也觉得应该将这方面的栈结果顺序维护好。我使用过plus.webview.all()函数,好像如果用show方法,在被show的之后的webview 貌似没被清空。
时间紧迫,我还没对这些缺陷做准确的验证,再提出来。
因此,希望官方提供一个专门的缺陷报告填写页过来,问答的方式 不是感觉很严谨呵。另外,mui的js源码也阅读了下,额,API不够用,当然可以自己写,但是官方的理解肯定深刻一些。5+部分的可否给一些原理图神马的。HB整体的发展路线图、蓝图,也能给一个,让大家也讨论下,让小伙伴们都参加进来,给予意见吧。
我还是挺喜欢HB的,但是这些或多或少的问题能得到解决。

收起阅读 »

分享一个利用融云开发伪IM的思路

IM 融云

在移动互联时代,IM基本都是必备品了,可惜的是DCloud在短期内貌似还没有加入IM系统的可能,最近研究了一下三方的IM模块,在这里和大家分享一个利用融云Web达成伪IM的思路。

1、为什么选择Web版本?
融云在Web版本外还提供iOS和Android版本的SDK,但是需要一定的原生开发能力来进行支持,虽然都是封装好的API接口,开发起来不是很难,但是在我的系统里,暂时是不需要除了文字外的其余多余的功能,为了一些莫名的功能,耗费时间和精力去研究SDK,实在是有些得不偿失,等以后有时间或者需求的时候再去研究,所以需要添加很多IM组件的小伙伴可以绕道了。

2、Web版本的优缺点?
优点:集成简单,只需要简单的几步,就可以集成一套IM系统在APP中。
(1)引入Web版本SDK

http://res.websdk.rong.io/RongIMClient{-版本号}-min.js

(2)初始化web sdk

RongIMClient.init("appkey");

(3)设置链接状态监听器

RongIMClient.setConnectionStatusListener({    
     onChanged: function (status) {}    
}); 

(4)链接融云服务器

RongIMClient.connect("token", {  
     onSuccess: function (userid) { },  
     onError: function (x) { }  
});

(5)设置消息监听器

RongIMClient.getInstance().setOnReceiveMessageListener({  
     onReceived: function (message) { }  
});

(6)得到RongIMClient实例对象,设置私人会话类型

var ins = RongIMClient.getInstance();  
var contype = RongIMClient.ConversationType.PRIVATE;

(7)发送

ins.sendMessage(contype, "targetId", RongIMClient.TextMessage.obtain("发送消息内容"), null, {  
       onSuccess: function () { },  
       onError: function (data) { }  
 });

缺点:无法PUSH,这个实在要吐槽一下,其实融云支持PUSH,但是我和他们交涉良久,他们总是认为Web版本不需要PUSH这样的东西,其实就是简单的在他们服务器上注册用户的deviceToken,就为了注册个deviceToken,就要分别集成iOS和Android两套SDK,实在是麻烦。我让他们加,未果;让他们把SDK再底层的API开放给我,我自己增加注册,也是未果。

3、为什么是伪IM?
这个和无法PUSH是相关的,因为无法PUSH,所以只有用户在打开APP的情况下才能收到对方发送的消息,无法忍受,那么如何解决呢?
方案如下:
(1)在用户退出APP,或者pause的时候,主动断开和融云服务器的连接。
(2)每次发出IM消息的时候,消息要通知到自己的服务器,自己的服务器去融云的服务器获取用户在线状态
(a)如果用户在线,不处理
(b)如果用户不在线,利用个推的PUSH提醒用户打开APP
(3)用户进入APP的时候,重连融云服务器,接受消息。

这样,就可以做到一个简单的IM系统了。服务器端就不在这里详细描述啦^_^

PS1:融云的Web版本其实不止是文字,还有其余的一些,譬如客服,图片等等,集成起来还是比较简单的,当然,如果你的需求比较高,那么还是集成iOS或者Android版本的SDK要更好些。
PS2:还有一些三方IM,我没有仔细的研究过,但是应该大同小异。

继续阅读 »

在移动互联时代,IM基本都是必备品了,可惜的是DCloud在短期内貌似还没有加入IM系统的可能,最近研究了一下三方的IM模块,在这里和大家分享一个利用融云Web达成伪IM的思路。

1、为什么选择Web版本?
融云在Web版本外还提供iOS和Android版本的SDK,但是需要一定的原生开发能力来进行支持,虽然都是封装好的API接口,开发起来不是很难,但是在我的系统里,暂时是不需要除了文字外的其余多余的功能,为了一些莫名的功能,耗费时间和精力去研究SDK,实在是有些得不偿失,等以后有时间或者需求的时候再去研究,所以需要添加很多IM组件的小伙伴可以绕道了。

2、Web版本的优缺点?
优点:集成简单,只需要简单的几步,就可以集成一套IM系统在APP中。
(1)引入Web版本SDK

http://res.websdk.rong.io/RongIMClient{-版本号}-min.js

(2)初始化web sdk

RongIMClient.init("appkey");

(3)设置链接状态监听器

RongIMClient.setConnectionStatusListener({    
     onChanged: function (status) {}    
}); 

(4)链接融云服务器

RongIMClient.connect("token", {  
     onSuccess: function (userid) { },  
     onError: function (x) { }  
});

(5)设置消息监听器

RongIMClient.getInstance().setOnReceiveMessageListener({  
     onReceived: function (message) { }  
});

(6)得到RongIMClient实例对象,设置私人会话类型

var ins = RongIMClient.getInstance();  
var contype = RongIMClient.ConversationType.PRIVATE;

(7)发送

ins.sendMessage(contype, "targetId", RongIMClient.TextMessage.obtain("发送消息内容"), null, {  
       onSuccess: function () { },  
       onError: function (data) { }  
 });

缺点:无法PUSH,这个实在要吐槽一下,其实融云支持PUSH,但是我和他们交涉良久,他们总是认为Web版本不需要PUSH这样的东西,其实就是简单的在他们服务器上注册用户的deviceToken,就为了注册个deviceToken,就要分别集成iOS和Android两套SDK,实在是麻烦。我让他们加,未果;让他们把SDK再底层的API开放给我,我自己增加注册,也是未果。

3、为什么是伪IM?
这个和无法PUSH是相关的,因为无法PUSH,所以只有用户在打开APP的情况下才能收到对方发送的消息,无法忍受,那么如何解决呢?
方案如下:
(1)在用户退出APP,或者pause的时候,主动断开和融云服务器的连接。
(2)每次发出IM消息的时候,消息要通知到自己的服务器,自己的服务器去融云的服务器获取用户在线状态
(a)如果用户在线,不处理
(b)如果用户不在线,利用个推的PUSH提醒用户打开APP
(3)用户进入APP的时候,重连融云服务器,接受消息。

这样,就可以做到一个简单的IM系统了。服务器端就不在这里详细描述啦^_^

PS1:融云的Web版本其实不止是文字,还有其余的一些,譬如客服,图片等等,集成起来还是比较简单的,当然,如果你的需求比较高,那么还是集成iOS或者Android版本的SDK要更好些。
PS2:还有一些三方IM,我没有仔细的研究过,但是应该大同小异。

收起阅读 »

点击小图浏览大图,双击缩放,赶快拿走

功能实现:
点击小图浏览大图,双击放大,滑动切换,上滑或下滑关闭,双指放大或缩小(到了最小是自动关闭),预加载缓冲。
调用的第三方插件,好处就是没有借用jquery,而且滑动,缩放效果超流畅
最底下有效果图和所需文件下载
1,引入CSS及JS

<link rel="stylesheet prefetch" href="../plugin/touchPhotos/photoswipe.css">  
<link rel="stylesheet prefetch" href="../plugin/touchPhotos/default-skin.css">  
<script src="../plugin/touchPhotos/stopExecutionOnTimeout.js"></script>  
<script src="../plugin/touchPhotos/photoswipe.min.js"></script>  
<script src="../plugin/touchPhotos/photoswipe-ui-default.min.js"></script>

2,小图片容器(这里要获取原图地址及图片尺寸),我是通过JSON来获取的,就写个JSON的写法了
//容器 my-simple-gallery,获取图片的宽度和高度

data += '<div class="my-simple-gallery">';  
for(var k=0; k<data.photos.length; k++){  
    data += '<figure itemprop="associatedMedia" itemscope=""><a href="'+data.photos[k].big+'" data-size="'+data.photos[k].size+'"><img src="'+data.photos[k].small+'" ></a></figure>';  
}  
data += '</div>';

自己讲data插入到ID里面了哈
然后就指定浏览图片的那个容器了
initPhotoSwipeFromDOM('.my-simple-gallery');

3.下面的代码写的有点恶心,直接拿去放到模板里面

<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true" style="">
<div class="pswpbg"></div>
<div class="pswp
scroll-wrap">
<div class="pswpcontainer" style="transform: translate3d(0px, 0px, 0px);">
<div class="pswp
item" style="display: block; transform: translate3d(-1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(288px, 56px, 0px) scale(1);"></div></div>
<div class="pswp
item" style="transform: translate3d(0px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(473px, 66.8125px, 0px) scale(0.146484375);"></div></div>
<div class="pswp
item" style="display: block; transform: translate3d(1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(447px, 44px, 0px) scale(0.6904296875);"></div></div>
</div>
<div class="pswp
ui pswpui--fit pswpui--hidden">
<div class="pswptop-bar">
<div class="pswp
counter">4 / 4</div>
<button class="pswpbutton pswpbutton--close" title="Close (Esc)"></button>
<div class="pswppreloader">
<div class="pswp
preloadericn">
<div class="pswp
preloadercut">
<div class="pswp
preloaderdonut"></div>
</div>
</div>
</div>
</div>
<div class="pswp
caption">
<div class="pswpcaptioncenter"></div>
</div>
</div>
</div>
</div>

4.处理大图的,直接拿去
var initPhotoSwipeFromDOM = function (gallerySelector) {
var parseThumbnailElements = function (el) {
var thumbElements = el.childNodes, numNodes = thumbElements.length, items = [], figureEl, childElements, linkEl, size, item;
for (var i = 0; i < numNodes; i++) {
if (window.CP.shouldStopExecution(1)) {
break;
}
figureEl = thumbElements[i];
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0];
size = linkEl.getAttribute('data-size').split('x');
item = {
src: linkEl.getAttribute('href'),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
item.msrc = linkEl.children[0].getAttribute('src');
}
item.el = figureEl;
items.push(item);
}
window.CP.exitedLoop(1);
return items;
};
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
var onThumbnailsClick = function (e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
var eTarget = e.target || e.srcElement;
var clickedListItem = closest(eTarget, function (el) {
return el.tagName && el.tagName.toUpperCase() === 'FIGURE';
});
if (!clickedListItem) {
return;
}
var clickedGallery = clickedListItem.parentNode, childNodes = clickedListItem.parentNode.childNodes, numChildNodes = childNodes.length, nodeIndex = 0, index;
for (var i = 0; i < numChildNodes; i++) {
if (window.CP.shouldStopExecution(2)) {
break;
}
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
window.CP.exitedLoop(2);
if (index >= 0) {
openPhotoSwipe(index, clickedGallery);
}
return false;
};
var photoswipeParseHash = function () {
var hash = window.location.hash.substring(1), params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split('&');
for (var i = 0; i < vars.length; i++) {
if (window.CP.shouldStopExecution(3)) {
break;
}
if (!vars[i]) {
continue;
}
var pair = vars[i].split('=');
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
window.CP.exitedLoop(3);
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
if (!params.hasOwnProperty('pid')) {
return params;
}
params.pid = parseInt(params.pid, 10);
return params;
};
var openPhotoSwipe = function (index, galleryElement, disableAnimation) {
var pswpElement = document.querySelectorAll('.pswp')[0], gallery, options, items;
items = parseThumbnailElements(galleryElement);
options = {
index: index,
galleryUID: galleryElement.getAttribute('data-pswp-uid'),
getThumbBoundsFn: function (index) {
var thumbnail = items[index].el.getElementsByTagName('img')[0], pageYScroll = window.pageYOffset || document.documentElement.scrollTop, rect = thumbnail.getBoundingClientRect();
return {
x: rect.left,
y: rect.top + pageYScroll,
w: rect.width
};
},
history: false,
focus: false
};
if (disableAnimation) {
options.showAnimationDuration = 0;
}
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
};
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
if (window.CP.shouldStopExecution(4)) {
break;
}
galleryElements[i].setAttribute('data-pswp-uid', i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
window.CP.exitedLoop(4);
var hashData = photoswipeParseHash();
if (hashData.pid > 0 && hashData.gid > 0) {
openPhotoSwipe(hashData.pid - 1, galleryElements[hashData.gid - 1], true);
}
};

继续阅读 »

功能实现:
点击小图浏览大图,双击放大,滑动切换,上滑或下滑关闭,双指放大或缩小(到了最小是自动关闭),预加载缓冲。
调用的第三方插件,好处就是没有借用jquery,而且滑动,缩放效果超流畅
最底下有效果图和所需文件下载
1,引入CSS及JS

<link rel="stylesheet prefetch" href="../plugin/touchPhotos/photoswipe.css">  
<link rel="stylesheet prefetch" href="../plugin/touchPhotos/default-skin.css">  
<script src="../plugin/touchPhotos/stopExecutionOnTimeout.js"></script>  
<script src="../plugin/touchPhotos/photoswipe.min.js"></script>  
<script src="../plugin/touchPhotos/photoswipe-ui-default.min.js"></script>

2,小图片容器(这里要获取原图地址及图片尺寸),我是通过JSON来获取的,就写个JSON的写法了
//容器 my-simple-gallery,获取图片的宽度和高度

data += '<div class="my-simple-gallery">';  
for(var k=0; k<data.photos.length; k++){  
    data += '<figure itemprop="associatedMedia" itemscope=""><a href="'+data.photos[k].big+'" data-size="'+data.photos[k].size+'"><img src="'+data.photos[k].small+'" ></a></figure>';  
}  
data += '</div>';

自己讲data插入到ID里面了哈
然后就指定浏览图片的那个容器了
initPhotoSwipeFromDOM('.my-simple-gallery');

3.下面的代码写的有点恶心,直接拿去放到模板里面

<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true" style="">
<div class="pswpbg"></div>
<div class="pswp
scroll-wrap">
<div class="pswpcontainer" style="transform: translate3d(0px, 0px, 0px);">
<div class="pswp
item" style="display: block; transform: translate3d(-1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(288px, 56px, 0px) scale(1);"></div></div>
<div class="pswp
item" style="transform: translate3d(0px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(473px, 66.8125px, 0px) scale(0.146484375);"></div></div>
<div class="pswp
item" style="display: block; transform: translate3d(1792px, 0px, 0px);"><div class="pswpzoom-wrap" style="transform: translate3d(447px, 44px, 0px) scale(0.6904296875);"></div></div>
</div>
<div class="pswp
ui pswpui--fit pswpui--hidden">
<div class="pswptop-bar">
<div class="pswp
counter">4 / 4</div>
<button class="pswpbutton pswpbutton--close" title="Close (Esc)"></button>
<div class="pswppreloader">
<div class="pswp
preloadericn">
<div class="pswp
preloadercut">
<div class="pswp
preloaderdonut"></div>
</div>
</div>
</div>
</div>
<div class="pswp
caption">
<div class="pswpcaptioncenter"></div>
</div>
</div>
</div>
</div>

4.处理大图的,直接拿去
var initPhotoSwipeFromDOM = function (gallerySelector) {
var parseThumbnailElements = function (el) {
var thumbElements = el.childNodes, numNodes = thumbElements.length, items = [], figureEl, childElements, linkEl, size, item;
for (var i = 0; i < numNodes; i++) {
if (window.CP.shouldStopExecution(1)) {
break;
}
figureEl = thumbElements[i];
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0];
size = linkEl.getAttribute('data-size').split('x');
item = {
src: linkEl.getAttribute('href'),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
item.msrc = linkEl.children[0].getAttribute('src');
}
item.el = figureEl;
items.push(item);
}
window.CP.exitedLoop(1);
return items;
};
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
var onThumbnailsClick = function (e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
var eTarget = e.target || e.srcElement;
var clickedListItem = closest(eTarget, function (el) {
return el.tagName && el.tagName.toUpperCase() === 'FIGURE';
});
if (!clickedListItem) {
return;
}
var clickedGallery = clickedListItem.parentNode, childNodes = clickedListItem.parentNode.childNodes, numChildNodes = childNodes.length, nodeIndex = 0, index;
for (var i = 0; i < numChildNodes; i++) {
if (window.CP.shouldStopExecution(2)) {
break;
}
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
window.CP.exitedLoop(2);
if (index >= 0) {
openPhotoSwipe(index, clickedGallery);
}
return false;
};
var photoswipeParseHash = function () {
var hash = window.location.hash.substring(1), params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split('&');
for (var i = 0; i < vars.length; i++) {
if (window.CP.shouldStopExecution(3)) {
break;
}
if (!vars[i]) {
continue;
}
var pair = vars[i].split('=');
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
window.CP.exitedLoop(3);
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
if (!params.hasOwnProperty('pid')) {
return params;
}
params.pid = parseInt(params.pid, 10);
return params;
};
var openPhotoSwipe = function (index, galleryElement, disableAnimation) {
var pswpElement = document.querySelectorAll('.pswp')[0], gallery, options, items;
items = parseThumbnailElements(galleryElement);
options = {
index: index,
galleryUID: galleryElement.getAttribute('data-pswp-uid'),
getThumbBoundsFn: function (index) {
var thumbnail = items[index].el.getElementsByTagName('img')[0], pageYScroll = window.pageYOffset || document.documentElement.scrollTop, rect = thumbnail.getBoundingClientRect();
return {
x: rect.left,
y: rect.top + pageYScroll,
w: rect.width
};
},
history: false,
focus: false
};
if (disableAnimation) {
options.showAnimationDuration = 0;
}
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
};
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
if (window.CP.shouldStopExecution(4)) {
break;
}
galleryElements[i].setAttribute('data-pswp-uid', i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
window.CP.exitedLoop(4);
var hashData = photoswipeParseHash();
if (hashData.pid > 0 && hashData.gid > 0) {
openPhotoSwipe(hashData.pid - 1, galleryElements[hashData.gid - 1], true);
}
};

收起阅读 »

Android平台API等级配置 - minSdkVersion&targetSdkVersion

Android

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

<a id="minsdkversion"></a>

minSdkVersion

minSdkVersion用于指定应用兼容的最低Android版本(API等级)。
如果APP某些功能无法支持低版本Android系统的设备,可以配置minSdkVersion确保APP只能安装到指定Android版本以上的设备。HBuilder|HBuilderX中可在manifest.json中进行配置。

⚠️注意: minSdkVersion升级时只能增加不能降低。minSdkVersion高的apk无法被minSdkVersion低的apk覆盖安装需要注意!!

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 minSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "plus": {  
        "distribute": {  
            "google":{  
                "minSdkVersion": 22  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "minSdkVersion": 22  
            }  
        }  
    }

Number类型,整数值,应用要求的最低系统版本,必须大于等于19(Android4.4)小于等于23(android 6.0),默认值为19
示例中设置值为22表示应用只能安装在Android5.1及以上设备。

<a id="targetsdkversion"></a>

targetSdkVersion

HBuilder3.2.13版本开始targetSdkVersion默认值由26调整为28
注意:某些uni原生插件可能没有适配好targetSdkVersion为28会引起部分功能异常,碰到这类情况请联系插件开发者进行适配

⚠️注意: targetSdkVersion升级时只能增加不能降低。targetSdkVersion高的apk无法被targetSdkVersion低的apk覆盖安装需要注意!!

targetSdkVersion用于指定应用适配的Android版本(API等级)。
在Android系统中设置低版本的targetSdkVersion会使APP兼容模式运行,也就可能无法用到新系统的特性,甚至在兼容模式下运行可能存在安全漏洞等问题。
随着Android系统的升级,一些应用市场会要求设置较高的targetSdkVersion才可以提交。HBuilder|HBuilderX中可在manifest.json中进行配置。

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 targetSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“targetSdkVersion”字段:
    "plus": {  
        "distribute": {  
            "google":{  
                "targetSdkVersion": 26  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“targetSdkVersion”字段:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "targetSdkVersion": 26  
            }  
        }  
    }

Number类型,整数值,云端打包默认的targetSdkVersion值为26

  • 5+App项目:最小值为19,最大值29
  • uni-app项目:最小值为26,最大值29

    HBuilderX2.8.3及以下版本targetSdkVersion最大值支持28
    HBuilderX2.8.4+版本targetSdkVersion最大值支持29

Android版本列表

API等级与Android版本对应列表如下:

API等级 Android版本号
14 Android4.0
15 Android4.0.3
16 Android4.1.2
17 Android4.2.2
18 Android4.3.1
19 Android4.4.2
20 Android4.4W.2
21 Android5.0.1
22 Android5.1
24 Android7.0
25 Android7.1.1
26 Android8.0
27 Android8.1
28 Android9.0
29 Android10.0(Android Q)
30 Android11.0

配置完成保存提交App云端打包后才能生效

继续阅读 »

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

<a id="minsdkversion"></a>

minSdkVersion

minSdkVersion用于指定应用兼容的最低Android版本(API等级)。
如果APP某些功能无法支持低版本Android系统的设备,可以配置minSdkVersion确保APP只能安装到指定Android版本以上的设备。HBuilder|HBuilderX中可在manifest.json中进行配置。

⚠️注意: minSdkVersion升级时只能增加不能降低。minSdkVersion高的apk无法被minSdkVersion低的apk覆盖安装需要注意!!

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 minSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "plus": {  
        "distribute": {  
            "google":{  
                "minSdkVersion": 22  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“minSdkVersion”字段,并配置要支持的最低Android版本号:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "minSdkVersion": 22  
            }  
        }  
    }

Number类型,整数值,应用要求的最低系统版本,必须大于等于19(Android4.4)小于等于23(android 6.0),默认值为19
示例中设置值为22表示应用只能安装在Android5.1及以上设备。

<a id="targetsdkversion"></a>

targetSdkVersion

HBuilder3.2.13版本开始targetSdkVersion默认值由26调整为28
注意:某些uni原生插件可能没有适配好targetSdkVersion为28会引起部分功能异常,碰到这类情况请联系插件开发者进行适配

⚠️注意: targetSdkVersion升级时只能增加不能降低。targetSdkVersion高的apk无法被targetSdkVersion低的apk覆盖安装需要注意!!

targetSdkVersion用于指定应用适配的Android版本(API等级)。
在Android系统中设置低版本的targetSdkVersion会使APP兼容模式运行,也就可能无法用到新系统的特性,甚至在兼容模式下运行可能存在安全漏洞等问题。
随着Android系统的升级,一些应用市场会要求设置较高的targetSdkVersion才可以提交。HBuilder|HBuilderX中可在manifest.json中进行配置。

可视化界面配置

打开项目的manifest.json文件,在 "App常用其它设置" 项中 "Android设置" 下的 targetSdkVersion编辑框中输入要支持的最低Android版本号:

源码视图配置

打开项目的manifest.json文件,切换到 "源码视图"

  • 5+APP项目
    在plus->distribute->google节点下添加“targetSdkVersion”字段:
    "plus": {  
        "distribute": {  
            "google":{  
                "targetSdkVersion": 26  
            }  
        }  
    }
  • uni-app项目
    在"app-plus"->distribute->android节点下添加“targetSdkVersion”字段:
    "app-plus": {  
        "distribute": {  
            "android":{  
                "targetSdkVersion": 26  
            }  
        }  
    }

Number类型,整数值,云端打包默认的targetSdkVersion值为26

  • 5+App项目:最小值为19,最大值29
  • uni-app项目:最小值为26,最大值29

    HBuilderX2.8.3及以下版本targetSdkVersion最大值支持28
    HBuilderX2.8.4+版本targetSdkVersion最大值支持29

Android版本列表

API等级与Android版本对应列表如下:

API等级 Android版本号
14 Android4.0
15 Android4.0.3
16 Android4.1.2
17 Android4.2.2
18 Android4.3.1
19 Android4.4.2
20 Android4.4W.2
21 Android5.0.1
22 Android5.1
24 Android7.0
25 Android7.1.1
26 Android8.0
27 Android8.1
28 Android9.0
29 Android10.0(Android Q)
30 Android11.0

配置完成保存提交App云端打包后才能生效

收起阅读 »

授权登录插件配置

OAuth

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

云端打包登录鉴权功能需要到第三方开发平台申请应用后获取相关配置参数,目前支持的平台包括:

  1. 微信:微信开放平台
  2. QQ:腾讯开放平台
  3. 微博:新浪微博开放平台
  4. 苹果登录:iOS 苹果授权登录(Sign in with Apple)教程
  5. Google登录:HBuilderX3.2.7+版本支持
  6. Facebook登录:HBuilder3.2.7+版本支持

配置参数需要提交云端打包后才能生效,如果需要真机运行生效请使用自定义基座

HBuilderX中配置登录鉴权

从微信/QQ/微博开放平台申请获取配置参数后,需在HBuilderX中配置并提交云端打包才能生效。
老版本HBuilder配置界面有差异,逻辑是一样的,建议更新使用HBuilderX

配置使用登录鉴权模块

打开应用的manifest.json文件,在“App模块配置”项中勾选“OAuth(登录鉴权)”:

配置登录鉴权参数

微信

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“微信登录”项,并输入从微信开放平台申请的参数:

QQ

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“QQ登录”项,并输入从腾讯QQ开放平台申请的参数:

  • appid:腾讯QQ开放平台申请应用的AppID值。

微博

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“新浪微博登录”项,并输入从新浪微博开放平台申请的参数:

  • appkey: "新浪微博平台应用appkey";
  • appsecret: "新浪微博平台应用appsecret";
  • redirect_uri: "新浪微博平台应用授权回调页地址"。

配置完成后Ctrl+S保存提交App云端打包生效。

<a id="secret"></a>

配置参数安全性问题

在HBuilder|HBuilderX中配置的参数云端打包后会保存在apk/ipa中,对于安全性要求高的开发者可能担心存在参数泄露的风险,可以采取以下方式处理。

  • JS代码中动态传参数
    如登录服务AuthService的请求授权认证方法authorize,可以通过第三个参数options动态传入appid、appSecret等。
    这些参数可以加密保存到js代码中或连网从服务器获取(避免保存在本地引起泄露风险,当然需要考虑网络传输过程的安全问题)。

  • 通过服务器完成授权认证
    根据OAuth规范,实际客户端授权只是为了获取授权临时票据(code),这时候可以仅在客户端配置appid参数调用登录服务AuthService的请求授权认证方法authorize获取临时票据,将票据提交到服务器完成后续的操作。
    这种情况授权的参数(如appsecret)仅保存服务器即可,安全性更高。
    在服务器的对接流程需要根据各开放平台的规范要求进行处理,如微信参考:授权后接口调用

这时在HBuilder|HBuilderX中可以填入任意值提交云端打包

继续阅读 »

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

云端打包登录鉴权功能需要到第三方开发平台申请应用后获取相关配置参数,目前支持的平台包括:

  1. 微信:微信开放平台
  2. QQ:腾讯开放平台
  3. 微博:新浪微博开放平台
  4. 苹果登录:iOS 苹果授权登录(Sign in with Apple)教程
  5. Google登录:HBuilderX3.2.7+版本支持
  6. Facebook登录:HBuilder3.2.7+版本支持

配置参数需要提交云端打包后才能生效,如果需要真机运行生效请使用自定义基座

HBuilderX中配置登录鉴权

从微信/QQ/微博开放平台申请获取配置参数后,需在HBuilderX中配置并提交云端打包才能生效。
老版本HBuilder配置界面有差异,逻辑是一样的,建议更新使用HBuilderX

配置使用登录鉴权模块

打开应用的manifest.json文件,在“App模块配置”项中勾选“OAuth(登录鉴权)”:

配置登录鉴权参数

微信

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“微信登录”项,并输入从微信开放平台申请的参数:

QQ

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“QQ登录”项,并输入从腾讯QQ开放平台申请的参数:

  • appid:腾讯QQ开放平台申请应用的AppID值。

微博

在manifest.json文件“App模块配置”项的“OAuth(登录鉴权)”下,勾选“新浪微博登录”项,并输入从新浪微博开放平台申请的参数:

  • appkey: "新浪微博平台应用appkey";
  • appsecret: "新浪微博平台应用appsecret";
  • redirect_uri: "新浪微博平台应用授权回调页地址"。

配置完成后Ctrl+S保存提交App云端打包生效。

<a id="secret"></a>

配置参数安全性问题

在HBuilder|HBuilderX中配置的参数云端打包后会保存在apk/ipa中,对于安全性要求高的开发者可能担心存在参数泄露的风险,可以采取以下方式处理。

  • JS代码中动态传参数
    如登录服务AuthService的请求授权认证方法authorize,可以通过第三个参数options动态传入appid、appSecret等。
    这些参数可以加密保存到js代码中或连网从服务器获取(避免保存在本地引起泄露风险,当然需要考虑网络传输过程的安全问题)。

  • 通过服务器完成授权认证
    根据OAuth规范,实际客户端授权只是为了获取授权临时票据(code),这时候可以仅在客户端配置appid参数调用登录服务AuthService的请求授权认证方法authorize获取临时票据,将票据提交到服务器完成后续的操作。
    这种情况授权的参数(如appsecret)仅保存服务器即可,安全性更高。
    在服务器的对接流程需要根据各开放平台的规范要求进行处理,如微信参考:授权后接口调用

这时在HBuilder|HBuilderX中可以填入任意值提交云端打包

收起阅读 »

Android证书的生成和指纹获取

可能iOS下各种证书的繁杂,所以官方只给出了iOS系统下证书的生成说明,Android证书的生成的确很简单,我简单说明一下:
1、安装JDK
2、在cmd下,进入到JDK的bin目录,输入:
keytool -genkey -alias yourapp -keyalg RSA -validity 20000 -keystore yourapp.keystore
说明:yourapp就是证书的别名,20000是证书的有效天数,yourapp.keystore就是生成的证书名字。
3、一路根据指示设置密码,组织等,注意密码是不会显示或者以***代替,但是其实已经输入了。确认后选择Y,生成的证书会在bin目录下。
4、获取证书的指纹,输入:
keytool -list -v -keystore "D:\Program Files\Java\jdk1.8.0_40\bin\yourapp.keystore" -alias yourapp
路径请使用自己安装JDK的路径代替。
这样就可以获取MD5、SHA1的证书指纹。

的确很简单,但是的确有人不会,譬如说我以前就不会,囧。
列出来,以供大家查阅。

继续阅读 »

可能iOS下各种证书的繁杂,所以官方只给出了iOS系统下证书的生成说明,Android证书的生成的确很简单,我简单说明一下:
1、安装JDK
2、在cmd下,进入到JDK的bin目录,输入:
keytool -genkey -alias yourapp -keyalg RSA -validity 20000 -keystore yourapp.keystore
说明:yourapp就是证书的别名,20000是证书的有效天数,yourapp.keystore就是生成的证书名字。
3、一路根据指示设置密码,组织等,注意密码是不会显示或者以***代替,但是其实已经输入了。确认后选择Y,生成的证书会在bin目录下。
4、获取证书的指纹,输入:
keytool -list -v -keystore "D:\Program Files\Java\jdk1.8.0_40\bin\yourapp.keystore" -alias yourapp
路径请使用自己安装JDK的路径代替。
这样就可以获取MD5、SHA1的证书指纹。

的确很简单,但是的确有人不会,譬如说我以前就不会,囧。
列出来,以供大家查阅。

收起阅读 »

【分享】头像类裁剪上传

头像裁剪

夜深了,分享个头像类的裁剪上传,代码写的比较啰嗦。
虽然说用jquery渲染力差,没办法,还是用了。然后借助cropper来完成裁剪

<script src="plugin/avatar/js/jquery.min.js"></script>  
<script src="plugin/avatar/js/cropper.js"></script>  
<link href="plugin/avatar/css/cropper.css" rel="stylesheet">

照片容器

<a href="#picture"><div id="changeAvatar"></div></a>

裁剪照片的容器(样式就自己写了)

<div id="showEdit">  
        <header class="mui-bar mui-bar-nav mui-nav-bg" style="background:none; position:absolute; top:0;">  
            <a class="mui-icon iconfont icon-roundclosefill mui-pull-left" style="color:#ffffff; font-size:36px;" onclick="closeEdit();"></a>  
            <a class="mui-icon iconfont icon-roundcheckfill mui-pull-right" style="color:#ffffff; font-size:36px;" onclick="confirm();"></a>  
        </header>  
        <div id="report"></div>  
    </div>

下面就很简单了,调用下相机和相册选择图片。然后加入cropper的几行代码就搞定了

//拍照  
function getImage() {  
    var cmr = plus.camera.getCamera();  
    cmr.captureImage( function (p) {  
        plus.io.resolveLocalFileSystemURL( p, function ( entry ) {      
            var localurl = entry.toLocalURL();//  
            $("#report").html('<img src="'+localurl+'">');  
            cutImg();  
            mui('#picture').popover('toggle');  
        });  
    });  
}  
//相册选取  
function galleryImgs(){  
    plus.gallery.pick( function(e){  
        $("#report").html('<img src="'+e.files[0]+'">');  
        cutImg();  
        mui('#picture').popover('toggle');  
    }, function ( e ) {  
        //outSet( "取消选择图片" );  
    },{filter:"image",multiple:true});  
}

照片裁剪类:

function cutImg(){    
    $("#showEdit").fadeIn();  
    var $image = $('#report > img');  
    $image.cropper({  
      aspectRatio: 1 / 1,  
      autoCropArea: 0.8  
    });  
}

确定裁剪并上传(base64)

function confirm(){  

    $("#showEdit").fadeOut();  
    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    $("#changeAvatar").html('<img src="'+dataURL+'" />');  
}  

function postAvatar() {  

    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    var data = {  
            base64: dataURL  
    };  
    $.post(url,data,function(data){  
        //这里就自己写了哈  
    });  
};

效果

有高手可以补充下的谢谢啦哈

继续阅读 »

夜深了,分享个头像类的裁剪上传,代码写的比较啰嗦。
虽然说用jquery渲染力差,没办法,还是用了。然后借助cropper来完成裁剪

<script src="plugin/avatar/js/jquery.min.js"></script>  
<script src="plugin/avatar/js/cropper.js"></script>  
<link href="plugin/avatar/css/cropper.css" rel="stylesheet">

照片容器

<a href="#picture"><div id="changeAvatar"></div></a>

裁剪照片的容器(样式就自己写了)

<div id="showEdit">  
        <header class="mui-bar mui-bar-nav mui-nav-bg" style="background:none; position:absolute; top:0;">  
            <a class="mui-icon iconfont icon-roundclosefill mui-pull-left" style="color:#ffffff; font-size:36px;" onclick="closeEdit();"></a>  
            <a class="mui-icon iconfont icon-roundcheckfill mui-pull-right" style="color:#ffffff; font-size:36px;" onclick="confirm();"></a>  
        </header>  
        <div id="report"></div>  
    </div>

下面就很简单了,调用下相机和相册选择图片。然后加入cropper的几行代码就搞定了

//拍照  
function getImage() {  
    var cmr = plus.camera.getCamera();  
    cmr.captureImage( function (p) {  
        plus.io.resolveLocalFileSystemURL( p, function ( entry ) {      
            var localurl = entry.toLocalURL();//  
            $("#report").html('<img src="'+localurl+'">');  
            cutImg();  
            mui('#picture').popover('toggle');  
        });  
    });  
}  
//相册选取  
function galleryImgs(){  
    plus.gallery.pick( function(e){  
        $("#report").html('<img src="'+e.files[0]+'">');  
        cutImg();  
        mui('#picture').popover('toggle');  
    }, function ( e ) {  
        //outSet( "取消选择图片" );  
    },{filter:"image",multiple:true});  
}

照片裁剪类:

function cutImg(){    
    $("#showEdit").fadeIn();  
    var $image = $('#report > img');  
    $image.cropper({  
      aspectRatio: 1 / 1,  
      autoCropArea: 0.8  
    });  
}

确定裁剪并上传(base64)

function confirm(){  

    $("#showEdit").fadeOut();  
    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    $("#changeAvatar").html('<img src="'+dataURL+'" />');  
}  

function postAvatar() {  

    var $image = $('#report > img');  
    var dataURL = $image.cropper("getDataURL");  
    var data = {  
            base64: dataURL  
    };  
    $.post(url,data,function(data){  
        //这里就自己写了哈  
    });  
};

效果

有高手可以补充下的谢谢啦哈

收起阅读 »

夜深了,分享个思路,解决图片“表面”上秒传效果

接触5+有一个月了,也是接触APP开发有一个月了。今天先分享个思路出来,供大家参考。
图片上传传统解决方案:
1.选择图片

  1. 可能还要经过canvas的压缩处理
  2. 传到服务器返回图片url,或者点击发布同时上传
    这样的流程无疑会造成发布时间慢,用户体验性差。
    解决思路,现在还是个思路,明天开始敲
    1.选择图片
  3. 返回图片本地地址
  4. 点击发布写入本地存储(websql),列表页就调用这个本地数据了
  5. 写个js在后台开始慢慢处理数据与服务器的同步
    表面上用户会以为自己发布的图片已经秒速发布成功,还能在列表页看到刚刚发布的内容。实际还在后面慢慢的同步。

有高手可以帮忙完善下思路或者敲两下代码的感激不尽~

继续阅读 »

接触5+有一个月了,也是接触APP开发有一个月了。今天先分享个思路出来,供大家参考。
图片上传传统解决方案:
1.选择图片

  1. 可能还要经过canvas的压缩处理
  2. 传到服务器返回图片url,或者点击发布同时上传
    这样的流程无疑会造成发布时间慢,用户体验性差。
    解决思路,现在还是个思路,明天开始敲
    1.选择图片
  3. 返回图片本地地址
  4. 点击发布写入本地存储(websql),列表页就调用这个本地数据了
  5. 写个js在后台开始慢慢处理数据与服务器的同步
    表面上用户会以为自己发布的图片已经秒速发布成功,还能在列表页看到刚刚发布的内容。实际还在后面慢慢的同步。

有高手可以帮忙完善下思路或者敲两下代码的感激不尽~

收起阅读 »