HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【交流分享】使用plus.uploader上传图片到阿里云OSS

uploader plus 阿里云OSS 技术分享

好几次看到有人在论坛里问怎么上传图片到阿里云oss,本来是想着搬个小板凳坐等大神传授经验什么的,而且其实之前也没有类似的需求,就懒得动了。结果前段时间突然用到阿里云oss了,马丹踩坑了,但是又不想放弃plus.uploader,所以自己搞了下这个。行走江湖基本靠猜了,幸运的是搞对了,于是有了下面的内容

1. 需要准备的

继续阅读前,请确保你已经开通了阿里云OSS服务,并拿到了下面三样东西

  • 自己申请的阿里云OSS的地址,例如http://XXXXXXXXXX-shanghai.aliyuncs.com/
  • 阿里云OSS给你的AccessKeyId 例如 "Lasdasnioiduai123jdsa"
  • 阿里云OSS给你的AccessKeySecret 例如 "sggrkconoian3r3oijo545nfoosjd"(这里是乱填的,实际要用自己的)
  • 下面是官方给的测试用的,不要上传奇怪的图片进行测试哦
    accessid= '6MKOqxGiGU4AUk44';
    accesskey= 'ufu7nS8kS59awNihtjSonMETLI0KLy';
    host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com';

上传的demo代码按照惯例附在文章末尾,已经在明显的地方做了注释,运行demo之前需要将上面三个东西一一替换

2. oss功能介绍

使用阿里云OSS之前,肯定要学习下怎么用,鉴于我们开发的是webapp,基本是在html页面上操作,所以要使用到他的web直传的功能,即通过Html表单提交直传OSS。请注意:本文参考的是官方第一个例子“JS客户端签名直接上传OSS”,官方明确强调:
采用JS客户端直接签名有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey.”,
采用JS客户端直接签名有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey.”,
采用JS客户端直接签名有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey.”,

本文仅仅是为了说明结合plus.uploader直接上传图片至OSS,实际使用过程请参考下方链接中《终极篇》的做法。

Web端直传实践

基础篇:客户端用JS直接签名,然后上传到OSS

进阶篇:应用服务器php返回签名

终级篇--应用服务器php返回签名及采用上传回调

3. 开搞

在这个基础篇:客户端用JS直接签名,然后上传到OSS例子中,官方给出了一个web直传的demo,对应的下载地址oss-h5-upload-js-direct.zip
先把官方介绍和demo至少都看一遍,方便后续理解。

demo下下来,目录结构如上图,二话不说编辑器打开“index.html”,会发现重点在下面这6个js(假设大家已经阅读了官方文章和demo的代码)

进一步阅读代码,咦,crypto.js、hmac.js、sha1.js、base64.js好像都跟加密有点关系,嗯先放一边吧。同时官方文章中提到“采用plupload 直接提交表单数据(即PostObject)到OSS ”,如下图。

这说明plupload.full.min.js应该就是个上传插件了,嗯,继续先放一边,我们看最后剩下的upload.js,哎哟卧槽,果然业务逻结代码都在这里面了,写的还是比较容易理解,多读几遍,上来就是三个变量,就是本文开篇处提到的三个必备的。

     accessid= '6MKOqxGiGU4AUk44';  
     accesskey= 'ufu7nS8kS59awNihtjSonMETLI0KLy';  
     host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com';

继续往下,都是变量和方法,前面几个方法和变量因为没有注释,所以看得云里雾里的,继续往下走,突然有个方法“set_upload_param”出现了!结尾一句“up.start()”,咋看的这么亲切,大腿一拍,这不跟新建uploader上传任务长的很像嘛,初始化完了来一发“task.start();”。

function set_upload_param(up, filename, ret)  
{  
    g_object_name = g_dirname;  
    if (filename != '') {  
        suffix = get_suffix(filename)  
        calculate_object_name(filename)  
    }  
    new_multipart_params = {  
        'key' : g_object_name,  
        'policy': policyBase64,  
        'OSSAccessKeyId': accessid,   
        'success_action_status' : '200', //让服务端返回200,不然,默认会返回204  
        'signature': signature,  
    };  

    up.setOption({  
        'url': host,  
        'multipart_params': new_multipart_params  
    });  

    up.start();  
}

好的,突然感觉有戏,回头继续看下这个方法,咦up.setOption,再拍一次大腿,这不就是跟plus.uploader里面的task.addData如出一辙嘛,进一步感觉有戏,突然就自我感觉良好了!

//阿里云ossdemo  
    up.setOption({  
        'url': host,  
        'multipart_params': new_multipart_params  
    });  
//plus.uploader的demo  
    task.addFile( "_www/a.doc", {key:"testdoc"} );  
    task.addData( "string_key", "string_value" );

好的,冷静一下,继续回头看这个up.setOption,两个参数,一个url,一个multipart_params。好的,暂停一下,url不就是plus.uploader.createUpload的第一个参数:要上传文件的目标地址吗,那这个multipart_params又要怎么玩,继续回头看。原来这个multipart_params的值来自自定义的new_multipart_params,是一个组装的json,里面的key和value不就是之前那几个没有注释的变量和方法一步一步生成出来的嘛,嘿嘿嘿嘿嘿嘿嘿,有趣!之前说,行走江湖基本靠猜,那就猜一下用task.addData试试好了,回头再看之前的变量和方法,就不难知道new_multipart_params的中变量的意思了。

    new_multipart_params = {  
        'key' : g_object_name,    //指定上传至OSS的文件名,可以是随机生成的,也可以是使用客户端本地的文件名,实际过程看你心情用哪种  
        'policy': policyBase64,     //经过一大堆计算将policyText进行转换得到的值,按照demo中的方法一样的计算即可  
        'OSSAccessKeyId': accessid,   //阿里云服务的AccessKeyId           
        'success_action_status' : '200', //让服务端返回200,不然,默认会返回204  
        'signature': signature,    //签名,经过一大堆计算,主要是对policyText进行签名  
    };

4.配置plus.uploader

有了上面的铺垫,plus.uploader就很好配置了
首先是引入几个加密计算用的js,不要偷懒,直接引用

    <script type="text/javascript" src="../js/crypto.js"></script>  
    <script type="text/javascript" src="../js/hmac.js"></script>  
    <script type="text/javascript" src="../js/base64.js"></script>  
    <script type="text/javascript" src="../js/sha1.js"></script>

按照demo进行各种转换计算,ctrl+c/ctrl+v即可

        /*  
         * 阿里云参数设置,用于计算签名signature  
         */  
        var policyText = {  
            "expiration": "2020-01-01T12:00:00.000Z", //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了  
            "conditions": [  
                ["content-length-range", 0, 1048576000] // 设置上传文件的大小限制  
            ]  
        };  
        var policyBase64 = Base64.encode(JSON.stringify(policyText));  
        var message = policyBase64;  
        var bytes = Crypto.HMAC(Crypto.SHA1, message, AccessKeySecret, {  
            asBytes: true  
        });  
        var signature = Crypto.util.bytesToBase64(bytes);

