如何用.NET和jQuery编写自定义表单的长尾词?
- 内容介绍
- 文章标签
- 相关推荐
本文共计5886个文字,预计阅读时间需要24分钟。
前言:两年前,在权力斗争的时期,我就想做一个类似的功能。当时大家讨论得很好,但最终因为种种原因搁浅了。没想到两年后,这个想法又重新被提出,需要写一个功能清单。对此,我也有点小激动。
前言
两年前在力控的时候就想做一个类似的功能,当时思路大家都讨论好了,诸多原因最终还是夭折了。没想到两年多后再这有重新提出要写一个绘制表单的功能。对此也是有点小激动呢?总共用时8.5天的时间基本功能也就实现了,当然再者中间也借用了网上的一些资料,公司前端也没有帮忙处理,所以样式和部分功能还没有更好地得到处理,github上出的code只有前端脚本,至于后端的处理,会在博客中体现出来。
1.工作前准备
1.1.实现的思路
思路一:
(1)ueditor添加自定义按钮
(2)绘制表单(控件会触发的脚步)
(3)保存表单时建立数据库表(无需存储表信息),并保存html字符串
(4)修改表单需同时修改数据库
(5)表单发起获取数据封装成json,进行后台保存。
思路二:
(1)jquery 拖动自定义标签,在指定区域进行绘制
(2)表单属性设置相应的表单属性和表单基本布局
(3)设置每个控件的属性值
(4)把表单信息和控件以json的形式传入后台进行保存
(5)从后台获取数据json对象用jquery 绘制表单页面
(6)创建一张表(F-F200)把表单数据存入表单中。
最终选择的是,原因是富文本编辑器绘制起来有很多自动生成的标签,让人感觉很是不爽。当然可以对ueditor进行处理(这个也是两年前的思路)。
1.2.实现过程的确定
整个的过程从借鉴开始网上有些类似的功能,从中得到很多帮助在这就不一一鸣谢了。然后就是没羞没臊的3天脚本修改工作。后台数据的处理完全没有什么可说的,中间用的了一些缓存问题,本来说是用redis呢,结果商量一些说不用难部署(难部署???好吧一脸懵逼),就用了c#的CacheHelper。
2.具体实现
绘制表单预览与保存
2.1.脚本
以上是表单创建的js脚本。
就是上边那个图片的实现。
html重要分左中右三部分。左边是页面上所用到的标签区域,中间是展示区域,右边是表单和控件属性的设置区域。htnl 脚本如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta " /> </li> </ul> </li> <!-- <li class="left half"> <label for="language" class="desc">语言 <a href="#" class="help" title="关于语言" rel="此属性用于指定系统提示信息所使用的语言,当用护访问表单时系统的提示信息(如提交时的验证错误信息)将以此语言显示。目前仅支持简体中文和英文两种语言。">(?)</a></label> <select id="language" name="LANG" class="xxl"> <option value="cn">简单中文</option> <option value="en">English</option> </select> </li> <li class="clear noheight"></li> <li id="liGoods" class="hide"> <fieldset> <legend>商品相关</legend> <ul> <li id="liSale" style="display: list-item;"> <input id="sale" name="SALE" type="checkbox" value="1"> <label for="sale">促销:</label> 满 <input type="text" id="salem" name="SALEM" disabled class="number" style="width:50px"/> 减 <input type="text" id="salej" name="SALEJ" disabled class="number" style="width:50px"/> <a href="#" class="help" title="关于商品促销" rel="当表单中有商品字段时,可以进行“满就减”的促销活动,当金额达到一定量时自动减掉相应金额,助您提升商品销量。">(?)</a> </li> <li id="liPay"> <div class="highlight"> <input type="checkbox" id="chkAliPay" value="1" name="ALIPAY"/> <label class="bold" for="chkAliPay">在线支付</label> <a class="help" href="#" title="关于在线支付" rel="目前仅支持支付宝在线支付,需要开通支付宝商家服务才能使用。<a href='help/formbuilder.html#t62' target='_blank'>查看开通方法</a>">(?)</a> <div id="divPay" class="hide"> <a href="#" id="btnPaySetting" class="btn no-icon btn-gray">配置支付参数</a><br/> <label>不跳转到在线支付条件</label> <a class="help" href="#" title="关于不跳转条件" rel="您可以根据用户填写的数据来确定是否跳转到在线支付页面,当满足如下条件时将不进行跳转,默认跳转。比如您可以添加一个“付款方式”的单选框,并在此设置当选择“货到付款”时不跳转到在线支付。">(?)</a> <div id="noAliConditionDiv" style="margin-bottom: 5px;"> <select id="noAliConditionField" name="PAYCONFLD" style="width:115px;"></select> 等于 <select id="noAliConditionValue" name="PAYCONVAL" style="width:115px;"></select> </div> </div> </div> </li> <li style="text-align: center;display:none;"> <a style="text-decoration: underline;color:#3B699F;" target="_blank" href="jsform-setup.msi">下载小票自动打印程序</a> <a href="#" class="help" title="关于小票自动打印程序" rel="小票打印程序是专为商品类表单设计的一款应用程序。安装后,当有新订单提交时将会自动打印出小票,您要做的仅是“见单送货”。非常适合外卖及实体店铺开展电子商务的应用场景。">(?)</a> </li> </ul> </fieldset> </li> <li class="clear noheight"></li> <li id="liConfirm" class="clear"> <fieldset class="confirm"><legend>跳转选项</legend> <ul> <li class="left"> <input id="confirmType_text" name="CFMTYP" value="T" checked="checked" type="radio"/> <label for="confirmType_text">显示文本</label> <a href="#" class="help" title="关于显示文本" rel="当用户提交表单成功时,系统将跳转至默认的确认页面,页面中将显示下面文本框设定的内容。">(?)</a> </li> <li class="right"> <input id="confirmType_url" name="CFMTYP" value="U" type="radio"/> <label for="confirmType_url">跳转至网页</label> <a href="#" class="help" title="关于跳转至网页" rel="当用户提交表单成功时,系统将转至下面文本框所设定的网址,而不是默认的确认页面。">(?)</a> </li> <li class="clear"> <textarea id="confirmMsg_text" name="CFMMSG" class="xxl hide" rows="3">Thank you. Your entry has been successfully submitted.</textarea> <input id="confirmMsg_url" name="CFMURL" class="xxl hide" type="text" value="" /> </li> </ul> </fieldset> </li> <li class="clear noheight"></li> <li> <fieldset><legend>填写控制</legend> <ul> <li> <label for="captcha">验证码 <a href="#" class="help" title="关于验证码" rel="通过一张只有人眼能识别而电脑无法识别的验证码图片来确定表单是手工提交,而 不是通过软件进行恶意的大量提交。默认情况下,系统将根据同一IP提交频率自动决定是否显示验证码。您也可以选择一直显示验证码或从不显示验证码。从不显示验证码通常用于在某个局域网络有多次提交的情况,用以简化用户输入。">(?)</a></label> <div> <select id="captcha" class="m" name="CAPTCHA"> <option value="1">自动 (推荐)</option> <option value="2">一直显示</option> <option value="0">从不显示</option> </select> </div> </li> <li> <label for="entriesLimit">达到如下数据量后关闭表单 <a href="#" class="help" title="关于数据量限制" rel="此属性用于指定表单可以收集数据的最大数据量,当达到此数据量后表单将自动关闭。如果不想进行最大数据量限制,请将此处设置为空。">(?)</a> </label> <input id="entriesLimit" class="intnumber m" name="ENLMT" maxlength="8" type="text"/> </li> <li> <input id="onePerIp" name="IPLMT" value="1" type="checkbox"/> <label class="choice" for="onePerIp">每个IP只允许提交一次</label> <a href="#" class="help" title="关于IP访问限制" rel="此属性用于指定每一个IP地址只能提交一次表单,可以防止用户在同一台计算机上进行多次提交。<br/><i>注意:相对外网而言,同一局域网内的不同IP可能会当作同一IP处理。局域网可通过手机验证码确定唯一性。</i>">(?)</a> <br/> </li> <li> <input id="chkAutoFill" type="checkbox" value="1" name="AUTOFILL"/> <label for="chkAutoFill">自动填充上次填写数据</label> <a href="#" class="help" title="自动填充上次填写数据" rel="当某个表单需要同一人(同一台设备)多次填写,并且填写数据变化不大时可以勾选此选项,自动填充上次填写数据,加快输入速度。">(?)</a> </li> <li> <div class="highlight"> <input id="schActive" value="1" name="SCHACT" type="checkbox"/> <label class="choice bold" for="schActive">表单只允许在规定的时间范围内访问</label> <a href="#" class="help" title="关于访问时间限制" rel="此属性用于指定表单的有效时间范围。表单将仅在此范围内能够正常访问,过期后将自动关闭。如果不需要进行访问时间限制,请不要勾选此选项。">(?)</a> <div id="listDateRange" class="hide"> <div id="startTime" class="oneline overhide reduction color-green start"> <label class="h3">开始时间</label> <span> <input class="yyyy" maxlength="4" type="text"/> <label>YYYY</label> </span> <span> <input class="mm" maxlength="2" type="text"/> <label>MM</label> </span> <span> <input class="dd" maxlength="2" type="text"/> <label>DD</label> </span> <span><input type="text" class="hide datepincker"></input></span> <span> <select class="ho"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="11">11</option> <option selected="selected" value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> </select> <label>HH</label> </span> <span> <select class="mi"> <option value="00">00</option> <option value="15">15</option> <option value="30">30</option> <option value="45">45</option> </select> <label>MM</label> </span> </div> <div id="endTime" class="oneline overhide reduction color-red end"> <label class="h3">结束时间</label> <span> <input class="yyyy" maxlength="4" type="text"/> <label>YYYY</label> </span> <span> <input class="mm" maxlength="2" type="text"/> <label>MM</label> </span> <span> <input class="dd" maxlength="2" type="text"/> <label>DD</label> </span> <span><input type="text" class="hide datepincker"></input></span> <span> <select class="ho"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="11">11</option> <option selected="selected" value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> </select> <label>HH</label> </span> <span> <select class="mi"> <option value="00">00</option> <option value="15">15</option> <option value="30">30</option> <option value="45">45</option> </select> <label>MM</label> </span> </div> <div class="noheight clear"></div> </div> </div> </li> </ul> </fieldset> </li> <li class="clear noheight"></li> <li> <fieldset><legend>数据查看</legend> <ul> <li> <label for="chkHideEmpty"><input type="checkbox" value="1" name="HDEMP" id="chkHideEmpty"> 查看数据时隐藏值为空的字段</label> <a title="关于隐藏值为空的字段" rel="当勾选此选项后,查看数据时将不显示值为空的字段。此设置对手机端查看和发送到邮箱的数据副本同样有效。" class="help" href="#">(?)</a> <div class="highlight"> <label for="chkPublicData"><input type="checkbox" value="1" name="PUBDT" id="chkPublicData"> 允许未登录用户查询数据</label> <a title="关于允许未登录用户查询数据" rel="当勾选此选项后,将对外发布一个查询页面,未登录的用户也可以通过此页面查询表单提交的数据,通常用于通讯录查询,执行进度查询,成绩查询等场景。" class="help" href="#">(?)</a> <a id="btnPubDataSetting" href="#" class="btn no-icon btn-gray hide">设置详细参数</a> </div> </li> </ul> </fieldset> </li> --> </ul> </div> <!-- form properties end --> </div> <!-- right end --> </div><!-- container end --> <div class="hide"> <input id="itemselectbtn" value="选择文件" type="button" /> </div> </div><!-- container end --> <div id="overlay" class="overlay hide"></div> <div id="lightBox" class="lightbox hide"> <div id="lbContent" class="lbcontent"></div> </div> <div id="status" class="status hide"> <div id="y" class="y" style="top:0xp;left:0px"> <div id="statusText" class="statusText">正在处理...</div> </div> </div> <span id="helpTip" class="helpTip hide"><b></b><em></em></span> <script type="text/javascript" src="~/Content/FormDesign/js/head.load.min.js"></script> <script type="text/javascript"> var M = { FRMNM: "表单名称", DESC: "", LANG: "cn", LBLAL: "T", CFMTYP: "T", CFMMSG: "提交成功。", SDMAIL: "0", CAPTCHA: "1", IPLMT: "0", SCHACT: "0", INSTR: "0", ISPUB: "1" } var F = []; var fieldsLimit = 150; var goodsNumber = 60; var imageNumber = 10; var LVL = 4; var isForTemplate = false; M.GID = M.GID || ''; IMGBUCKET = "jsformimages"; head.js("/Content/FormDesign/js/jquery-1.7.2.min.js", "/Content/FormDesign/js/jquery-ui-1.8.24.custom.min.js", "/Content/FormDesign/js/wangEditor.min.js?v=20160929", "/Content/FormDesign/js/ajaxfileupload.js?v=20160929", "/Content/FormDesign/js/plupload.full.min.js?v=20160929", "/Content/FormDesign/js/directfileupload.js?v=20160929", "/Content/FormDesign/js/utils.js?v=20160929", "/Content/FormDesign/js/widgets.js?v=20160929", "/Content/FormDesign/js/jquery.mCustomScrollbar.min.js?v=20160929", "/Content/FormDesign/js/jquery.mousewheel.min.js?v=20160929", "/Content/FormDesign/js/formbuilder.js?v=20160929", "/Content/FormDesign/js/address-cn.js?v=20160929"); </script> </body> </html>
以上脚本中主要的操作就是拖动,设置每个控件的时候改变对应json的值。至于样式的变化这里没绘制表单的时候复杂一些。如果那位哥们要用这个模板稍微看一下代码即可你只要修改一下地方即可:
复制代码 代码如下:var M={FRMNM:"表单名称",DESC:"",LANG:"cn",LBLAL:"T",CFMTYP:"T",CFMMSG:"提交成功。",SDMAIL:"0",CAPTCHA:"1",IPLMT:"0",SCHACT:"0",INSTR:"0",ISPUB:"1"}
var F=[];
其中M是表单信息json串,F是所用控件数据集字符串。所以在绘制的后保存的时候把这两个值传到后台保存即可,只有修改的货在页面初始加载的时候进行赋值就行啦。
2.2后台代码(MVC)
[HttpGet] public ActionResult FormView(string formData, string parameterData) { ViewBag.FormInfo = Session["formData"]; ViewBag.FormParameter = Session["parameterData"]; return View(); } /// <summary> /// 页面预览 /// </summary> /// <param name="formData">表单信息</param> /// <param name="parameterData">表单控件信息</param> /// <returns></returns> [HttpPost] //[ValidateInput(false)] public ActionResult FormView(string formData, string parameterData) { Session["formData"] = formData; Session["parameterData"] = parameterData; return pageHelper.OpenTab("FormDesignDetail", "预览", "/CustomFrom/FormDesign/FormDesignDetail"); }
页面预览操作。不保存只是在页面上显示而已。因为绘制表单不处理样式的,所以最终样式以预览的样式为标准,所见即所得。
/// <summary> /// 保存表单信息 /// </summary> /// <param name="arry">{表单信息,表单字段信息}</param> /// <returns></returns> [HttpPost] [ValidateInput(false)] public ActionResult FormSave(string formData, string parameterData) { try { ActionResult result = base.AddExecuteScript(); if (result != null) return result; string strGUID = Guid.NewGuid().ToString(); //直接返回字符串类型 JavaScriptSerializer js = new JavaScriptSerializer(); List<FormControlInfo> fclist = js.Deserialize<List<FormControlInfo>>(parameterData); FormDesign model = js.Deserialize<FormDesign>(formData); model.FormInfo = formData; model.Form_Guid = strGUID; model.ZhuangTai = "0"; model.LuRuRen = userHelper.GetUser().UName; model.LuRuRen = DateTime.Now.ToString("yyyy-MM-dd"); var flag = bll.AddFormDesign(model, fclist); return flag ? pageHelper.CloseOpenTab("FormDesignAdd", "FormDesignDetail" + strGUID, "表单信息详情", "/CustomFrom/FormDesign/FormDesignDetail?formgid=" + strGUID) : pageHelper.ExtAlert("新增失败!"); } catch (Exception ex) { return null; } }
页面保存信息,把json信息的元素都进行存储。表单信息和表单控件信息外键关联用的是guid。
注:代码请忽略一些细节和我们框架有关
预览脚本
3.脚本要不要说一下???
[中午吃饭的时候写的,周末抽时间详细描述一下哈]
function createFields() { var b, //标签对其方式 a = $('#fields').empty(); if ('L' === M.LBLAL) { a.addClass('leftLabel') } else { if ('R' === M.LBLAL) { a.addClass('leftLabel labelRight') } } var mwith = 0; //循环数据 typ="text" min="2323" max="432" reqd="1" uniq="1" def="默认值" $.each(F, function (d, c) { if (!c) { return true } b = $(DEFFLD.field_li); b.attr('id', M.GID + d); b.attr("typ", c.TYP); if (c.min != undefined && c.min != "") { b.attr("min", c.min) }; if (c.max != undefined && c.max != "") { b.attr("max", c.max) } ; if (c.reqd != undefined && c.reqd != "0") { b.attr("reqd", c.reqd) }; if (c.uniq != undefined && c.uniq != "0") { b.attr("uniq", c.uniq) }; if (c.def != undefined && c.def != "") { b.attr("def", c.def) }; //布局非单行布局的时候,div宽度增加。 if (c.LAYOUT != undefined && c.LAYOUT != "") { b.addClass(c.LAYOUT) mwith += 200; } if (c.INSTR != undefined && c.INSTR != "") { b.addClass("fieldInstruct"); } createField(b, c, d + 1) a.append(b); }); if ($.isEmptyObject(F)) { $('#nofields').show(); $('#formButtons').hide() } if (M.GID != "") { a.append(DEFFLD['form_but'].html); } }
首先页面的布局是这样的
其中控件的属性值主要是在li特定属性中进行控制。上边代码就是根据json字符串进行对li进行赋值的。当表单如果要两行布局的时候宽度会进行增加
function createField(r, q, num) { if (!q) { return } var p, o, l, m; // r.removeClass('one two three oneByOne fieldInstruct'); // r.attr('title', '点击编辑,拖动排序'); r.empty(); $('#nofields').hide(); $('#formButtons').show(); l = $(DEFFLD[q.TYP].html); //l.attr("name","F"+num); if ('goods' == q.TYP && '1' == q.NOIMG) { l = $(DEFFLD.goodsnoimg.html) } if (q.TYP === 'html' || q.TYP === 'section') { p = l.find('label.desc'); m = l.find('div.content') } else { p = l.filter('label.desc'); m = l.filter('div.content') } if (q.TYP === 'likert') { p = m.find('label.desc') } o = p.find('span'); if (q.REQD === '1') { o.removeClass('hide') } p.text(q.LBL); p.append(o); if (q.TYP === 'text' && '1' == q.QRINPUT) { m.find('i.qrinput').removeClass('hide') m.find('text').attr('name', 'F' + num); } else { if (q.TYP === 'phone' && q.FMT) { m.html(DEFFLD[$.format('phone_{0}_{1}', q.FMT, M.LANG)]); '1' == q.AUTH ? m.find('.sendcode').show() : m.find('.sendcode').hide(); '1' == q.AUTH ? $('#signcnt').show() : $('#signcnt').hide() } else { if (q.TYP === 'date' && q.FMT) { m.html(DEFFLD[$.format('date_{0}', q.FMT)]) } else { if (q.TYP === 'name' && q.FMT) { if (q.FMT === 'short') { m.html(DEFFLD.name_short) } else { m.html(DEFFLD[$.format('name_{0}_{1}', q.FMT, M.LANG)]) } } else { if (q.TYP === 'address') { m.html(DEFFLD[$.format('address_{0}', M.LANG)]); if (q.DEF) { var g = q.DEF.split('-'); m.find('select.province').empty().append($.format('<option>{0}</option>', g[0] || '省/自治区/直辖市')); m.find('select.city').empty().append($.format('<option>{0}</option>', g[1] || '市')); m.find('select.zip').empty().append($.format('<option>{0}</option>', g[2] || '区/县')) } } else { if (q.TYP === 'radio') { createRadioItemsPreview(q, m) } else { if (q.TYP === 'checkbox') { m.empty(); var b; var a; var i = false; $.each(q.ITMS, function (j, c) { if (c.IMG) { i = true; return false } }); $.each(q.ITMS, function (j, c) { b = $(DEFFLD.item_checkbox_f); b.find('label').text(c.VAL); b.find(':checkbox').prop('checked', c.CHKED === '1'); if (i) { if (c.IMG) { a = $('<div class=\'goods-item\'><div class=\'image-center\'><img class=\'img\' src=\'' + IMAGESURL + c.IMG + '\'/></div><div class=\'text-wapper center\'><span>' + b.html() + '</span></div></div>') } else { a = $('<div class=\'goods-item\'><div class=\'image-center\'><img class=\'img\' src=\'\'/></div><div class=\'text-wapper center\'><span>' + b.html() + '</span></div></div>') } m.append(a) } else { m.append(b) } }) } else { if (q.TYP === 'image') { m.find('img').attr('src', q.IMG ? IMAGESURL + q.IMG : '/rs/images/defaultimg.png') //m.find('img').attr('name','F'+num); } else { if (q.TYP === 'goods') { createGoodsItemsPreView(q, m) } else { if (q.TYP === 'section') { m.html($.enterToBr((q.SECDESC || ''))) } else { if (q.TYP === 'html') { m.html($.encodeScript(q.HTML || '')) } else { if (q.TYP === 'likert') { createLikertPreview(q, l) } else { if (q.TYP === 'dropdown2') { var d = q.pN || '2'; if (d !== '2') { d = parseInt(d); m.find('select').remove(); for (var f = 0; f < d; f++) { m.append('<select disabled="disabled" class="input"></select>') } m.find('select').css({ width: (100 / d - 1) + '%', 'margin-right': '1%' }) } for (var e = 0; e < q.ITMS.length; e++) { if (q.ITMS[e].CHKED === '1') { m.find('select:first').empty().append($.format('<option>{0}</option>', q.ITMS[e].VAL)); break } } } } } } } } } } } } } } } if (q.TYP === 'dropdown') { $.each(q.ITMS, function (j, c) { if (c.CHKED === '1' && c.VAL) { m.find('select').append($.tmpl('<option selected="true">${$data}</option>', c.VAL)); // return false } else { m.find('select').append($.format('<option>{0}</option>', c.VAL)); } }) } if (q.DEF) { var s = [ 'text', 'textarea', 'number', 'ulr', 'email', 'money', 'phone' ]; if ($.inArray(q.TYP, s) >= 0) { if (q.TYP === 'phone' && q.FMT === 'tel') { $.each(q.DEF.split('-'), function (j, c) { m.find(':text:eq(' + j + ')').val(c) }) } else { l.find(':input').val(q.DEF) } } } if (q.FLDSZ) { m.find(':text,textarea,select').removeClass('s m xxl').addClass(q.FLDSZ) } var h = $(DEFFLD.instruct); if (q.INSTR) { h.text(q.INSTR) } r.append(DEFFLD.icon).append(l).append(h).append(DEFFLD.fieldActions); if (isInstruct(q.TYP)) { r.addClass('fieldInstruct') } if (q.LAY) { r.addClass(q.LAY) } if (q.SCU == 'pri') { r.addClass('private') } m.find(':text,textarea,select,img,hidden').attr('name', 'F' + num); }
根据不同的控件类型进行不同空间的绘制,其中DEFFLD变量初始化了所需控件html脚本,可以下载源码查看。
总结:一次后台程序员写前端脚本的过程,完成了2年前留下的遗憾。
源码:github.com/kmonkey9006/FormDesign
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易盾网络。
本文共计5886个文字,预计阅读时间需要24分钟。
前言:两年前,在权力斗争的时期,我就想做一个类似的功能。当时大家讨论得很好,但最终因为种种原因搁浅了。没想到两年后,这个想法又重新被提出,需要写一个功能清单。对此,我也有点小激动。
前言
两年前在力控的时候就想做一个类似的功能,当时思路大家都讨论好了,诸多原因最终还是夭折了。没想到两年多后再这有重新提出要写一个绘制表单的功能。对此也是有点小激动呢?总共用时8.5天的时间基本功能也就实现了,当然再者中间也借用了网上的一些资料,公司前端也没有帮忙处理,所以样式和部分功能还没有更好地得到处理,github上出的code只有前端脚本,至于后端的处理,会在博客中体现出来。
1.工作前准备
1.1.实现的思路
思路一:
(1)ueditor添加自定义按钮
(2)绘制表单(控件会触发的脚步)
(3)保存表单时建立数据库表(无需存储表信息),并保存html字符串
(4)修改表单需同时修改数据库
(5)表单发起获取数据封装成json,进行后台保存。
思路二:
(1)jquery 拖动自定义标签,在指定区域进行绘制
(2)表单属性设置相应的表单属性和表单基本布局
(3)设置每个控件的属性值
(4)把表单信息和控件以json的形式传入后台进行保存
(5)从后台获取数据json对象用jquery 绘制表单页面
(6)创建一张表(F-F200)把表单数据存入表单中。
最终选择的是,原因是富文本编辑器绘制起来有很多自动生成的标签,让人感觉很是不爽。当然可以对ueditor进行处理(这个也是两年前的思路)。
1.2.实现过程的确定
整个的过程从借鉴开始网上有些类似的功能,从中得到很多帮助在这就不一一鸣谢了。然后就是没羞没臊的3天脚本修改工作。后台数据的处理完全没有什么可说的,中间用的了一些缓存问题,本来说是用redis呢,结果商量一些说不用难部署(难部署???好吧一脸懵逼),就用了c#的CacheHelper。
2.具体实现
绘制表单预览与保存
2.1.脚本
以上是表单创建的js脚本。
就是上边那个图片的实现。
html重要分左中右三部分。左边是页面上所用到的标签区域,中间是展示区域,右边是表单和控件属性的设置区域。htnl 脚本如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta " /> </li> </ul> </li> <!-- <li class="left half"> <label for="language" class="desc">语言 <a href="#" class="help" title="关于语言" rel="此属性用于指定系统提示信息所使用的语言,当用护访问表单时系统的提示信息(如提交时的验证错误信息)将以此语言显示。目前仅支持简体中文和英文两种语言。">(?)</a></label> <select id="language" name="LANG" class="xxl"> <option value="cn">简单中文</option> <option value="en">English</option> </select> </li> <li class="clear noheight"></li> <li id="liGoods" class="hide"> <fieldset> <legend>商品相关</legend> <ul> <li id="liSale" style="display: list-item;"> <input id="sale" name="SALE" type="checkbox" value="1"> <label for="sale">促销:</label> 满 <input type="text" id="salem" name="SALEM" disabled class="number" style="width:50px"/> 减 <input type="text" id="salej" name="SALEJ" disabled class="number" style="width:50px"/> <a href="#" class="help" title="关于商品促销" rel="当表单中有商品字段时,可以进行“满就减”的促销活动,当金额达到一定量时自动减掉相应金额,助您提升商品销量。">(?)</a> </li> <li id="liPay"> <div class="highlight"> <input type="checkbox" id="chkAliPay" value="1" name="ALIPAY"/> <label class="bold" for="chkAliPay">在线支付</label> <a class="help" href="#" title="关于在线支付" rel="目前仅支持支付宝在线支付,需要开通支付宝商家服务才能使用。<a href='help/formbuilder.html#t62' target='_blank'>查看开通方法</a>">(?)</a> <div id="divPay" class="hide"> <a href="#" id="btnPaySetting" class="btn no-icon btn-gray">配置支付参数</a><br/> <label>不跳转到在线支付条件</label> <a class="help" href="#" title="关于不跳转条件" rel="您可以根据用户填写的数据来确定是否跳转到在线支付页面,当满足如下条件时将不进行跳转,默认跳转。比如您可以添加一个“付款方式”的单选框,并在此设置当选择“货到付款”时不跳转到在线支付。">(?)</a> <div id="noAliConditionDiv" style="margin-bottom: 5px;"> <select id="noAliConditionField" name="PAYCONFLD" style="width:115px;"></select> 等于 <select id="noAliConditionValue" name="PAYCONVAL" style="width:115px;"></select> </div> </div> </div> </li> <li style="text-align: center;display:none;"> <a style="text-decoration: underline;color:#3B699F;" target="_blank" href="jsform-setup.msi">下载小票自动打印程序</a> <a href="#" class="help" title="关于小票自动打印程序" rel="小票打印程序是专为商品类表单设计的一款应用程序。安装后,当有新订单提交时将会自动打印出小票,您要做的仅是“见单送货”。非常适合外卖及实体店铺开展电子商务的应用场景。">(?)</a> </li> </ul> </fieldset> </li> <li class="clear noheight"></li> <li id="liConfirm" class="clear"> <fieldset class="confirm"><legend>跳转选项</legend> <ul> <li class="left"> <input id="confirmType_text" name="CFMTYP" value="T" checked="checked" type="radio"/> <label for="confirmType_text">显示文本</label> <a href="#" class="help" title="关于显示文本" rel="当用户提交表单成功时,系统将跳转至默认的确认页面,页面中将显示下面文本框设定的内容。">(?)</a> </li> <li class="right"> <input id="confirmType_url" name="CFMTYP" value="U" type="radio"/> <label for="confirmType_url">跳转至网页</label> <a href="#" class="help" title="关于跳转至网页" rel="当用户提交表单成功时,系统将转至下面文本框所设定的网址,而不是默认的确认页面。">(?)</a> </li> <li class="clear"> <textarea id="confirmMsg_text" name="CFMMSG" class="xxl hide" rows="3">Thank you. Your entry has been successfully submitted.</textarea> <input id="confirmMsg_url" name="CFMURL" class="xxl hide" type="text" value="" /> </li> </ul> </fieldset> </li> <li class="clear noheight"></li> <li> <fieldset><legend>填写控制</legend> <ul> <li> <label for="captcha">验证码 <a href="#" class="help" title="关于验证码" rel="通过一张只有人眼能识别而电脑无法识别的验证码图片来确定表单是手工提交,而 不是通过软件进行恶意的大量提交。默认情况下,系统将根据同一IP提交频率自动决定是否显示验证码。您也可以选择一直显示验证码或从不显示验证码。从不显示验证码通常用于在某个局域网络有多次提交的情况,用以简化用户输入。">(?)</a></label> <div> <select id="captcha" class="m" name="CAPTCHA"> <option value="1">自动 (推荐)</option> <option value="2">一直显示</option> <option value="0">从不显示</option> </select> </div> </li> <li> <label for="entriesLimit">达到如下数据量后关闭表单 <a href="#" class="help" title="关于数据量限制" rel="此属性用于指定表单可以收集数据的最大数据量,当达到此数据量后表单将自动关闭。如果不想进行最大数据量限制,请将此处设置为空。">(?)</a> </label> <input id="entriesLimit" class="intnumber m" name="ENLMT" maxlength="8" type="text"/> </li> <li> <input id="onePerIp" name="IPLMT" value="1" type="checkbox"/> <label class="choice" for="onePerIp">每个IP只允许提交一次</label> <a href="#" class="help" title="关于IP访问限制" rel="此属性用于指定每一个IP地址只能提交一次表单,可以防止用户在同一台计算机上进行多次提交。<br/><i>注意:相对外网而言,同一局域网内的不同IP可能会当作同一IP处理。局域网可通过手机验证码确定唯一性。</i>">(?)</a> <br/> </li> <li> <input id="chkAutoFill" type="checkbox" value="1" name="AUTOFILL"/> <label for="chkAutoFill">自动填充上次填写数据</label> <a href="#" class="help" title="自动填充上次填写数据" rel="当某个表单需要同一人(同一台设备)多次填写,并且填写数据变化不大时可以勾选此选项,自动填充上次填写数据,加快输入速度。">(?)</a> </li> <li> <div class="highlight"> <input id="schActive" value="1" name="SCHACT" type="checkbox"/> <label class="choice bold" for="schActive">表单只允许在规定的时间范围内访问</label> <a href="#" class="help" title="关于访问时间限制" rel="此属性用于指定表单的有效时间范围。表单将仅在此范围内能够正常访问,过期后将自动关闭。如果不需要进行访问时间限制,请不要勾选此选项。">(?)</a> <div id="listDateRange" class="hide"> <div id="startTime" class="oneline overhide reduction color-green start"> <label class="h3">开始时间</label> <span> <input class="yyyy" maxlength="4" type="text"/> <label>YYYY</label> </span> <span> <input class="mm" maxlength="2" type="text"/> <label>MM</label> </span> <span> <input class="dd" maxlength="2" type="text"/> <label>DD</label> </span> <span><input type="text" class="hide datepincker"></input></span> <span> <select class="ho"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="11">11</option> <option selected="selected" value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> </select> <label>HH</label> </span> <span> <select class="mi"> <option value="00">00</option> <option value="15">15</option> <option value="30">30</option> <option value="45">45</option> </select> <label>MM</label> </span> </div> <div id="endTime" class="oneline overhide reduction color-red end"> <label class="h3">结束时间</label> <span> <input class="yyyy" maxlength="4" type="text"/> <label>YYYY</label> </span> <span> <input class="mm" maxlength="2" type="text"/> <label>MM</label> </span> <span> <input class="dd" maxlength="2" type="text"/> <label>DD</label> </span> <span><input type="text" class="hide datepincker"></input></span> <span> <select class="ho"> <option value="0">00</option> <option value="1">01</option> <option value="2">02</option> <option value="3">03</option> <option value="4">04</option> <option value="5">05</option> <option value="6">06</option> <option value="7">07</option> <option value="8">08</option> <option value="9">09</option> <option value="10">10</option> <option value="11">11</option> <option selected="selected" value="12">12</option> <option value="13">13</option> <option value="14">14</option> <option value="15">15</option> <option value="16">16</option> <option value="17">17</option> <option value="18">18</option> <option value="19">19</option> <option value="20">20</option> <option value="21">21</option> <option value="22">22</option> <option value="23">23</option> </select> <label>HH</label> </span> <span> <select class="mi"> <option value="00">00</option> <option value="15">15</option> <option value="30">30</option> <option value="45">45</option> </select> <label>MM</label> </span> </div> <div class="noheight clear"></div> </div> </div> </li> </ul> </fieldset> </li> <li class="clear noheight"></li> <li> <fieldset><legend>数据查看</legend> <ul> <li> <label for="chkHideEmpty"><input type="checkbox" value="1" name="HDEMP" id="chkHideEmpty"> 查看数据时隐藏值为空的字段</label> <a title="关于隐藏值为空的字段" rel="当勾选此选项后,查看数据时将不显示值为空的字段。此设置对手机端查看和发送到邮箱的数据副本同样有效。" class="help" href="#">(?)</a> <div class="highlight"> <label for="chkPublicData"><input type="checkbox" value="1" name="PUBDT" id="chkPublicData"> 允许未登录用户查询数据</label> <a title="关于允许未登录用户查询数据" rel="当勾选此选项后,将对外发布一个查询页面,未登录的用户也可以通过此页面查询表单提交的数据,通常用于通讯录查询,执行进度查询,成绩查询等场景。" class="help" href="#">(?)</a> <a id="btnPubDataSetting" href="#" class="btn no-icon btn-gray hide">设置详细参数</a> </div> </li> </ul> </fieldset> </li> --> </ul> </div> <!-- form properties end --> </div> <!-- right end --> </div><!-- container end --> <div class="hide"> <input id="itemselectbtn" value="选择文件" type="button" /> </div> </div><!-- container end --> <div id="overlay" class="overlay hide"></div> <div id="lightBox" class="lightbox hide"> <div id="lbContent" class="lbcontent"></div> </div> <div id="status" class="status hide"> <div id="y" class="y" style="top:0xp;left:0px"> <div id="statusText" class="statusText">正在处理...</div> </div> </div> <span id="helpTip" class="helpTip hide"><b></b><em></em></span> <script type="text/javascript" src="~/Content/FormDesign/js/head.load.min.js"></script> <script type="text/javascript"> var M = { FRMNM: "表单名称", DESC: "", LANG: "cn", LBLAL: "T", CFMTYP: "T", CFMMSG: "提交成功。", SDMAIL: "0", CAPTCHA: "1", IPLMT: "0", SCHACT: "0", INSTR: "0", ISPUB: "1" } var F = []; var fieldsLimit = 150; var goodsNumber = 60; var imageNumber = 10; var LVL = 4; var isForTemplate = false; M.GID = M.GID || ''; IMGBUCKET = "jsformimages"; head.js("/Content/FormDesign/js/jquery-1.7.2.min.js", "/Content/FormDesign/js/jquery-ui-1.8.24.custom.min.js", "/Content/FormDesign/js/wangEditor.min.js?v=20160929", "/Content/FormDesign/js/ajaxfileupload.js?v=20160929", "/Content/FormDesign/js/plupload.full.min.js?v=20160929", "/Content/FormDesign/js/directfileupload.js?v=20160929", "/Content/FormDesign/js/utils.js?v=20160929", "/Content/FormDesign/js/widgets.js?v=20160929", "/Content/FormDesign/js/jquery.mCustomScrollbar.min.js?v=20160929", "/Content/FormDesign/js/jquery.mousewheel.min.js?v=20160929", "/Content/FormDesign/js/formbuilder.js?v=20160929", "/Content/FormDesign/js/address-cn.js?v=20160929"); </script> </body> </html>
以上脚本中主要的操作就是拖动,设置每个控件的时候改变对应json的值。至于样式的变化这里没绘制表单的时候复杂一些。如果那位哥们要用这个模板稍微看一下代码即可你只要修改一下地方即可:
复制代码 代码如下:var M={FRMNM:"表单名称",DESC:"",LANG:"cn",LBLAL:"T",CFMTYP:"T",CFMMSG:"提交成功。",SDMAIL:"0",CAPTCHA:"1",IPLMT:"0",SCHACT:"0",INSTR:"0",ISPUB:"1"}
var F=[];
其中M是表单信息json串,F是所用控件数据集字符串。所以在绘制的后保存的时候把这两个值传到后台保存即可,只有修改的货在页面初始加载的时候进行赋值就行啦。
2.2后台代码(MVC)
[HttpGet] public ActionResult FormView(string formData, string parameterData) { ViewBag.FormInfo = Session["formData"]; ViewBag.FormParameter = Session["parameterData"]; return View(); } /// <summary> /// 页面预览 /// </summary> /// <param name="formData">表单信息</param> /// <param name="parameterData">表单控件信息</param> /// <returns></returns> [HttpPost] //[ValidateInput(false)] public ActionResult FormView(string formData, string parameterData) { Session["formData"] = formData; Session["parameterData"] = parameterData; return pageHelper.OpenTab("FormDesignDetail", "预览", "/CustomFrom/FormDesign/FormDesignDetail"); }
页面预览操作。不保存只是在页面上显示而已。因为绘制表单不处理样式的,所以最终样式以预览的样式为标准,所见即所得。
/// <summary> /// 保存表单信息 /// </summary> /// <param name="arry">{表单信息,表单字段信息}</param> /// <returns></returns> [HttpPost] [ValidateInput(false)] public ActionResult FormSave(string formData, string parameterData) { try { ActionResult result = base.AddExecuteScript(); if (result != null) return result; string strGUID = Guid.NewGuid().ToString(); //直接返回字符串类型 JavaScriptSerializer js = new JavaScriptSerializer(); List<FormControlInfo> fclist = js.Deserialize<List<FormControlInfo>>(parameterData); FormDesign model = js.Deserialize<FormDesign>(formData); model.FormInfo = formData; model.Form_Guid = strGUID; model.ZhuangTai = "0"; model.LuRuRen = userHelper.GetUser().UName; model.LuRuRen = DateTime.Now.ToString("yyyy-MM-dd"); var flag = bll.AddFormDesign(model, fclist); return flag ? pageHelper.CloseOpenTab("FormDesignAdd", "FormDesignDetail" + strGUID, "表单信息详情", "/CustomFrom/FormDesign/FormDesignDetail?formgid=" + strGUID) : pageHelper.ExtAlert("新增失败!"); } catch (Exception ex) { return null; } }
页面保存信息,把json信息的元素都进行存储。表单信息和表单控件信息外键关联用的是guid。
注:代码请忽略一些细节和我们框架有关
预览脚本
3.脚本要不要说一下???
[中午吃饭的时候写的,周末抽时间详细描述一下哈]
function createFields() { var b, //标签对其方式 a = $('#fields').empty(); if ('L' === M.LBLAL) { a.addClass('leftLabel') } else { if ('R' === M.LBLAL) { a.addClass('leftLabel labelRight') } } var mwith = 0; //循环数据 typ="text" min="2323" max="432" reqd="1" uniq="1" def="默认值" $.each(F, function (d, c) { if (!c) { return true } b = $(DEFFLD.field_li); b.attr('id', M.GID + d); b.attr("typ", c.TYP); if (c.min != undefined && c.min != "") { b.attr("min", c.min) }; if (c.max != undefined && c.max != "") { b.attr("max", c.max) } ; if (c.reqd != undefined && c.reqd != "0") { b.attr("reqd", c.reqd) }; if (c.uniq != undefined && c.uniq != "0") { b.attr("uniq", c.uniq) }; if (c.def != undefined && c.def != "") { b.attr("def", c.def) }; //布局非单行布局的时候,div宽度增加。 if (c.LAYOUT != undefined && c.LAYOUT != "") { b.addClass(c.LAYOUT) mwith += 200; } if (c.INSTR != undefined && c.INSTR != "") { b.addClass("fieldInstruct"); } createField(b, c, d + 1) a.append(b); }); if ($.isEmptyObject(F)) { $('#nofields').show(); $('#formButtons').hide() } if (M.GID != "") { a.append(DEFFLD['form_but'].html); } }
首先页面的布局是这样的
其中控件的属性值主要是在li特定属性中进行控制。上边代码就是根据json字符串进行对li进行赋值的。当表单如果要两行布局的时候宽度会进行增加
function createField(r, q, num) { if (!q) { return } var p, o, l, m; // r.removeClass('one two three oneByOne fieldInstruct'); // r.attr('title', '点击编辑,拖动排序'); r.empty(); $('#nofields').hide(); $('#formButtons').show(); l = $(DEFFLD[q.TYP].html); //l.attr("name","F"+num); if ('goods' == q.TYP && '1' == q.NOIMG) { l = $(DEFFLD.goodsnoimg.html) } if (q.TYP === 'html' || q.TYP === 'section') { p = l.find('label.desc'); m = l.find('div.content') } else { p = l.filter('label.desc'); m = l.filter('div.content') } if (q.TYP === 'likert') { p = m.find('label.desc') } o = p.find('span'); if (q.REQD === '1') { o.removeClass('hide') } p.text(q.LBL); p.append(o); if (q.TYP === 'text' && '1' == q.QRINPUT) { m.find('i.qrinput').removeClass('hide') m.find('text').attr('name', 'F' + num); } else { if (q.TYP === 'phone' && q.FMT) { m.html(DEFFLD[$.format('phone_{0}_{1}', q.FMT, M.LANG)]); '1' == q.AUTH ? m.find('.sendcode').show() : m.find('.sendcode').hide(); '1' == q.AUTH ? $('#signcnt').show() : $('#signcnt').hide() } else { if (q.TYP === 'date' && q.FMT) { m.html(DEFFLD[$.format('date_{0}', q.FMT)]) } else { if (q.TYP === 'name' && q.FMT) { if (q.FMT === 'short') { m.html(DEFFLD.name_short) } else { m.html(DEFFLD[$.format('name_{0}_{1}', q.FMT, M.LANG)]) } } else { if (q.TYP === 'address') { m.html(DEFFLD[$.format('address_{0}', M.LANG)]); if (q.DEF) { var g = q.DEF.split('-'); m.find('select.province').empty().append($.format('<option>{0}</option>', g[0] || '省/自治区/直辖市')); m.find('select.city').empty().append($.format('<option>{0}</option>', g[1] || '市')); m.find('select.zip').empty().append($.format('<option>{0}</option>', g[2] || '区/县')) } } else { if (q.TYP === 'radio') { createRadioItemsPreview(q, m) } else { if (q.TYP === 'checkbox') { m.empty(); var b; var a; var i = false; $.each(q.ITMS, function (j, c) { if (c.IMG) { i = true; return false } }); $.each(q.ITMS, function (j, c) { b = $(DEFFLD.item_checkbox_f); b.find('label').text(c.VAL); b.find(':checkbox').prop('checked', c.CHKED === '1'); if (i) { if (c.IMG) { a = $('<div class=\'goods-item\'><div class=\'image-center\'><img class=\'img\' src=\'' + IMAGESURL + c.IMG + '\'/></div><div class=\'text-wapper center\'><span>' + b.html() + '</span></div></div>') } else { a = $('<div class=\'goods-item\'><div class=\'image-center\'><img class=\'img\' src=\'\'/></div><div class=\'text-wapper center\'><span>' + b.html() + '</span></div></div>') } m.append(a) } else { m.append(b) } }) } else { if (q.TYP === 'image') { m.find('img').attr('src', q.IMG ? IMAGESURL + q.IMG : '/rs/images/defaultimg.png') //m.find('img').attr('name','F'+num); } else { if (q.TYP === 'goods') { createGoodsItemsPreView(q, m) } else { if (q.TYP === 'section') { m.html($.enterToBr((q.SECDESC || ''))) } else { if (q.TYP === 'html') { m.html($.encodeScript(q.HTML || '')) } else { if (q.TYP === 'likert') { createLikertPreview(q, l) } else { if (q.TYP === 'dropdown2') { var d = q.pN || '2'; if (d !== '2') { d = parseInt(d); m.find('select').remove(); for (var f = 0; f < d; f++) { m.append('<select disabled="disabled" class="input"></select>') } m.find('select').css({ width: (100 / d - 1) + '%', 'margin-right': '1%' }) } for (var e = 0; e < q.ITMS.length; e++) { if (q.ITMS[e].CHKED === '1') { m.find('select:first').empty().append($.format('<option>{0}</option>', q.ITMS[e].VAL)); break } } } } } } } } } } } } } } } if (q.TYP === 'dropdown') { $.each(q.ITMS, function (j, c) { if (c.CHKED === '1' && c.VAL) { m.find('select').append($.tmpl('<option selected="true">${$data}</option>', c.VAL)); // return false } else { m.find('select').append($.format('<option>{0}</option>', c.VAL)); } }) } if (q.DEF) { var s = [ 'text', 'textarea', 'number', 'ulr', 'email', 'money', 'phone' ]; if ($.inArray(q.TYP, s) >= 0) { if (q.TYP === 'phone' && q.FMT === 'tel') { $.each(q.DEF.split('-'), function (j, c) { m.find(':text:eq(' + j + ')').val(c) }) } else { l.find(':input').val(q.DEF) } } } if (q.FLDSZ) { m.find(':text,textarea,select').removeClass('s m xxl').addClass(q.FLDSZ) } var h = $(DEFFLD.instruct); if (q.INSTR) { h.text(q.INSTR) } r.append(DEFFLD.icon).append(l).append(h).append(DEFFLD.fieldActions); if (isInstruct(q.TYP)) { r.addClass('fieldInstruct') } if (q.LAY) { r.addClass(q.LAY) } if (q.SCU == 'pri') { r.addClass('private') } m.find(':text,textarea,select,img,hidden').attr('name', 'F' + num); }
根据不同的控件类型进行不同空间的绘制,其中DEFFLD变量初始化了所需控件html脚本,可以下载源码查看。
总结:一次后台程序员写前端脚本的过程,完成了2年前留下的遗憾。
源码:github.com/kmonkey9006/FormDesign
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易盾网络。

