一. 需求
考虑下表,有这样一些特点:
1. 考评表可能会根据年份或者地区的变化而有所不同,需要进行订制;
2. 考评表分为“考评项目”和“考评内容”两个层次的结构,当然每条“考核内容”里会有类如“检查方式”等属性;
如何来订制?感觉适合采用ColumnTree的形式来实现。
二. 最后效果
三. 实现过程
1. Editable ColumnTree 原型
找到一个Demo,基于Ext的插件 Editable Column Tree (学习Ext 一定要常到英文官网转转,经常有些好东西:)。
Demo 由五个文件构成:
- editable-column-tree.js (核心文件,主要配置、操作都在这里)
- editable-column-tree.html (展现页面,只是指定一个div,告诉 columnTree 在这渲染)
- editable-column-tree.css (样式文件,没穿衣服是不好意思见人的)
- ColumnNodeUI.js (节点UI,节点的展现方式,没有它就不一定叫**树了)
- treeSerializer.js (序列化,可以把树或节点以Json或XML形式的String返回)
2. 动态数据
Demo 的数据是放在 editable-column-tree.js 文件中的几条 Json 数据,需要改成从数据库中获取。
- var tree = new EXT.tree.ColumnTree( {
-
-
-
- el : 'tree-ct',
-
-
- rootVisible : false,
-
- width : 800,
- autoHeight : true,
- autoScroll : true,
- expandable : false,
- enableDD : true,
- title : '编制模板',
-
-
- root : new EXT.tree.AsyncTreeNode( {
- "text" : "考核内容",
- "id" : "1",
- "allowDrag" : false
- })
- });
-
- tree.on('beforeload', function(node) {
-
-
- if (node.id != '1') {
- tree.loader.dataUrl = $('#tree-ct').attr("url") + '&id=' + node.id;
- }
-
-
- else {
- tree.loader.dataUrl = $('#tree-ct').attr("url") + '&id=0';
- }
- });
var tree = new EXT.tree.ColumnTree( {
// ......
// 指定展现树的div容器
el : 'tree-ct',
// 根节点不显示
rootVisible : false,
width : 800,
autoHeight : true,
autoScroll : true,
expandable : false,
enableDD : true,
title : '编制模板',
// 定义一个根节点,id为1
root : new EXT.tree.AsyncTreeNode( {
"text" : "考核内容",
"id" : "1",
"allowDrag" : false
})
});
tree.on('beforeload', function(node) {
// 如果点击一个节点,加载前就去异步获取该节点下的数据,看~参数id是你点击的那个
if (node.id != '1') {
tree.loader.dataUrl = $('#tree-ct').attr("url") + '&id=' + node.id;
}
// 默认加载根节点,可以指定一个id,只要action根据此id获取到第一层节点数据即可
else {
tree.loader.dataUrl = $('#tree-ct').attr("url") + '&id=0';
}
});
div指定树的展现容器,同时可以在这定义action的url,好从后台取数据(如何获取数据并以Json串返回从略)
- <table width="800">
- <tr >
- <td align="center">
- <div id="tree-ct" url="***.do}" </div>
- </td>
- </tr>
- </table>
<table width="800">
<tr >
<td align="center">
<div id="tree-ct" url="***.do}" </div>
</td>
</tr>
</table>
3. CRUD操作
在tbar菜单里定义按钮,listeners会监听click事件,执行action的动作。
这里定义一个弹出页面进行新增的操作。需要带参数可以再页面中定义一个hidden变量,如下带了一个参数id。
- text : '考核项目',
- tooltip : '新增考核项目',
- iconCls : 'folder-icon',
- listeners : {
- 'click' : function() {
- var href = "***Action_add.do?id=" + $('#id').val();
- window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
- }
- }
text : '考核项目',
tooltip : '新增考核项目',
iconCls : 'folder-icon',
listeners : {
'click' : function() {
var href = "***Action_add.do?id=" + $('#id').val();
window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
}
}
新增考核内容需选定其所属的考核项目。
- text : '考核内容',
- tooltip : '新增考核内容',
- iconCls : 'page-icon',
- listeners : {
- 'click' : function() {
- var selectedItem = tree.getSelectionModel().getSelectedNode();
- if (!selectedItem || selectedItem.attributes.leaf == true) {
- EXT.Msg.alert('提示框', '请选择考核项目.');
- return false;
- }
-
- var href = "***Action_addChild.do?id=" + $('#id').val() + "&parentId=" + selectedItem.id;
- window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
- }
- }
text : '考核内容',
tooltip : '新增考核内容',
iconCls : 'page-icon',
listeners : {
'click' : function() {
var selectedItem = tree.getSelectionModel().getSelectedNode();
if (!selectedItem || selectedItem.attributes.leaf == true) {
EXT.Msg.alert('提示框', '请选择考核项目.');
return false;
}
var href = "***Action_addChild.do?id=" + $('#id').val() + "&parentId=" + selectedItem.id;
window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
}
}
- text : '修改',
- tooltip : '修改',
- iconCls : 'modify-icon',
- listeners : {
- 'click' : function() {
- var selectedItem = tree.getSelectionModel().getSelectedNode();
- if (!selectedItem) {
- EXT.Msg.alert('提示框', '请选择修改项.');
- return false;
- }
- var href = "***Action_modify.do?id=" + selectedItem.id;
- window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
- }
- }
text : '修改',
tooltip : '修改',
iconCls : 'modify-icon',
listeners : {
'click' : function() {
var selectedItem = tree.getSelectionModel().getSelectedNode();
if (!selectedItem) {
EXT.Msg.alert('提示框', '请选择修改项.');
return false;
}
var href = "***Action_modify.do?id=" + selectedItem.id;
window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
}
}
- text : '删除',
- tooltip : '删除',
- iconCls : 'delete-icon',
- listeners : {
- 'click' : function() {
- var selectedItem = tree.getSelectionModel().getSelectedNode();
- if (!selectedItem) {
- EXT.Msg.alert('提示框', '请选择删除项.');
- return false;
- }
-
- if (selectedItem.attributes.leaf == true) {
- EXT.MessageBox.confirm('确认框', '您确定要删除<b><font color=#ff0000>' + selectedItem.attributes.itemDesc + '</font></b>信息吗?', showResult);
- } else {
- EXT.MessageBox.confirm('提示框', '<b><font color=#ff0000>该项目下的所有子项也将一同删除!</font></b>', showResult);
- }
-
- function showResult(btn) {
- if (btn == "yes") {
- var href = "***Action_delete.do" + "?assessItemId=" + selectedItem.id;
-
- $.post(href, null, function(msg) {
- tree.root.reload();
- tree.expandAll();
- });
- }
- };
-
- }
- }
text : '删除',
tooltip : '删除',
iconCls : 'delete-icon',
listeners : {
'click' : function() {
var selectedItem = tree.getSelectionModel().getSelectedNode();
if (!selectedItem) {
EXT.Msg.alert('提示框', '请选择删除项.');
return false;
}
if (selectedItem.attributes.leaf == true) {
EXT.MessageBox.confirm('确认框', '您确定要删除<b><font color=#ff0000>' + selectedItem.attributes.itemDesc + '</font></b>信息吗?', showResult);
} else {
EXT.MessageBox.confirm('提示框', '<b><font color=#ff0000>该项目下的所有子项也将一同删除!</font></b>', showResult);
}
function showResult(btn) {
if (btn == "yes") {
var href = "***Action_delete.do" + "?assessItemId=" + selectedItem.id;
$.post(href, null, function(msg) {
tree.root.reload();
tree.expandAll();
});
}
};
}
}
4. 刷新
新增、修改操作之后需要关闭弹出窗口、并对Column Tree 进行刷新, 在弹出窗口页面引入的js中加入这两行即可。
- window.opener.location.reload();
- window.close();
window.opener.location.reload(); //刷新父窗口
window.close(); //关闭当前窗口
5. 屏蔽双击默认操作
在树上双击节点查看详细信息
- tree.on('dblclick', function(n) {
- var selectedItem = tree.getSelectionModel().getSelectedNode();
- var isLeaf = selectedItem.attributes.leaf;
- var href = "***Action_view.do?id=" + selectedItem.id + "&isLeaf=" + isLeaf;
- window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
- });
tree.on('dblclick', function(n) {
var selectedItem = tree.getSelectionModel().getSelectedNode();
var isLeaf = selectedItem.attributes.leaf;
var href = "***Action_view.do?id=" + selectedItem.id + "&isLeaf=" + isLeaf;
window.open(href, "", "height=550, width=800, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
});
如果点击非叶子节点,菜单还同时展开或者收起的动作,如何屏蔽掉呢?在 ColumnNodeUI 里面加上下面的代码。
-
- EXT.override(BoxUI.tree.TreeNodeUI, {
- onDblClick : function(e) {
- e.preventDefault();
- if (this.disabled) {
- return;
- }
-
- if (this.checkbox) {
- this.toggleCheck();
- }
-
- if (!this.animating && this.node.hasChildNodes()) {
- var isExpand = this.node.ownerTree.doubleClickExpand;
- if (isExpand) {
- this.node.toggle();
- };
- }
-
- this.fireEvent("dblclick", this.node, e);
- }
- });