初始化plus.uploader

        // 上传文件  
        function upload() {  
            if(files.length <= 0) {  
                plus.nativeUI.alert("没有添加上传文件!");  
                return;  
            }  
            outSet("开始上传:")  
            var wt = plus.nativeUI.showWaiting();  
            var task = plus.uploader.createUpload(server, {  
                    method: "POST"  
                },  
                function(t, status) { //上传完成  
                    console.log("t" + JSON.stringify(t));  
                    if(status == 200) {  
                        outLine("上传成功:" + t.responseText);  
                        //至此上传成功,上传后的图片完整地址为server+testName  
                        var uploaderFileObj = {  
                            "server":server,  
                            "path":testName  
                        };  
                        plus.storage.setItem("uploader", JSON.stringify(uploaderFileObj));  
                        var w = plus.webview.create("uploader_ret.html", "uploader_ret.html", {  
                            scrollIndicator: 'none',  
                            scalable: false  
                        });  
                        w.addEventListener("loaded", function() {  
                            wt.close();  
                            w.show("slide-in-right", 300);  
                        }, false);  
                    } else {  
                        outLine("上传失败:" + status);  
                        wt.close();  
                    }  
                }  
            );  
            var suffix1 = get_suffix(fname);  //文件后缀  例如   .jpg  
            var keyname = dir + new Date().getTime() + suffix1;  

            testName = keyname;  
            console.log("keyname" + keyname);  
            //按照之前说明的参数类型,按顺序添加参数  
            task.addData("key", keyname);  
            task.addData("policy", policyBase64);  
            task.addData("OSSAccessKeyId", OSSAccessKeyId);  
            task.addData("success_action_status", "200");  
            task.addData("signature", signature);  
            console.log("files[0]" + JSON.stringify(files[0]));  
            var f = files[0];  
            task.addFile(f.path, {  
                key: "file",  
                name: "file",  
                mime: "image/jpeg"  
            });  
            task.start();  
        }

排版有点乱,开心就好。这段时间挺忙,瞎忙还不赚钱那种,身体也不好,头老是疼,万一哪天没影了,应该就是挂了吧……

附件demo

附件,plus.uploader上传图片至阿里云OSS:

继续阅读 »

好几次看到有人在论坛里问怎么上传图片到阿里云oss,本来是想着搬个小板凳坐等大神传授经验什么的,而且其实之前也没有类似的需求,就懒得动了。结果前段时间突然用到阿里云oss了,马丹踩坑了,但是又不想放弃plus.uploader,所以自己搞了下这个。行走江湖基本靠猜了,幸运的是搞对了,于是有了下面的内容

1. 需要准备的

继续阅读前,请确保你已经开通了阿里云OSS服务,并拿到了下面三样东西

  • 自己申请的阿里云OSS的地址,例如http://XXXXXXXXXX-shanghai.aliyuncs.com/
  • 阿里云OSS给你的AccessKeyId 例如 "Lasdasnioiduai123jdsa"
  • 阿里云OSS给你的AccessKeySecret 例如 "sggrkconoian3r3oijo545nfoosjd"(这里是乱填的,实际要用自己的)
  • 下面是官方给的测试用的,不要上传奇怪的图片进行测试哦
    accessid= '6MKOqxGiGU4AUk44';
    accesskey= 'ufu7nS8kS59awNihtjSonMETLI0KLy';
    host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com';

上传的demo代码按照惯例附在文章末尾,已经在明显的地方做了注释,运行demo之前需要将上面三个东西一一替换

2. oss功能介绍

使用阿里云OSS之前,肯定要学习下怎么用,鉴于我们开发的是webapp,基本是在html页面上操作,所以要使用到他的web直传的功能,即通过Html表单提交直传OSS。请注意:本文参考的是官方第一个例子“JS客户端签名直接上传OSS”,官方明确强调:
采用JS客户端直接签名有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey.”,
采用JS客户端直接签名有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey.”,
采用JS客户端直接签名有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey.”,

本文仅仅是为了说明结合plus.uploader直接上传图片至OSS,实际使用过程请参考下方链接中《终极篇》的做法。

Web端直传实践

基础篇:客户端用JS直接签名,然后上传到OSS

进阶篇:应用服务器php返回签名

终级篇--应用服务器php返回签名及采用上传回调

3. 开搞

在这个基础篇:客户端用JS直接签名,然后上传到OSS例子中,官方给出了一个web直传的demo,对应的下载地址oss-h5-upload-js-direct.zip
先把官方介绍和demo至少都看一遍,方便后续理解。

demo下下来,目录结构如上图,二话不说编辑器打开“index.html”,会发现重点在下面这6个js(假设大家已经阅读了官方文章和demo的代码)

进一步阅读代码,咦,crypto.js、hmac.js、sha1.js、base64.js好像都跟加密有点关系,嗯先放一边吧。同时官方文章中提到“采用plupload 直接提交表单数据(即PostObject)到OSS ”,如下图。

这说明plupload.full.min.js应该就是个上传插件了,嗯,继续先放一边,我们看最后剩下的upload.js,哎哟卧槽,果然业务逻结代码都在这里面了,写的还是比较容易理解,多读几遍,上来就是三个变量,就是本文开篇处提到的三个必备的。

     accessid= '6MKOqxGiGU4AUk44';  
     accesskey= 'ufu7nS8kS59awNihtjSonMETLI0KLy';  
     host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com';

继续往下,都是变量和方法,前面几个方法和变量因为没有注释,所以看得云里雾里的,继续往下走,突然有个方法“set_upload_param”出现了!结尾一句“up.start()”,咋看的这么亲切,大腿一拍,这不跟新建uploader上传任务长的很像嘛,初始化完了来一发“task.start();”。

function set_upload_param(up, filename, ret)  
{  
    g_object_name = g_dirname;  
    if (filename != '') {  
        suffix = get_suffix(filename)  
        calculate_object_name(filename)  
    }  
    new_multipart_params = {  
        'key' : g_object_name,  
        'policy': policyBase64,  
        'OSSAccessKeyId': accessid,   
        'success_action_status' : '200', //让服务端返回200,不然,默认会返回204  
        'signature': signature,  
    };  

    up.setOption({  
        'url': host,  
        'multipart_params': new_multipart_params  
    });  

    up.start();  
}

好的,突然感觉有戏,回头继续看下这个方法,咦up.setOption,再拍一次大腿,这不就是跟plus.uploader里面的task.addData如出一辙嘛,进一步感觉有戏,突然就自我感觉良好了!

//阿里云ossdemo  
    up.setOption({  
        'url': host,  
        'multipart_params': new_multipart_params  
    });  
//plus.uploader的demo  
    task.addFile( "_www/a.doc", {key:"testdoc"} );  
    task.addData( "string_key", "string_value" );

好的,冷静一下,继续回头看这个up.setOption,两个参数,一个url,一个multipart_params。好的,暂停一下,url不就是plus.uploader.createUpload的第一个参数:要上传文件的目标地址吗,那这个multipart_params又要怎么玩,继续回头看。原来这个multipart_params的值来自自定义的new_multipart_params,是一个组装的json,里面的key和value不就是之前那几个没有注释的变量和方法一步一步生成出来的嘛,嘿嘿嘿嘿嘿嘿嘿,有趣!之前说,行走江湖基本靠猜,那就猜一下用task.addData试试好了,回头再看之前的变量和方法,就不难知道new_multipart_params的中变量的意思了。

    new_multipart_params = {  
        'key' : g_object_name,    //指定上传至OSS的文件名,可以是随机生成的,也可以是使用客户端本地的文件名,实际过程看你心情用哪种  
        'policy': policyBase64,     //经过一大堆计算将policyText进行转换得到的值,按照demo中的方法一样的计算即可  
        'OSSAccessKeyId': accessid,   //阿里云服务的AccessKeyId           
        'success_action_status' : '200', //让服务端返回200,不然,默认会返回204  
        'signature': signature,    //签名,经过一大堆计算,主要是对policyText进行签名  
    };

4.配置plus.uploader

有了上面的铺垫,plus.uploader就很好配置了
首先是引入几个加密计算用的js,不要偷懒,直接引用

    <script type="text/javascript" src="../js/crypto.js"></script>  
    <script type="text/javascript" src="../js/hmac.js"></script>  
    <script type="text/javascript" src="../js/base64.js"></script>  
    <script type="text/javascript" src="../js/sha1.js"></script>

按照demo进行各种转换计算,ctrl+c/ctrl+v即可

        /*  
         * 阿里云参数设置,用于计算签名signature  
         */  
        var policyText = {  
            "expiration": "2020-01-01T12:00:00.000Z", //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了  
            "conditions": [  
                ["content-length-range", 0, 1048576000] // 设置上传文件的大小限制  
            ]  
        };  
        var policyBase64 = Base64.encode(JSON.stringify(policyText));  
        var message = policyBase64;  
        var bytes = Crypto.HMAC(Crypto.SHA1, message, AccessKeySecret, {  
            asBytes: true  
        });  
        var signature = Crypto.util.bytesToBase64(bytes);

