老大让我写个表单验证的插件,市面上的表单验证插件不再少数。但是老大让我写肯定是想锻炼我
2015-09-07 17:43
288 查看
作为一个初学者我只能写到这个样子了,对于内存,优化什么的也顾不了哪些了,勉勉强强算是实现了功能,还有很多漏洞。我写的第一个插件,不为别的留下来做个纪念。
//给jq添加一个verfiable方法
//2.2版本将验证方法分装成函数模块,通过属性来调用相对应的验证函数
//2.3TODO增加刷新验证的功能,修复动态增加输入框而不能进行验证的问题(setTnterval),修复表单提交的状态
//2.4增加用户指定元素显示错误的功能,将显示错误的代码提取出来。
//2.5功能模块化分为:初始化-执行验证模块-元素绑定函数模块-函数验证模块-验证结果处理模块-表单提交模块
//2.6优化验证函数的返回值,删除判断是否成功的代码。
//2.7将函数模块封装成一个对象,改用brack case代替value
//2.8添加表单提交的时的判断,其中有一个未通过验证则,表单不能提交
//2.9添加ajax远程验证,ajax作为特殊验证单独处理,未放在funModel内,而与其他条件并行验证
//2.10增加日期验证,调优submit提交验证
//2.11绑定select,checkbox,radios的事件,在点击提交按钮时验证这三种元素
//2.12将一些公用的部分提出来作为工具函数
//2.13表单序列化
/*data-rules中的属性氛围str,obj两种类型
data-rules={
id://用于提交表单提交时提醒用户该id出验证失败(如果用户自定义验证失败的函数,次属性可以省略,否则不可省略)
success://验证通过时的验证信息
userEle://用户指定的提示元素
isEmpt:str//验证是否为空
isChinese://是否为中文
isNumber://判断是否为数字
isPosint://是否为正整数
isEmall: //检验邮箱
isPhone: //检验电话号码
isUrl: //判断网址
isLong:{ //验证长度
min://最大长度
max://最小长度
tip://验证不通过时的提示信息
}
isID: {
minAge: 最小年龄
maxAge: 最大年龄
sex: ['male', 'female'] male 男性 female 女性
province: 省份名称,如北京、天津等
tip://提示信息
}
范围比较
isRange
{
* type: ['length','number', 'integer'] length 长度比较 number 数值比较 integer 整数数值比较(包含0)
min: 最小值
max: 最大值
tip:
}
uRegexp:{ //用户自定义正则表达式
* rule:'^1[3|4|5|8|7]\\d{9}$'//正则表达式格式必须使用new regexp的格式
igm://参数
tip://错误提示信息
}
uFunction:function(d){} //用户自定义验证函数
compare:{
* to://要比较的元素选择器
* condition://比较符号
tip://提示信息
}
vAjax:{
* url: // 远程校验的网址
* type:['POST','GET'] 方法,默认为post
* data:提交给服务器端的数据,可以是key1=value1&key2=value2、{key1:value1}、function(){return {key1:value1}}等形式
* dataType:['text','json', 'html']等
* verify:函数,用来校验返回数据,如果返回true,说明校验成功,如果返回false,说明校验失败。如果没有这个函数,则返回的数据为true或者'true'时认为成功,其他都为失败
tip:校验码填写不正确
}
checkbox:{
min:
max:
tip:
}
radio:str
select:str
}
设置修改提示元素的样式:
验证通过的class:success
验证不通过的class:error
TODO: 1.下拉表,单选框,多选框。
2.ajax远程验证
data-submit={
success:fun() 表单验证通过时的函数
error:fun() 表单验证未通过的时候执行的函数
}
*/
(function($){
$.fn.verfiable = function()
{
/*********************************初始化数据*******************************/
var form = $(this);
var all = form.find("[data-rules]"); //找出form下的所有需要验证的的元素
var ele = all.filter('[type=checkbox], [type=radio], select') //过滤出需要验证的checkbox,select,radio
var inps = all.not(ele) //剔除ele,剩下的则是输入框
var iniVal = { //定义初始值
success:"通过!",
error:"验证失败!"
}
/*********************************初始化数据*******************************/
/*********************************给每个需要验证的绑定函数*******************************/
var yanzhen = function (form){
inps.each(function(){
$(this).unbind("blur").bind("blur", function() //给每个input绑定失去焦点的函数
{
var data = $(this).val(); //此处获取val值,避免验证中重复获取
var rules = getRule(this,'data-rules'); //使用工具函数获取rules对象
var TipShow = getTipEle(this, rules); //用来显示提示信息(成功或失败)的元素
var TipTxt; //存放提示信息
//绑定获得焦点时函数,去除上次的错误提示start
$(this).unbind("focus").bind("focus",function()
{
if ($(this).next().hasClass("addEle")) { //如果是动态添加的tipshow则删除
$(this).next().remove();
} else if (rules.userEle || $(this).next().hasClass("userEle")) { //否则清除html内容
if ($(TipShow).hasClass('error')){
$(TipShow).removeClass("error");
} else {
$(TipShow.removeClass("success"));
}
$(TipShow).html("");
}
})
//绑定获得焦点时函数,去除上次的错误提示end
//遍历rules对象中的属性
for (var i in rules) { //将对象的每个属性都执行一遍对应的函数
var fun = eval("funModel."+i); //将rules的各个属性转化成函数模块funModel的属性
if (fun == undefined) { //如果函数库中没有对应的验证函数(保函ajax的情况),则跳过不验证次属性
continue;
};
//fun!=undefined说明funModel中有次对应的函数,且不是vAjax时则应执行此函数
var result = fun(rules[i], data ,this); //将rules穿参进去,执行每个验证规则的方法
TipTxt = getTipTxt(this, rules[i]);
/*根据返回值判断验证是否成功start*/
if (result===false) { //返回为false则验证不通过
TipShow.addClass("error").html(TipTxt);//将显示错误信息的元素添加class样式
this.setAttribute('data-pass', "false"); //标记为验证不通过
return false;
}
}
//执行到这里说明前方验证已通过,返回值不为false则视为成功 如果有ajax则还需进行ajax验证
//如果有ajax验证这一项则进行
if (rules.vAjax) {
var resultA;
//发动ajax请求
(function(R,T){
$.ajax({url:R.vAjax.url,type:R.vAjax.type,data:T.name+"="+data,async: true,success:function(data){
if(R.vAjax.fun){ //如果设置了验证函数,则将返回值穿入函数中验证该函数返回的为true或者false
resultA=R.vAjax.fun(data);
} else { //没设置验证函数则返回的必须是true或者false
resultA=data;
}
if (resultA == false) { //当验证失败时
console.log("ajax失败")
if (R.vAjax.tip) {
TipShow.addClass("error").html(R.vAjax.tip);
} else {
TipShow.addClass("error").html(iniVal.error);
}
T.setAttribute('data-pass', "false");//设置成功标记
} else { //验证成功时
if (R.success) {
TipShow.addClass("success").html(R.success);
} else {
TipShow.addClass("success").html(iniVal.success);
}
T.setAttribute('data-pass', "true");//设置成功标记
}
}})
})(rules, this)
/*******ajax远程验证结束*********/
} else { //如果没有
if (rules.success!=undefined) { //判断用户是否自定义提示信息
TipTxt = rules.success;
} else {
TipTxt =iniVal.success;
}
TipShow.addClass("success").html(TipTxt);
this.setAttribute('data-pass', "true");//设置成功标记
return true;
}
})
})
}
/*********************************绑定验证并调用函数*******************************/
/*********************************验证函数区域*******************************/
var funModel = {
//验证是否为空
isEmpt : function (Rule, data) //这里函数内的this指向的是window,所以需要将this一参数的形式传进来,
{
if ($.trim(data) == "") {
return false;
}
},
//验证长度是否通过
isLong : function (Rule, data)
{
if (data.length < Rule.min || data.length > Rule.max) {
return false;
}
},
//验证邮箱
isEmall : function (Rule, data)
{
var RegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|\.|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)((com(\.[a-z]{2})?)|net|cn|cc)$/i;
if(!RegExp.test(data)){
return false;
}
},
//验证手机号
isPhone : function ( Rule, data)
{
if(!/^1[3|4|5|8|7]\d{9}$/.test(data)){
return false;
}
},
//检测是否中文
isChinese : function (Rule, data)
{
if(!/^[\u4e00-\u9fa5]+$/.test(data)){
return false;
}
},
//验证是否为数字
isNumber : function(Rule, data)
{
if (isNaN(data)) {
return false;
};
},
//验证是否为为金额格式
isPosint: function(Rule, data)
{
if (/^[1-9](\d+)?$/.test(data)) {
return false;
};
},
//验证是否为url
isUrl : function (Rule, data)
{
var regexp = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
if (!regexp.test(data)) {
return false;
}
},
//范围值检验
isRange: function(rule, val){
if (undefined === rule.min) {
rule.min = Number.MIN_VALUE;
}
if (undefined === rule.max) {
rule.max = Number.MAX_VALUE;
}
switch(rule.type) {
case 'integer':
if( ! /^-?\d+$/.test(val)){
return false;
}
return val <= rule.max && val >= rule.min;
break;
case 'length':
return val.length <= rule.max && val.length >= rule.min;
break;
case 'number':
if(!isNaN(val)){
return val <= rule.max && val >= rule.min;
}else{
return false;
}
break;
}
},
//验证身份证
isID : function(rule, data)
{
if (!data) return rule.tip;
var len = data.length;
if (len != 15 && len != 18) {
return false;
}
// 检测基本格式是否正确
if (!/^(\d{15})|(\d{17}([0-9xX]))$/.test(data)) {
return false;
}
// 根据校验规则检查身份证合法性
if (len == 18) {
var total = 0, v = [1,0,'X',9,8,7,6,5,4,3,2], mod, rightCode;
$.each([7, 9, 10, 5, 8, 4, 2, 1, 6, 3], function(i){
if (i < 7) {
total += ((parseInt(data.charAt(i)) + parseInt(data.charAt(i+10))) * this);
} else {
total += (parseInt(data.charAt(i))) * this;
}
});
mod = total % 11;
rightCode = v[mod] + '';
if (data.charAt(17).toLowerCase() != rightCode.toLowerCase()) {
return false;
}
}
// 校验地区的合法性
if (!this.cities) {
this.cities = {11:'北京',12:'天津',13:'河北',14:'山西',15:'内蒙古',21:'辽宁',22:'吉林',23:'黑龙江 ',31:'上海',32:'江苏',33:'浙江',34:'安徽',35:'福建',36:'江西',37:'山东',41:'河南',42:'湖北',43:'湖南',44:'广东',45:'广西',46:'海南',50:'重庆',51:'四川',52:'贵州',53:'云南',54:'西藏 ',61:'陕西',62:'甘肃',63:'青海',64:'宁夏',65:'新疆',71:'台湾',81:'香港',82:'澳门',91:'国外'};
}
if (!(data.substr(0, 2) in this.cities)) {
return false;
}
// 检测限制的地区是否正确
if (this.cities[data.substr(0,2)] != rule.province) {
return false;
}
// 检查性别
if (rule.sex) {
var tag = data.substr(len == 15 ? len -1 : len - 2, 1);
if (tag % 2 == 0) {
if (rule.sex != 'female') {
return false;
}
} else {
if (rule.sex != 'male') {
return false;
}
}
}
// 检测生日的合法性
var yearLen = len == 15 ? 2 : 4,
year = parseInt(len == 2 ? '19' + data.substr(6, yearLen) : data.substr(6, 4), 10),
month = parseInt(data.substr(6 + yearLen, 2), 10),
day = parseInt(data.substr(8 + yearLen, 2), 10),
d = new Date(year, month - 1, day);
if (d.getFullYear() != year || d.getMonth() != month - 1 || d.getDate() != day) {
return false;
}
var offDay = parseInt((new Date().getTime() - d.getTime())/(1000*3600*24));
// 检查最小年龄
console.log("开始")
if (rule.minAge) {
if (offDay < 365 * rule.minAge) {
return false;
}
}
// 检查最大年龄
if (rule.maxAge) {
if (offDay > 365 * rule.maxAge) {
return false;
}
}
},
//验证是否为日期正则表达式
isDate : function(rule, val) {
var _dateReg = /^([12][0-9]{3})[\-\/\_\.\s]?(0?[1-9]|1[012])[\-\/\_\.\s]?(0?[1-9]|[12][0-9]|3[01])$/;
var ms = _dateReg.exec(val), date;//类似于test方法,匹配成功返回的为一个结果ji数组,失败则返回null
if (ms) { //若正则表达式匹配成功则执行
var y = parseInt(ms[1], 10), m = parseInt(ms[2], 10) - 1, d = parseInt(ms[3], 10);//取出年月日
try {
return new Date(y, m, d);//返回一个日期
} catch (e) {
return false;
}
}
return false;
},
//isDate:{
// min:
// max:
// tip:
// }
ruleDate:function(rule, val){
var date = funModel.isDate(rule,val);
if (date == false) {
return false;
}
// 检查最小数
if (rule.min || rule.max) {
var now = new Date();
if (rule.min) {
if (!isNaN(rule.min)) {
if ( (date.getTime() - now.getTime())/1000 < rule.min ) {
return false;
}
} else {
var min = funModel.isDate(rule, rule.min);
// if (!min) {
// return hapj.log.error('hapj.ui.verifiable date format wrong');
// }
console.log(min)
if (date.getTime() < min.getTime()) {
return false;
}
}
}
if (rule.max) {
if (!isNaN(rule.max)) {
if ( (date.getTime() - now.getTime())/1000 > rule.max ) {
return false;
}
} else {
var max = funModel.isDate(rule, rule.max);
// if (!max) {
// return hapj.log.error('hapj.ui.verifiable date format wrong');
// }
if (date.getTime() > max.getTime()) {
return false;
}
}
}
}
return true;
},
//比较验证
compare: function(rule, data) {
var cVal = $(rule.to).val();//to
if (!rule.condition) { //condition去除字符串两端的空格
rule.condition = '=';
}
switch(rule.condition) {
case '=':
case 'equal':
return data == cVal;
case '!=':
case '<>':
case 'notEqual':
return data != cVal;
case '>':
case 'great':
return data > cVal;
case '<':
case 'less':
return data < cVal;
case '>=':
case 'notGreat':
return data >= cVal;
case '<=':
case 'notLess':
return data <= cVal;
default:
return H.log.error('hapj.ui.verifiable the condition(' + rule.condition + ') is not defined');
}
},
//自定义正则表达式
uRegexp:function(rule ,data){
var regexp = new RegExp (rule.rule,rule.igm);
if (!regexp.test(data)) {
return false;
};
},
//自定义验证函数
uFunction:function(rule ,data,T){
if(T.type=="checkbox"||T.type=="radio"){
console.log("单选复选框啊")
}
return rule(data);
}
}
/*********************************验证函数区域end*******************************/
/*********************checkbox,radio,select*****************************/
function checkboxs(form){
var check = ele.filter(":checkbox"); //找出需要检验的多选框
if (check.length == 0) {//如果没有需要检验的多选框则退出
return true;
}
check.each(function(){
var T = $(this)
var rule = T.attr("data-rules");
rule= eval('['+rule+']')[0];
var cname = check.attr("name"); //取出name
var checks = form.find("[name="+cname+"]");//找出同name的所有复选框
checks = checks.filter(":checked"); //找出所有的被选中的复选框
if (rule.checkbox.min > checks.length || rule.checkbox.max < checks.length) {
//验证不通过
T.attr("data-pass","false");
} else {
T.attr("data-pass","true");
}
})
}
function radios(form){
var radios = ele.filter(":radio"); //找出所有需要检验的单选框
if (radios.length == 0) {
return true;
};
for(var i=0;i<radios.length;i++){
var name = radios[i].name;
var nameR = form.find("[name="+name+"]");//找出所有同name的单选
nameR = nameR.filter(":checked"); //查找被选中的元素
if(nameR.length == 0){// 如果没有元素被选中则验证不通过
radios[i].setAttribute("data-pass","false");
} else {
radios[i].setAttribute("data-pass","true") //如果有元素被选中则 表示通过
}
}
}
function selects(){
var selects = ele.filter("select"); //找出所有的下拉框
if (selects.length == 0) {
return true;
}
selects.each(function(){
console.log(this.value)
if (this.value == "" ||this.value == undefined) {//只要select的值不为""和undefined则验证通过
this.setAttribute("data-pass",false);
} else {
this.setAttribute("data-pass",true);
}
})
}
/***********************************工具函数start**********************************/
function getRule(elem, str){ //该函数用于获取rule对象
var dar = $(elem).attr(str);
var rules = eval('['+dar+']')[0];
return rules;
}
function getTipEle(elem, rules){//获取显示错误信息的元素
var addEle = "<span class='addEle'></span>"; //自动添加错误提示的html代码
var Tipshow;
if (rules.userEle) { //如果用户有指定错误的显示位置
TipShow = $(rules.userEle);
} else {
TipShow = $(addEle).insertAfter($(elem));//自己添加显示错的元素
}
return TipShow;
//控制错误信息在哪个里显示end
}
function getTipTxt(elem, rule){//获取错误提示信息
var TipTxt;
if (rule instanceof Object) {//如果是个对象,则判断用户是否自定义提示信息,有则显示自定义,无则显示初始值。
if (rule.tip) {
TipTxt = rule.tip;
} else {
TipTxt = iniVal.error;
}
} else {
//若为字符串类型,说明该属性的值是提示文本则将问题显示出来
TipTxt = rule;
}
return TipTxt;
}
//表单序列化
function getForm(form){
var datas = form.find("[name]");
var str="";
$.each(datas,function(i,v){
str=str+"&"+v.name+"="+v.value;
})
str=str.slice(1);
return str;
}
/***********************************工具函数end**********************************/
/*****************submit表单提交*******************************************/
//表单提交时候的验证函数
function vSubmit()
{
radios(form);
selects(form);
checkboxs(form);
for(var i=0;i<all.length;i++){
if($(all[i]).attr("data-pass") != 'true'){
var rule = getRule(all[i], "data-rules");
if (rule.id == undefined) {//如果忘了写id则同样不能提交
return "请将表单填写完整";
};
return rule.id;//返回验证失败的元素的data-rule.id属性,便于用户查找是哪里出了错
}
}
return true;//验证成功则放回true了,验证失败则将提示信息返回
}
//表单提交时候的对话框
function tanChuang(){//生成弹窗
$("<div class='v_daiLog' style='display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(200,200,200,.8)'></div>").appendTo("body");
$("<div class='v_mask'><p></p></div>").appendTo(".v_daiLog")
var mask = $(".v_mask");
mask.append("<button class='v_close'>关闭</button>");
mask.css({width:300,height:200,position:"fixed",textAlign:"center",
backgroundColor:"#999",top:($(window).height()-300)/2,left:($(window).width()-200)/2})
$(".v_mask .v_close").css({display:"block",width:"100%"})
.click(function(){
$(".v_daiLog").css("display",'none');
})
}
//先判断用户是否制定submit
if($("[data-submit]").length!=0){
// form.attr("onsubmit","return false");//如果用户设定了.submit则说明用户不希望原生提交,则阻止原生提交方式
form.find("[data-submit]").unbind("click").click(function(){
var submit = getRule(this, "data-submit"); //获取data-submit对形象
if($(".v_mask").length==0 && submit.isDefault==true){//如果没有弹窗并且用户要使用默认弹窗则生成一个弹窗
tanChuang();
}
var p = vSubmit();
if (p != true) {
if (submit.isDefault) {//如果制指定了要用默认样式才使用
$(".v_daiLog").css("display","block");
$(".v_mask p").text(p);
}
submit.error();
return false;
}
var string = getForm(form)//获取表单数据
$.ajax({url:form[0].action,data:string,type:form[0].method,success:function(data){
submit.success();
},error:function(){
alert("服务器无响应,请重新提交");
}})
})
} else { //否则用户希望用原生方式提交
if($(".v_mask").length==0){
tanChuang();
}
$("[type = submit]").unbind("click").click(function(){
if($(".v_mask").length==0 && submit.isDefault==true){//如果没有弹窗并且用户要使用默认弹窗则生成一个弹窗
tanChuang();
}
var p = vSubmit();
if(p != true){
//弹出遮罩层显示结果
$(".v_daiLog").css("display","block");
$(".v_mask p").text(p);//更具元素id属性,提示用户是哪一个元素验证失败
return false;
} else {
alert("可以提交!!!!!")
form.attr("onsubmit","return true");
}
})
}
/*****************submit表单提交*******************************************/
yanzhen(form);
}
})(jQuery);
//给jq添加一个verfiable方法
//2.2版本将验证方法分装成函数模块,通过属性来调用相对应的验证函数
//2.3TODO增加刷新验证的功能,修复动态增加输入框而不能进行验证的问题(setTnterval),修复表单提交的状态
//2.4增加用户指定元素显示错误的功能,将显示错误的代码提取出来。
//2.5功能模块化分为:初始化-执行验证模块-元素绑定函数模块-函数验证模块-验证结果处理模块-表单提交模块
//2.6优化验证函数的返回值,删除判断是否成功的代码。
//2.7将函数模块封装成一个对象,改用brack case代替value
//2.8添加表单提交的时的判断,其中有一个未通过验证则,表单不能提交
//2.9添加ajax远程验证,ajax作为特殊验证单独处理,未放在funModel内,而与其他条件并行验证
//2.10增加日期验证,调优submit提交验证
//2.11绑定select,checkbox,radios的事件,在点击提交按钮时验证这三种元素
//2.12将一些公用的部分提出来作为工具函数
//2.13表单序列化
/*data-rules中的属性氛围str,obj两种类型
data-rules={
id://用于提交表单提交时提醒用户该id出验证失败(如果用户自定义验证失败的函数,次属性可以省略,否则不可省略)
success://验证通过时的验证信息
userEle://用户指定的提示元素
isEmpt:str//验证是否为空
isChinese://是否为中文
isNumber://判断是否为数字
isPosint://是否为正整数
isEmall: //检验邮箱
isPhone: //检验电话号码
isUrl: //判断网址
isLong:{ //验证长度
min://最大长度
max://最小长度
tip://验证不通过时的提示信息
}
isID: {
minAge: 最小年龄
maxAge: 最大年龄
sex: ['male', 'female'] male 男性 female 女性
province: 省份名称,如北京、天津等
tip://提示信息
}
范围比较
isRange
{
* type: ['length','number', 'integer'] length 长度比较 number 数值比较 integer 整数数值比较(包含0)
min: 最小值
max: 最大值
tip:
}
uRegexp:{ //用户自定义正则表达式
* rule:'^1[3|4|5|8|7]\\d{9}$'//正则表达式格式必须使用new regexp的格式
igm://参数
tip://错误提示信息
}
uFunction:function(d){} //用户自定义验证函数
compare:{
* to://要比较的元素选择器
* condition://比较符号
tip://提示信息
}
vAjax:{
* url: // 远程校验的网址
* type:['POST','GET'] 方法,默认为post
* data:提交给服务器端的数据,可以是key1=value1&key2=value2、{key1:value1}、function(){return {key1:value1}}等形式
* dataType:['text','json', 'html']等
* verify:函数,用来校验返回数据,如果返回true,说明校验成功,如果返回false,说明校验失败。如果没有这个函数,则返回的数据为true或者'true'时认为成功,其他都为失败
tip:校验码填写不正确
}
checkbox:{
min:
max:
tip:
}
radio:str
select:str
}
设置修改提示元素的样式:
验证通过的class:success
验证不通过的class:error
TODO: 1.下拉表,单选框,多选框。
2.ajax远程验证
data-submit={
success:fun() 表单验证通过时的函数
error:fun() 表单验证未通过的时候执行的函数
}
*/
(function($){
$.fn.verfiable = function()
{
/*********************************初始化数据*******************************/
var form = $(this);
var all = form.find("[data-rules]"); //找出form下的所有需要验证的的元素
var ele = all.filter('[type=checkbox], [type=radio], select') //过滤出需要验证的checkbox,select,radio
var inps = all.not(ele) //剔除ele,剩下的则是输入框
var iniVal = { //定义初始值
success:"通过!",
error:"验证失败!"
}
/*********************************初始化数据*******************************/
/*********************************给每个需要验证的绑定函数*******************************/
var yanzhen = function (form){
inps.each(function(){
$(this).unbind("blur").bind("blur", function() //给每个input绑定失去焦点的函数
{
var data = $(this).val(); //此处获取val值,避免验证中重复获取
var rules = getRule(this,'data-rules'); //使用工具函数获取rules对象
var TipShow = getTipEle(this, rules); //用来显示提示信息(成功或失败)的元素
var TipTxt; //存放提示信息
//绑定获得焦点时函数,去除上次的错误提示start
$(this).unbind("focus").bind("focus",function()
{
if ($(this).next().hasClass("addEle")) { //如果是动态添加的tipshow则删除
$(this).next().remove();
} else if (rules.userEle || $(this).next().hasClass("userEle")) { //否则清除html内容
if ($(TipShow).hasClass('error')){
$(TipShow).removeClass("error");
} else {
$(TipShow.removeClass("success"));
}
$(TipShow).html("");
}
})
//绑定获得焦点时函数,去除上次的错误提示end
//遍历rules对象中的属性
for (var i in rules) { //将对象的每个属性都执行一遍对应的函数
var fun = eval("funModel."+i); //将rules的各个属性转化成函数模块funModel的属性
if (fun == undefined) { //如果函数库中没有对应的验证函数(保函ajax的情况),则跳过不验证次属性
continue;
};
//fun!=undefined说明funModel中有次对应的函数,且不是vAjax时则应执行此函数
var result = fun(rules[i], data ,this); //将rules穿参进去,执行每个验证规则的方法
TipTxt = getTipTxt(this, rules[i]);
/*根据返回值判断验证是否成功start*/
if (result===false) { //返回为false则验证不通过
TipShow.addClass("error").html(TipTxt);//将显示错误信息的元素添加class样式
this.setAttribute('data-pass', "false"); //标记为验证不通过
return false;
}
}
//执行到这里说明前方验证已通过,返回值不为false则视为成功 如果有ajax则还需进行ajax验证
//如果有ajax验证这一项则进行
if (rules.vAjax) {
var resultA;
//发动ajax请求
(function(R,T){
$.ajax({url:R.vAjax.url,type:R.vAjax.type,data:T.name+"="+data,async: true,success:function(data){
if(R.vAjax.fun){ //如果设置了验证函数,则将返回值穿入函数中验证该函数返回的为true或者false
resultA=R.vAjax.fun(data);
} else { //没设置验证函数则返回的必须是true或者false
resultA=data;
}
if (resultA == false) { //当验证失败时
console.log("ajax失败")
if (R.vAjax.tip) {
TipShow.addClass("error").html(R.vAjax.tip);
} else {
TipShow.addClass("error").html(iniVal.error);
}
T.setAttribute('data-pass', "false");//设置成功标记
} else { //验证成功时
if (R.success) {
TipShow.addClass("success").html(R.success);
} else {
TipShow.addClass("success").html(iniVal.success);
}
T.setAttribute('data-pass', "true");//设置成功标记
}
}})
})(rules, this)
/*******ajax远程验证结束*********/
} else { //如果没有
if (rules.success!=undefined) { //判断用户是否自定义提示信息
TipTxt = rules.success;
} else {
TipTxt =iniVal.success;
}
TipShow.addClass("success").html(TipTxt);
this.setAttribute('data-pass', "true");//设置成功标记
return true;
}
})
})
}
/*********************************绑定验证并调用函数*******************************/
/*********************************验证函数区域*******************************/
var funModel = {
//验证是否为空
isEmpt : function (Rule, data) //这里函数内的this指向的是window,所以需要将this一参数的形式传进来,
{
if ($.trim(data) == "") {
return false;
}
},
//验证长度是否通过
isLong : function (Rule, data)
{
if (data.length < Rule.min || data.length > Rule.max) {
return false;
}
},
//验证邮箱
isEmall : function (Rule, data)
{
var RegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|\.|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)((com(\.[a-z]{2})?)|net|cn|cc)$/i;
if(!RegExp.test(data)){
return false;
}
},
//验证手机号
isPhone : function ( Rule, data)
{
if(!/^1[3|4|5|8|7]\d{9}$/.test(data)){
return false;
}
},
//检测是否中文
isChinese : function (Rule, data)
{
if(!/^[\u4e00-\u9fa5]+$/.test(data)){
return false;
}
},
//验证是否为数字
isNumber : function(Rule, data)
{
if (isNaN(data)) {
return false;
};
},
//验证是否为为金额格式
isPosint: function(Rule, data)
{
if (/^[1-9](\d+)?$/.test(data)) {
return false;
};
},
//验证是否为url
isUrl : function (Rule, data)
{
var regexp = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
if (!regexp.test(data)) {
return false;
}
},
//范围值检验
isRange: function(rule, val){
if (undefined === rule.min) {
rule.min = Number.MIN_VALUE;
}
if (undefined === rule.max) {
rule.max = Number.MAX_VALUE;
}
switch(rule.type) {
case 'integer':
if( ! /^-?\d+$/.test(val)){
return false;
}
return val <= rule.max && val >= rule.min;
break;
case 'length':
return val.length <= rule.max && val.length >= rule.min;
break;
case 'number':
if(!isNaN(val)){
return val <= rule.max && val >= rule.min;
}else{
return false;
}
break;
}
},
//验证身份证
isID : function(rule, data)
{
if (!data) return rule.tip;
var len = data.length;
if (len != 15 && len != 18) {
return false;
}
// 检测基本格式是否正确
if (!/^(\d{15})|(\d{17}([0-9xX]))$/.test(data)) {
return false;
}
// 根据校验规则检查身份证合法性
if (len == 18) {
var total = 0, v = [1,0,'X',9,8,7,6,5,4,3,2], mod, rightCode;
$.each([7, 9, 10, 5, 8, 4, 2, 1, 6, 3], function(i){
if (i < 7) {
total += ((parseInt(data.charAt(i)) + parseInt(data.charAt(i+10))) * this);
} else {
total += (parseInt(data.charAt(i))) * this;
}
});
mod = total % 11;
rightCode = v[mod] + '';
if (data.charAt(17).toLowerCase() != rightCode.toLowerCase()) {
return false;
}
}
// 校验地区的合法性
if (!this.cities) {
this.cities = {11:'北京',12:'天津',13:'河北',14:'山西',15:'内蒙古',21:'辽宁',22:'吉林',23:'黑龙江 ',31:'上海',32:'江苏',33:'浙江',34:'安徽',35:'福建',36:'江西',37:'山东',41:'河南',42:'湖北',43:'湖南',44:'广东',45:'广西',46:'海南',50:'重庆',51:'四川',52:'贵州',53:'云南',54:'西藏 ',61:'陕西',62:'甘肃',63:'青海',64:'宁夏',65:'新疆',71:'台湾',81:'香港',82:'澳门',91:'国外'};
}
if (!(data.substr(0, 2) in this.cities)) {
return false;
}
// 检测限制的地区是否正确
if (this.cities[data.substr(0,2)] != rule.province) {
return false;
}
// 检查性别
if (rule.sex) {
var tag = data.substr(len == 15 ? len -1 : len - 2, 1);
if (tag % 2 == 0) {
if (rule.sex != 'female') {
return false;
}
} else {
if (rule.sex != 'male') {
return false;
}
}
}
// 检测生日的合法性
var yearLen = len == 15 ? 2 : 4,
year = parseInt(len == 2 ? '19' + data.substr(6, yearLen) : data.substr(6, 4), 10),
month = parseInt(data.substr(6 + yearLen, 2), 10),
day = parseInt(data.substr(8 + yearLen, 2), 10),
d = new Date(year, month - 1, day);
if (d.getFullYear() != year || d.getMonth() != month - 1 || d.getDate() != day) {
return false;
}
var offDay = parseInt((new Date().getTime() - d.getTime())/(1000*3600*24));
// 检查最小年龄
console.log("开始")
if (rule.minAge) {
if (offDay < 365 * rule.minAge) {
return false;
}
}
// 检查最大年龄
if (rule.maxAge) {
if (offDay > 365 * rule.maxAge) {
return false;
}
}
},
//验证是否为日期正则表达式
isDate : function(rule, val) {
var _dateReg = /^([12][0-9]{3})[\-\/\_\.\s]?(0?[1-9]|1[012])[\-\/\_\.\s]?(0?[1-9]|[12][0-9]|3[01])$/;
var ms = _dateReg.exec(val), date;//类似于test方法,匹配成功返回的为一个结果ji数组,失败则返回null
if (ms) { //若正则表达式匹配成功则执行
var y = parseInt(ms[1], 10), m = parseInt(ms[2], 10) - 1, d = parseInt(ms[3], 10);//取出年月日
try {
return new Date(y, m, d);//返回一个日期
} catch (e) {
return false;
}
}
return false;
},
//isDate:{
// min:
// max:
// tip:
// }
ruleDate:function(rule, val){
var date = funModel.isDate(rule,val);
if (date == false) {
return false;
}
// 检查最小数
if (rule.min || rule.max) {
var now = new Date();
if (rule.min) {
if (!isNaN(rule.min)) {
if ( (date.getTime() - now.getTime())/1000 < rule.min ) {
return false;
}
} else {
var min = funModel.isDate(rule, rule.min);
// if (!min) {
// return hapj.log.error('hapj.ui.verifiable date format wrong');
// }
console.log(min)
if (date.getTime() < min.getTime()) {
return false;
}
}
}
if (rule.max) {
if (!isNaN(rule.max)) {
if ( (date.getTime() - now.getTime())/1000 > rule.max ) {
return false;
}
} else {
var max = funModel.isDate(rule, rule.max);
// if (!max) {
// return hapj.log.error('hapj.ui.verifiable date format wrong');
// }
if (date.getTime() > max.getTime()) {
return false;
}
}
}
}
return true;
},
//比较验证
compare: function(rule, data) {
var cVal = $(rule.to).val();//to
if (!rule.condition) { //condition去除字符串两端的空格
rule.condition = '=';
}
switch(rule.condition) {
case '=':
case 'equal':
return data == cVal;
case '!=':
case '<>':
case 'notEqual':
return data != cVal;
case '>':
case 'great':
return data > cVal;
case '<':
case 'less':
return data < cVal;
case '>=':
case 'notGreat':
return data >= cVal;
case '<=':
case 'notLess':
return data <= cVal;
default:
return H.log.error('hapj.ui.verifiable the condition(' + rule.condition + ') is not defined');
}
},
//自定义正则表达式
uRegexp:function(rule ,data){
var regexp = new RegExp (rule.rule,rule.igm);
if (!regexp.test(data)) {
return false;
};
},
//自定义验证函数
uFunction:function(rule ,data,T){
if(T.type=="checkbox"||T.type=="radio"){
console.log("单选复选框啊")
}
return rule(data);
}
}
/*********************************验证函数区域end*******************************/
/*********************checkbox,radio,select*****************************/
function checkboxs(form){
var check = ele.filter(":checkbox"); //找出需要检验的多选框
if (check.length == 0) {//如果没有需要检验的多选框则退出
return true;
}
check.each(function(){
var T = $(this)
var rule = T.attr("data-rules");
rule= eval('['+rule+']')[0];
var cname = check.attr("name"); //取出name
var checks = form.find("[name="+cname+"]");//找出同name的所有复选框
checks = checks.filter(":checked"); //找出所有的被选中的复选框
if (rule.checkbox.min > checks.length || rule.checkbox.max < checks.length) {
//验证不通过
T.attr("data-pass","false");
} else {
T.attr("data-pass","true");
}
})
}
function radios(form){
var radios = ele.filter(":radio"); //找出所有需要检验的单选框
if (radios.length == 0) {
return true;
};
for(var i=0;i<radios.length;i++){
var name = radios[i].name;
var nameR = form.find("[name="+name+"]");//找出所有同name的单选
nameR = nameR.filter(":checked"); //查找被选中的元素
if(nameR.length == 0){// 如果没有元素被选中则验证不通过
radios[i].setAttribute("data-pass","false");
} else {
radios[i].setAttribute("data-pass","true") //如果有元素被选中则 表示通过
}
}
}
function selects(){
var selects = ele.filter("select"); //找出所有的下拉框
if (selects.length == 0) {
return true;
}
selects.each(function(){
console.log(this.value)
if (this.value == "" ||this.value == undefined) {//只要select的值不为""和undefined则验证通过
this.setAttribute("data-pass",false);
} else {
this.setAttribute("data-pass",true);
}
})
}
/***********************************工具函数start**********************************/
function getRule(elem, str){ //该函数用于获取rule对象
var dar = $(elem).attr(str);
var rules = eval('['+dar+']')[0];
return rules;
}
function getTipEle(elem, rules){//获取显示错误信息的元素
var addEle = "<span class='addEle'></span>"; //自动添加错误提示的html代码
var Tipshow;
if (rules.userEle) { //如果用户有指定错误的显示位置
TipShow = $(rules.userEle);
} else {
TipShow = $(addEle).insertAfter($(elem));//自己添加显示错的元素
}
return TipShow;
//控制错误信息在哪个里显示end
}
function getTipTxt(elem, rule){//获取错误提示信息
var TipTxt;
if (rule instanceof Object) {//如果是个对象,则判断用户是否自定义提示信息,有则显示自定义,无则显示初始值。
if (rule.tip) {
TipTxt = rule.tip;
} else {
TipTxt = iniVal.error;
}
} else {
//若为字符串类型,说明该属性的值是提示文本则将问题显示出来
TipTxt = rule;
}
return TipTxt;
}
//表单序列化
function getForm(form){
var datas = form.find("[name]");
var str="";
$.each(datas,function(i,v){
str=str+"&"+v.name+"="+v.value;
})
str=str.slice(1);
return str;
}
/***********************************工具函数end**********************************/
/*****************submit表单提交*******************************************/
//表单提交时候的验证函数
function vSubmit()
{
radios(form);
selects(form);
checkboxs(form);
for(var i=0;i<all.length;i++){
if($(all[i]).attr("data-pass") != 'true'){
var rule = getRule(all[i], "data-rules");
if (rule.id == undefined) {//如果忘了写id则同样不能提交
return "请将表单填写完整";
};
return rule.id;//返回验证失败的元素的data-rule.id属性,便于用户查找是哪里出了错
}
}
return true;//验证成功则放回true了,验证失败则将提示信息返回
}
//表单提交时候的对话框
function tanChuang(){//生成弹窗
$("<div class='v_daiLog' style='display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(200,200,200,.8)'></div>").appendTo("body");
$("<div class='v_mask'><p></p></div>").appendTo(".v_daiLog")
var mask = $(".v_mask");
mask.append("<button class='v_close'>关闭</button>");
mask.css({width:300,height:200,position:"fixed",textAlign:"center",
backgroundColor:"#999",top:($(window).height()-300)/2,left:($(window).width()-200)/2})
$(".v_mask .v_close").css({display:"block",width:"100%"})
.click(function(){
$(".v_daiLog").css("display",'none');
})
}
//先判断用户是否制定submit
if($("[data-submit]").length!=0){
// form.attr("onsubmit","return false");//如果用户设定了.submit则说明用户不希望原生提交,则阻止原生提交方式
form.find("[data-submit]").unbind("click").click(function(){
var submit = getRule(this, "data-submit"); //获取data-submit对形象
if($(".v_mask").length==0 && submit.isDefault==true){//如果没有弹窗并且用户要使用默认弹窗则生成一个弹窗
tanChuang();
}
var p = vSubmit();
if (p != true) {
if (submit.isDefault) {//如果制指定了要用默认样式才使用
$(".v_daiLog").css("display","block");
$(".v_mask p").text(p);
}
submit.error();
return false;
}
var string = getForm(form)//获取表单数据
$.ajax({url:form[0].action,data:string,type:form[0].method,success:function(data){
submit.success();
},error:function(){
alert("服务器无响应,请重新提交");
}})
})
} else { //否则用户希望用原生方式提交
if($(".v_mask").length==0){
tanChuang();
}
$("[type = submit]").unbind("click").click(function(){
if($(".v_mask").length==0 && submit.isDefault==true){//如果没有弹窗并且用户要使用默认弹窗则生成一个弹窗
tanChuang();
}
var p = vSubmit();
if(p != true){
//弹出遮罩层显示结果
$(".v_daiLog").css("display","block");
$(".v_mask p").text(p);//更具元素id属性,提示用户是哪一个元素验证失败
return false;
} else {
alert("可以提交!!!!!")
form.attr("onsubmit","return true");
}
})
}
/*****************submit表单提交*******************************************/
yanzhen(form);
}
})(jQuery);
相关文章推荐
- 推荐大家使用的CSS书写规范、顺序
- java基本类型和字符串的转换
- C# 使用 ExcelLibrary 读写 Excel 文件
- Gradle eclipse识别JavaWeb项目相关蛋疼问题
- 美团和百度都遇到的问题
- Manacher算法--O(n)回文子串算法
- hdu 5241__Friends
- 静态区-栈区-堆区区别
- Java获取客户端真实IP地址的两种方法
- MAC OS 9个实用的终端命令
- visio二次开发——图纸解析
- COCI CONTEST #3 29.11.2014 SILUETA
- ios ui element usage guidelines
- Maven 3.3.3 with JDK1.7, java.lang.SecurityException
- C++程序
- C语言基础测试题01
- mkswap,swapon, swapoff命令:创建交换分区
- centos cron curl使用
- C++类中初始化成员变量总结
- Android中SurfaceView的使用详解