大熊君JavaScript插件化开发------(实战篇之DXJ UI ------ Tab功能扩展完结版)
一,开篇分析
Hi,大家好!大熊君又和大家见面了,还记得上一篇文章吗。主要讲述了一个“Tab”插件是如何组织代码以及实现的”,以及过程化设计与面向对象思想设计相结合的方式是
如何设计一个插件的,两种方式各有利弊取长补短,本系列文章是以学习为导向的,具体场景大家自己定夺使用方式。在从这篇文章中,我们还是以那个“Tab”实例为主,
继续扩展相关功能。嘿嘿嘿,废话少说,进入正题。直接上实际效果图:
大家看到了吧,增加了一个新的功能,如果我们在初始化时,我们的模块配置信息项目的条目数大于我们指定的,那么就会显示在“更多模块”
操作项的隐藏列表中,我们的初始化参数配置也从新做了调整比如多了一个“displayMax”指定初始化时的条目数,还有一个项目属性,“status”
在初始化时也去掉了不需要配置了,在程序中动态生成配置,增加了程序的灵活性,下面就具体分析一下吧。
(二),实例分析
(1),首先确定这个插件做什么事。下面看一下插件的调用方式,以及配置参数说明。如下代码:
1 { 2 buttonText : "添加模块" , 3 result : [ 4 { 5 text : "向导提示" , 6 url : "help.html" , 7 showClose : "0" 8 } , 9 { 10 text : "学生信息" , 11 url : "info.html" , 12 showClose : "1" 13 } , 14 { 15 text : "学生分类" , 16 url : "category.html" , 17 showClose : "1" 18 } , 19 { 20 text : "大熊君{{bb}}" , 21 url : "bb.html" , 22 showClose : "1" 23 } , 24 { 25 text : "Beta测试模块" , 26 url : "test.html" , 27 showClose : "1" 28 } , 29 { 30 text : "三胖子" , 31 url : "help.html" , 32 showClose : "1" 33 } , 34 { 35 text : "四秃子" , 36 url : "help.html" , 37 showClose : "1" 38 } 39 ] , 40 displayMax : 5 // 最多显示项目 41 }
“bigbear.ui.createTab”里面包含两个参数,第一个是dom节点对象,第二个是插件参数选项,"buttonText "代表“Tab“插件中,操作按钮的文字描述。
”result“是一个数组,里面包含的是选项卡项目的属性,包括文字描述,点击选项卡项目时做请求使用的url,”showClose“代表选项卡的选项是否显示关闭按钮。
“status”在初始化时也去掉了不需要配置了,在程序中动态生成配置。可能会有关闭状态,分别表示为:1-默认显示,0-关闭状态,2-超过默认的条目数。
(2),功能分步骤介绍
1---,通过可选参数,初始化插件:
1 $(function(){ 2 bigbear.ui.createTab($("#tab"),{ 3 buttonText : "添加模块" , 4 result : [ 5 { 6 text : "向导提示" , 7 url : "help.html" , 8 showClose : "0" 9 } , 10 { 11 text : "学生信息" , 12 url : "info.html" , 13 showClose : "1" 14 } , 15 { 16 text : "学生分类" , 17 url : "category.html" , 18 showClose : "1" 19 } , 20 { 21 text : "大熊君{{bb}}" , 22 url : "bb.html" , 23 showClose : "1" 24 } , 25 { 26 text : "Beta测试模块" , 27 url : "test.html" , 28 showClose : "1" 29 } , 30 { 31 text : "三胖子" , 32 url : "help.html" , 33 showClose : "1" 34 } , 35 { 36 text : "四秃子" , 37 url : "help.html" , 38 showClose : "1" 39 } 40 ] , 41 displayMax : 5 // 最多显示项目 42 }) ; 43 }) ;
2---,渲染并且完成时间绑定以及相关的业务逻辑,比如初始化时条目数量验证。
1 tabProto.init = function(){ 2 if(this._isEmptyResult()){ 3 this._setContent("暂无任何模块!") ; 4 } 5 var that = this ; 6 this.getElem().find(".title .adder") 7 .text("+" + this.getOpts()["buttonText"]) 8 .on("click",function(){ 9 that.getElem().find(".console-panel").slideToggle(function(){ 10 that._renderConsolePanel("0") ; 11 }) ; 12 }) ; 13 $.each(this.getOpts()["result"],function(i,item){ 14 if(that._isDisplayMax(i + 1)){ 15 that._saveOrUpdateStatus(item,"1") ; 16 } 17 else{ 18 that._saveOrUpdateStatus(item,"2") ; 19 } 20 that._render(item) ; 21 }) ; 22 if(!that._isDisplayMax(this.getOpts()["result"].length)){ 23 this.getElem().find(".title .more-mod").fadeIn(function(){ 24 $(this).find(".tag").on("click",function(){ 25 var root = $(this).next() ; 26 root.empty() ; 27 $.each(that._getItemListByStatus("2"),function(i,data){ 28 $("<div></div>").text(data["text"]) 29 .on("click",function(){ 30 if(that._getItemListByStatus("1").length < that.getOpts()["displayMax"]){ 31 that.getElem().find(".title .items div").eq(data["index"]).fadeIn(function(){ 32 that._saveOrUpdateStatus(data,"1") ; 33 }) ; 34 } 35 else{ 36 alert("不能添加任何模块,目前已经是最大数量!") ; 37 } 38 }) 39 .appendTo(root) ; 40 }) ; 41 root.toggle() ; 42 }) ; 43 44 }); 45 } 46 this.getElem().find(".title .items div") 47 .eq(0) 48 .trigger("click") ; // 假定是必须有一项,否则插件意义就不大了! 49 } ;
3---,选项卡切换以及数据内容渲染操作。
1 tabProto._setCurrent = function(index){ 2 var items = this.getElem().find(".title .items div").removeClass("active") ; 3 items.eq(index).addClass("active") ; 4 var contents = this.getElem().find(".content .c").hide() ; 5 contents.eq(index).show() ; 6 } ;
1 item.on("click",function(){ 2 that._setCurrent($(this).index()) ; 3 that._getContent(data["url"]).done(function(result){ 4 that._setContent(result) ; 5 }) 6 .fail(function(){ 7 throw new Error("Net Error !") ; 8 }); 9 })
1 tabProto._setContent = function(html){ 2 this.getElem().find(".content").html(html) ; 3 } ; 4 tabProto._getContent = function(url){ 5 return $.ajax({ 6 url : url 7 }) ; 8 } ;
4---,核心的辅助数据操作方法,不涉及dom。
1 /* update time 2015 1/26 15:36 */ 2 tabProto._isDisplayMax = function(size){ 3 var displayMax = this.getOpts()["displayMax"] || 5 ; 4 return (size <= displayMax) ? true : false ; 5 } ; 6 tabProto._isEmptyResult = function(){ 7 if(!this.getOpts()["result"].length){ 8 return false ; 9 } 10 return true ; 11 } ; 12 tabProto._saveOrUpdateStatus = function(item,status){ 13 item["status"] = status ; 14 } ; 15 tabProto._getItemListByStatus = function(status){ 16 var list = [] ; 17 var result = this.getOpts()["result"] ; 18 $.each(result,function(i,item){ 19 if(status == item["status"]){ 20 list.push(item) ; 21 } 22 }) ; 23 return list ; 24 } ; 25 tabProto._getStatusByIndex = function(index){ 26 var status = null ; 27 var result = this.getOpts()["result"] ; 28 $.each(result,function(i,item){ 29 if(index == item["index"]){ 30 status = item["status"] ; 31 } 32 }) ; 33 return status ; 34 } ;
(三),完整代码以供学习,本代码已经过测试,包括目录结构以及相关的文件。
1,html
1 <body> 2 <div class="dxj-ui-hd"> 3 大熊君{{bb}} - DXJ UI ------ Tab 4 </div> 5 <div class="dxj-ui-bd"> 6 <div id="tab"> 7 <div class="title"> 8 <div class="adder"> 9 + 添加学生信息 10 </div> 11 <div class="items"> 12 <!--<div><span class="del">X</span>欢迎页</div> 13 <div><span class="del">X</span>用户管理</div> 14 <div><span class="del">X</span>Bigbear</div>--> 15 </div> 16 <div class="more-mod"> 17 <div class="tag">更多模块</div> 18 <div class="mods"> 19 20 </div> 21 </div> 22 </div> 23 <div class="console-panel"> 24 </div> 25 <div class="content"> 26 <!--<div class="c"> 27 28 <div class="input-content"><span>姓名:</span><input type="text" /></div> 29 <div class="input-content"><span>备注:</span><textarea></textarea></div> 30 31 </div> <div class="input-content"><input type="button" value="保存" /></div> 32 --> 33 </div> 34 </div> 35 </div> 36 </body>
2,css
1 .dxj-ui-hd { 2 padding:0px ; 3 margin : 0 auto; 4 margin-top:30px; 5 width:780px; 6 height:60px; 7 line-height: 60px; 8 background: #3385ff; 9 color:#fff; 10 font-family: "微软雅黑" ; 11 font-size: 28px; 12 text-align: center; 13 font-weight:bold; 14 } 15 .dxj-ui-bd { 16 padding:0px ; 17 margin : 0 auto; 18 width:778px; 19 padding-top : 30px ; 20 padding-bottom : 30px ; 21 overflow: hidden; 22 border:1px solid #3385ff; 23 } 24 .dxj-ui-bd #tab { 25 padding:0px ; 26 margin : 0 auto; 27 width:720px; 28 overflow: hidden; 29 position:relative; 30 } 31 .dxj-ui-bd #tab .title { 32 width:720px; 33 overflow: hidden; 34 border-bottom:2px solid #3385ff; 35 } 36 .dxj-ui-bd #tab .title .adder { 37 width:160px; 38 height:32px; 39 line-height: 32px; 40 background: #DC143C; 41 color:#fff; 42 font-family: "微软雅黑" ; 43 font-size: 14px; 44 text-align: center; 45 font-weight:bold; 46 float : left; 47 cursor:pointer; 48 } 49 .dxj-ui-bd #tab .title .more-mod { 50 overflow:hidden; 51 border:1px solid #DC143C; 52 width:70px; 53 position:absolute; 54 right:0; 55 margin-right:6px; 56 display:none; 57 } 58 .dxj-ui-bd #tab .title .more-mod .tag{ 59 height:32px; 60 line-height:32px; 61 width:70px; 62 background: #DC143C; 63 color:#fff; 64 font-family: arial ; 65 font-size: 12px; 66 text-align: center; 67 cursor:pointer; 68 } 69 .dxj-ui-bd #tab .title .more-mod .mods { 70 overflow:hidden; 71 width:70px; 72 display:none; 73 } 74 .dxj-ui-bd #tab .title .more-mod .mods div { 75 height:24px; 76 line-height:24px; 77 width:62px; 78 font-family: arial ; 79 font-size: 12px; 80 cursor:pointer; 81 padding-left:10px; 82 } 83 .dxj-ui-bd #tab .title .items { 84 height:32px; 85 86 width:480px; 87 overflow: hidden; 88 float : left; 89 } 90 .dxj-ui-bd #tab .title .items div { 91 padding:0px; 92 margin-left:10px; 93 width:84px; 94 height:32px; 95 line-height: 32px; 96 background: #3385ff; 97 color:#fff; 98 font-family: arial ; 99 font-size: 12px; 100 text-align: center; 101 position:relative; 102 float : left; 103 cursor:pointer; 104 } 105 .dxj-ui-bd #tab .title .items div span.del { 106 width:16px; 107 height:16px; 108 line-height: 16px; 109 display:block; 110 background: #DC143C; 111 position:absolute; 112 right:0 ; 113 top:0; 114 cursor:pointer; 115 } 116 .dxj-ui-bd #tab .content { 117 width:716px; 118 padding-top:30px; 119 overflow: hidden; 120 border:2px solid #3385ff; 121 border-top:0px; 122 min-height:130px; 123 text-align:center; 124 } 125 .dxj-ui-bd #tab .content table { 126 margin : 0 auto ; 127 } 128 .dxj-ui-bd #tab .content div.c { 129 padding-top : 20px ; 130 padding-left:20px; 131 background:#eee; 132 height:140px; 133 } 134 .dxj-ui-bd #tab .content div.c .input-content { 135 margin-top : 10px ; 136 font-family: arial ; 137 font-size: 12px; 138 } 139 .dxj-ui-bd #tab .console-panel { 140 width:716px; 141 padding-top:20px; 142 padding-bottom:20px; 143 overflow: hidden; 144 border:2px solid #3385ff; 145 border-top:0px; 146 border-bottom:2px solid #3385ff; 147 background:#fff; 148 display:none; 149 } 150 151 .active { 152 font-weight:bold ; 153 }
3,bigbear.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | ( function ($){ var win = window ; var bb = win.bigbear = win.bigbear || { ui : {} } ; var ui = bb.ui = {} ; var Tab = function (elem,opts){ this .elem = elem ; this .opts = opts ; } ; var tabProto = Tab.prototype ; /* update time 2015 1/26 15:36 */ tabProto._isDisplayMax = function (size){ var displayMax = this .getOpts()[ "displayMax" ] || 5 ; return (size <= displayMax) ? true : false ; } ; tabProto._isEmptyResult = function (){ if (! this .getOpts()[ "result" ].length){ return false ; } return true ; } ; tabProto._saveOrUpdateStatus = function (item,status){ item[ "status" ] = status ; } ; tabProto._getItemListByStatus = function (status){ var list = [] ; var result = this .getOpts()[ "result" ] ; $.each(result, function (i,item){ if (status == item[ "status" ]){ list.push(item) ; } }) ; return list ; } ; tabProto._getStatusByIndex = function (index){ var status = null ; var result = this .getOpts()[ "result" ] ; $.each(result, function (i,item){ if (index == item[ "index" ]){ status = item[ "status" ] ; } }) ; return status ; } ; tabProto._renderConsolePanel = function (status){ var that = this ; var root = that.getElem().find( ".console-panel" ) ; this ._resetConsolePanel() ; $.each(that._getItemListByStatus(status), function (i,item){ var elem = $( "<div style='float:left';></div>" ).appendTo(root) ; $( "<input type='radio' name='addmod' />" ) .data( "item" ,item) .appendTo(elem) ; $( "<span></span>" ).text(item[ "text" ]).appendTo(elem) ; }) ; if (root.find( "div" ).size()){ $( "<input type='button' value='添加模块' style='margin-left:20px'/>" ) .on( "click" , function (){ var data = root.find( "input[type=radio]:checked" ).data( "item" ) ; if (that._getItemListByStatus( "1" ).length < that.getOpts()[ "displayMax" ]){ that.getElem().find( ".title .items div" ).eq(data[ "index" ]).fadeIn( function (){ that._saveOrUpdateStatus(data, "1" ) ; }) .trigger( "click" ) ; } else { that._saveOrUpdateStatus(data, "2" ) ; } that.getElem().find( ".title .adder" ).trigger( "click" ) ; }) .appendTo(root) ; } else { root.text( "暂无任何可添加的项目!" ) ; } } ; /* update time 2015 1/26 15:36 */ tabProto._setCurrent = function (index){ var items = this .getElem().find( ".title .items div" ).removeClass( "active" ) ; items.eq(index).addClass( "active" ) ; var contents = this .getElem().find( ".content .c" ).hide() ; contents.eq(index).show() ; } ; tabProto.getElem = function (){ return this .elem ; } ; tabProto.getOpts = function (){ return this .opts ; } ; tabProto._resetContent = function (){ this .getElem().find( ".content" ).html( "" ) ; } ; tabProto._setContent = function (html){ this .getElem().find( ".content" ).html(html) ; } ; tabProto._getContent = function (url){ return $.ajax({ url : url }) ; } ; tabProto._deleteItem = function (elem){ var that = this ; this .getElem().find( ".title .items div" ) .eq(elem.index()) .fadeOut( function (){ that._resetContent() ; that._saveOrUpdateStatus(elem.data( "item" ), "0" ) ; that._triggerItem(elem.index() + 1) ; }) ; } ; tabProto._triggerItem = function (next){ var nextStatus = this ._getStatusByIndex(next) ; var items = this .getElem().find( ".title .items div" ) ; next = items.eq(next) ; if (next.size() && "1" == nextStatus){ //后继dom节点存在 next.trigger( "click" ) ; } else { items.eq(0).trigger( "click" ) ; } } ; tabProto._resetConsolePanel = function (){ this .getElem().find( ".console-panel" ).empty() ; } ; tabProto.init = function (){ if ( this ._isEmptyResult()){ this ._setContent( "暂无任何模块!" ) ; } var that = this ; this .getElem().find( ".title .adder" ) .text( "+" + this .getOpts()[ "buttonText" ]) .on( "click" , function (){ that.getElem().find( ".console-panel" ).slideToggle( function (){ that._renderConsolePanel( "0" ) ; }) ; }) ; $.each( this .getOpts()[ "result" ], function (i,item){ if (that._isDisplayMax(i + 1)){ that._saveOrUpdateStatus(item, "1" ) ; } else { that._saveOrUpdateStatus(item, "2" ) ; } that._render(item) ; }) ; if (!that._isDisplayMax( this .getOpts()[ "result" ].length)){ this .getElem().find( ".title .more-mod" ).fadeIn( function (){ $( this ).find( ".tag" ).on( "click" , function (){ var root = $( this ).next() ; root.empty() ; $.each(that._getItemListByStatus( "2" ), function (i,data){ $( "<div></div>" ).text(data[ "text" ]) .on( "click" , function (){ if (that._getItemListByStatus( "1" ).length < that.getOpts()[ "displayMax" ]){ that.getElem().find( ".title .items div" ).eq(data[ "index" ]).fadeIn( function (){ that._saveOrUpdateStatus(data, "1" ) ; }) ; } else { alert( "不能添加任何模块,目前已经是最大数量!" ) ; } }) .appendTo(root) ; }) ; root.toggle() ; }) ; }); } this .getElem().find( ".title .items div" ) .eq(0) .trigger( "click" ) ; // 假定是必须有一项,否则插件意义就不大了! } ; tabProto._render = function (data){ var that = this ; var item = $( "<div></div>" ).text(data[ "text" ]).appendTo( this .getElem().find( ".title .items" )) ; data[ "index" ] = item.index() ; item.on( "click" , function (){ that._setCurrent($( this ).index()) ; that._getContent(data[ "url" ]).done( function (result){ that._setContent(result) ; }) .fail( function (){ throw new Error( "Net Error !" ) ; }); }) .data( "item" ,data) ; if ( "2" == data[ "status" ]){ item.hide() ; } if ( "1" == data[ "showClose" ]){ $( "<span class='del'>X</span>" ) .on( "click" , function (){ if (win.confirm( "是否删除此项?" )){ that._deleteItem(item) ; return false ; // 阻止冒泡 } }) .appendTo(item) ; } } ; ui.createTab = function (elem,opts){ var tab = new Tab(elem,opts) ; tab.init() ; return tab ; } ; })(jQuery) ; |
(四),最后总结
(1),面向对象的思考方式合理分析功能需求。
(2),以类的方式来组织我们的插件逻辑。
(3),不断重构上面的实例,如何进行合理的重构那?不要设计过度,要游刃有余,推荐的方式是过程化设计与面向对象思想设计相结合。
哈哈哈,本篇结束,未完待续,希望和大家多多交流够沟通,共同进步。。。。。。呼呼呼……(*^__^*)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?