初始化plus.uploader

        // 上传文件  
        function upload() {  
            if(files.length <= 0) {  
                plus.nativeUI.alert("没有添加上传文件!");  
                return;  
            }  
            outSet("开始上传:")  
            var wt = plus.nativeUI.showWaiting();  
            var task = plus.uploader.createUpload(server, {  
                    method: "POST"  
                },  
                function(t, status) { //上传完成  
                    console.log("t" + JSON.stringify(t));  
                    if(status == 200) {  
                        outLine("上传成功:" + t.responseText);  
                        //至此上传成功,上传后的图片完整地址为server+testName  
                        var uploaderFileObj = {  
                            "server":server,  
                            "path":testName  
                        };  
                        plus.storage.setItem("uploader", JSON.stringify(uploaderFileObj));  
                        var w = plus.webview.create("uploader_ret.html", "uploader_ret.html", {  
                            scrollIndicator: 'none',  
                            scalable: false  
                        });  
                        w.addEventListener("loaded", function() {  
                            wt.close();  
                            w.show("slide-in-right", 300);  
                        }, false);  
                    } else {  
                        outLine("上传失败:" + status);  
                        wt.close();  
                    }  
                }  
            );  
            var suffix1 = get_suffix(fname);  //文件后缀  例如   .jpg  
            var keyname = dir + new Date().getTime() + suffix1;  

            testName = keyname;  
            console.log("keyname" + keyname);  
            //按照之前说明的参数类型,按顺序添加参数  
            task.addData("key", keyname);  
            task.addData("policy", policyBase64);  
            task.addData("OSSAccessKeyId", OSSAccessKeyId);  
            task.addData("success_action_status", "200");  
            task.addData("signature", signature);  
            console.log("files[0]" + JSON.stringify(files[0]));  
            var f = files[0];  
            task.addFile(f.path, {  
                key: "file",  
                name: "file",  
                mime: "image/jpeg"  
            });  
            task.start();  
        }

排版有点乱,开心就好。这段时间挺忙,瞎忙还不赚钱那种,身体也不好,头老是疼,万一哪天没影了,应该就是挂了吧……

附件demo

附件,plus.uploader上传图片至阿里云OSS:

收起阅读 »

px转rem提示功能指南(HBuilder7.5.2以后含此功能)

技巧

更新:HBuilderX是在菜单设置-编辑器配置里配转换比例。

1. 所有项目统一配置的配置界面
主菜单"工具"->"项目"->"代码助手设置"

    

2. 针对指定项目单独配置的配置界面
右键项目, 点击"属性"->"代码助手设置"

    

3. 配置方法
a. 选中"启用px转rem提示", 填写合适的 "px转rem比例" 和 "rem小数点部分保留长度", 保存配置
b. 编写代码时,当输入"xxxpx"时就会出现转换为"rem"的提示了

    

更新说明:
版本7.6.4.201612241943

  1. 解决px换rem功能启用后有时不生效的问题
    1. less、scss编辑器添加px转rem的提示功能
继续阅读 »

更新:HBuilderX是在菜单设置-编辑器配置里配转换比例。

1. 所有项目统一配置的配置界面
主菜单"工具"->"项目"->"代码助手设置"

    

2. 针对指定项目单独配置的配置界面
右键项目, 点击"属性"->"代码助手设置"

    

3. 配置方法
a. 选中"启用px转rem提示", 填写合适的 "px转rem比例" 和 "rem小数点部分保留长度", 保存配置
b. 编写代码时,当输入"xxxpx"时就会出现转换为"rem"的提示了

    

更新说明:
版本7.6.4.201612241943

  1. 解决px换rem功能启用后有时不生效的问题
    1. less、scss编辑器添加px转rem的提示功能
收起阅读 »

用HBuilder开发的蜗牛音乐APP 公测,欢迎轻拍

分享 案例

试水Hybrid HTML5+ ,使用H5+,& Framework7 混合开发的音乐APP
webview性能还是不错得。喜欢google material ui, 所以用了framework7。
纯业余选手,欢迎指教!

在这里下载

继续阅读 »

试水Hybrid HTML5+ ,使用H5+,& Framework7 混合开发的音乐APP
webview性能还是不错得。喜欢google material ui, 所以用了framework7。
纯业余选手,欢迎指教!

在这里下载

收起阅读 »

分享另一个(WWW资源)差量升级的方法

HBuider 提供差量升级方法,但需要为每级来做资源包。
分享一个,我一直用的(www资源)差量升级方法:
此方法:是客户端在启动时,先从服务器获取文件列表,然后对比本地文件的修改时间来升级相应的文件。
我用着挺爽的,修改、删除、添加功能,改变页面,客户端只要重新就是最新的。

服务器端(ASP)代码:比如 文件是 files.asp
[HOMEPAGE]c_main.html
<%
show "c_main.html"
show "c_chat.html"
show "c_setup.html"
show "c_showmoney.html"

show "js/enum.js"
show "js/date.js"
show "js/const.js"

show "image/message.png"
show "image/phone.png"
show "image/tablebody.jpg"
show "image/task.png"
show "css/main.css"
show "css/jquery.mobile-1.4.2.min.css"

'==================================FUNCTION========================
sub show(file)
dim md,fso,f
set fso=server.createobject("scripting.filesystemobject")
if fso.FileExists(server.mappath(file)) then
set f=fso.GetFile(server.mappath(file))
md=f.DateLastModified
response.write "/" & file &"|" & md & "|" & f.size & vbcrlf
end if
End Sub
===================END===============================================
上面是服务器端代码,
[HOMEPAGE] 是指定启动页面。
show 函数是显示需要更新的文件的信息,格式是 /filename/修改时间/文件大小[最后是回车]
注:以前的做法是直接遍历所有文件,但有一次不小心做备份,打包成rar,造成了客户端也下载了。所以还是指定可靠些。

下面是HBuilder 端代码

var RootUrl="服务器的地址"; //不用加http
var LocalRoot="_doc";
var ServerPath="/保存路径";
var HomePage;
var Downcount=0;

    document.addEventListener('plusready', function(){  
        //console.log("所有plus api都应该在此事件发生后调用,否则会出现plus is undefined。"  
        DownloadRes();  

    });  

    function StartProgram(){  

        location.href="..\/doc\/"+ServerPath.substr(1) + HomePage;  
    }  

    function Down(name){  
        var down=plus.downloader.createDownload("http://"+RootUrl +ServerPath+ name,{filename:LocalRoot +ServerPath + name},function(down,status) {  
            plus.downloader.enumerate(function (downs) {  
                Downcount--;  
                if (Downcount==0) { //都下载完了执行  
                    StartProgram();  
                }  
            });  
        });  
        down.start();  
    }  

    function DownloadRes(){  
        plus.downloader.clear();  
        var xhr=new plus.net.XMLHttpRequest();  
        xhr.onreadystatechange=function(){  
            if (xhr.readyState==4 && xhr.status==200) {  
                var body=xhr.responseText;  
                lines=body.split("\n");  
                for(var i=0;i<lines.length;i++){  
                    var attr=lines[i].split("|");  

                    if (attr[0]=="") continue;  
                    if (attr[0].indexOf("[HOMEPAGE]")==0) {  
                        HomePage="/"+ attr[0].substr(10);  
                        continue;  
                    } else {  
                        (function(name,time,ii,leng){  
                            plus.io.resolveLocalFileSystemURL(LocalRoot+ServerPath +name,function(fs){  
                                fs.file(function (f) {  
                                    if (f.lastModifiedDate<new Date(time)) {  
                                        Downcount++;  
                                        Down(name);  
                                    } else {  
                                        if (ii==leng-2 && Downcount==0) {  
                                            StartProgram();  
                                        }  
                                    }  

                                });  
                            },function(e) {  
                                Downcount++;  
                                Down(name);  
                            });  
                        })(attr[0],attr[1],i,lines.length);  
                    }  
                }  
            }  
        }  

        xhr.open("GET","http://"+ RootUrl +"服务器files.asp");  
        xhr.send();  
    }  
