Ext4+Java構建Web應用程序後臺經典界面

      在我以前寫的一篇文章ExtJS4+Servlet/Struts2+JSON+accordion佈局動態Ext.tree.Panel菜單,大家都對Ext.tree.Panel組件和Ext.tab.Panel組件相結合起來比較困惑。對於Ext.tree.Panel的異步加載也有問題。寫這篇文章分享我對Ext開發的一些做法和認識。談談自己如何構建Ext應用框架的,下面讓大家看看效果圖:


  •  這個圖的左邊是一個樹,這個樹的結構是從數據庫通過異步的方式拿出來的,數據格式是JSON。什麼異步呢?就是加載父節點而不加載子節點,需要的時候再去加載子節點,從而節省數資源。
  • 圖的上面是一個很簡單的應用名稱,下面有個工具欄,展示登錄人,系統時間和退出按鈕等一些組件。
  • 圖的右邊是一個標籤頁,這個地方主要是用戶的操作界面,這裏是樹控件的一些數據來生成標籤頁的。
       這個項目是從我的那篇文章中擴展過來的,增加的數據庫的部分。數據庫是MYSQL,數據庫很簡單就只是一張表,表名是resource:

    這裏面字段比較簡單,
  • id:主鍵ID
  • component:一個Ext擴展類或者是調用的頁面,說白了就是你要讓用戶看到的內容
  • descriptio:描述,與業務關鍵,純屬冗餘
  • iconCls:樹節點顯示的圖標
  • text:樹節點顯示的名稱
  • sort:排序用的
  • type:資源的類型,COMPONENT/URL,是組件的形式或者是URL
  • parent_id:父節點
  • leaf:是否是根節點
        這個是數據表結構,現在我們看下JS實現代碼:
Ext.Loader.setConfig({
	enabled : true
});

Ext.Loader.setPath({
	'Ext.ux' : 'ext4/ux',
	'Ext.app' : 'ext4/app'
});

Ext.require(['Ext.app.Portlet', 'Ext.app.PortalColumn', 'Ext.app.PortalPanel',
		'Ext.app.PortalDropZone', 'Ext.ux.TabReorderer',
		'Ext.ux.TabCloseMenu']);

        這裏配置Ext動態加載功能,並引入了一些需要的Ext類,如果大家對Ext動態加載不怎麼了解,大家可以去看下黃燈橋老師的文章:在應用中使用Ext Loader,這裏對Ext的動態加載用詳細的介紹,我在這裏就不在贅述。下面看下應用的整體佈局,整個框架一共三個組件,上面的爲:title,左邊的爲:tree,右邊的爲:tab,下面看看這些組件的實現,先看title:
	var title = Ext.create("Ext.panel.Panel", {
						height : 80,
						html : '業務基礎平臺',
						region : 'north',
						split : true,
						bbar : [{
							iconCls : 'icon-user',
							text : '管理員'
						},'-',{
							text : Ext.Date.format(new Date(),'Y年m月d日')
						},'->',{
							text : '退出',
							iconCls : 'icon-logout'
						}],
						bodyStyle : 'backgroud-color:#99bbe8;line-height : 50px;padding-left:20px;font-size:22px;color:#000000;font-family:黑體;font-weight:bolder;' +
								'background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(153,187, 232, 0.4) ), color-stop(50%, rgba(153, 187, 232, 1) ),color-stop(0%, rgba(153, 187, 232, 0.4) ) )'
					});
       這個是title,這個組件很簡單,主要是顯示了“業務基礎平臺”的系統名稱,並配置了相關的樣式。並定義了一個bbar,用於顯示用戶名,當前時間和退出按鈕等信息。下面看tab:
var tab = Ext.create('Ext.tab.Panel', {
						activeTab : 0,
						enableTabScroll : true,
						animScroll : true,
						border : true,
						autoScroll : true,
						region : 'center',
						split : true,
						items : [{
							iconCls : 'icon-activity',
							title : '平臺首頁',
							xtype:'portalpanel',
							layout:'column',
							items : [{
									xtype : 'portalcolumn',
									columnWidth : 0.7,
					                items:[{ title: '新聞動態',height : 150,iconCls : 'icon-news' },
					                	{title: '最新通知',height : 150, iconCls : 'icon-notice' },
					                	{title: '業績報表',height : 150, iconCls : 'icon-chart'}]
					            },{
					            	xtype : 'portalcolumn',
					            	columnWidth : 0.3,
					                items:[{ title: '功能鏈接', height : 150, iconCls : 'icon-link'},
					                	{title: '待辦事項',height : 150,iconCls : 'icon-note' },
					                	{title: '郵件列表', height : 150,iconCls : 'icon-email-list'}]
					            }]
						}],
						plugins: [Ext.create('Ext.ux.TabReorderer'),
		        		  Ext.create('Ext.ux.TabCloseMenu',{
		        		  	closeTabText: '關閉面板',
		        		  	closeOthersTabsText: '關閉其他',
		        		  	closeAllTabsText: '關閉所有'
		        		  })]
					});

       這裏我們定義了一個tabpanel,一些很普通的配置,大家可以去看官方文檔。這個代碼裏面有關鍵的兩個部分:
  • 我們定義了一個portalpanel,這個是Ext的一個擴展應用,這個是可以允許用戶進行排版的,大家可以看看效果:
  • 還有兩個插件:Ext.ux.TabReorderer,這個是用來標籤拖動的,Ext.ux.TabCloseMenu,這個是標籤的右鍵菜單,用來關閉標籤頁。
        下面看看左邊樹面板的實現,大家可能對這段代碼比較熟悉,關鍵是accordion佈局,就不多做解釋:
var tree = Ext.create("Ext.panel.Panel", {
						region : 'west',
						title : "系統菜單",
						width : 250,
						iconCls : "icon-tree",
						autoScroll : false,
						layout : 'accordion',
						collapsible : true,
						layoutConfig : {
							animate : true
						},
						split : true
					});
        下面我們將這些組件放在viewport組件裏面,展示出來:

			Ext.create('Ext.container.Viewport',{
				layout : 'border',
				items : [title,tab,tree],
				listeners : {
					afterrender : function(){
						Ext.getBody().mask('正在加載系統菜單....');
						ajax({
							url : "app",// 獲取面板的地址
							params : {
								action : "list"
							},
							callback : addTree
						});
					}
				}
			});
       通過這段代碼的實現,將組件放入到頁面當中,這裏可能大家需要看看ext的佈局方面的知識。大家注意了,在這段代碼中我們註冊了一個afterender的事件,這個事件的主要作用是,在組件渲染完成之後,去獲取系統菜單,ajax方法就是發送一個請求到後臺獲得樹面板的數據,下面我們看看服務器端的實現,服務器端是用java寫的:
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String action = request.getParameter("action");
		initHeader(response);
		if (action.equals("list")) {// 獲取屬面板列表
			renderText(this.getTreePanelList(), response);
		} else if (action.equals("node")) {
			renderText(this.getTreeNodeList(request.getParameter("id")),
					response);
		}
	}
	
	public String getTreePanelList() {
		String sql = "select  t.id, t.text, t.component, "
				+ " t.description, t.type, t.iconCls, t.sort "
				+ " from resource t where t.parent_id is null";
		return BaseDAO.findBySql(sql).toString();
	}

	public String getTreeNodeList(String id) {
		String sql = "select  t.id, t.text, t.component, "
				+ " t.description, t.type, t.iconCls, t.sort,t.leaf "
				+ " from resource t where t.parent_id = '" + id + "'";
		return BaseDAO.findBySql(sql).toString();
	}
      這段代碼的實現的是,通過參數判斷是獲取樹面板還是樹節點,BaseDAO.findBySql方法將返回的結果集轉換成一個List<JSONObject>對象,獲得數據後發送到頁面,這裏面有兩個獲得數據的方法:
  • getTreePanelList,獲取樹面板,這裏的邏輯判斷是沒有父節點的數據
  • getTreeNodeList,獲取樹節點,通過父節點查找子節點
      在ajax方法中註冊了一個回調函數,addTree,用於添加樹面板:
function addTree(data) {
				Ext.getBody().unmask();
				for (var i = 0; i < data.length; i++) {
					tree.add(Ext.create("Ext.tree.Panel", {
								title : data[i].text,
								iconCls : data[i].iconCls,
								//useArrows: true,
								autoScroll : true,
								rootVisible : false,
								viewConfig : {
									loadingText : "正在加載..."
								},
								store : createStore(data[i].id),
								listeners : {
									afterlayout : function() {
										if (this.getView().el) {
											var el = this.getView().el;
											var table = el
													.down("table.x-grid-table");
											if (table) {
												table.setWidth(el.getWidth());
											}
										}
									},
									itemclick : function(view,node){
										if (node.isLeaf()) {
											if(node.data.type === 'URL'){
												var panel = Ext.create('Ext.panel.Panel',{
													title : node.data.text,
													closable : true,
													iconCls : 'icon-activity',
													html : '<iframe width="100%" height="100%" frameborder="0" src="http://www.baidu.com"></iframe>'
												});
												tab.add(panel);
												tab.setActiveTab(panel);
											}else if(node.data.type === 'COMPONENT'){
												var panel = Ext.create(node.data.component,{
													title : node.data.text,
													closable : true,
													iconCls : 'icon-activity'
												});
												tab.add(panel);
												tab.setActiveTab(panel);
											}
										}
									}
								}
							}));
					tree.doLayout();

				}
			}
			var model = Ext.define("TreeModel", { // 定義樹節點數據模型
				extend : "Ext.data.Model",
				fields : [{name : "id",type : "string"},
						{name : "text",type : "string"},
						{name : "iconCls",type : "string"},
						{name : "leaf",type : "boolean"},
						{name : 'type'},
						{name : 'component'}]
			});
			var createStore = function(id) { // 創建樹面板數據源
				var me = this;
				return Ext.create("Ext.data.TreeStore", {
							defaultRootId : id, // 默認的根節點id
							model : model,
							proxy : {
								type : "ajax", // 獲取方式
								url : "app?action=node" // 獲取樹節點的地址
							},
							clearOnLoad : true,
							nodeParam : "id"// 設置傳遞給後臺的參數名,值是樹節點的id屬性
						});
			};
       這段代碼在我的那片文章中由說明,在這裏就不再贅述,下面大家看看生成好的樹面板的效果:

     就這樣樹空間就生成好了,大家注意在上邊的addTree方法的那段代碼中,我們註冊了一個itemclick事件,itemclick會在點擊樹節點的時候觸發:
itemclick : function(view,node){
										if (node.isLeaf()) { //判斷是否是葉子節點
											if(node.data.type === 'URL'){ //判斷資源類型
												var panel = Ext.create('Ext.panel.Panel',{
													title : node.data.text,
													closable : true,
													iconCls : 'icon-activity',
													html : '<iframe width="100%" height="100%" frameborder="0" src="http://www.baidu.com"></iframe>'
												});
												tab.add(panel);
												tab.setActiveTab(panel);
											}else if(node.data.type === 'COMPONENT'){
												var panel = Ext.create(node.data.component,{
													title : node.data.text,
													closable : true,
													iconCls : 'icon-activity'
												});
												tab.add(panel);
												tab.setActiveTab(panel);
											}
										}
									}
     這裏的業務邏輯是,判斷點擊的節點是否是葉子節點,如果在葉子節點的話再判斷節點的類型,如果是URL,我這裏做了簡單的處理,嵌套百度到tab當中,如果是COMPONENT的話,創建對應組件,添加到tab組件當中。效果圖:

grid組件

       這裏有幾個要注意的地方:
  • 創建組件並添加後必須調用tab組件的setActiveTab方法來激活組件,讓其顯示出來
  • Ext.create('class')方法,如果有定義這個class,Ext會直接創建,如果沒有Ext會通過配置的動態加載的規則進行加載
  • 如果要調用node.data裏面的屬性,一定要在store使用的model裏面進行定義,否則就是一個undefined
    至此,這個平臺都搭建就講完了,文采不好,希望大家海涵!!

   
   http://download.csdn.net/detail/leecho571/4307693 實例下載,裏面有個app.sql的數據庫文件,用mysql數據庫導入即可
   看文章評論一下是美德,你的評論是我最大的動力!!期待你的意見!!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章