花了3-4个小时整了一个有关ExtJS5的TreePanel组件的节点增删改查操作以及拖拽排序的demo,demo里仅仅是前端操作,不涉及后台代码。因为TreePanel是ExtJS里使用难度稍微大点的一个组件之一,特此写这篇文章记录一下,也希望能给那些对ExtJS同样感兴趣的童鞋们一些帮助。
/*****************ExtJS TreePanel面板*********************/ Ext.define("OA.view.TreePanel",{ extend:'Ext.tree.Panel', alias : 'widget.mytreepanel', alternateClassName: ["OA.TreePanel"], initComponent : function(){ Ext.apply(this,{ title:"导航菜单", animate:true, animCollapse: true, autoScroll : true, scroll: "vertical", rootVisible : false, lines: false, useArrows: true, containerScroll: true, collapsed: false, collapsible: false, layout: "fit", border: false, width: 200, dockedItems: { dock : 'top', xtype : 'toolbar', items : [ { xtype : 'button', text : '刷新' }, { xtype : 'button', text : '展开' }, { xtype : 'button', text : '收缩' } ] }, viewConfig : { loadingText : "正在加载...", plugins: { ptype: 'treeviewdragdrop' }, listeners: { drop: function(node, data, dropRec, dropPosition) { //store.sync(); } } }, store: Ext.create("OA.store.TreeStore") }); this.callParent(arguments); } });
plugins: {
ptype: 'treeviewdragdrop'
},
这里是给TreePanel添加拖拽插件的,从而支持通过鼠标拖拽节点实现节点排序,添加此插件仅仅是实现了前端界面上的排序效果,要达到真正的排序还是需要借助后台代码来实现的。比如拖拽后在drop事件里,去请求后台处理,具体逻辑就是把被拖拽节点从其原始父节点里删除,将其父节点设置为当前投放的节点。
/*****************ExtJS Tree数据源类*********************/ Ext.define("OA.store.TreeStore", { extend : "Ext.data.TreeStore", requires: ['OA.util.AppUtil'], model: 'OA.model.TreeModel', singleton: false, root: { id: 0, expanded: true, children: [ { id: 1, text: "系统管理", leaf: true }, { id: 2, text: "订单管理", leaf: true }, { id: 3, text: "流程管理", leaf: true } ] }, clearOnLoad : true, nodeParam: "id" });
TreePanel的数据源类里直接定义了数据,没有从后台加载数据,为了演示方便,不想弄的太麻烦。如果想弄成从后台动态加载数据,可以配置api,比如:
proxy: { type: 'ajax', api: { create: 'xxxxxxxxxxxxxxx/tree/add', read: 'xxxxxxxxxxxxxxxxx/tree/list', update: 'xxxxxxxxxxxxxxx/tree/edit', destroy: 'xxxxxxxxxxxxxx/tree/delete' }, writer: { type: 'json', allowSingle: false, encode: true, root: 'records' } },
create即添加请求,read即查询请求,update即修改请求,destory即删除请求。
分别对应各自的后台请求URL,你懂的。
/*****************ExtJS Tree数据模型类*********************/ Ext.define("OA.model.TreeModel", { extend : "Ext.data.Model", fields : [ {name : "id",type : "int"}, {name : "text",type : "string"}, {name : "leaf",type : "boolean"}, {name : "expanded",type : "boolean"} ] });
TreePanel需要的数据模型,text即节点名称,leaf即是否为叶子节点,expanded即默认是否展开,如果你希望在节点前面加一个小图标,那么你还可以在数据模型再加一个属性iconCls,给它赋值一个css样式即可。
/********************TreePanel右键菜单***************************/ Ext.define("OA.view.TreeContextMenu",{ extend: 'Ext.menu.Menu', alias : 'widget.treecontextmenu', alternateClassName: ["OA.TreeContextMenu"], initComponent: function(){ Ext.apply(this,{ floating :true, plain : true, floating:true, items :[ { itemId: 'add', text : '添加' }, { itemId: 'edit', text : '编辑' }, { itemId: 'delete', text : '删除' } ] }); this.callParent(arguments); } });
这是一个右键菜单,没什么好说的
/********************TreePanel节点数据编辑窗体*********************/ Ext.define("OA.view.TreeEditWindow",{ extend: 'Ext.window.Window', alias : 'widget.treeeditwindow', alternateClassName: ["OA.TreeEditWindow"], initComponent: function(){ Ext.apply(this,{ title: "编辑节点", width: 230, height: 100, layout: "fit", closeAction: "hide", items :[ { xtype: "form", defaultType: 'textfield', defaults: { anchor: '100%' }, fieldDefaults: { labelWidth: 60, labelAlign: "left", flex: 1, margin: 5 }, items: [ { xtype: "textfield", name: "nodeName", fieldLabel: "节点名称", allowBlank: false } ] } ], buttons: [ { xtype: "button", itemId: "ok",text: "确定"}, { xtype: "button", itemId: "cancle", text: "取消"} ] }); this.callParent(arguments); } });
这是一个普普通通的Window,点击菜单里的编辑,即创建窗体对象并show出来。
其实主要的事件处理逻辑都写在Controller里,代码如下:
/*******************全局应用程序控制器类******************/ //首先载入工具类 Ext.require( [ 'OA.util.CommonDoActionUtil' ] ); Ext.define('OA.controller.AppController', { extend: 'Ext.app.Controller', requires: ['OA.util.CommonDoActionUtil'], //数据模型注册 models: [ 'TreeModel' ], //视图注册 views: ["TreePanel","TreeContextMenu","TreeEditWindow"], //数据源注册 stores: [ 'TreeStore' ], init: function() { var id_ = 111; var currentRecord = null; this.control({ "mytreepanel": { itemcontextmenu: function(_this, record, item, index, evt, eOpts) { if(!this.ctxMenu) { this.ctxMenu = Ext.create("OA.view.TreeContextMenu"); } this.ctxMenu.showAt(evt.getXY()); //缓存当前鼠标右键选中的节点数据Name currentRecord = record; evt.preventDefault(); //evt.stopEvent(); _this.getSelectionModel().select(index); }, containerclick: function(_this,e,eOpts) { if(this.ctxMenu) { this.ctxMenu.hide(); } } }, "treecontextmenu > menuitem[itemId=add]": { click: function(item, event, eOpts) { if(currentRecord) { currentRecord.appendChild( { id: id_++, "text": "测试节点_" + id_, "leaf": true } ); //展开当前节点 currentRecord.expand(); //发送ajax请求到后台插入添加的节点数据,你懂的 } } }, "treecontextmenu > menuitem[itemId=edit]": { click: function(item, event, eOpts) { if(currentRecord) { if(!this.editWin) { this.editWin = Ext.create("OA.view.TreeEditWindow"); } this.editWin.show(); } } }, "treecontextmenu > menuitem[itemId=delete]": { click: function(item, event, eOpts) { if(currentRecord) { currentRecord.parentNode.removeChild(currentRecord); currentRecord.commit(); } } }, "treeeditwindow button[itemId=ok]": { click: function(_this, e, eOpts ) { if(currentRecord) { //获取表单数据 var formData = _this.up("treeeditwindow").down("form").getForm().getValues(); var nodeText = formData.nodeName; //修改节点数据 currentRecord.set("text",nodeText); currentRecord.commit(); //同理,发送Ajax请求到后台修改节点数据 } //关闭窗体 _this.up("treeeditwindow").hide(); } }, "treeeditwindow button[itemId=cancle]": { click: function(_this, e, eOpts ) { _this.up("treeeditwindow").hide(); } } }); this.commonAction = Ext.create('OA.util.CommonDoActionUtil'); } });
最后效果图如下:
各位看官如果还有什么疑问或者需要demo源码的,请加我QQ:7-3-6-0-3-1-3-0-5,或者加裙