ExtFrame里的树封装

嗯,先上图

然后是代码,这个么,这个框架只能给出JS代码,后台代码考虑到公司版权问题不敢乱放,只能描述下了

JS代码就一行:

var maintree = new AutomationTree({ name: 'navigator', rootVisible: false });

指定创建一个名称为navigator的AutomationTree(名字俗了些,谁有好名字给个建议?)

 

重点在AutomationTree的封装上了,其实也很简单,甚至就两个方法100行代码:

View Code
 1 function AutomationTree(config) {
 2     this.name = config.name;
 3     if (config.border == null) {
 4         config.border = false;
 5     }
 6     if (config.width == null) {
 7         config.width = '100%';
 8     }
 9     if (config.height == null) {
10         config.height = '100%';
11     }
12     var requestconfig = {
13         url: '/Tree/Get',
14         method: 'post',
15         params: { name: this.name },
16         async: false,
17         callback: function(options, success, response) {
18             var r = Ext.util.JSON.decode(response.responseText);
19             var rootconfig = { text: unescape(r.label), expanded: true };
20             if (r.iconCls != null) {
21                 rootconfig.iconCls = r.iconCls;
22             }
23             if (r.icon != null) {
24                 rootconfig.icon = r.icon;
25             }
26             if (r.handler != null) {
27                 rootconfig.listeners = { 'click': function() { eval(this.handler); } };
28             }
29             var root = new Ext.tree.TreeNode(rootconfig);
30             root.handler = unescape(r.handler);
31             root.dataRefresh = r.expandRefresh == true;
32             root.dataRead = false;
33             root.nodeID = "-1";
34             root.dataID = "";
35             config.root = root;
36         }
37     };
38     Ext.Ajax.request(requestconfig);
39 
40     AutomationTree.superclass.constructor.call(this, config);
41     this.addListener('beforeexpandnode', function(node) { this.loadsub(node); });
42 };
43 
44 Ext.extend(AutomationTree, Ext.tree.TreePanel, {
45     loadsub: function(node) {
46         if (node.dataRefresh == true || node.dataRead == false) {
47             if (node.dataRead && node.hasChildNodes()) {
48                 while (node.hasChildNodes()) {
49                     node.removeChild(node.lastChild);
50                 }
51             }
52             node.dataRead = true;
53             var requestconfig = {
54                 url: '/Tree/GetChild',
55                 method: 'post',
56                 params: { name: this.name, id: node.nodeID, dataID: node.dataID },
57                 async: false,
58                 callback: function(options, success, response) {
59                     var list = Ext.util.JSON.decode(response.responseText);
60                     for (var i = 0; i < list.length; i++) {
61                         var r = list[i];
62                         var nodeconfig = { text: unescape(r.label) };
63                         if (r.iconCls != null) {
64                             nodeconfig.iconCls = r.iconCls;
65                         }
66                         if (r.icon != null) {
67                             nodeconfig.icon = r.icon;
68                         }
69                         if (r.handler != null) {
70                             nodeconfig.listeners = { 'click': function() { eval(this.handler); } };
71                         }
72                         if (r.hasChild == true) {
73                             nodeconfig.expandable = true;
74                         }
75 
76                         var childnode = new Ext.tree.TreeNode(nodeconfig);
77                         childnode.handler = unescape(r.handler);
78                         childnode.nodeID = r.id;
79                         childnode.dataRead = false;
80                         if (r.dataID != null) {
81                             childnode.dataID = r.dataID;
82                         }
83                         else {
84                             childnode.dataID = "";
85                         }
86                         if (r.autoExpand == true) {
87                             childnode.expanded = true;
88                         }
89                         childnode.dataRefresh = r.expandRefresh == true;
90                         node.appendChild(childnode);
91                     }
92                 }
93             };
94             Ext.Ajax.request(requestconfig);
95         }
96     }
97 });

这个树就两个方法:根据名称创建根,根据上一层节点创建下一层节点

对应的Ajax请求为TreeController里的两个方法,这个更简单,加一起70行代码

View Code
        public string Get(string name)
        {
            return AutomationTreeConfig.Instance().GetTree(name).ToJson();
        }

        public string GetChild(string name, string id, string dataID)
        {
            AutomationTree tree = AutomationTreeConfig.Instance().GetTree(name);
            AutomationTreeNode pnode = tree.GetNode(id);

            List<string> list = tree.GetSubNode(id);
            if (list == null || list.Count == 0)
            {
                return "[]";
            }

            string r = "";
            foreach (string nodeid in list)
            {
                AutomationTreeNode node = tree.GetNode(nodeid);
                INodePrivilegeFilter filter = null;
                if (!string.IsNullOrEmpty(node.PrivilgeFilter))
                {
                    filter = IOCContainer.GetIOCObject(node.PrivilgeFilter) as INodePrivilegeFilter;
                    if (filter.TypeCheck)
                    {
                        if (!filter.CheckBeforeLoad(name, pnode, node, dataID))
                        {
                            continue;
                        }
                    }
                }
                string loader = string.IsNullOrEmpty(node.Loader) ? "defaultnodeloader" : node.Loader;
                INodeLoader nodeloader = IOCContainer.GetIOCObject(loader) as INodeLoader;
                List<TreeData> datalist = nodeloader.Load(name, pnode, node, dataID);

                List<INodeRender> renderlist = new List<INodeRender>();
                if (!string.IsNullOrEmpty(node.Renders))
                {
                    string[] s = node.Renders.Split(',');
                    foreach (string render in s)
                    {
                        renderlist.Add(IOCContainer.GetIOCObject(render) as INodeRender);
                    }
                }
                if (datalist != null && datalist.Count > 0)
                {
                    if (!string.IsNullOrEmpty(node.PrivilgeFilter) && !filter.TypeCheck)
                    {
                        datalist = filter.Check(name, pnode, node, dataID, datalist);
                    }
                    foreach (TreeData data in datalist)
                    {
                        r += r == "" ? "[" : ",";

                        if (!string.IsNullOrEmpty(node.Renders))
                        {
                            foreach (INodeRender noderender in renderlist)
                            {
                                noderender.Render(data);
                            }
                        }
                        r += data.ToJson();
                    }
                }
            }

            r += "]";
            return r;
        }