继续阅读 »

HBuider 提供差量升级方法,但需要为每级来做资源包。
分享一个,我一直用的(www资源)差量升级方法:
此方法:是客户端在启动时,先从服务器获取文件列表,然后对比本地文件的修改时间来升级相应的文件。
我用着挺爽的,修改、删除、添加功能,改变页面,客户端只要重新就是最新的。

服务器端(ASP)代码:比如 文件是 files.asp
[HOMEPAGE]c_main.html
<%
show "c_main.html"
show "c_chat.html"
show "c_setup.html"
show "c_showmoney.html"

show "js/enum.js"
show "js/date.js"
show "js/const.js"

show "image/message.png"
show "image/phone.png"
show "image/tablebody.jpg"
show "image/task.png"
show "css/main.css"
show "css/jquery.mobile-1.4.2.min.css"

'==================================FUNCTION========================
sub show(file)
dim md,fso,f
set fso=server.createobject("scripting.filesystemobject")
if fso.FileExists(server.mappath(file)) then
set f=fso.GetFile(server.mappath(file))
md=f.DateLastModified
response.write "/" & file &"|" & md & "|" & f.size & vbcrlf
end if
End Sub
===================END===============================================
上面是服务器端代码,
[HOMEPAGE] 是指定启动页面。
show 函数是显示需要更新的文件的信息,格式是 /filename/修改时间/文件大小[最后是回车]
注:以前的做法是直接遍历所有文件,但有一次不小心做备份,打包成rar,造成了客户端也下载了。所以还是指定可靠些。

下面是HBuilder 端代码

var RootUrl="服务器的地址"; //不用加http
var LocalRoot="_doc";
var ServerPath="/保存路径";
var HomePage;
var Downcount=0;

    document.addEventListener('plusready', function(){  
        //console.log("所有plus api都应该在此事件发生后调用,否则会出现plus is undefined。"  
        DownloadRes();  

    });  

    function StartProgram(){  

        location.href="..\/doc\/"+ServerPath.substr(1) + HomePage;  
    }  

    function Down(name){  
        var down=plus.downloader.createDownload("http://"+RootUrl +ServerPath+ name,{filename:LocalRoot +ServerPath + name},function(down,status) {  
            plus.downloader.enumerate(function (downs) {  
                Downcount--;  
                if (Downcount==0) { //都下载完了执行  
                    StartProgram();  
                }  
            });  
        });  
        down.start();  
    }  

    function DownloadRes(){  
        plus.downloader.clear();  
        var xhr=new plus.net.XMLHttpRequest();  
        xhr.onreadystatechange=function(){  
            if (xhr.readyState==4 && xhr.status==200) {  
                var body=xhr.responseText;  
                lines=body.split("\n");  
                for(var i=0;i<lines.length;i++){  
                    var attr=lines[i].split("|");  

                    if (attr[0]=="") continue;  
                    if (attr[0].indexOf("[HOMEPAGE]")==0) {  
                        HomePage="/"+ attr[0].substr(10);  
                        continue;  
                    } else {  
                        (function(name,time,ii,leng){  
                            plus.io.resolveLocalFileSystemURL(LocalRoot+ServerPath +name,function(fs){  
                                fs.file(function (f) {  
                                    if (f.lastModifiedDate<new Date(time)) {  
                                        Downcount++;  
                                        Down(name);  
                                    } else {  
                                        if (ii==leng-2 && Downcount==0) {  
                                            StartProgram();  
                                        }  
                                    }  

                                });  
                            },function(e) {  
                                Downcount++;  
                                Down(name);  
                            });  
                        })(attr[0],attr[1],i,lines.length);  
                    }  
                }  
            }  
        }  

        xhr.open("GET","http://"+ RootUrl +"服务器files.asp");  
        xhr.send();  
    }  
收起阅读 »

插件的简单配置

App离线打包 插件
    今天重新捡起插件归纳下步骤,俗话说好记性不如烂笔头,不要相信自己的记性有多好。有错误之处感谢指出  

前言

    首先得有离线打包吧,我用的是Webview集成方式。各种打包方式转下走  

Android集成5 SDK的几种方式的应用场景

google analytice为例子

1.在js层新建一个xxx.js,我这里取名为googleanalytice,js

document.addEventListener("plusready", function() {  
    var _BARCODE = 'googleanalytics',  
        B = window.plus.bridge;  
    var googleanalytics = {  
        googleanalyticstest: function(successCallback, errorCallback) {  
            var success = typeof successCallback !== 'function' ? null : function(args) {  
                    successCallback(args);  
                },  
                fail = typeof errorCallback !== 'function' ? null : function(code) {  
                    errorCallback(code);  
                };  
            callbackID = B.callbackId(success, fail);  
            return B.exec(_BARCODE, "googleanalyticstest", [callbackID]);  
        }  
    };  
    window.plus.googleanalytics = googleanalytics;  
}, true);

这里面有几个参数要说明下googleanalytics,这个要对应这2中feature的name一致
googleanalyticstest要和3中的方法名一致

2.在assets目录下的data目录下的properties.xml加上这句话

<feature name="googleanalytics" value="cn.org.GoogleAnalytics.GoogleAnalytics" />

3.在2中可以看到feature下的value值,它对应着java类,我这边写的是

public class GoogleAnalytics extends StandardFeature {  
    public void googleanalyticstest(final IWebview pWebview, final JSONArray array){  
        Intent intent = new Intent(pWebview.getActivity(),AnalyticsCeshi.class);  
        pWebview.getActivity().startActivityForResult(intent, 1);  
    }  
}

我这里写了个跳转,可以直接在googleanalyticstest写逻辑代码,我比较喜欢在跳转一次到Activity里写逻辑,所以我的逻辑写在AnalyticsCeshi.java里,Activity别忘记在androidmainfest里声明。

4.最后在哪里调用就写上
plus.googleanalytics.googleanalyticstest();

别忘记引入js文件

最后

就想到这么多了,有错误之处感谢指出。(这编辑器有毒吗)

继续阅读 »
    今天重新捡起插件归纳下步骤,俗话说好记性不如烂笔头,不要相信自己的记性有多好。有错误之处感谢指出  

前言

    首先得有离线打包吧,我用的是Webview集成方式。各种打包方式转下走  

Android集成5 SDK的几种方式的应用场景

google analytice为例子

1.在js层新建一个xxx.js,我这里取名为googleanalytice,js

document.addEventListener("plusready", function() {  
    var _BARCODE = 'googleanalytics',  
        B = window.plus.bridge;  
    var googleanalytics = {  
        googleanalyticstest: function(successCallback, errorCallback) {  
            var success = typeof successCallback !== 'function' ? null : function(args) {  
                    successCallback(args);  
                },  
                fail = typeof errorCallback !== 'function' ? null : function(code) {  
                    errorCallback(code);  
                };  
            callbackID = B.callbackId(success, fail);  
            return B.exec(_BARCODE, "googleanalyticstest", [callbackID]);  
        }  
    };  
    window.plus.googleanalytics = googleanalytics;  
}, true);

这里面有几个参数要说明下googleanalytics,这个要对应这2中feature的name一致
googleanalyticstest要和3中的方法名一致

2.在assets目录下的data目录下的properties.xml加上这句话

<feature name="googleanalytics" value="cn.org.GoogleAnalytics.GoogleAnalytics" />

3.在2中可以看到feature下的value值,它对应着java类,我这边写的是

public class GoogleAnalytics extends StandardFeature {  
    public void googleanalyticstest(final IWebview pWebview, final JSONArray array){  
        Intent intent = new Intent(pWebview.getActivity(),AnalyticsCeshi.class);  
        pWebview.getActivity().startActivityForResult(intent, 1);  
    }  
}

