现在的位置: 首页 > 综合 > 正文

谈谈web树形目录结构的原理和操作

2013年07月28日 ⁄ 综合 ⁄ 共 6118字 ⁄ 字号 评论关闭

拨号项目中,虽然自己也鼓捣个树形结构,但思路却不是很清晰,而且是利用树形结构信息是存在xml文件中的。看完drp,并不难,但感触最深的是王勇老师分析问题,把解决问题的思路清晰化。

 

对树形结构,从数据表、web前端div生成树原理,再到对树形信息的增删改查,由两边向中间逐层分析。

 

数据库设计

树的几种设计方式

  1. 不带冗余字段,idpid。效率低,查找相对繁琐。
  1. 带冗余字段,idpidisleafchildrencount。多冗余字段,但方便查找。
  1. 采用固定字符串,00010010001。不易理解,编程复杂。

    1. 00,所有分销商
    1. 01,华北区
    2. 001,北京市
    1. 0001,北京医药股份有限公司

 

 

这里采用第二种,带冗余字段idpid

自关联,id为主键,pidparent
id
)为外键


 

 

div生成树的原理


原理图:


 

html

<div>
<img alt="展开"style="cursor:hand;" onClick="display('1');"
id="img1"src="../images/plus.gif">
<imgid="im1" src="../images/closedfold.gif">
<a href="client_node_crud.html"target="clientDispAreaFrame">所有分销商</a>
<divstyle="display:none;" id="div1">
<div>
<imgsrc="../images/white.gif">
<img alt="展开"style="cursor:hand;" onClick="display('2');"
id="img2"src="../images/plus.gif">
<imgid="im2" src="../images/closedfold.gif">
<a href="client_node_crud.html"target="clientDispAreaFrame">华北区</a>
<divstyle="display:none;" id="div2">
<div>
<imgsrc="../images/white.gif">
<imgsrc="../images/white.gif">
<img alt="展开"style="cursor:hand;" onClick="display('3');"
id="img3"src="../images/plus.gif">
<imgid="im3" src="../images/closedfold.gif">
<a href="client_node_crud.html"target="clientDispAreaFrame">北京市</a>
<divstyle="display:none;" id="div3">
<div>
<imgsrc="../images/white.gif">
<imgsrc="../images/white.gif">
<imgsrc="../images/white.gif">
<imgsrc="../images/minus.gif">
<imgsrc="../images/openfold.gif">
<a href="client_crud.html"target="clientDispAreaFrame">北京医药股份有限公司</a>
</div>
</div>
</div>
</div>
</div>
<div>
<imgsrc="../images/white.gif">
<imgsrc="../images/minus.gif">
<imgsrc="../images/openfold.gif">
<a href="client_node_crud.html"target="clientDispAreaFrame">东北区</a>
</div>
<div>
<imgsrc="../images/white.gif">
<imgsrc="../images/minus.gif">
<imgsrc="../images/openfold.gif">
<a href="client_node_crud.html"target="clientDispAreaFrame">华南区</a>
</div>
</div>
</div>

 

js控制

functiondisplay(id) {
 eval("var div=div"+id);
 eval("var img=img"+id);
 eval("var im=im"+id);
 div.style.display=div.style.display=="block"?"none":"block";
 img.src=div.style.display=="block"?"../images/minus.gif":"../images/plus.gif";
 im.src=div.style.display=="block"?"../images/openfold.gif":"../images/closedfold.gif";
 img.alt=div.style.display=="block"?"关闭":"展开";
}

 

树的读取

id,pid递归读取

ClientTreeReader.java

/**
 * 完成分销商树的递归读取
 * @author Administrator
 *
 */
