一。一些废话
近日来,有几个项目用到了EXTJS作为Web前端。也看到了一些童鞋苦苦在网上寻觅树形列表控件,碰巧项目中用到了一种,现在给大家分享一下。
二。前端
前端代码,用的是JS。主要就是指定数据读取地址,然后定义列表表头。这里实例化TreeGrid对象
// function loadGrid(json) 方法中的内容,由第三个代码片段,取得表头方法调用 // 等号左边,定义了一个对象,相当于var a=,这里用的是命名空间statisticBaseSummary,定义一个属于这个命名空间的对象 // 等号右边实例一个TreeGrid,在ExtJs4以前的版本都是在Ext.ux.tree命名空间下,ExtJs4以及日后版本全部整理到了Ext.Forml的空间下 // 并且名称发生了变化 statisticBaseSummary.gridPanel = new Ext.ux.tree.TreeGrid({ autoScroll : 'auto',// 滚动条 baseParams: statisticBaseSummary.parameter,// 请求树形数据参数 columnLines: true,// 斑马线 columns: Ext.util.JSON.decode(createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr)),// 动态列,如果不用动态列可以直接定义,关于动态列的做法另开博文介绍 tbar : statisticBaseSummary_tbar, region : 'center', loadMask : true,// 如何加载 useArrows: true, enableSort : false, animate: true, enableDD: true, containerScroll: true, requestMethod : 'POST',// 请求方式 // 这里发现没有用到store对象,直接定义了一个服务器请求地址(statisticBaseSummary.reportUrl,这个是在文件头部定义的静态常量,value是url地址,比如:path + '/getTreeGridInfo'等等) // 我们可以看看TreeGrid的源码发现,虽然没有定义store对象但是在源码中确实实例了一个store对象,可以想象,我们应该可以用store来处理数据请求 // 不过设立了dataUrl这种方式虽然有数据处理局限性,但是目前我还是建议用3.x版本的童鞋都采用这种方式 dataUrl: statisticBaseSummary.reportUrl, listeners: { // 数据返回到页面之后触发这个事件 load : function(node) { // 监听你想的处理,比如load的等待画面隐藏之类的 } } });
下面这个方法,是做一个列头出来。
/** 基本信息-数据列 */ function createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr) { var statisticColumns = "["; for (var i = 0; i < fieldsArr.length; i++) { statisticColumns += "{header:\"<div style='text-align:center'>" + headerArr[i] + "</div>\", width:" + fieldsWidthArr[i] + ", dataIndex:'" + fieldsArr[i] + "', hidden:" + headerHiddenArr[i] + ", align:'" + fieldsAlingArr[i] + "'}"; if (i < fieldsArr.length - 1) { statisticColumns += ","; } } statisticColumns += "]"; return statisticColumns; }下面这个方法是上面的方法所需的参数,是异步取得,但是一定要在new gridtree之前取得。
/** 取得表头 */ function getData() { Share.AjaxRequest({ method: 'POST', url: homePage.homePageHeaderUrl, // 设定Ajax请求数据源路径 showMsg : false, params : homePage.parameter, callback : function(json) { //var obj = Ext.util.JSON.decode(request.responseText); var obj = json.o; homePage.headerMsg = obj; loadGrid(json); // 这个loadGrid方法就是上面的构造树形列表的方法 } }); }
三。后端
相对前端来说,后端动态取得一个可以让TreeGrid能够识别的数据则是关键的地方。后端我用的是JAVA,框架是SpringMVC。数据总体来说是一个List(里面放着Map)。
(一) 我们首先需要分析,官方给的例子的JSON数据是什么样子的。
[{ // 表头第一列,该列也是树形列(aList:把这一列的数据作为aList),可以看图一 task:'Project: Shopping', // 表头第二列,从这列开始就是与第一列的每一个节点所对应的数据字段了(把从第二以及以后列的数据作为bList) duration:13.25, user:'Tommy Maintz',// 表头第三列 iconCls:'task-folder',// 节点图标样式,与数据无关 expanded: true,//是否可以展开,与数据无关 // 接下来就是上面那个节点task:'Project: Shopping'的子节点以及子节点对应的数据 children:[{ // 表头第一列,该列也是树形列(aList:把这一列的数据作为aList),可以看图一 task:'Housewares', // 表头第二列,从这列开始就是与第一列的每一个节点所对应的数据字段了(把从第二以及以后列的数据作为bList) duration:1.25, user:'Tommy Maintz',// 表头第三列 iconCls:'task-folder',// 节点图标样式,与数据无关 // 接下来就是上面那个节点task:'Project: Shopping'的子节点以及子节点对应的数据 children:[{ task:'Kitchen supplies', duration:0.25, user:'Tommy Maintz', leaf:true, iconCls:'task' } // 下面的代码我就略去了,上面的编码已经说明了Treegrid的数据格式 // 首先task这个属性是固定的 ....... ]} }]
(二)看过数据格式了,那么我们分析一下。
从这段代码可以看出,数据属性格式,一共分为三部分,一个是节点,一个是节点对应的数据,一个是节点对应的子节点以及子节点的数据。请往下看
第一部分(节点):
task:树形的节点,这个请固定不要更改
iconCls:树形节点的icon
expanded:树形节点是否可以展开
第二部分(节点对应的数据):
duration、user:这是数据表示列,样例中是有两个,但是我们可以定义很多列,当然你需要表示的列为准
第三部分(子节点以及节点对应数据)
children:隶属于该节点的下属节点以及节点数据。
针对于第三部分来说,我们可以发现,children中的结构实际上就是包含了上述这三部分属性内容。如果这块比较难以理解的话,我们可以倒过来想。有一个这样的叶子节点A,这种节点是一个没有子节点的节点,那么A的children属性是没有内容的是null,B节点是A节点的父节点,那么B节点的children属性内容就是A。这样大家明了了吧,如果再进一步,假设C节点是B节点的父呢?C节点的children属性内容就是B,那么C-B-A就构成了三级树形结构,其中这三个节点的属性字段都是一致的只是内容不同。这里大家会问,如果B节点的子节点是A和D呢,那么B的children属性值就是A+D,储存形式是:chidren.add(A),chidren.add(D)。这样一来B的子节点就是A和D了。
(三)如何实现。
首先,我们要定义一个List allList;
其次,我们要定义一个Model,这里我们定义为HashMap rootMap;
最后,我们要有两个中间交换变量,一个是childrenList,childrenMap;
那么我们现在从数据库检索出来两个List:
1.aList:代表树形节点数据,里面的Model一定会有这样几种字段:
parentId(该节点的父节点id)
id(该节点id,一般为主键)
name(该节点名称)
type(是否为叶子节点)
level(节点等级)
weight(权重,一般用于排序来用)
这些字段,我们只用前4个即可。
2.bList:代表对应树形节点的数据,这里面的Model一般是我们自定义的,且一定有一个或者多个字段与树形节点对应。
接下来我们谈谈如何打造出样例中的数据。
其实就是将aList与bList结构逻辑化为样例中的数据格式,就是讲这两个List置换成rootMap放到allList中。
// 我们需要两个方法,一个是调用,一个是执行转换 public ArrayList result() { // 。。。。。前面的代码省略 // 先定义一个返回List,就是最终我们需要的Treegrid默认的数据格式 ArrayList allList = new ArrayList<HashMap<String, Object>>(); // 定义一个根Map,这个Map直接被allList.add(rootMap)这个List中 HashMap rootMap = new HashMap(); // 定义子List ArrayList childrenList= new ArrayList<HashMap<String, Object>>(); // 定义子Map,这个Map直接被allList.add(rootMap)这个List中 HashMap childrenMap = new HashMap(); // 下面是aList, bList的定义,大家可以更改 List<HashMap> bList = dao.getBList(); HashMap<String, Object> bResult = new HashMap<String, Object>(); String temp = ""; for (Iterator iterator = bList.iterator(); iterator.hasNext();) { HashMap rs = (HashMap) iterator.next(); ModelB modelB= new ModelB(); temp = rs.get("ID") == null ? "" : rs.get("ID").toString();// 对应的树形节点ID modelB.setFiled1(Integer.parseInt(rs.get("filed1") == null ? "0" : rs.get("filed1").toString())); modelB.setFiled2(Integer.parseInt(rs.get("filed2") == null ? "0" : rs.get("filed2").toString())); modelB.setFiled3(Integer.parseInt(rs.get("filed3") == null ? "0" : rs.get("filed3").toString())); bResult.put(temp, modelB); } ArrayList aList = dao.getAList(); Iterator iterator = aList.iterator(); if (iterator.hasNext()) { HashMap tempMap = (tempMap)iterator.next(); String id = tempMap.get("ID"); String parentId = tempMap.get("parentId"); String name = tempMap.get("name");//第一条树形结构数据,一定要是根节点。比如总计、合计等。也就是说在aList取得的语句中要排序 rootMap.put("task", name); rootMap.put("expanded", "true"); run(iterator, rootMap, null, bResult, childrenList) } // 调用执行转换方法返回追最终的List allList.add(rootMap); // 返回查询的List return allList; } /** * Treegrid数据转换 * * @param iterator:节点记录 * @param parent:父节点对象 * @param last:子节点对象 * @param bResult:节点对应的数据 * @param childrenList:子节点集合 * @return */ public void run(Iterator iterator, HashMap parent, HashMap last, HashMap bResult, ArrayList childrenList) { // 如果last(这个对象是子节点对象,包括子节点和对应的数据,第一次进到这个方法的时候为null)非空,则将last放到parent中 if (last!= null) parent.put("children", last); if (childrenList== null) childrenList= new ArrayList<HashMap<String, Object>>(); while (iterator.hasNext()) { // 得到节点 HashMap rs = (HashMap) iterator.next(); // 声明一个ic,这个对象就是子节点对象,包括节点和节点对应的数据 HashMap ic = new HashMap(); String id = rs.get("ID"); String parentId = rs.get("parentId"); String name = rs.get("name"); ic.put("id", id); ic.put("parentId", parentId); ic.put("task", name); ic.put("expanded", "true"); // 利用节点ID从bResult中寻找是否有对应该节点的数据,如果有的话则将节点对应的数据put到ic中 if (bResult.containsKey(ic.get("id"))) ic.put("children", bResult.get(ic.get("id"))); else ic.put("children", ""); // 下面的代码就很重要了,是一个递归调用自身的逻辑 // 首先判断节点对应的数据是否存在,如果为null则代表该节点为根节点 if (last == null) { // 将根节点对应的数据put到父节点对象中 childrenList.add(ic); parent.put("children", childrenList); // 我们得到的ic子节点对象就变为下一次循环的父节点对象了 last = ic; // 根节点记录处理结束,进行下一节点处理,就是走上面的那个while循环的处理。 } else { // 如果当前子节点对象ic所对应的父节点与前一个子节点对象last所对应的节点记录一致 // 代表当前子节点为前一节点子节点 while (ic != null && ic.get("parentId").equals(last.get("id"))) ic = run(iterator, last, ic, bResult, null); // 如果当前子节点对象ic所对应的父节点与父节点对象parent所对应的节点记录一致 // 代表当前子节点与前一节点同为parent的子节点 if (ic != null && ic.get("parentId").equals(parent.get("id"))) { childrenList.add(ic); parent.put("children", childrenList); last = ic; } else return ic; } }
以上代码则完成了aList,bList的转换,返回结果为Treegrid能够解析的数据,接下来就是返回给页面端的Controller.java
介绍这个文件主要就是告诉大家,利用SpringMVC框架直接返回List对象给TreeGrid控件。
/** Report列表数据 */ @RequestMapping(value = "statisticSummarizing", method = RequestMethod.POST) @ResponseBody public ListstatisticSummarizing(ExtPager pager, HttpServletRequest request, @RequestParam(required = false, defaultValue = "") HashMap<String, String> queryParam) { //清除空字符串 Criteria criteria = new Criteria(request, queryParam, dataDialect); if (pager.getLimit() != null && pager.getStart() != null) { criteria.setSplit(pager); } //这里返回了List容器对象,这个对象直接会被TreeGrid控件解析 return statisticManager.statisticSummarizing(criteria); }
四。总结
以上就是EXTJS实现的TREEGRID的具体步骤,如有不明请留言。
五。整个代码
<%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ page import="java.util.*;"%> <%@ include file="/WEB-INF/views/commons/taglibs.jsp"%> <div id="${param.id}_div"></div> <script type="text/javascript"> Ext.ns("Ext.Conac.homePage"); homePage = Ext.Conac.homePage; homePage = { homePageHeaderUrl : ctx + "/homepage/homePageHeader", homePageDataUrl : ctx + "/homepage/homePageData", zhuanSongPage : ctx + "/instauration/zhuanSongPage", parameter : new Object(), headerMsg : '', reportType : 'summary', // 统计报告类型:简表:simple,汇总:summary reportFlag : '2', searchFlag : '0', textFlag : '0', treeParentId : '', treeGroupId : '1', treeGroupName : 'XXXXXX', headerMsg : '', type : '1' // 统计表查询功能 }; /** Search页面 */ homePage.searchPanel = new Ext.form.FormPanel({ region : 'north', margins:'1 1 1 1', border:false, height: 60, layout : 'absolute', defaults: { style: { margin: '0 0 0 10px' } }, items: [{ xtype: 'checkbox', name :'searchFlag', boxLabel: '<b>点击这里搜索</b>', listeners :{ check : function(chkBox,checked){ var f = homePage.searchPanel.findById('homePage-fieldset1'); var c = homePage.searchPanel.findById('homePage-container1'); if(checked){ f.setVisible(true); homePage.searchPanel.setHeight(170); c.setPosition(0,145); }else{ homePage.searchPanel.getForm().reset(); f.setVisible(false); homePage.searchPanel.setHeight(60); c.setPosition(0, 35); } if(homePage.myPanel){ homePage.myPanel.doLayout(false); } } } },{ id:'homePage-fieldset1', xtype: 'fieldset', height: 130, layout: 'absolute', hidden: true, border: false, x: 30, y: 25, items: [{ xtype: 'label', text: 't1:', x: 5, y: 5 },{ xtype: 'label', text: 't2:', x: 5, y: 30 },{ xtype: 'label', text: 't3:', x: 5, y: 50 },{ xtype: 'textfield', name:'certId', maxLength: 12, maxLength: 12, width: 225, x: 75, y: 0 },{ xtype: 'textfield', name:'unitName', maxLength: 50, width: 225, x: 75, y: 25 },{ id:'holdUnit', xtype: 'textfield', name:'holdUnit', editable: false, width: 225, x: 75, y: 50 },{ xtype: 'button', width: 50, iconCls:'tick', text: '选择', x: 300, y: 50, handler :function(){ Share.SelectJbdwCategory.show('holdUnit', 'jbdwId'); } },{ xtype: 'button', width: 50, iconCls:'query', text: '搜索', x: 160, y: 77, handler :function(){ var params = homePage.searchPanel.getForm().getValues(); homePage.parameter["flag"] = params.flag; // 0: ;1: homePage.parameter["certId"] = params.certId; homePage.parameter["unitName"] = params.unitName; homePage.parameter["holdUnit"] = params.holdUnit; callReport(); } },{ xtype: 'radio', name : 'flag', checked :true, inputValue: '0', boxLabel: '不搜索子节点', x: 370, y: 50 },{ xtype: 'radio', name : 'flag', inputValue: '1', boxLabel: '搜索子节点', x: 470, y: 50 },{ id : 'jbdwId', xtype: 'hidden', name:'jbdwId' } ] },{ xtype: 'container', id : 'homePage-container1', html: "<div style=\"color:blue;\"><a href=\"#\" onclick=\"Share.openMorePage('/homepage/quickAnnualCheck','快速年检')\"><b>${annualCheckYear}t4</b></a></div>", x: 0, y: 35 } ] }); /** Report报表 */ homePage.reportGrid = new Ext.FormPanel({ layout: 'fit', border:false, region : 'center' }); /** 最外层布局 */ homePage.myPanel = new Ext.Panel({ renderTo : '${param.id}' + '_div', layout: 'border', border:false, items: [homePage.searchPanel, homePage.reportGrid], height : index.tabPanel.getInnerHeight() - 1 }); /** 基本信息-数据列 */ function createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr) { var statisticColumns = "["; for (var i = 0; i < fieldsArr.length; i++) { statisticColumns += "{header:\"<div style='text-align:center'>" + headerArr[i] + "</div>\", width:" + fieldsWidthArr[i] + ", dataIndex:'" + fieldsArr[i] + "', hidden:" + headerHiddenArr[i] + ", align:'" + fieldsAlingArr[i] + "'}"; if (i < fieldsArr.length - 1) { statisticColumns += ","; } } statisticColumns += "]"; return statisticColumns; } /** report表单展示 */ function loadGrid(json) { waiting = Ext.Msg.wait('正在处理,请稍等...', '', ''); var fieldsArr = new Array(); var headerArr = new Array(); var headerHiddenArr = new Array(); var fieldsWidthArr = new Array(); var fieldsAlingArr = new Array(); var records = json.rows; // 将Json数据过滤得到每一行数据 // 将过滤过的Json的行数分进行如下操作 for(var i=0;i<records.length;i++){ // 取得每一个域名种类 var record = records[i]; fieldsArr[i] = record.name; headerArr[i] = record.text; headerHiddenArr[i] = record.hidden; fieldsWidthArr[i] = record.width; fieldsAlingArr[i] = record.alignment; } /** Report顶部工具条 */ homePage.tbar = new Ext.Toolbar({ height: 26, items: ["<div id='_tbar_item'>t5</div>"] }); /** Report中央布局如果已经渲染关闭数据加载窗口 */ if (homePage.reportGrid) { /** Report顶部工具条-信息 */ if (waiting != null) { waiting.hide(); } } /** Report中央布局 */ homePage.gridPanel = new Ext.ux.tree.TreeGrid({ enableSort : false, autoScroll : 'auto', baseParams: homePage.parameter, columns: Ext.util.JSON.decode(createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr)), tbar : homePage.tbar, region : 'center', loadMask : true, useArrows: true, autoScroll: true, animate: true, enableDD: true, containerScroll: true, border: false, requestMethod : 'POST', dataUrl: homePage.homePageDataUrl, listeners: { load : function(node) { if (node.isFirst) { if (waiting != null) { waiting.hide(); } } } } }); homePage.reportGrid.add(homePage.gridPanel); homePage.reportGrid.doLayout(false); } /** 取得表头 */ function getData() { Share.AjaxRequest({ method: 'POST', url: homePage.homePageHeaderUrl, // 设定Ajax请求数据源路径 showMsg : false, params : homePage.parameter, callback : function(json) { //var obj = Ext.util.JSON.decode(request.responseText); var obj = json.o; homePage.headerMsg = obj; loadGrid(json); } }); } /** 确定按钮操作 */ function callReport() { homePage.reportGrid.remove(homePage.gridPanel); homePage.parameter["start"] = '0'; // 初期表示数据 homePage.parameter["limit"] = homePage.pageSize; // 统计表查询功能 // 取得表头 getData(); } callReport(); //转送 function openZhuanSong(sydwName, applyId) { homePage.zhuansongWindow = new Ext.Window({ title : '转送', pageY : 100, width :370, height : 180, plain : true, border : false, modal : true, resizable : false, autoScroll : true, autoLoad : { url : homePage.zhuanSongPage, params : { sydwName : sydwName, applyId : applyId }, method: 'GET', scripts : true, nocache : true } }); homePage.zhuansongWindow.show(); } </script>
相关推荐
在本实例中,我们关注的是如何利用ExtJS4实现一个TreeGrid来创建一个report报表。 首先,理解TreeGrid的基础概念。TreeGrid是树形结构和网格的结合,它将树节点的层次结构与表格的列布局结合在一起,每个树节点都...
实现treegrid组件的(CRUD)读取、新增、修改、删除 //设置grid单元格处于编辑状态 selcell:function(arow,acol){ this.editingPlugin.startEditByPosition({row:arow,column:acol}); }, selcell2:function(node,...
最近在自学Extjs,做了一个小例子,后台使用SSH,前台是ExtJs,其中包含了很多内容,例如grid,TreeGrid,comboxTree,分页等内容,数据库采用的是mysql,文件中包含了数据库文件,导入mysql数据库即可,希望可以对初学者有一点...
在实际项目中,结合EXTJS4.0.7后台管理框架,开发者还需要掌握服务器端的技术,如Java、PHP、.NET等,以及数据库管理知识,如MySQL、Oracle、SQL Server等,以完成完整的后台系统开发。压缩包中的"extjs_licheng...
系统可作为OA、网站、电子政务、ERP、CRM、APP后台等基于B/S架构的应用软件系统的快速开发框架。 特色功能 1采用Spring MVC的静态加载缓存功能,在首页将Javascript文件、CSS文件和图片等静态资源文件加载进来放进...
通用后台管理系统(ExtJS4.2+Hibernate4.1.7+SpringMVC3.2.8).pdf
在ExtJS中,TreeGrid是通过`Ext.tree.Panel`类实现的,它继承自`Ext.grid.Panel`,增加了树形结构的功能。 在本地数据的例子中,我们通常会用到`Ext.data.TreeStore`来存储和管理数据。TreeStore是ExtJS中的一个...
【ASP.NET ExtJS 网站通用后台框架详解】 ASP.NET ExtJS 网站通用后台框架是一款基于Microsoft的ASP.NET技术与Sencha的ExtJS前端框架构建的高效、灵活的网站开发解决方案。该框架结合了.NET的强大后端处理能力和...
这个“Extjs4后台框架”可能指的是一个特定的、与ExtJS4集成的后端解决方案,用于支持前端应用的数据交互和管理。让我们深入探讨一下ExtJS4及其相关的后台开发概念。 首先,ExtJS4提供了丰富的组件库,包括表格、...
extjs实现一个后台管理框架,界面美观,可换主题颜色
使用java,extjs,配合后台oracle数据库的代码框架,这段代码是extjs的grid的一个详细案例使用java,extjs,配合后台oracle数据库的代码框架,这段代码是extjs的grid的一个详细案例使用java,extjs,配合后台oracle...
可二次开发Extjs4.0通用后台管理系统源码完整大型项目。数据库在项目里面的一个sql文件里面 1、采用Spring MVC的静态加载缓存功能,在首页将Javascript文件、CSS文件和图片等静态资源文件加载进来放进内存,极大提高...
总的来说,这个基于ExtJS的通用后台管理系统提供了一个完整的前端界面和后端服务框架,适用于企业内部的日常运营管理。开发者可以在此基础上根据实际需求进行定制化开发,增加或调整功能,以满足特定业务场景。同时...
SpringMVC作为Spring框架的一部分,是Java企业级应用中常用的MVC(Model-View-Controller)架构模式实现。它简化了处理HTTP请求和响应的过程,使得业务逻辑与视图层分离,提高了代码的可维护性和可测试性。在财务...
在"ExtJS2.0管理后台框架"中,开发者利用ExtJS的功能,构建了一个完整的后端管理系统的基础架构。这个框架可能包括了用户界面组件、数据管理、交互逻辑等多个关键部分。 首先,让我们深入了解ExtJS的核心特性。...
在本框架中,ExtJS负责处理用户界面的展示和交互,通过Ajax技术与后台进行异步通信,实现数据的动态加载和实时更新。 ASP.NET是微软的Web开发平台,它包含了开发和部署基于Web的应用程序所需的所有工具和服务。ASP...
在给定的“13款extjs后台框架”中,我们可以看到各种基于ExtJS开发的后台管理模板,这些模板通常包含了一系列预设的页面布局、功能组件和样式设计,旨在帮助开发者快速搭建后台管理系统。 1. **免费后台管理模板**...
【标题】:“(Java+JSP)可二次开发Extjs4.0通用后台管理系统源码完整大型项目.zip”指的是一个基于Java和JSP技术,并利用Extjs4.0框架开发的可扩展的后台管理系统源码。这个项目是针对中大型企业的需求设计的,提供...
ASP.NET + ExtJS + 网站通用后台框架是一个基于微软的ASP.NET技术和Sencha的ExtJS前端框架构建的高效、可复用的网站开发解决方案。这个框架集合了后端的C#编程语言、ASP.NET Web应用程序框架以及SQL数据库管理,为...
ASP.NET + ExtJS 网站通用后台框架是一款利用ASP.NET技术和ExtJS 2.2版本构建的高效、灵活的管理界面模板。该框架旨在提供一个标准的后台管理平台,适用于各种类型的Web应用程序,帮助开发者快速搭建功能丰富的管理...