我这里写了个跳转,可以直接在googleanalyticstest写逻辑代码,我比较喜欢在跳转一次到Activity里写逻辑,所以我的逻辑写在AnalyticsCeshi.java里,Activity别忘记在androidmainfest里声明。

4.最后在哪里调用就写上
plus.googleanalytics.googleanalyticstest();

别忘记引入js文件

最后

就想到这么多了,有错误之处感谢指出。(这编辑器有毒吗)

收起阅读 »

mui.prompt执行多次的奇葩问题

业务描述:
简单的html页面, 页面上有一个"预约"按钮, 其他部分为广告信息.

期待结果:点击预约按钮, 弹出预约金额对话框, 输入金额,点击确定, 提交ajax请求.

实际结果:点击预约按钮, 在对话框中数据金额, 点击确定按钮, 提交ajax请求两次!!!两次!!!

代码如下:

$(document).ready(function(){  
        $("button#bookProduct").unbind();  
        $("button#bookProduct").bind("click", function() {  
            mui.prompt(  
                "请输入预约信息:\n产品名称:"+$("input[name=productname]").val(),  
                "预约金额(单位:元)",  
                "预约信息",["取消","确定"],function(e) {  
                    if (e.index == 1) {  
                        if(e.value==''){  
                            mui.alert("金额必须为数字");  
                            return false;  
                        }else{  
                            $.post("/wap/productInfo/bookProduct",  
                            {  
                                account: e.value,  
                                nickname:$("input[name=nickname]").val(),  
                                productName:$("input[name=productname]").val(),  
                                managerOpenId : $("input[name=managerOpenId]").val(),  
                                openId:$("input[name=openId]").val()  
                            });   
                            mui.alert("预约信息已发送至客户经理。","预约成功",function(){  
                                WeixinJSBridge.call('closeWindow',5000);  
                            });  
                        }  
                    } else {  

                    }  
                });  
            document.querySelector('.mui-popup-input input').type='number';  
        });  
    });

最后经查证, 本页面中引入前后引入两次:<script src="xxxxx/Mui/js/mui.min.js"></script>

分享一下教训:
出现页面中点击按钮出现多次提交的情况一般有以下两种原因:

  1. 像我本次犯得错误一样, 多次引入相同的js库文件.(这种情况执行次数一般是固定的)
  2. 在页面加载事件(load或ready)中使用了bind事件并且在此之前没有解绑定(unbind).(这种情况执行次数会每刷新一次页面增加一次)

希望大家引以为戒, 避免跳坑.

继续阅读 »

业务描述:
简单的html页面, 页面上有一个"预约"按钮, 其他部分为广告信息.

期待结果:点击预约按钮, 弹出预约金额对话框, 输入金额,点击确定, 提交ajax请求.

实际结果:点击预约按钮, 在对话框中数据金额, 点击确定按钮, 提交ajax请求两次!!!两次!!!

代码如下:

$(document).ready(function(){  
        $("button#bookProduct").unbind();  
        $("button#bookProduct").bind("click", function() {  
            mui.prompt(  
                "请输入预约信息:\n产品名称:"+$("input[name=productname]").val(),  
                "预约金额(单位:元)",  
                "预约信息",["取消","确定"],function(e) {  
                    if (e.index == 1) {  
                        if(e.value==''){  
                            mui.alert("金额必须为数字");  
                            return false;  
                        }else{  
                            $.post("/wap/productInfo/bookProduct",  
                            {  
                                account: e.value,  
                                nickname:$("input[name=nickname]").val(),  
                                productName:$("input[name=productname]").val(),  
                                managerOpenId : $("input[name=managerOpenId]").val(),  
                                openId:$("input[name=openId]").val()  
                            });   
                            mui.alert("预约信息已发送至客户经理。","预约成功",function(){  
                                WeixinJSBridge.call('closeWindow',5000);  
                            });  
                        }  
                    } else {  

                    }  
                });  
            document.querySelector('.mui-popup-input input').type='number';  
        });  
    });

最后经查证, 本页面中引入前后引入两次:<script src="xxxxx/Mui/js/mui.min.js"></script>

分享一下教训:
出现页面中点击按钮出现多次提交的情况一般有以下两种原因:

  1. 像我本次犯得错误一样, 多次引入相同的js库文件.(这种情况执行次数一般是固定的)
  2. 在页面加载事件(load或ready)中使用了bind事件并且在此之前没有解绑定(unbind).(这种情况执行次数会每刷新一次页面增加一次)

希望大家引以为戒, 避免跳坑.

收起阅读 »

【分享】图片预览增加标题 及 父子web下全屏

图片预览
  1. 图片全屏预览加了个标题 ,父子页面全屏用的是通知父页面显示遮罩的方法(有更好的办法可以留言),有需要的可以看一下,在原先的基础上做了些改动,css和js放在了附件里,改动的地方可以在js里搜索关键字zrh
    标题效果:
  2. 简单讲一下改动的地方吧,有漏掉的地方请谅解,
  • 使用方法是在img标签里增加data-content="照片标题",

  • css里增加了个样式

    .mui-preview-footer .mui-preview-footerContent {  
    display: block;  
    line-height: 25px;  
    color: #fff;  
    text-align: center;  
    margin: 15px auto 4;  
    
    background-color: rgba(0, 0, 0, 0.4);  
    
    font-size: 16px;  
    }
  • .mui-preview-footer里的height改到20px;其它还有没有自己对比哈。

  • js里代码第3行里有个mui-preview-footer mui-hidden的class,去掉了mui-hidden

  • 13行加了个标签<span class="mui-preview-footerContent"></span>

  • 其余的改动自己搜索zrh看吧

  • 325行和353行那几行代码是显示和关闭遮罩的。父页面的代码是

    
    var flag = false;  
    var mask = mui.createMask(callback);  
    function callback(){  
    return flag;  
    }  
    window.addEventListener('showMask',function(event){  
    mask.show();//显示遮罩  

});
window.addEventListener('closeMask',function(event){
flag = true ;
mask.close();
flag = false ;
});



就这么多吧,有问题留言。
继续阅读 »
  1. 图片全屏预览加了个标题 ,父子页面全屏用的是通知父页面显示遮罩的方法(有更好的办法可以留言),有需要的可以看一下,在原先的基础上做了些改动,css和js放在了附件里,改动的地方可以在js里搜索关键字zrh
    标题效果:
  2. 简单讲一下改动的地方吧,有漏掉的地方请谅解,
  • 使用方法是在img标签里增加data-content="照片标题",

  • css里增加了个样式

    .mui-preview-footer .mui-preview-footerContent {  
    display: block;  
    line-height: 25px;  
    color: #fff;  
    text-align: center;  
    margin: 15px auto 4;  
    
    background-color: rgba(0, 0, 0, 0.4);  
    
    font-size: 16px;  
    }
  • .mui-preview-footer里的height改到20px;其它还有没有自己对比哈。

  • js里代码第3行里有个mui-preview-footer mui-hidden的class,去掉了mui-hidden

  • 13行加了个标签<span class="mui-preview-footerContent"></span>

  • 其余的改动自己搜索zrh看吧

  • 325行和353行那几行代码是显示和关闭遮罩的。父页面的代码是

    
    var flag = false;  
    var mask = mui.createMask(callback);  
    function callback(){  
    return flag;  
    }  
    window.addEventListener('showMask',function(event){  
    mask.show();//显示遮罩  

});
window.addEventListener('closeMask',function(event){
flag = true ;
mask.close();
flag = false ;
});



就这么多吧,有问题留言。
收起阅读 »

Dcloud HTML5 监听蓝牙设备 调用 原生安卓实现

蓝牙 监听 插件

最近一直搞Dcloud ,这是HTML5版本的开发,打包时候,可以打包成 apk 和ipa 分别运行在安卓和ios 机器上面,