publicclass ClientTreeReader {
 
privateStringBuffer sbTreeHTML = new StringBuffer();
 
/**
 * 返回HTML字符串
 * @return
 */
publicString getClientTreeHTMLString() {
Connectionconn = null;
try {
conn= DbUtil.getConnection();
readClientTree(conn,0, 0);
}catch(Exceptione) {
e.printStackTrace();
}finally{
DbUtil.close(conn);
}
returnsbTreeHTML.toString();
}
 
/**
 * 递归读取分销商树
 *
 *第四步:        采用div生成树形结构
 * @param conn
 * @param id
 * @param level 控制层次
 */
privatevoid readClientTree(Connection conn, int id, int level)
throwsSQLException {
Stringsql = "select * from t_client where pid=?";
PreparedStatementpstmt = null;
ResultSetrs = null;
try {
pstmt= conn.prepareStatement(sql);
pstmt.setInt(1,id);
rs =pstmt.executeQuery();
 
while(rs.next()) {
sbTreeHTML.append("<div>");
sbTreeHTML.append("\n");// \n html代码换行显示 <br>布局换行显示
for(int i=0; i<level; i++) {
sbTreeHTML.append("<imgsrc=\"../images/white.gif\">");
sbTreeHTML.append("\n");
}
//if("N".equals(rs.getString("is_leaf"))) {
if(Constants.NO.equals(rs.getString("is_leaf"))) {
// +
sbTreeHTML.append("<imgalt=\"展开\" style=\"cursor:hand;\"onClick=\"display('" + rs.getInt("id") + "');\"id=\"img" + rs.getInt("id") + "\"src=\"../images/plus.gif\">");
sbTreeHTML.append("\n");
//关闭的文件夹
sbTreeHTML.append("<imgid=\"im" + rs.getInt("id") + "\"src=\"../images/closedfold.gif\">");
sbTreeHTML.append("\n");
// 文本
sbTreeHTML.append("<ahref=\"client_node_crud.jsp?id=" + rs.getInt("id") +"\" target=\"clientDispAreaFrame\">" +rs.getString("name") + "</a>");
sbTreeHTML.append("\n");
//递归孩子区域
sbTreeHTML.append("<divstyle=\"display:none;\" id=\"div" +rs.getInt("id") + "\">");
sbTreeHTML.append("\n");
 
readClientTree(conn,rs.getInt("id"), level + 1);
 
sbTreeHTML.append("</div>");
sbTreeHTML.append("\n");
}else{
sbTreeHTML.append("<imgsrc=\"../images/minus.gif\">");
sbTreeHTML.append("\n");
 
sbTreeHTML.append("<imgsrc=\"../images/openfold.gif\">");
sbTreeHTML.append("\n");
 
//如果是分销商
if(Constants.YES.equals(rs.getString("is_client"))) {
sbTreeHTML.append("<ahref=\"client_crud.jsp?id=" + rs.getInt("id") +"\" target=\"clientDispAreaFrame\">" +rs.getString("name") + "</a>");
}else{
//如果是区域
sbTreeHTML.append("<ahref=\"client_node_crud.jsp?id=" + rs.getInt("id") +"\" target=\"clientDispAreaFrame\">" +rs.getString("name") + "</a>");
}
sbTreeHTML.append("\n");
}
sbTreeHTML.append("</div>");
sbTreeHTML.append("\n");
}
}finally {
DbUtil.close(rs);
DbUtil.close(pstmt);
}
}        
 
}

 

 

增加节点


 

删除节点


clientManager.java

/**
 * 删除分销商或者区域
 * @param id
 */
publicvoid delClientOrRegion(int id){
Connectionconn = null;
 
try{
conn= DbUtil.getConnection();
//开启手动事务
DbUtil.beginTransaction(conn);
 
//保存当前节点信息(子方法应该向上抛出异常,否则事务不能发现异常,也就不能进行回滚)
ClientcurrentNode = findClientOrRegionById(id);
 
//递归删除(包括当前节点)(子方法应该向上抛出异常,否则事务不能发现异常,也就不能进行回滚)
recursionDelNode(conn,id);
 
//当前节点的父节点如果没有其他孩子,则为叶子节点(子方法应该向上抛出异常,否则事务不能发现异常,也就不能进行回滚)
if(getChildrenCount(conn, currentNode.getPid()) == 0) {
modifyIsLeafField(conn,currentNode.getPid(), Constants.YES);
}
 
//提交事务
DbUtil.commitTransaction(conn);
}catch (Exception e) {
e.printStackTrace();
//回滚事务
DbUtil.rollbackTransaction(conn);
}finally {
//恢复事务状态
DbUtil.resetConnection(conn);
DbUtil.close(conn);
}
}
 
/**
 * 递归删除
 * @param conn
 * @param id
 * @throws SQLException
 */
privatevoid recursionDelNode(Connection conn, int id) throws SQLException{
Stringsql = "select * from t_client where pid=?";
PreparedStatementpstmt = null;
ResultSetrs = null;
 
try{
pstmt= conn.prepareStatement(sql);
pstmt.setInt(1,id);
//得到当前节点的子节点
rs= pstmt.executeQuery();
 
//删除子节点
while(rs.next()) {
//如果当前节点的子节点不是叶子,继续向下找
if(Constants.NO.equals(rs.getString("is_leaf"))) {
recursionDelNode(conn,rs.getInt("id"));
}else {
//如果当前节点的子节点为叶子,直接删除
delNode(conn,rs.getInt("id"));
}
}
 
//删除当前节点
delNode(conn,id);
 
}  finally {
DbUtil.close(rs);
DbUtil.close(pstmt);
}
}
 
/**
 * 删除节点
 * @param conn
 * @param id
 * @throws SQLException
 */
privatevoid delNode(Connection conn, int id) throws SQLException{
Stringsql = "delete t_client where id=?";
PreparedStatementpstmt = null;
 
try{
pstmt= conn.prepareStatement(sql);
pstmt.setInt(1,id);
pstmt.executeUpdate();
 
}  finally {
DbUtil.close(pstmt);
}
}

 

 

 

待完善

信息更改后,页面刷新,不能恢复修改前的状态

刷新jswindow.parent.clientTreeFrame.location.reload();

 

在开发时,树形目录结构可能不用我们从头开始做,不用自己实现,比如.net中有treeview控件,jquery中有相应的插件,但明白它的原理还是很重要的,况且也挺有意思滴。


抱歉!评论已关闭.