首先看数据库的构造:
第一个是总的类别表(例表1[emergencytype]),此表中存放的是总的类别表,理论是不可编辑的,表中详细信息见表1-1.
第二个是总的分类别表下的小类别(例表2[emergencyworkflow]),所做的主要操作,是对表2的增删改查.表中详细信息见表2-2.
首先
表1:
表1-1:
表2:
表2-2:
首先是根据表的结构的结构生成树:
var treeloader=new Ext.tree.TreeLoader({dataUrl:'tree.aspx?action=getAll'});
var tree=new Ext.tree.TreePanel({
autoWidth:true,
autoHeight:true,
animate:false,//开启动画效果
enableDD:false,//不允许子节点拖动
rootVisible:false,
loader:treeloader
});
tree.on('dblclick',function(node,e){nodeClickEvent(node);});
tree.on('contextmenu', contextmenu, tree);
//定义树的跟节点
var root=new Ext.tree.AsyncTreeNode({
draggable:false, //不能拖放
expanded :true
});
tree.setRootNode(root);
tree.expandAll(); //设置的是全部展开.
其中contextmenu设置的是树形节点的右击菜单,方法如下:
function contextmenu(node, e) {
// if(node.getDepth() > 1)
// {
var treeMenu = new Ext.menu.Menu( {
items : [
new Ext.menu.Item( {
text : '添加',
icon:'../../Image/Workflow/ICO/TTWeather.ico',
handler : addTreeNode.createCallback(node)
}),
new Ext.menu.Item( {
text : '添加子项',
icon:'../../Image/Workflow/ICO/LowPriority.ico',
handler : addChildTreeNode.createCallback(node)
}),
new Ext.menu.Item( {
text : '删除',
icon:'../../Image/Workflow/ICO/exit.ico',
handler : deleteTreeNode.createCallback(node)
}),
new Ext.menu.Item( {
text : '重命名',
icon:'../../Image/Workflow/ICO/favicon.ico',
handler : renameTreeNode.createCallback(node)
})
]
});
treeMenu.showAt(e.getXY());
// }
}
因为理论上第一节点是不可编辑的,但是为了把功能做全,就让第一菜单也具有增删改查的功能.所有把菜单的节点深度(node.getDepth()>1)限制注消掉了.
菜单上的四个不同的方法如下:
在方法中有几个变量都需要用到,所有需要设置几个全局变量:
var rightClickTreeNode = null; //右键点击树节点
//获取最上层节点ID,即父类别ID,在数据库中指EMERGENCYWORKFLOW表TYPE_ID字段,该字段与EMERGENCYTYPE表PK_ID字段关联,级联删除。
function getNodeTypeID(node)
{
if(node.getDepth() != 1)
{
return getNodeTypeID(node.parentNode);
}
return node.attributes.attributes.pk_id;
}
//添加节点菜单函数
var addTreeNode = function(node)
{
rightClickTreeNode = node;
Ext.MessageBox.prompt('节点名称', '请输入新节点名称:', addTreeNodeAjax);
};
//添加节点菜单处理函数
function addTreeNodeAjax(btn,text)
{
if(btn == 'ok')
{
if(text.Trim() == '')
{
Ext.Msg.alert('友情提示','节点名称不能为空!');
rightClickTreeNode = null;
return;
}
else
{
var typeId = getNodeTypeID(rightClickTreeNode);
text = encodeURI(text.Trim());
var depth=rightClickTreeNode.getDepth();
if(depth!='1')
{
treeloader.dataUrl = 'tree.aspx? action=addTreeNode&pid='+rightClickTreeNode.parentNode.attributes.attributes.pk_id+'&depth='+rightClickTreeNode.getDepth()
+'&typeId='+typeId+'&newNode='+text;
}
else
{
treeloader.dataUrl = tree.aspx?action=addTreeNode&depth='+rightClickTreeNode.getDepth()+'&newNode='+text;
}
root.reload();
tree.expandAll();
rightClickTreeNode = null;
}
}
};
//添加子节点菜单函数
var addChildTreeNode = function (node)
{
rightClickTreeNode = node;
Ext.MessageBox.prompt('友情提示', '请输入子节点名称:', addChildTreeNodeAjax);
};
//添加子节点菜单处理函数
function addChildTreeNodeAjax(btn,text)
{
if(btn == 'ok')
{
if(text.Trim() == '')
{
Ext.Msg.alert('友情提示','子节点名称不能为空!');
rightClickTreeNode = null;
return;
}
else
{
var depth=rightClickTreeNode.getDepth();
var typeId = getNodeTypeID(rightClickTreeNode);
text = encodeURI(text.Trim());
if(depth!='1')
{
treeloader.dataUrl = 'tree.aspx?action=addChildTreeNode&id='+rightClickTreeNode.attributes.attributes.pk_id+'&typeId='+typeId+'&newNode='+text;
}
else
{
treeloader.dataUrl = 'tree.aspx?action=addChildTreeNode&typeId='+typeId+'&newNode='+text;
}
root.reload();
tree.expandAll();
rightClickTreeNode = null;
}
}
};
//删除树节点函数
var deleteTreeNode = function(node)
{
rightClickTreeNode = node;
Ext.MessageBox.confirm('友情提示', '确定删除节点吗?(注意:此动作将同时删除该节点下的所有子节点)',deleteChildTreeNodeAjax);
};
//删除树节点处理函数
function deleteChildTreeNodeAjax(btn)
{
if(btn == 'yes')
{
var depth=rightClickTreeNode.getDepth();
treeloader.dataUrl = 'tree.aspx?action=deleteChildTreeNode&depth='+depth+'&id='+rightClickTreeNode.attributes.attributes.pk_id;
root.reload();
tree.expandAll();
rightClickTreeNode = null;
}
};
//重命名树节点函数
var renameTreeNode = function (node)
{
rightClickTreeNode = node;
Ext.MessageBox.prompt('友情提示', '请输入新节点名称:', renameTreeNodeAjax,this,false,node.text);
};
//重命名树节点处理函数
function renameTreeNodeAjax(btn,text)
{
if(btn == 'ok')
{
if(text.Trim() == '')
{
Ext.Msg.alert('友情提示','子节点名称不能为空!');
rightClickTreeNode = null;
return;
}
else if(text.Trim() == rightClickTreeNode.text)
{
Ext.Msg.alert('友情提示','请输入不同的名称!');
rightClickTreeNode = null;
return;
}
else
{
text = encodeURI(text.Trim());
var depth=rightClickTreeNode.getDepth();
treeloader.dataUrl = tree.aspx?action=renameTreeNode&depth='+depth+'&id='+rightClickTreeNode.attributes.attributes.pk_id+'&newName='+text;
root.reload();
tree.expandAll();
rightClickTreeNode = null;
}
}
}
附:
一个树形菜单一般不会单独做一个页面,一般都会放到一个别的容器里(如ViewPort);
先定义在ViewPort前面一个Panel:
var managePanel = new Ext.Panel({
title:'管理页面',
height: 800,
width : 700,
layout:'absolute',
renderTo : 'Div',
tbar: new Ext.Toolbar({
enableOverflow: true,
disabled : true,
items: [
{
text: '添加节点',
icon : '../../Image/Workflow/ICO/Events.ico',
handler: createNode.createCallback(null)
},'-',{
text: '添加线条',
icon : '../../Image/Workflow/ICO/Events.ico',
menu: [{
text: '左箭头',
handler: createLine.createCallback('左箭头')
},{
text: '右箭头',
handler: createLine.createCallback('右箭头')
}]
},'-',{
text: '删除选中项',
icon : '../../Image/Workflow/ICO/Events.ico',
handler: deleteItem.createCallback()
},'-',{
text: '面板大小',
icon : '../../Image/Workflow/ICO/Events.ico',
menu: [{
text: '设置高度',
handler: setHeight.createCallback()
},{
text: '设置宽度',
handler: setWidth.createCallback()
}]
}]
})
});
var viewport = new Ext.Viewport({
header : false,
layout:'border',
items:[{
region:'west',
id:'west-panel',
headerCfg :{align:'center',cls: 'x-panel-header'},
title:'应急预案流程',
split:true,
width: 200,
minSize: 175,
maxSize: 350,
collapsible: true,
autoScroll:true,
items: [{
header:false,
border:false,
items:[tree]
}]
},
{
region:'center',
id : 'center-panel',
//autoWidth:true,
autoScroll : true,
height:500,
width:500,
layout:'anchor',
items:[managePanel] // panel自己可以在前面初始定义一个.
}]
});
所有有方法都是向后台发送请求,然后后台根据不同的action来执行不同的方法.一般都把方法归总到一个类CS文件(App_Code/tree.cs)里:
(1) 生成树的方法如下:
//获得总节点的所有信息
public static string GetAllType()
{
string result = "";
try
{
string sql = "select * from emergencytype";
DataTable typeDT = dbHelpSql.Query(sql);
string pid = "";
TreeNode root = new TreeNode("根节点", "根节点");
TreeNode node = null;
foreach (DataRow row in typeDT.Rows)
{
pid = row["PK_ID"].ToString();
node = new TreeNode(row["TYPE"].ToString(), pid);
BuildWorkFlowChild(node);
root.ChildNodes.Add(node);
}
result = JsonHelper.GetJSTreeJSONData(root.ChildNodes);
}
catch (Exception ex)
{
result = "{" + JsonHelper.GetErrorJson(ex.Message) + "}";
}
return result;
}
//根据总节点获得此节点下所有的子节点信息
private static void BuildWorkFlowChild(TreeNode node)
{
try
{
string sql = string.Format("select * from emergencyworkflow where type_id = {0} order by pk_Id,parent_id", node.Value);
DataTable dt = dbHelpSql.Query(sql);
BuildNode(node, dt);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
//创建父节点下所有子节点
private static void BuildNode(TreeNode node, DataTable source)
{
TreeNode tNode = null;
DataView dv = null;
DataTable tDt = null;
foreach (DataRow row in source.Rows)
{
if (row["PARENT_ID"].ToString() == "")
{
tNode = new TreeNode(row["NODE_DESC"].ToString(), row["PK_ID"].ToString());
dv = source.DefaultView;
dv.RowFilter = string.Format("parent_id = '{0}'", tNode.Value);
tDt = dv.ToTable();
if (tDt.Rows.Count > 0)
{
BuildChildNode(tNode, tDt, source);
}
node.ChildNodes.Add(tNode);
}
}
}
//若子节点下还有下一级,则遍历循环
private static void BuildChildNode(TreeNode node, DataTable fDT, DataTable source)
{
TreeNode tNode = null;
DataView dv = null;
DataTable tDt = null;
foreach (DataRow row in fDT.Rows)
{
tNode = new TreeNode(row["NODE_DESC"].ToString(), row["PK_ID"].ToString());
dv = source.DefaultView;
dv.RowFilter = string.Format("parent_id = '{0}'", tNode.Value);
tDt = dv.ToTable();
if (tDt.Rows.Count > 0)
{
BuildChildNode(tNode, tDt, source);
}
node.ChildNodes.Add(tNode);
}
}
(2)增删改查的方法:
1.增加同级树的节点(根据深度来判断)
// 添加树节点
public static string AddTreeNode(string pid, string name, string depth, string typeId)
{
string result = "";
try
{
string sql;
if (depth == "1")
{
sql = string.Format("insert into emergencytype(pk_id, type)values(SEQ_EMERGENCYTYPE.NEXTVAL,'{0}')", name);
}
else if (depth == "2")
{
sql = string.Format("insert into emergencyworkflow values(SEQ_EMERGENCYWORKFLOW.Nextval,'{0}',null,{1},null)", name, typeId);
}
else
{
sql = string.Format("insert into emergencyworkflow values(SEQ_EMERGENCYWORKFLOW.Nextval,'{0}',{1},{2},null)", name, pid, typeId);
}
if (dbHelpSql.ExecuteSql(sql) == 1)
{
result = GetAllType();
}
}
catch (Exception ex)
{
result = "{" + JsonHelper.GetErrorJson(ex.Message) + "}";
}
return result;
}
2.增加树的子节点
// 添加子节点
public static string AddChildTreeNode(string id, string name, string typeId)
{
string result = "";
try
{
string sql;
if (id == null || string.IsNullOrEmpty(id))
{
sql = string.Format("insert into emergencyworkflow values(SEQ_EMERGENCYWORKFLOW.Nextval,'{0}',null,{1},null)", name, typeId);
}
else
{
sql = string.Format("insert into emergencyworkflow values(SEQ_EMERGENCYWORKFLOW.Nextval,'{0}',{1},{2},null)", name, id, typeId);
}
if (dbHelpSql.ExecuteSql(sql) == 1)
{
result = GetAllType();
}
}
catch (Exception ex)
{
result = "{" + JsonHelper.GetErrorJson(ex.Message) + "}";
}
return result;
}
3.删除树的节点(同时删除以此节点为父节点的节点)
// 删除指定ID节点及其子节点
public static string DeleteTreeNode(string id, string depth)
{
string result = "";
OracleConnection con = null;
OracleTransaction trans = null;
OracleCommand cmd = null;
string sql;
try
{
con = dbHelpSql.GetOracleConnection();
con.Open();
trans = con.BeginTransaction();
cmd = con.CreateCommand();
cmd.Transaction = trans;
if (depth == "1")
{
sql = string.Format("delete emergencytype where pk_id = {0}", id);
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
sql = string.Format("delete emergencyworkflow where type_id = {0} ", id);
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
trans.Commit();
}
else
{
sql = string.Format("delete emergencyworkflow where pk_id = {0} or parent_id = {0} ", id);
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
trans.Commit();
}
//if (dbHelpSql.ExecuteSql(sql) > 0)
//{
result = GetAllType();
// }
}
catch (Exception ex)
{
result = "{" + JsonHelper.GetErrorJson(ex.Message) + "}";
trans.Rollback();
}
finally
{
if (cmd != null)
{
cmd.Dispose();
}
if (trans != null)
{
trans.Dispose();
}
if (con != null)
{
con.Dispose();
}
}
return result;
}
4.重命子节点
// 重命名指定id树节点
public static string RenameTreeNode(string id, string newName, string depth)
{
string result = "";
try
{
string sql;
if (depth != "1")
{
sql = string.Format("update emergencyworkflow set node_desc = '{0}' where pk_id = {1} ", newName, id);
}
else
{
sql = string.Format("update emergencytype set type = '{0}' where pk_id = {1} ", newName, id);
}
if (dbHelpSql.ExecuteSql(sql) == 1)
{
result = GetAllType();
}
}
catch (Exception ex)
{
result = "{" + JsonHelper.GetErrorJson(ex.Message) + "}";
}
return result;
}
附录: (JSON处理类[App_Code/JsonHelper.cs]中的方法)
/// <summary>
/// 将TreeNode数组中的每一个节点及其全部子节点转换为Json格式字符串
/// </summary>
/// <param name="nodes"></param>
/// <returns></returns>
public static string GetJSTreeJSONData(TreeNodeCollection nodes)
{
StringBuilder treeString = new StringBuilder();
//构建JSON开头
treeString.Append("[");
//foreach (TreeNode node in nodes)
for (int i = 0; i < nodes.Count;i++ )
{
//如果有子节点,则进行子节点构建
if (nodes[i].ChildNodes.Count != 0)
{
treeString.Append("{ attributes: {pk_id: '" + nodes[i].Value + "'}, text:'" + nodes[i].Text + "',expanded: false");
RecurrenceBuildJSTreeJSONData(treeString, nodes[i]);
}
else
{
treeString.Append("{ attributes: {pk_id: '" + nodes[i].Value + "'}, text:'" + nodes[i].Text + "',leaf: true");
}
//构建节点结尾
treeString.Append("}");
if(i != nodes.Count -1)
{
treeString.Append(",");
}
}
treeString.Append("]");
return treeString.ToString();
}
/// <summary>
/// 将单个节点及其所有子节点转换为Json格式字符串
/// </summary>
/// <param name="nodes"></param>
/// <returns></returns>
public static string GetJSTreeJSONData(TreeNode nodes)
{
StringBuilder treeString = new StringBuilder();
//构建JSON开头
treeString.Append("[");
//构建节点
treeString.Append("{ attributes: {pk_id: '" + nodes.Value + "'}, text:'" + nodes.Text + "',expanded: false");
//如果有子节点,则进行子节点构建
if (nodes.ChildNodes.Count != 0)
{
RecurrenceBuildJSTreeJSONData(treeString, nodes);
}
//构建节点结尾
treeString.Append("}");
//构建JSON结尾
treeString.Append("]");
return treeString.ToString();
}
private static void RecurrenceBuildJSTreeJSONData(StringBuilder treeString, TreeNode nodes)
{
//构建子节点开头
treeString.Append(",children: [");
for (int i = 0; i < nodes.ChildNodes.Count; i++)
{
treeString.Append("{ attributes: {pk_id: '" + nodes.ChildNodes[i].Value + "'}, text:'" + nodes.ChildNodes[i].Text + "'");
if (nodes.ChildNodes[i].ChildNodes.Count != 0)
RecurrenceBuildJSTreeJSONData(treeString, nodes.ChildNodes[i]);
else
treeString.Append(",leaf:true");
//构建最后一条节点的结尾
if (i == nodes.ChildNodes.Count - 1)
treeString.Append("}");
//构建非最后一条节点的结尾
else
treeString.Append("},");
}
//构建子节点结尾
treeString.Append("]");
}
/// <summary>
/// 转换异常信息为同一的Json数据格式
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public static string GetErrorJson(string msg)
{
if(msg.IndexOf("/n") > 0)
{
msg = msg.Substring(0, msg.IndexOf("/n"));
}
return "err:{msg:'" + msg + "'}";
}
/// <summary>
/// 将DataTable数据转换为Json表格数据格式
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetTableJson(DataTable dt)
{
//{success;true,data:{clientName: "Fred. Olsen Lines", portOfLoading:"FXT",portOfDischarge: "OSL"}}
string data = "data:[";
if (dt != null && dt.Rows.Count > 0)
{
foreach(DataRow row in dt.Rows)
{
data += "{";
foreach(DataColumn column in dt.Columns)
{
data += column.ColumnName + ":'" + row[column].ToString() + "',";
}
data = data.Substring(0, data.Length - 1);
data += "},";
}
data = data.Substring(0, data.Length - 1);
}
data += "]";
return data;
}