在我以前寫的一篇文章ExtJS4+Servlet/Struts2+JSON+accordion佈局動態Ext.tree.Panel菜單,大家都對Ext.tree.Panel組件和Ext.tab.Panel組件相結合起來比較困惑。對於Ext.tree.Panel的異步加載也有問題。寫這篇文章分享我對Ext開發的一些做法和認識。談談自己如何構建Ext應用框架的,下面讓大家看看效果圖:
- 這個圖的左邊是一個樹,這個樹的結構是從數據庫通過異步的方式拿出來的,數據格式是JSON。什麼異步呢?就是加載父節點而不加載子節點,需要的時候再去加載子節點,從而節省數資源。
- 圖的上面是一個很簡單的應用名稱,下面有個工具欄,展示登錄人,系統時間和退出按鈕等一些組件。
- 圖的右邊是一個標籤頁,這個地方主要是用戶的操作界面,這裏是樹控件的一些數據來生成標籤頁的。
- id:主鍵ID
- component:一個Ext擴展類或者是調用的頁面,說白了就是你要讓用戶看到的內容
- descriptio:描述,與業務關鍵,純屬冗餘
- iconCls:樹節點顯示的圖標
- text:樹節點顯示的名稱
- sort:排序用的
- type:資源的類型,COMPONENT/URL,是組件的形式或者是URL
- parent_id:父節點
- leaf:是否是根節點
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,這個是標籤的右鍵菜單,用來關閉標籤頁。
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
});
}
}
});
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,獲取樹節點,通過父節點查找子節點
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屬性
});
};
這段代碼在我的那片文章中由說明,在這裏就不再贅述,下面大家看看生成好的樹面板的效果: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組件當中。效果圖:- 創建組件並添加後必須調用tab組件的setActiveTab方法來激活組件,讓其顯示出來
- Ext.create('class')方法,如果有定義這個class,Ext會直接創建,如果沒有Ext會通過配置的動態加載的規則進行加載
- 如果要調用node.data裏面的屬性,一定要在store使用的model裏面進行定義,否則就是一個undefined