這次我們來學習下ExtJS中的樹面板(TreePanel),很多人都說,只是為了樹,都要好好學習ExtJS!可見大家對Tree的一往情深。從另一方面來說,Tree這種結(jié)構(gòu)在實際項目中也的確用得相當廣泛,所以我們很有必要研究一下它。這次我們完成的樹大致上有這樣的功能:它的節(jié)點是動態(tài)異步從后臺(存儲在數(shù)據(jù)庫中一張電子產(chǎn)品分類表)加載的,節(jié)點之間可以拖曳,節(jié)點可以編輯,同時還支持右鍵菜單,而且,它能夠和TabPanel結(jié)合構(gòu)成經(jīng)典的布局方式。呵呵,是不是很強大?!大家已經(jīng)看到,我們組件的講解是逐步遞推的,所以我們這里也會用到剛學過的GridPanel和TabPanel這些實用的面板。我們看效果先:
1.????? 首先還是主要的顯示頁面tree.html,這里有兩個地方要注意一下,一個是我們引用的JS如果采用GBK的默認編碼,瀏覽器會顯示未結(jié)束的字符串常量的錯誤,所以我們一般會修改JS文件的編碼方式為UTF-8,或者在導(dǎo)入JS時加上編碼字符集。第二個是我們要定義一個顯示TreePanel的DIV。
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css" href="resources/css/ext-all.css" />
<script type="text/javascript" src="resources/js/ext-base.js"></script>
<script type="text/javascript" src="resources/js/ext-all.js"></script>
<script type="text/javascript" src="resources/js/tree.js" charset="gbk"></script>
</head>
<body>
<div id="tree-panel" style="overflow:auto; height:300px;width:200px;border:2px solid #c3daf9;"></div>
</body>
</html>
?
2.????? 然后是我們的主體JS文件,tree.js,為了凸顯主題,這個我已經(jīng)做了必要的簡化,還做了詳細的注釋,大家好好看一下。
?
Ext.onReady(function() {
??? Ext.QuickTips.init();// 浮動信息提示
??? Ext.BLANK_IMAGE_URL = 'resources/images/default/s.gif';// 替換圖片文件地址為本地
?
??? // 創(chuàng)建一個簡寫
??? var Tree = Ext.tree;
??? // 定義根節(jié)點的Loader
??? var treeloader = new Tree.TreeLoader({
??????????? // dataUrl : 'tree.jsp'//這里可以不需要指定URL,在加載前事件響應(yīng)里面設(shè)置
??????????? });
?
??? // 添加一個樹形面板
??? var treepanel = new Tree.TreePanel({
??????? // renderTo:"tree_div",//如果使用renderTo,則不能使用setRootNode()方法,需要在TreePanel中設(shè)置root屬性。
??????? el : 'tree-panel',// 將樹形添加到一個指定的div中,非常重要!
??????? region : 'west',
??????? title : '功能菜單',
??????? width : 200,
??????? minSize : 180,
??????? maxSize : 250,
??????? split : true,
??????? autoHeight : false,
??????? frame : true,// 美化界面
??????? // width : 200,//面板寬度
??????? // title : '可編輯和拖動的異步樹',//標題
??????? // autoScroll : true, // 自動滾動
??????? enableDD : true,// 是否支持拖拽效果
??????? containerScroll : true,// 是否支持滾動條
??????? rootVisible : true, // 是否隱藏根節(jié)點,很多情況下,我們選擇隱藏根節(jié)點增加美觀性
??????? border : true, // 邊框
??????? animate : true, // 動畫效果
??????? loader : treeloader
??????????? // 樹加載
??????? });
??? // 異步加載根節(jié)點
??? var rootnode = new Tree.AsyncTreeNode({
??????????????? id : '0',
??????????????? text : '家電品牌總類',
??????????????? draggable : false,// 根節(jié)點不容許拖動
??????????????? expanded : true
??????????? });
?
??? // 為tree設(shè)置根節(jié)點
??? treepanel.setRootNode(rootnode);
?
??? // 響應(yīng)加載前事件,傳遞node參數(shù)
??? treepanel.on('beforeload', function(node) {
??????????????? treepanel.loader.dataUrl = 'tree.jsp?parentId=' + node.id; // 定義子節(jié)點的Loader
??????????? });
??? // 渲染樹形
??? treepanel.render();
??? // 展開節(jié)點,第一個參數(shù)表示是否級聯(lián)展開子節(jié)點
??? rootnode.expand(true);
?
??? // 設(shè)置樹的點擊事件
??? function treeClick(node, e) {
??????? if (node.isLeaf()) {
??????????? e.stopEvent();
??????????? var n = contentPanel.getComponent(node.id);
??????????? if (!n) {
??????????????? var n = contentPanel.add({
??????????????????????????? 'id' : node.id,
??????????????????????????? 'title' : node.text,
??????????????????????????? closable : true,
??????????????????????????? autoLoad : {
??????????????????????????????? url : 'tabFrame.jsp?url=grid.html',
??????????????????????????????? scripts : true
??????????????????????????? } // 通過autoLoad屬性載入目標頁,如果要用到腳本,必須加上scripts屬性
??????????????????????? });
??????????? }
??????????? contentPanel.setActiveTab(n);
??????? }
??? }
??? // 增加鼠標單擊事件
??? treepanel.on('click', treeClick);
?
??? // 定義右鍵菜單
??? var rightClick = new Ext.menu.Menu({
??????????????? id : 'rightClickCont',
??????????????? items : [{
??????????????????????????? id : 'rMenu1',
??????????????????????????? text : '添加節(jié)點',
??????????????????????????? // 增加菜單點擊事件
??????????????????????????? handler : function() {
??????????????????????????????? alert('添加節(jié)點的實現(xiàn)!');
??????????????????????????? }
??????????????????????? }, {
??????????????????????????? id : 'rMenu2',
??????????????????????????? text : '編輯節(jié)點'
??????????????????????? }, {
??????????????????????????? id : 'rMenu3',
??????????????????????????? text : '刪除節(jié)點'
??????????????????????? }]
??????????? });
??? // 增加右鍵點擊事件
??? treepanel.on('contextmenu', function(node, event) {// 聲明菜單類型
??????????????? event.preventDefault();// 阻止瀏覽器默認右鍵菜單顯示
??????????????? rightClick.showAt(event.getXY());// 取得鼠標點擊坐標,展示菜單
??????????? });
?
??? /*
???? * 設(shè)置tree的節(jié)點放置函數(shù)此函數(shù)有一個很重要的參數(shù)對象e e對象有三個重要的屬性,分別為dropNode,target,point
???? * 1.dropNode為在拖動時鼠標抓住的節(jié)點 2.target為將要放置在某處的節(jié)點
???? * 3.point為被放置的狀態(tài),分別有append表示添加,above節(jié)點的上方,below節(jié)點的下方。
???? *
???? */
??? treepanel.on('nodedrop', function(e) {
?
??????????????? if (e.point == 'append') {
??????????????????? alert('當前"' + e.dropNode.text + '"劃到"' + e.target.text
??????????????????????????? + '"里面!');
??????????????? } else if (e.point == 'above') {
??????????????????? alert('當前"' + e.dropNode.text + '"放在了"' + e.target.text
??????????????????????????? + '"上面!');
??????????????? } else if (e.point == 'below') {
??????????????????? alert('當前"' + e.dropNode.text + '"放在了"' + e.target.text
??????????????????????????? + '"下面!');
??????????????? }
??????????? });
?
??? // 在原有的樹形添加一個TreeEditor
??? var treeEditer = new Tree.TreeEditor(treepanel, {
??????????????? allowBlank : false
??????????? });
??? /*
???? * 為創(chuàng)建的treeEditer添加事件 有兩個事件最為常用,一個為beforestartedit另一個為complete
???? * 從名字就可以看出,beforestartedit事件是在編輯前的事件,因此可以通過它來判斷那些節(jié)點可以編輯那些不可以。
???? * complete為編輯之后的事件,在這里面可以添加很多事件,比如添加一個Ext.Ajax向后臺傳送修改的值等等。
???? */
??? treeEditer.on("beforestartedit", function(treeEditer) {
??????????????? var tempNode = treeEditer.editNode;// 將要編輯的節(jié)點
??????????????? if (tempNode.isLeaf()) {// 這里設(shè)定葉子節(jié)點才容許編輯
??????????????????? return true;
??????????????? } else {
??????????????????? return false;
??????????????? }
??????????? });
?
??? treeEditer.on("complete", function(treeEditer) {
??????????????? alert("被修改為" + treeEditer.editNode.text);
??????????? });
?
??? // (1)通過TabPanel控件的html屬性配合<iframe>實現(xiàn)。該方法是利用
??? // html屬性中包含<iframe>的語法來調(diào)用另一個頁面,具體見代碼。
??? // (2)通過TabPanel控件的autoLoad屬性實現(xiàn)。該方法是利用autoLoad屬性,它有很多參數(shù),
??? // 其中有兩個比較重要,url表示要載入的文件,scripts表示載入的文件是否含有腳本,該屬性相當重要,
??? // 如果在新的頁面中要創(chuàng)建Ext控件,必須指定該參數(shù)。該方法實現(xiàn)較前一個復(fù)雜,因為引入的文件不是一個完整的html文件,
??? // 有可能只是內(nèi)容的一部分,但是資源占用較少,而且載入速度較快(它有一個載入指示)
?
??? // 添加第一個節(jié)點(html)
??? treepanel.root.appendChild(new Ext.tree.TreeNode({
??????? id : 'htmlPanel',
??????? text : '通過html打開',
??????? listeners : {
??????????? 'click' : function(node, event) {
??????????????? event.stopEvent();
??????????????? var n = contentPanel.getComponent(node.id);
??????????????? if (!n) { // 判斷是否已經(jīng)打開該面板
??????????????????? n = contentPanel.add({
??????????????????????? 'id' : node.id,
??????????????????????? 'title' : node.text,
??????????????????????? closable : true, // 通過html載入目標頁
??????????????????????? html : '<iframe scrolling="auto" frameborder="0" width="100%" height="100%" src="grid.html"></iframe>'
??????????????????? });
??????????????? }
??????????????? contentPanel.setActiveTab(n);
??????????? }
??????? }
??? }));
?
??? // 添加第二個節(jié)點(autoLoad)
??? treepanel.root.appendChild(new Ext.tree.TreeNode({
??????????????? id : 'autoLoadPanel',
??????????????? text : '通過autoLoad打開',
??????????????? listeners : {
??????????????????? 'click' : function(node, event) {
??????????????????????? event.stopEvent();
??????????????????????? var n = contentPanel.getComponent(node.id);
??????????????????????? if (!n) { // //判斷是否已經(jīng)打開該面板
??????????????????????????? n = contentPanel.add({
??????????????????????????????????????? 'id' : node.id,
??????????????????????????????????????? 'title' : node.text,
??????????????????????????????????????? closable : true,
??????????????????????????????????????? autoLoad : {
??????????????????????????????????????????? url : 'tabFrame.jsp?url=grid.html',
??????????????????????????????????????????? scripts : true
??????????????????????????????????????? } // 通過autoLoad屬性載入目標頁,如果要用到腳本,必須加上scripts屬性
??????????????????????????????????? });
??????????????????????? }
??????????????????????? contentPanel.setActiveTab(n);
??????????????????? }
??????????????? }
??????????? }));
?
??? // 右邊具體功能面板區(qū)
??? var contentPanel = new Ext.TabPanel({
??????? region : 'center',
??????? enableTabScroll : true,
??????? activeTab : 0,
??????? items : [{
??????????? id : 'homePage',
??????????? title : '首頁',
??????????? autoScroll : true,
??????????? html : '<div style="position:absolute;color:#ff0000;top:40%;left:40%;">Tree控件和TabPanel控件結(jié)合功能演示</div>'
??????? }]
??? });
?
??? new Ext.Viewport({
??????????????? layout : 'border', // 使用border布局
??????????????? defaults : {
??????????????????? activeItem : 0
??????????????? },
??????????????? items : [treepanel, contentPanel]
??????????? });
?
});
?
4.?????? 后臺用到的JAVA類,這里包括訪問數(shù)據(jù)庫的數(shù)據(jù)源工廠類DataSourceFactory(這里用到了DBCP連接池,大家要記得導(dǎo)入連接sqlserver 2000數(shù)據(jù)庫和dbcp連接池的相關(guān)JAR包!),定義樹節(jié)點的屬性,包括節(jié)點ID、Text、圖標、是否為葉子節(jié)點、是否展開等的類JSONTreeNode,還有類似DAO能夠封裝數(shù)據(jù)訪問和格式轉(zhuǎn)換細節(jié)的JSONTree。
??????????? ds.setDriverClassName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
??????????? ds.setUrl("jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=java28");
??????? String SQLString = "SELECT * FROM Categories WHERE parentId="+this.parentId+" ORDER BY categoryId";???
??????????? rs = st.executeQuery("SELECT parentId FROM Categories WHERE parentId>0 Group By parentId Order By parentId");???????????
??????????? JSONArray JsonArray = JSONArray.fromObject(treeNodeArray); //得到JSON數(shù)組????
??????????? System.out.println("getJSONString() of JSONTree.java throws : "+e.toString());
5.????? 最后是我們的數(shù)據(jù)庫腳本script.sql,大家用SQLServer 2000數(shù)據(jù)庫創(chuàng)建一個java28的database,然后執(zhí)行下列腳本即可。
SELECT parentId FROM Categories WHERE parentId>0 Group By parentId Order By parentId