但是这里面的资料很少,遇到问题,之后只能自己钻研总结,

现在有这么一个需求,需要实现蓝牙开启后,监听蓝牙接口传递的数据,然后呈现给用户。我试了很多方法,之前是在 HTML5上直接写方法,监听蓝牙状态,但是jS是单线程,直接把进程阻塞了其他的啥也干不了,而且时间长了监听,总会莫名中断,

这是我当时写的算法

http://ask.dcloud.net.cn/publish/article/875

这是直接在html5上监听蓝牙,后来多次测量之后发现,根本不能用。

使用了多种方法,比如 使用work开启多线程,但是问题是 work开启了但是无法调用plus对象,没有任何用处。

使用异步调用,也无法实现,

后来经过多次询问大牛之后,发现,这个问题目前最好使用html5插件 的形式解决,写完了插件,然后,用html点击之后,触发按钮调用原生的蓝牙监听程序。

具体的文档在这里:

http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/66

具体的实现方法,这篇文档,我至少要读了5遍左右,终于写出了插件,成功的调用了原生监听程序。

要知道如果我们要写安卓插件的话,只能使用离线打包,目前不支持在线打包。

因此如果写原生插件,首先要会离线打包,使用安卓程序打包,

具体的打包流程请参考

http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/38

我建议大家多多几遍,需要注意的事项:

(1) 离线打包要下载他们的sdk 地址:

http://ask.dcloud.net.cn/article/103

(2)注意:因为其它第三方的SDK也使用到包名相关资源配置,所以需要在AndroidManifest.xml将所有“io.dcloud.HBuilder.Hello”替换成要修改成的包名。

(3)打开assets -> apps 目录,将下面“HelloH5”目录名称修改为应用manifest.json中的id名称(这步非常重要,否则会导致应用无法正常启动),并将所有应用资源拷贝到其下的www目录中:

(4)打开assets -> data下的control.xml文件,修改appid值:

其中appid值为HBuilder应用的appid,必须与应用manifest.json中的id值完全一致;appver为应用的版本号,用于应用资源的升级,必须保持与manifest.json中的version -> name值完全一致;version值为应用基座版本号(plus.runtime.innerVersion返回的值),不要随意修改。

学会了如何打包之后,我们可以做一个很小的打包的源程序,专门用来打包用,

然后下一步就是写插件了:

具体文档在:http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/66

具体的方法就是,先使用安卓源程序上写好监听的方法,然后把html5的文件放到资源文件夹assess底下,调用写好的安卓监听方法;

具体需要注意的地方:
(1)首先要下载HTML5+基座的Android版SDK点击下载解压后将HBuilder-Integrate工程导入到ADT中。 这里你会下载到好几个文件夹,有一个HBuilder-hello的 这个是离线打包用的,还有个是 HBuilder-Integrate,这个已经写好了插件了,运行一下看看

(2) 关联JS插件名和原生类

在编写扩展插件时需要修改“/assets/data”中properties.xml文件,在其中添加JS对象名称和Android包的类名对应关系,SDK会根据对应的类名查找并生成相应的对象并执行对应的逻辑。

注意这里是在feature 的属性底下加上这个,例子中在service底下加了,如果不在feature底下加的话会报错,缺少plusintest插件的权限。

"应用未添加plugintest权限,请在manifest.json文件中permissions节点进行配置,"
出现这个错误,怎么解决

就是这个错误:

(3)两个都好了之后,把写好的插件整合到 你的项目中。

继续阅读 »

最近一直搞Dcloud ,这是HTML5版本的开发,打包时候,可以打包成 apk 和ipa 分别运行在安卓和ios 机器上面,

但是这里面的资料很少,遇到问题,之后只能自己钻研总结,

现在有这么一个需求,需要实现蓝牙开启后,监听蓝牙接口传递的数据,然后呈现给用户。我试了很多方法,之前是在 HTML5上直接写方法,监听蓝牙状态,但是jS是单线程,直接把进程阻塞了其他的啥也干不了,而且时间长了监听,总会莫名中断,

这是我当时写的算法

http://ask.dcloud.net.cn/publish/article/875

这是直接在html5上监听蓝牙,后来多次测量之后发现,根本不能用。

使用了多种方法,比如 使用work开启多线程,但是问题是 work开启了但是无法调用plus对象,没有任何用处。

使用异步调用,也无法实现,

后来经过多次询问大牛之后,发现,这个问题目前最好使用html5插件 的形式解决,写完了插件,然后,用html点击之后,触发按钮调用原生的蓝牙监听程序。

具体的文档在这里:

http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/66

具体的实现方法,这篇文档,我至少要读了5遍左右,终于写出了插件,成功的调用了原生监听程序。

要知道如果我们要写安卓插件的话,只能使用离线打包,目前不支持在线打包。

因此如果写原生插件,首先要会离线打包,使用安卓程序打包,

具体的打包流程请参考

http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/38

我建议大家多多几遍,需要注意的事项:

(1) 离线打包要下载他们的sdk 地址:

http://ask.dcloud.net.cn/article/103

(2)注意:因为其它第三方的SDK也使用到包名相关资源配置,所以需要在AndroidManifest.xml将所有“io.dcloud.HBuilder.Hello”替换成要修改成的包名。

(3)打开assets -> apps 目录,将下面“HelloH5”目录名称修改为应用manifest.json中的id名称(这步非常重要,否则会导致应用无法正常启动),并将所有应用资源拷贝到其下的www目录中:

(4)打开assets -> data下的control.xml文件,修改appid值:

其中appid值为HBuilder应用的appid,必须与应用manifest.json中的id值完全一致;appver为应用的版本号,用于应用资源的升级,必须保持与manifest.json中的version -> name值完全一致;version值为应用基座版本号(plus.runtime.innerVersion返回的值),不要随意修改。

学会了如何打包之后,我们可以做一个很小的打包的源程序,专门用来打包用,

然后下一步就是写插件了:

具体文档在:http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/66

具体的方法就是,先使用安卓源程序上写好监听的方法,然后把html5的文件放到资源文件夹assess底下,调用写好的安卓监听方法;

具体需要注意的地方:
(1)首先要下载HTML5+基座的Android版SDK点击下载解压后将HBuilder-Integrate工程导入到ADT中。 这里你会下载到好几个文件夹,有一个HBuilder-hello的 这个是离线打包用的,还有个是 HBuilder-Integrate,这个已经写好了插件了,运行一下看看

(2) 关联JS插件名和原生类

在编写扩展插件时需要修改“/assets/data”中properties.xml文件,在其中添加JS对象名称和Android包的类名对应关系,SDK会根据对应的类名查找并生成相应的对象并执行对应的逻辑。

注意这里是在feature 的属性底下加上这个,例子中在service底下加了,如果不在feature底下加的话会报错,缺少plusintest插件的权限。

"应用未添加plugintest权限,请在manifest.json文件中permissions节点进行配置,"
出现这个错误,怎么解决

就是这个错误:

(3)两个都好了之后,把写好的插件整合到 你的项目中。

收起阅读 »

常用的正则表达式

