大熊君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

    

(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),不断重构上面的实例,如何进行合理的重构那?不要设计过度,要游刃有余,推荐的方式是过程化设计与面向对象思想设计相结合。

   

 

                   哈哈哈,本篇结束,未完待续,希望和大家多多交流够沟通,共同进步。。。。。。呼呼呼……(*^__^*)                

posted @ 2015-01-26 19:39  大熊君Bigbear  阅读(1899)  评论(11编辑  收藏  举报