这几天状态不佳,杂事太多有些烦心,这一次就少讲点吧。
本次将主要讲Extjs树形菜单的实现。在很多系统里边,树形菜单被广泛应用,主要因为其有清晰的层次结构。记得最早见到的树形菜单是通过<ul><li>实现的,非常简单。之后用C#的现成控件拖拽了一个,当时还沾沾自喜。再后来在学习师兄的一个通用后台代码时,看到了梅花雪,当时感觉太强大了,每个节点都可以存在数据库,并且能够进行自定义。之后接触到一些UI库之后,更是见到了更强大的树形菜单。
或许实现一个简单的树形菜单对于不少人并非难事,但是想要实现一个功能比较完善的确要花不少功夫。好在目前有很多现成的供我们使用,而Extjs也提供了功能丰富的树形菜单。本次我们就来讲最简单的一种:Ext.tree.Panel。
正如之前讲到的,我们在实现一个Extjs组件时,应该按照如下的步骤:
以下就是各部分的代码实现:
//model
Ext.define('et.model.Menu', { extend: 'Ext.data.Model', fields: ['mid', 'text','cls','columns','url','expanded','optype'] });
//或许你看到这些字段和下边json数据并不对应,这个并不重要,这里边有几个是我自己拓展的,只是没用到,只要几个关键的字段设置即可,下边会有介绍。
//store
Ext.define('et.store.Menus',{ extend: 'Ext.data.TreeStore', requires: 'et.model.Menu', model: 'et.model.Menu', autoLoad: true, //设置自动加载,在打开页面的时候数据就自动加载 proxy: { type: 'ajax', url: 'data/manager.json', reader: { type: 'json', successProperty: 'success' } } });
//view
Ext.define('et.view.Menu',{ extend: 'Ext.tree.Panel', alias: 'widget.sxptmenu', requires:['et.store.Menus'], initComponent : function(){ Ext.apply(this,{ id: 'menu-panel', title: '系统菜单', iconCls:'icon-menu', margins : '0 0 -1 1', region:'west', border : false, enableDD : false, split: true, width : 212, minSize : 130, maxSize : 300, rootVisible: false, containerScroll : true, collapsible : true, autoScroll: false, store:Ext.create('et.store.Menus') }); this.callParent(arguments); } });
//controller
Ext.define('et.controller.Menu',{ extend: 'Ext.app.Controller', stores: ['Menus'], models: ['Menu'], views: ['Menu'], init: function () { //初始化部分,下面是部分是给菜单绑定单击事件,接下来会用,这里先注释 this.control({ 'sxptmenu': { itemmousedown: this.loadMenu } }); }, loadMenu:function(selModel, record){ //加载菜单项对应panel,首先判断是不是叶子结点,是的话判断当前该panel是否已经创建,已创建的话直接激活,未创建的话创建并加入content-panel if (record.get('leaf')) { if(record.get('optype')=='window'){ var win= Ext.getCmp(record.get('url')); if(!win){ win=Ext.widget(record.get('url')) } win.show(); } else{ var panel = Ext.getCmp(record.get('id')); if(!panel){ panel ={ id:record.get('url'), title: record.get('text'), xtype:record.get('url'), closable: true }; this.openTab(panel,record.get('url')); }else{ var main = Ext.getCmp("content-panel"); main.setActiveTab(panel); } } } }, openTab : function (panel,id){ var o = (typeof panel == "string" ? panel : id || panel.id); var main = Ext.getCmp("content-panel"); var tab = main.getComponent(o); if (tab) { main.setActiveTab(tab); } else if(typeof panel!="string"){ panel.id = o; var p = main.add(panel); main.setActiveTab(p); } } });
//app.js
Ext.Loader.setConfig({enabled: true}); Ext.application({ name:'et', autoCreateViewport: true, appFolder:'app', 'Menu' //加入菜单的controller ] });
我们只要在后台将菜单按照固定格式的json将数据传过来即可,下面我们看一下一个简单菜单的json数据:
[{"text":"题目管理", "mid":"1", "cls":"folder", "leaf":false, "expanded": true, "children":[{"text":"题目列表", "mid":"2", "cls":"file", "url":"subjectlist", "leaf":true, "children":[]} ] },{"text":"分组管理", "mid":"3", "cls":"folder", "leaf":false, "expanded": true, "children":[{"text":"选题分组确认", "mid":"4", "cls":"file", "url":"mstudentlist", "leaf":true, "children":[]}, {"text":"分组列表", "mid":"5", "cls":"file", "url":"mstudentlist", "leaf":true, "children":[]}, {"text":"报告审阅", "mid":"6", "cls":"file", "url":"mstudentlist", "leaf":true, "children":[]} ] }]
其中我们可以看到,text指定了结点名,cls指定了图标样式(如folder为文件夹图标,file为文件图标),leaf指定是否为叶子结点,children内嵌套子结点。其他几个均为自定义,如这里url来指定打开panel的别名。
基本的menu的用法就是这么简单,如果有更高的需求,如添加复选按钮、可拖动、动态编辑等,可参考extjs自带的实例代码。
最后讨论一下json的生成,对于灵活性比较高的系统(如权限管理),一般把每个菜单项存在数据库比较方便,可根据情况组合后拼接json返回前台。但是很多时候我们或许并不需要这么高的灵活度,例如每个角色菜单固定,那么这是我们可以考虑按照如上json格式将每个菜单写进一个json文件,在用户登陆时判断角色加载对应文件即可。这样做能够省不少事(但要注意防止用户直接打开json文件路径,来越权操作功能,所以后台最好在拦截器里再进行下判断)。
代码在见上一遍最后。