zoukankan      html  css  js  c++  java
  • asp.net通用Web2.0仿淘宝脚本验证组件

          最近在做一个比较大型的电子商务网站,项目组内一直没人去把通用脚本验证封装出来,每个组员都是在开发页面时自己写脚本验证,这样搞不光开发效率低而且维护性极差,解决方式就是需要封装出通用的脚本验证组件,传统的组件都是很丑的,这里介绍一种既实用又漂亮的组件FieldVerify,先看一下效果图:

    脚本验证脚本验证

           看完效果图之后,我们可以讲一下这一块的思路了,传统模式(很多项目都在用的模式)都是自己写js验证代码,在鼠标定位到输入框时捕获onfocus事件,在这个事件里加入友好提示,例如“请输入用户信息!”,鼠标移出输入框时捕获失去焦点事件onblur,在这个事件里对此控件进行脚本验证,像判空、长度、格式等等,这种模式的缺点是:每个页面都要自己的验证js代码,验证方式五花八门,每个页面输出给客户的也是不一致的效果,可维护性、扩展性极差,而且每个人水平都一样,容易产生大量bug;通用模式就是为了解决这一点,为了增强代码的可维护性、可扩展性,项目小组调用的都是统一的脚本验证组件,反馈给客户的界面也是一致的,具体怎么做呢?其实开发思路和模式1是差不多的,只不过是把被验证控件的一些通用的方法、代码、样式等抽取出来了,事件还是那两个,只不过调用这两个事件的js方法是通用的,通过传递不同的参数来去封装,还是把代码贴出来吧,没有代码的描述都是空洞的,JS文件代码如下:

    /*
    * 功能:Web 2.0通用脚本验证,友好人机交互界面,参考demo页
    * 作者:李蒙强
    * 日期:2012.07.08
    */ function GetElement(i) { return document.getElementById(i) } function killErrors() { return true; } window.onerror = killErrors; function parsestr(str) { if (isNaN(parseFloat(str))) return str; else return parseFloat(str); } // 输出正确或错误提示 function addspan(Obj, msg) { var color; if (msg == "ok") { color = "green"; SetResult("right", "", "dv" + Obj.id, Obj.id, "dr" + Obj.id); } else color = "red"; if (GetElement("E_" + Obj.name)) { if (msg != "ok") { SetResult("error", msg, "dv" + Obj.id, Obj.id, "dr" + Obj.id); } } else { if (msg != "ok") { SetResult("error", msg, "dv" + Obj.id, Obj.id, "dr" + Obj.id); } } } // 验证控件输入 function CkValidate(Obj) { var msg = ""; var msg1 = ""; Obj = GetElement(Obj.name); var t = Obj.v_title.split('~|~'); if (t.length > 3) { switch (t[3]) { case "hlg": if (GetValue(Obj).length < t[4] || GetValue(Obj).length > t[5]) msg = "输入字符数应在" + t[4] + "--" + t[5] + "之间"; break; case "mxn": if (parsestr(GetValue(Obj)) < t[4] || parsestr(GetValue(Obj)) > t[5]) msg = "输入数值应在" + t[4] + "--" + t[5] + "之间"; break; case "rep": if (GetValue(Obj) != GetValue(GetElement(t[4]))) msg = "两次输入密码不一至"; msg1 = msg; break; } } if (t.length > 2) { if (!RegExp(t[2], "i").test(String(GetValue(Obj)))) msg = t[1]; } if (t.length > 0) { if (!/.+/.test(GetValue(Obj)) && t[0] == "必填") msg = "不能为空!"; if (!/.+/.test(GetValue(Obj)) && t[0] == "选填") msg = ""; } if (GetElement(t[4])) if (msg) { addspan(Obj, msg); addspan(GetElement(t[4]), msg1); } else { addspan(Obj, "ok"); addspan(GetElement(t[4]), "ok"); } else if (msg) { addspan(Obj, msg); } else { addspan(Obj, "ok"); } return msg; } function GetValue(el) { var sType = el.type; switch (sType) { case "text": case "hidden": case "password": case "file": case "textarea": return el.value; case "checkbox": case "radio": return GetValueChoose(el); case "select-one": case "select-multiple": return GetValueSel(el); } function GetValueChoose(el) { var sValue = ""; var tmpels = document.getElementsByName(el.name); for (var i = 0; i < tmpels.length; i++) { if (tmpels[i].checked) { sValue += "0"; } } return sValue; } function GetValueSel(el) { var sValue = ""; for (var i = 0; i < el.options.length; i++) { if (el.options[i].selected && el.options[i].value != "") { sValue += "0"; } } return sValue; } } // 验证整个表单输入域合法性 function CheckForm(oForm) { var els = oForm.elements; var msg = ""; for (var i = 0; i < els.length; i++) { if (els[i].v_title) { msg = Ck(els[i]); if (msg) { break; } } } if (!msg) oForm.submit(); } //检测用户名是否合法 function CheckUserName(userName, divId, controlId, rowId) { userName = removeAllSpace(userName); if (userName.toString().length > 0) { $.ajax({ type: 'POST', url: '/Controls/Register/CS/QueryAjax.ashx', data: 'name=Register.Simple.ascx&userName=' + escape(userName), cache: false, complete: function(msg, textStatus) { var data = msg.responseText.split("|"); if (data[0] == "true") { SetResult("right", data[1], divId, controlId, rowId); } else { SetResult("error", data[1], divId, controlId, rowId); } } }); } else { SetResult("error", "用户名不能为空", divId, controlId, rowId); } } //检测Email是否合法 function CheckEmail(email, divId, controlId, rowId) { email = removeAllSpace(email); if (email.toString().length > 0) { $.ajax({ type: 'POST', url: '/We7Controls/Register/CS/QueryAjax.ashx', data: 'name=Register.Simple.ascx&email=' + escape(email), cache: false, complete: function(msg, textStatus) { var data = msg.responseText.split("|"); if (data[0] == "true") { SetResult("right", data[1], divId, controlId, rowId); } else { SetResult("error", data[1], divId, controlId, rowId); } } }); } else { SetResult("error", "电子邮箱不能为空", divId, controlId, rowId); } } //检测密码是否合法 function CheckPWD(pwd, divId, controlId, rowId) { pwd = removeAllSpace(pwd); var data = ""; if (!(pwd.toString().length >= 6 && pwd.toString().length <= 16)) { data = "密码必须在6-16个字符内"; SetResult("error", data, divId, controlId, rowId); } else { SetResult("right", "", divId, controlId, rowId); } } //检测确认密码是否合法 function CheckRePWD(pwdID, rePwd, divId, controlId, rowId) { var pwd = removeAllSpace($("#" + pwdID).val()); rePwd = removeAllSpace(rePwd); var data = ""; if (pwd != rePwd) { data = "密码不匹配"; SetResult("error", data, divId, controlId, rowId); } else { SetResult("right", "", divId, controlId, rowId); } } //处理结果 function SetResult(type, text, divId, controlId, rowId) { var result = ""; if (type == "error") { InitStyle(divId, controlId, rowId); result = "<table width='100%' style='line-height:19px;' align='left'><tr><td width='20px' valign='top' align='left'><img src='images/error.jpg'/></td><td valign='top'>" + text + "</td></tr><table>"; if ($("#" + controlId).val() != "") { $("#" + divId).html(""); $("#" + divId).html(result); $("#" + divId).addClass("error"); $("#" + controlId).addClass("error"); } $("#" + controlId).attr("validate", "false"); } else if (type == "right") { InitStyle(divId, controlId, rowId); result = "<img width='20px' src='images/right.jpg'/>"; //<table width='100%' style='line-height:19px;' align='left'><tr><td width='20px' valign='top' align='left'>图片</td><td valign='top'>" + text + "</td></tr><table> $("#" + divId).html(""); $("#" + divId).html(result); $("#" + divId).addClass("allRight"); $("#" + controlId).attr("validate", "true"); } else if (type == "init") { InitStyle(divId, controlId, rowId); result = "<table width='100%' style='line-height:19px;' align='left'><tr><td width='20px' valign='top' align='left'><img src='images/init.jpg'/></td><td valign='top'>" + text + "</td></tr><table>"; $("#" + divId).html(""); $("#" + divId).html(result); $("#" + divId).addClass("initDiv"); $("#" + rowId).addClass("initRow"); // alert(controlId); // alert($("#" + controlId).attr("class")); } else if (type == "initValidateCode") { $("#" + divId).html(""); $("#" + rowId).addClass("initRow"); } else if (type == "errorValidateCode") { $("#" + rowId).removeClass("initRow"); if ($("#" + controlId).val() != "") { $("#" + divId).html("<img src='images/error.jpg'/>"); } $("#" + controlId).attr("validate", "false"); } else if (type == "rightValidateCode") { $("#" + rowId).removeClass("initRow"); $("#" + divId).html("<img src='images/right.jpg'/>"); $("#" + controlId).attr("validate", "true"); } } //判断最后提交按钮是否可用 function CheckValidate(emailId, userNameId, pwdId, rePwdId, btnId, validateId) { alert($("#" + validateId).attr("validate")); if ($("#" + emailId).attr("validate") == "true" && $("#" + userNameId).attr("validate") == "true" && $("#" + pwdId).attr("validate") == "true" && $("#" + rePwdId).attr("validate") == "true" && $("#" + validateId).attr("validate") == "true") { $("#" + btnId).removeAttr('disabled'); } else { $("#" + btnId).attr("disabled", 'disabled'); } } //出现该项目的填写帮助 function InitControlHelp(text, divId, controlId, rowId) { SetResult("init", text, divId, controlId, rowId); } //去除该项目的填写帮助 function InitStyle(divId, controlId, rowId) { var initText = ""; $("#" + divId).html(initText); $("#" + divId).removeClass("initDiv"); $("#" + rowId).removeClass("initRow"); $("#" + divId).removeClass("error"); $("#" + controlId).removeClass("error"); $("#" + divId).removeClass("allRight"); $("#" + controlId).removeClass("allRight"); } ///去除所有空格 function removeAllSpace(str) { return str.replace(/\s+/g, ""); } //检测验证码是否正确 function CheckValidateCode(validateCode, divId, controlId, rowId) { $.ajax({ type: 'POST', url: '/Controls/Register/CS/QueryAjax.ashx', data: 'name=Register.Simple.ascx&validateCode=' + escape(validateCode), cache: false, complete: function(msg, textStatus) { var data = msg.responseText; if (data == "true") { SetResult("rightValidateCode", "", divId, controlId, rowId); } else { SetResult("errorValidateCode", "", divId, controlId, rowId); } } }); }

    这个js主要是对控件的一些通用逻辑进行了封装,主要的两个方法是:InitControlHelp(text, divId, controlId, rowId) 这个主要是onfucs的调用;CkValidate(obj)这个主要是对控件的验证,具体的脚本逻辑这里不再赘述。接下来是我们的前台界面Demo调用页面了,代码如下:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="FV_Demo.aspx.cs" Inherits="BSTest.FieldVarify.FV_Demo" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>通用脚本验证Demo页面</title>
    
        <script src="jquery-1.4.2.js" type="text/javascript"></script>
    
        <script src="FieldVerify.js" type="text/javascript"></script>
    
        <link rel="Stylesheet" href="FieldVerify.css" />
    
    
    </head>
    <body>
        <form id="form1" runat="server" method="get" action="FieldVerify.aspx">
      
        <div style="font-size: 12px" class="Register_Simple_common">
            <div class="reg-form">
                <table>
                    <tr style="height: 40px" id="trName">
                        <td>
                            真实姓名:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('请输入姓名。','dvName',this.id,'trName');"
                                name="Name" id="Name" v_title="必填~|~只允许中文且长度在2到5个字符~|~^[\u0391-\uFFE5]+$~|~hlg~|~2~|~5" /><span
                                    id="E_Name" style='color: red'>*</span>
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvName" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trNick">
                        <td>
                            英文名:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('请输入英文名。','dvNick',this.id,'trNick');"
                                name="Nick" id="Nick" v_title="必填~|~只允许英文字母且长度在4到20个字符~|~^[A-Za-z]+$~|~hlg~|~4~|~20"><span
                                    id="E_Nick" style='color: red'>*</span>
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvNick" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trHomepage">
                        <td>
                            主页:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('请输入正确的主页。','dvHomepage',this.id,'trHomepage');"
                                name="Homepage" id="Homepage" v_title="选填~|~非法的Url~|~^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>])*$">
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvHomepage" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trPassword">
                        <td>
                            密码:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('请输入密码。','dvPassword',this.id,'trPassword');"
                                name="Password" id="Password" v_title="必填~|~~|~~|~rep~|~RePassword" type='password' />
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvPassword" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trRePassword">
                        <td>
                            重复密码:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('请再次输入密码。','dvRePassword',this.id,'trRePassword');"
                                name="RePassword" id="RePassword" v_title="必填~|~~|~~|~rep~|~Password" type='password'>
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvRePassword" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trEmail">
                        <td>
                            电子邮件:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('请输入电子邮件。','dvEmail',this.id,'trEmail');"
                                name="Email" id="Email" v_title="选填~|~电子邮件格式不正确~|~^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$" />
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvEmail" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trage">
                        <td>
                            年龄:
                        </td>
                        <td>
                            <input onblur='CkValidate(this)' onfocus="InitControlHelp('年龄必需为数字,请输入正确的年龄。','dvage',this.id,'trage');"
                                name="age" id="age" v_title="必填~|~年龄必需为数字!~|~^\d+$~|~mxn~|~16~|~99" />
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvage" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr style="height: 40px" id="trDesc">
                        <td>
                            自我介绍:
                        </td>
                        <td>
                            <textarea onblur='CkValidate(this)' onfocus="InitControlHelp('请输入备注信息,字符数应在10--500之间!','dvDesc',this.id,'trDesc');"
                                name="Desc" id="Desc" v_title="必填~|~输入字符数应在10--500之间~|~~|~hlg~|~10~|~500"></textarea>
                        </td>
                        <td align="left">
                            <div style="text-align: left" id="dvDesc" class="disPlayStyle">
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td colspan="2">
                            <input type="button" name="Submit" value="确定" onclick="javascript:Test();" />
                            <asp:Button ID="btnOK" runat="server" Text="服务器端控件确定" OnClientClick="return CheckForm(form1)"
                                OnClick="btnOK_Click" />
                        </td>
                    </tr>
                </table>
            </div>
        </div>
        </form>
    </body>
    </html>

    前台界面我们需要注意一下几点:1、<div style="font-size: 12px" class="Register_Simple_common"><div class="reg-form">这两个div必须得有,主要是对样式的控制;2、每个验证的tr的id和div提示的id规范分别是tr+控件id号、dv+控件id号;3、每个控件都要有v_title而且要符合demo页面的规范;4、另外对于服务器端确定按钮的逻辑要加入OnClientClick="return CheckForm(form1)"事件,这样页面就基本搞定了。另外验证效果样式都已经封装为样式文件FieldVerify.css,代码如下:

    .flk13 A:link {
        COLOR: #005aa0
    }
    .flk13 A:visited {
        COLOR: #005aa0
    }
    A.flk13:link {
        COLOR: #005aa0
    }
    A.flk13:visited {
        COLOR: #005aa0
    }
    .title.RetrievePassword_Common_one {
        TEXT-ALIGN: center; LINE-HEIGHT: 40px; MARGIN-TOP: 30px; WIDTH: 600px
    }
    .RetrievePassword_Common_one .reg-form {
        LINE-HEIGHT: 40px
    }
    .Register_Simple_common .regContent {
        TEXT-ALIGN: center; MARGIN-TOP: 0px; WIDTH: auto; BACKGROUND-REPEAT: repeat-x; FONT-FAMILY: "宋体"; BACKGROUND-POSITION: 50% top; HEIGHT: auto; MARGIN-LEFT: auto; FONT-SIZE: 12px; MARGIN-RIGHT: auto
    }
    .Register_Simple_common .label {
        TEXT-ALIGN: right; WIDTH: 100px; PADDING-RIGHT: 10px
    }
    .Register_Simple_common LABEL.error {
        COLOR: red; MARGIN-LEFT: 5px
    }
    .Register_Simple_common INPUT {
        BORDER-BOTTOM: #dcdcdc 1px solid; BORDER-LEFT: #dcdcdc 1px solid; PADDING-BOTTOM: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; HEIGHT: 20px; FONT-SIZE: 12px; BORDER-TOP: #dcdcdc 1px solid; BORDER-RIGHT: #dcdcdc 1px solid; PADDING-TOP: 0px
    }
    .Register_Simple_common .button {
        PADDING-BOTTOM: 0px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; CURSOR: pointer; PADDING-TOP: 0px
    }
    .Register_Simple_common .initRow {
        BACKGROUND-COLOR: #f5fcfe
    }
    .Register_Simple_common .initDiv {
        BORDER-BOTTOM: #40b3ff 2px solid; BORDER-LEFT: #40b3ff 2px solid; BACKGROUND-COLOR: #e5f5ff; WIDTH: 300px; HEIGHT: 38px; VERTICAL-ALIGN: top; BORDER-TOP: #40b3ff 2px solid; BORDER-RIGHT: #40b3ff 2px solid
    }
    .Register_Simple_common .error {
        BORDER-BOTTOM: #ff8080 2px solid; BORDER-LEFT: #ff8080 2px solid; BACKGROUND-COLOR: #fff2f2; WIDTH: 300px; VERTICAL-ALIGN: top; BORDER-TOP: #ff8080 2px solid; BORDER-RIGHT: #ff8080 2px solid
    }
    .Register_Simple_common .allRight {
        TEXT-ALIGN: left
    }
    .Register_Simple_common .flow-steps {
        MARGIN-TOP: 3px; MARGIN-LEFT: 60px; OVERFLOW: hidden
    }
    .Register_Simple_common .flow-steps .num3 OL {
        LIST-STYLE-TYPE: none
    }
    .Register_Simple_common .flow-steps .num3 LI {
        LIST-STYLE-TYPE: none; WIDTH: 230px
    }
    .Register_Simple_common .flow-steps .num4 LI {
        WIDTH: 222px
    }
    .Register_Simple_common .flow-steps .num5 LI {
        WIDTH: 175px
    }
    .Register_Simple_common .flow-steps .num6 LI {
        WIDTH: 143px
    }
    .Register_Simple_common .flow-steps LI {
        TEXT-ALIGN: center; PADDING-BOTTOM: 0px; LINE-HEIGHT: 23px; PADDING-LEFT: 0px; PADDING-RIGHT: 15px; BACKGROUND: url(/Admin/images/flow_steps_bg.png) #e4e4e4 no-repeat 100% 0px; FLOAT: left; HEIGHT: 23px; COLOR: #404040; FONT-SIZE: 14px; OVERFLOW: hidden; FONT-WEIGHT: bold; PADDING-TOP: 0px
    }
    .flow-steps LI SPAN {
        DISPLAY: block
    }
    .flow-steps LI STRONG {
        DISPLAY: block
    }
    .flow-steps LI .first {
        BACKGROUND: url(/Admin/images/flow_steps_bg.png) #e4e4e4 no-repeat -12px -69px
    }
    .Register_Simple_common .flow-steps LI.last {
        BACKGROUND-POSITION: 100% -138px
    }
    .Register_Simple_common .flow-steps LI.current {
        BACKGROUND-COLOR: #f60; COLOR: #fff
    }
    .Register_Simple_common .flow-steps LI.current .first {
        BACKGROUND-COLOR: #f60; BACKGROUND-POSITION: -12px -92px
    }
    .Register_Simple_common .flow-steps LI.current-prev {
        BACKGROUND-POSITION: 100% -23px
    }
    .Register_Simple_common .flow-steps LI.done {
        BACKGROUND-COLOR: #ffe6ba; BACKGROUND-POSITION: 100% -46px; COLOR: #f60
    }
    .Register_Simple_common .flow-steps LI.done .first {
        BACKGROUND-COLOR: #ffe6ba; BACKGROUND-POSITION: -12px -115px
    }
    .Register_Simple_common .flow-steps LI.last-current {
        BACKGROUND-COLOR: #f60; BACKGROUND-POSITION: 100% -161px; COLOR: #fff
    }
    .Register_Simple_common .reg-email {
        BORDER-BOTTOM: #94d3fe 1px solid; BORDER-LEFT: #94d3fe 1px solid; PADDING-BOTTOM: 20px; BACKGROUND-COLOR: #f1faff; MARGIN: 30px auto 0px 60px; WIDTH: 735px; BORDER-TOP: #94d3fe 1px solid; BORDER-RIGHT: #94d3fe 1px solid
    }
    .Register_Simple_common .reg-form {
        MARGIN-TOP: 30px; WIDTH: 735px; MARGIN-LEFT: 60px
    }
    .Register_Simple_common .reg-form .button {
        PADDING-BOTTOM: 0px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; CURSOR: pointer; PADDING-TOP: 0px
    }
    .Register_Simple_common .table_form {
        TEXT-ALIGN: left; LINE-HEIGHT: 30px; WIDTH: 735px; FLOAT: left; MARGIN-LEFT: 40px
    }
    .Register_Simple_common .formTitle {
        TEXT-ALIGN: right; WIDTH: 200px; FONT-WEIGHT: bold; MARGIN-RIGHT: 5px
    }
    .Register_Simple_common .formValue {
        TEXT-ALIGN: left; MARGIN-LEFT: 10px; VERTICAL-ALIGN: middle
    }
    .Register_Simple_common .button_style {
        MARGIN-TOP: 10px; WIDTH: 80px; HEIGHT: 30px; VERTICAL-ALIGN: middle; CURSOR: pointer
    }
    .Register_Simple_common .disPlayStyle {
        TEXT-ALIGN: left; FLOAT: left
    }

          总结一下:这个脚本下一步还可以继续完善一下,把它封装为面向对象的脚本会更通用,另外为了方式页面被黑客利用或攻击,开发后台中最好也要对必要控件进行验证,这样双管齐下才是最好的验证!

  • 相关阅读:
    toj4119HDFS
    hdu2952Counting Sheep
    hdu2393Higher Math
    hdu2317Nasty Hacks
    hdu2309ICPC Score Totalizer Software
    hdu2304Electrical Outlets
    hdu2399GPA
    一、 软件测试概述
    JQuery选择器大全
    如何避免jQuery库和其他库的冲突
  • 原文地址:https://www.cnblogs.com/limengqiang/p/FieldVerify.html
Copyright © 2011-2022 走看看