更全的正则表达式请访问 最全的常用正则表达式大全
由于工作需要,需要用到很多正则表达式来进行相关的验证,下面是一些常用的正则表达式,如有错误请及时指正:

  1. 判断是否为纯数字(比如电话等)
    /^\d+$/ ,
    例: /^\d+$/.test(str) ;
  2. 判断字符串中是否包含数字(比如姓名等)
    /[0-9]/,
    例:/[0-9]/.test(str);
  3. 判断字符串中是否包含特殊字符(比如姓名等)
    /[~!@#\$%\^\&\*\(\)_\+<>\?:"\{\},\.\\\/;'\[\]]/im, 例:/[~!@#\$%\^\&*()_+<>\?:"{},.\\/;'[]]/im.test(str);(注:此正则表达式并不能包含所有的特殊字符,根据实际情况而定)
  4. 判断电话号码的位数(包含7位和11位)
    /(\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$/,
    例: /(\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$/.test(str);
  5. 判断邮箱格式是否正确
    /^([a-zA-Z0-9.-])+\@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/,
    例:/^([a-zA-Z0-9
    .-])+\@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/.test(str);
  6. 判断是否只包含数字和字母:
    /^[A-Za-z0-9]+$/。

正则表达式——古老而又强大的文本处理工具。仅用一段简短的表达式语句,就能快速地实现一个复杂的业务逻辑。掌握正则表达式,让你的开发效率有一个质的飞跃。

正则表达式经常被用于字段或任意字符串的校验,比如下面这段校验基本日期格式的JavaScript代码:

var reg = /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/;
var r = fieldValue.match(reg);
if(r==null)alert('Date format error!');

1、校验密码强度
密码的强度必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间

^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,10}$

2、校验中文
字符串只能是中文

^[\u4e00-\u9fa5]{0,}$

3、由数字,26个英文字母或下划线组成的字符串

^\w+$

4、校验E-Mail 地址

[\w!#$%&'+/=?^_`{|}~-]+(?:\.[\w!#$%&'+/=?^_`{|}~-]+)@(?:[\w](?:[\w-][\w])?\.)+\w?

5、校验身份证号码
15位:

^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$

18位:

^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$

6、校验日期
“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年

^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$

7、校验金额
金额校验,精确到2位小数

^[0-9]+(.[0-9]{2})?$

8、校验手机号
下面是国内 13、15、18开头的手机号正则表达式

^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$

9、判断IE的版本

^.MSIE 5-8?(?!.Trident\/[5-9]\.0).*$

10、校验IP-v4地址

\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b

11、校验IP-v6地址

(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))

12、检查URL的前缀

if (!s.match(/^[a-zA-Z]+:\/\//)){
s = 'http://' + s;}

13、提取URL链接

^(f|ht){1}(tp|tps):\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?

14、文件路径及扩展名校验

^([a-zA-Z]\:|\\)\\([^\\]+\\)[^\/:?"<>|]+\.txt(l)?$

15、提取Color Hex Codes

^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$

16、提取网页图片

\< [img][^\\>][src] = [\"\']{0,1}([^\"\'\ >]*)

17、提取页面超链接

(<a\s(?!.\brel=)[^>])(href="https?:\/\/)((?!(?:(?:www\.)?'.implode('|(?:www\.)?', $follow_list).'))[^"]+)"((?!.\brel=)[^>])(?:[^>])>

18、查找CSS属性

^\s[a-zA-Z\-]+\s[:]{1}\s[a-zA-Z0-9\s.#]+[;]{1}

19、抽取注释

<!--(.*?)-->

20、匹配HTML标签

<\/?\w+((\s+\w+(\s=\s(?:".?"|'.?'|[\^'">\s]+))?)+\s|\s)\/?>

正则表达式 Cheat Sheet,可以用来快速查找相关语法

文/技匠(简书签约作者)
原文链接:http://www.jianshu.com/p/e7bb97218946

https://mp.weixin.qq.com/s?__biz=MzAxNjM3NzM5Mg==&mid=2453281996&idx=1&sn=3fb5bdc7c3d1cc1259aa0f453253e459&chksm=8c3b0f93bb4c8685e6cebf474ca3eb4f562e4dfe8c954e8cd2293cf2836107f2ce16ebbeb4b8&scene=0&key=1509e3bfc22f49824b0a4028c5f7432cbc673973567dc74eec078b2d8fac9e8bb5d50d85c3627b8efbf2205e1fb2034c20faf1639bf33b76a22885ae82de62daa81f7ae43d4b63b3d25f7d4c9aadda54&ascene=0&uin=MTYzOTA2OTA0MA%3D%3D&devicetype=iMac14%2C3+OSX+OSX+10.12.2+build(16C67)&version=12010210&nettype=WIFI&fontScale=100&pass_ticket=l7XDNuCDiHDNdGhimfV5LgEMsqKPsuNBtmPpkQs9Iuq0lmwWrQs1oURNiyKZNKaK

继续阅读 »

更全的正则表达式请访问 最全的常用正则表达式大全
由于工作需要,需要用到很多正则表达式来进行相关的验证,下面是一些常用的正则表达式,如有错误请及时指正:

  1. 判断是否为纯数字(比如电话等)
    /^\d+$/ ,
    例: /^\d+$/.test(str) ;
  2. 判断字符串中是否包含数字(比如姓名等)
    /[0-9]/,
    例:/[0-9]/.test(str);
  3. 判断字符串中是否包含特殊字符(比如姓名等)
    /[~!@#\$%\^\&\*\(\)_\+<>\?:"\{\},\.\\\/;'\[\]]/im, 例:/[~!@#\$%\^\&*()_+<>\?:"{},.\\/;'[]]/im.test(str);(注:此正则表达式并不能包含所有的特殊字符,根据实际情况而定)
  4. 判断电话号码的位数(包含7位和11位)
    /(\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$/,
    例: /(\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$/.test(str);
  5. 判断邮箱格式是否正确
    /^([a-zA-Z0-9.-])+\@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/,
    例:/^([a-zA-Z0-9
    .-])+\@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/.test(str);
  6. 判断是否只包含数字和字母:
    /^[A-Za-z0-9]+$/。

正则表达式——古老而又强大的文本处理工具。仅用一段简短的表达式语句,就能快速地实现一个复杂的业务逻辑。掌握正则表达式,让你的开发效率有一个质的飞跃。

正则表达式经常被用于字段或任意字符串的校验,比如下面这段校验基本日期格式的JavaScript代码:

var reg = /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/;
var r = fieldValue.match(reg);
if(r==null)alert('Date format error!');

1、校验密码强度
密码的强度必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间

^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,10}$

2、校验中文
字符串只能是中文

^[\u4e00-\u9fa5]{0,}$

3、由数字,26个英文字母或下划线组成的字符串

^\w+$

4、校验E-Mail 地址

[\w!#$%&'+/=?^_`{|}~-]+(?:\.[\w!#$%&'+/=?^_`{|}~-]+)@(?:[\w](?:[\w-][\w])?\.)+\w?

5、校验身份证号码
15位:

^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$

18位:

^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$

6、校验日期
“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年

^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$

7、校验金额
金额校验,精确到2位小数

^[0-9]+(.[0-9]{2})?$

8、校验手机号
下面是国内 13、15、18开头的手机号正则表达式

^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$

9、判断IE的版本

^.MSIE 5-8?(?!.Trident\/[5-9]\.0).*$

10、校验IP-v4地址

\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b

11、校验IP-v6地址

(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))

12、检查URL的前缀

if (!s.match(/^[a-zA-Z]+:\/\//)){
s = 'http://' + s;}

13、提取URL链接

^(f|ht){1}(tp|tps):\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?

14、文件路径及扩展名校验

^([a-zA-Z]\:|\\)\\([^\\]+\\)[^\/:?"<>|]+\.txt(l)?$

15、提取Color Hex Codes

^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$

16、提取网页图片

\< [img][^\\>][src] = [\"\']{0,1}([^\"\'\ >]*)

17、提取页面超链接

(<a\s(?!.\brel=)[^>])(href="https?:\/\/)((?!(?:(?:www\.)?'.implode('|(?:www\.)?', $follow_list).'))[^"]+)"((?!.\brel=)[^>])(?:[^>])>

18、查找CSS属性

^\s[a-zA-Z\-]+\s[:]{1}\s[a-zA-Z0-9\s.#]+[;]{1}

19、抽取注释

<!--(.*?)-->

20、匹配HTML标签

<\/?\w+((\s+\w+(\s=\s(?:".?"|'.?'|[\^'">\s]+))?)+\s|\s)\/?>

正则表达式 Cheat Sheet,可以用来快速查找相关语法

文/技匠(简书签约作者)
原文链接:http://www.jianshu.com/p/e7bb97218946

https://mp.weixin.qq.com/s?__biz=MzAxNjM3NzM5Mg==&mid=2453281996&idx=1&sn=3fb5bdc7c3d1cc1259aa0f453253e459&chksm=8c3b0f93bb4c8685e6cebf474ca3eb4f562e4dfe8c954e8cd2293cf2836107f2ce16ebbeb4b8&scene=0&key=1509e3bfc22f49824b0a4028c5f7432cbc673973567dc74eec078b2d8fac9e8bb5d50d85c3627b8efbf2205e1fb2034c20faf1639bf33b76a22885ae82de62daa81f7ae43d4b63b3d25f7d4c9aadda54&ascene=0&uin=MTYzOTA2OTA0MA%3D%3D&devicetype=iMac14%2C3+OSX+OSX+10.12.2+build(16C67)&version=12010210&nettype=WIFI&fontScale=100&pass_ticket=l7XDNuCDiHDNdGhimfV5LgEMsqKPsuNBtmPpkQs9Iuq0lmwWrQs1oURNiyKZNKaK

收起阅读 »

mui下拉模块安卓有问题

共用模板,下拉刷新

安卓机父子webview,子webview用上下拉刷新模块后,无法往下滑动,且下拉距离非常短,不够正在刷新那个div的高度;
mui("#refresh").scroll({
scrollY: true, //是否竖向滚动
scrollX: false, //是否横向滚动
startX: 0, //初始化时滚动至x
startY: 0, //初始化时滚动至y
indicators: false, //是否显示滚动条
deceleration: 0.0005, //阻尼系数,系数越小滑动越灵敏
bounce: true //是否启用回弹
});
mui.init({
pullRefresh: {
container: "#refresh", //下拉刷新容器标识,querySelector能定位的css选择器均可,比如:id、.class等
down: {
height: 50, //可选,默认50.触发下拉刷新拖动距离,
auto: false, //可选,默认false.自动下拉刷新一次
contentdown: "下拉可以刷新", //可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容
contentover: "释放立即刷新", //可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容
contentrefresh: "正在刷新...", //可选,正在刷新状态时,下拉刷新控件上显示的标题内容
callback: pullupfresh //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据;
}
}
});

        function pullupfresh() {  
            setTimeout(function() {  
                alert("shuaxin");  
                mui('#refresh').pullRefresh().endPulldownToRefresh();  
            })  
        }  

这些都写了,ios没问题,就安卓的无法往下滑动。

继续阅读 »

安卓机父子webview,子webview用上下拉刷新模块后,无法往下滑动,且下拉距离非常短,不够正在刷新那个div的高度;
mui("#refresh").scroll({
scrollY: true, //是否竖向滚动
scrollX: false, //是否横向滚动
startX: 0, //初始化时滚动至x
startY: 0, //初始化时滚动至y
indicators: false, //是否显示滚动条
deceleration: 0.0005, //阻尼系数,系数越小滑动越灵敏
bounce: true //是否启用回弹
});
mui.init({
pullRefresh: {
container: "#refresh", //下拉刷新容器标识,querySelector能定位的css选择器均可,比如:id、.class等
down: {
height: 50, //可选,默认50.触发下拉刷新拖动距离,
auto: false, //可选,默认false.自动下拉刷新一次
contentdown: "下拉可以刷新", //可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容
contentover: "释放立即刷新", //可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容
contentrefresh: "正在刷新...", //可选,正在刷新状态时,下拉刷新控件上显示的标题内容
callback: pullupfresh //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据;
}
}
});

        function pullupfresh() {  
            setTimeout(function() {  
                alert("shuaxin");  
                mui('#refresh').pullRefresh().endPulldownToRefresh();  
            })  
        }  

这些都写了,ios没问题,就安卓的无法往下滑动。

收起阅读 »

移动应用与HTML5产业峰会召开啦,HTML5 中国产业联盟将在大会上举行挂靠于工信部通信研究院标准所仪式,欢迎踊跃报名

规范 联盟 HTML5+

由工业和信息化部、中国信息通信研究院、移动智能终端技术创新与产业联盟举办的峰会、W3C (中国)及HTML5 中国产业联盟共同承办的“移动应用与HTML5产业”分论坛邀你参加!

会议亮点

  • HTML5 中国产业联盟挂靠工信部仪式
  • HTML5行业的最新动态,标准和应用的发展研讨
  • 小程序及流应用等动态App会对移动互联网发展产生怎样的影响,开发者、流量平台、手机厂商、从业人士都怎么看。

时间

10月28日(9:00-12:00)

地点

北京国家会议中心(鸟巢旁)

会场位置(北京)

会议日程

扫描二维码,选择分会场四免费报名参会:


风雨雾霾都无阻,我们等你!!

继续阅读 »

由工业和信息化部、中国信息通信研究院、移动智能终端技术创新与产业联盟举办的峰会、W3C (中国)及HTML5 中国产业联盟共同承办的“移动应用与HTML5产业”分论坛邀你参加!

会议亮点

  • HTML5 中国产业联盟挂靠工信部仪式
  • HTML5行业的最新动态,标准和应用的发展研讨
  • 小程序及流应用等动态App会对移动互联网发展产生怎样的影响,开发者、流量平台、手机厂商、从业人士都怎么看。

时间

10月28日(9:00-12:00)

地点

北京国家会议中心(鸟巢旁)

会场位置(北京)

会议日程

扫描二维码,选择分会场四免费报名参会:


风雨雾霾都无阻,我们等你!!

收起阅读 »

js,判断对象的类型,typeof,constructor

js判断对象的类型

javascript,判断对象的类型,typeof,constructor

用typeof操作符判断对象类型:(红色文字,是上面一句代码的执行结果)

用对象的构造函数属性(constructor),来判断对象的类型:

区别:

constructor判断对象的类型,会比typeof更精确,constructor能获取一些复杂对象的类型,typeof做不到。

下面有一段程序示例:

代码示例:

<script type="text/javascript" charset="utf-8">

var s='a string';

var arr=[];

var obj=new Object();

if(typeof s=='string'){

console.log( "typeof s=='string' true"); //true

}

//打开浏览器的控制台,可以看到此代码的输出

console.log( 's.constructor==String :'+ (s.constructor==String) );

console.log( 'arr.constructor==Array :'+ (arr.constructor==Array) );

console.log( 'obj.constructor==Object :'+ (obj.constructor==Object) );

//复杂类型的对象,判断其类型

function User(name , age){

this.name=name;

this.age=age;

}

var u=new User();

console.log( 'typeof u :'+typeof u ); //输出object //显然,使用typeof判断复杂类型的对象,就失效了,但使用constructor就可以获取其真实类型

console.log( 'u.constructor.name :'+u.constructor.name );

</script>

继续阅读 »

js判断对象的类型

javascript,判断对象的类型,typeof,constructor

用typeof操作符判断对象类型:(红色文字,是上面一句代码的执行结果)

用对象的构造函数属性(constructor),来判断对象的类型:

区别:

constructor判断对象的类型,会比typeof更精确,constructor能获取一些复杂对象的类型,typeof做不到。

下面有一段程序示例:

代码示例:

<script type="text/javascript" charset="utf-8">

var s='a string';

var arr=[];

var obj=new Object();

if(typeof s=='string'){

console.log( "typeof s=='string' true"); //true

}

//打开浏览器的控制台,可以看到此代码的输出

console.log( 's.constructor==String :'+ (s.constructor==String) );

console.log( 'arr.constructor==Array :'+ (arr.constructor==Array) );

console.log( 'obj.constructor==Object :'+ (obj.constructor==Object) );

//复杂类型的对象,判断其类型

function User(name , age){

this.name=name;

this.age=age;

}

var u=new User();

console.log( 'typeof u :'+typeof u ); //输出object //显然,使用typeof判断复杂类型的对象,就失效了,但使用constructor就可以获取其真实类型

console.log( 'u.constructor.name :'+u.constructor.name );

</script>

收起阅读 »