代码就不解释了,后台的更多类也不解释了,只解释下架构:

系统通过配置,设定了树结构,前台提供一个名称,创建树的根

然后自动通过根创建第一层树叶,下面每一层根据需要再去创建

创建每一层时,通过IOC容器(这个是自己写的)指定的Loader的名字去获取一个Loader的IOC对象,这是个INodeLoader接口,通过这个接口加载数据

然后如果这个树叶上有指定的Render对象,则加载Render的IOC对象(如果是多个,按顺序运行),通过INodeDataRender对象去对树叶的信息进行处理(例如一些格式化、数据转换等等)

还有第三个IOC对象:INodePrivilegeFilter接口,通过这个接口去控制权限或业务逻辑过滤,使当前数据不返回给用户,过滤有两种,一是过滤整个定义的数据类型,二是在返回的数据集里进行数据过滤,通过TypeCheck去判定

这三个IOC对象,就可以完全实现整个树的控制,如果有业务逻辑很特殊,则只需要再写个特殊的IOC对象(一般通过继承重载来实现)就满足要求了

树的配置定义:

View Code
<?xml version="1.0" encoding="utf-8" ?>
<ApplicationConfig>
  <tree name="navigator" moniker="navigator" label="系统导航" iconCls="icon-system" >
    <node id="01" moniker="systemmodule" iconCls="icon-system" label="系统管理" autoExpand="true" expandRefresh="true">
      <node id="0101" moniker="usermanage" iconCls="icon-user" label="用户管理" handler="showPanel(userpanel);" privilegeFilter="nodeprivilegefilter" params="{datamoniker:'systemmodule',dataid:'1002',privid:0}"/>
      <node id="0102" moniker="rolemanage" iconCls="icon-role" label="角色管理" handler="showPanel(rolepanel);" privilegeFilter="nodeprivilegefilter" params="{datamoniker:'systemmodule',dataid:'1003',privid:0}"/>
      <node id="0103" moniker="modulemanage" iconCls="icon-module" label="系统模块管理" handler="showPanel(modulepanel);" privilegeFilter="nodeprivilegefilter" params="{datamoniker:'systemmodule',dataid:'1001',privid:0}"/>
      <node id="0104" moniker="flowmanage" iconCls="icon-flow" label="流程管理" handler="showPanel(flowpanel);" privilegeFilter="nodeprivilegefilter" params="{datamoniker:'systemmodule',dataid:'1004',privid:0}"/>
    </node>
    <node id="02" moniker="mail" label="我的邮箱" iconCls="icon-marketing" handler="openmail();" />
    <node id="03" moniker="requestlist" label="新增申请" iconCls="icon-request" >
      <node id="0301" moniker="carrequest" label="申请用车" iconCls="icon-car" handler="window_add_carrequest.show();" />
    </node>
    <node id="04" moniker="myapprove" label="等待我审批项目" iconCls="icon-wait" expandRefresh="true">
      <node id="0401" moniker="approveitem" label="等待审批" iconCls="icon-wait" loader="approveitemloader"/>
    </node>
  </tree>
</ApplicationConfig>

这颗树支持各种类型子节点,默认的导航树上当然大多都是写死的(这种情况使用默认的Loader加载),通过特殊的Loader就可以实现类似BOM的结构

如果是递归的结构是这么写的

View Code
<node id="05" loader="parentloader"......>
    <node id="0501" loader="childloader" ------->
         <node id="0501" />
    </node>
</node>

xml属性是这样的:

id:用于标识父子结构,如果有相同ID node出现(例如不同的类型下面都挂了相同的子结构),后面的无需再定义属性和子结构
moniker:数据类型标识(例如用户为User,客户为Customer),如果是非数据类型,可以任意定义
iconCls和icon:icon的css或图片路径,只能出现一个
label:显示的字符串,如果是实体类型可以在label里定义显示参数(写成{field}及其他格式等等)
handler:点击后执行的代码,可以为空(这也是我的框架里核心的设计之一:脚本完全进行Command模式封装)
autoExpand:当载入此节点后自动加载并展开下级节点数据
expandRefresh:每次点击展开此节点时自动重新加载数据
loader:加载数据的IOC对象,写死的节点为空(使用的是defaultnodeloader),加载数据的对象默认为“defaultdataloader”
privilgeFilter:节点是否显示的权限类IOC对象,为空不控制权限
renders:对数据进行特殊处理的IOC对象,如果有多个,用逗号隔开
params:一个Json字符串,给loader、privilegeFilter和renders对象使用的额外的参数,可以有多个参数,IOC对象各自挑选自己需要的

 

这颗树的封装基本上把复杂的ExtJS树编程给变成了配置,写起来简单多了,尤其是脚本,几乎没有代码了,后台代码也很清晰,易于调试,不象接手维护的过去的几个项目,脚本满天飞,Ajax代码一大堆,痛苦的要死(我实在是读不懂那些一层一层跳来跳去的脚本啊)

转载于:https://www.cnblogs.com/zuxOK/archive/2012/05/07/2487312.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值