1. 程式人生 > >SSH+Easyui之TreeGrid樹形展現資料

SSH+Easyui之TreeGrid樹形展現資料

本文介紹Struts2、Spring、Hibernate與easyui的TreeGrid結合,以樹的形式展現資料庫中資料。在開發中會碰到很多需要以樹的形式展現資料,如導航條、許可權管理模組中的資源管理,通常在資料庫中都是以樹的形式存在。例子程式以資源管理為例,使用treegrid展現資源管理子模組的資料。最終效果如下圖:


使用easyui使用樹形展現很簡單,只需將<table>標籤的class設為easyui-treegrid即可

<body class="easyui-layout" data-options="fit:true,border:false">
<div>
<span style="white-space:pre">	</span><table id="resourcelist" class="easyui-treegrid"
        data-options="url:'privilegemgmt/resourceAction_getAll.action',idField:'id',treeField:'resourceName',toolbar:'#tb',border:false">
    <thead>
        <tr>
            <th data-options="field:'id',width:40">編號</th>
            <th data-options="field:'resourceName',width:150">資源名稱</th>
            <th data-options="field:'resourceUrl',width:200">資源路徑</th>
            <th data-options="field:'resourceOrder',width:50">排序</th>
            <th data-options="field:'icon',width:80">圖示</th>
            <th data-options="field:'resourceType',width:80">資源型別</th>
            <th data-options="field:'parentid',width:80">上級資源ID</th>
            <th data-options="field:'enable',width:50">狀態</th>
            <th data-options="field:'action',width:120">操作</th>
        </tr>
    </thead>
<span style="white-space:pre">	</span></table>
<span style="white-space:pre">	</span><div id="tb">
<span style="white-space:pre">		</span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-add" plain="true" onclick="onAdd()">增加</a>
<span style="white-space:pre">	</span>    <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-edit" plain="true" onclick="onUpdate()">編輯</a>
<span style="white-space:pre">		</span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-remove" plain="true" onclick="onDelete()">刪除</a>  
<span style="white-space:pre">	</span></div>
</div>
</body>
以上就為如圖所示的樹形展現html程式碼,data-options定義的各屬性值作用為:

url:定義遠端獲取資料的路徑,資料以json格式返回

idField:為樹節點的標識,該值唯一

treeField:定義節點的欄位,如以上定義的節點欄位為資源名稱,當該節點下包含子節點時,資源名稱前面顯示一個可展開的圖示

toolbar:工具欄,傳入的引數為html標籤的id值

border:是否顯示邊框

field:欄位名,即該列所需展現的資料,easyui通過解析json字串,迴圈輸出到表單中,根據欄位名與json字串中欄位的匹配確定哪項資料輸出到哪列

formatter:單元格formatter(格式化器)函式,為該列的資料顯示做格式化操作,達到自己想要的顯示效果。函式包含三個引數:value(欄位值),rowData(行記錄資料),rowIndex(行索引)。

接下來就是訪問Action從伺服器端獲取資料。以上呼叫了ResourceAction的getAll方法來獲取這可資源樹。

ResourceAction.java

public class ResourceAction extends BaseAction {
	private Resource resource;
	private ResourceServiceI resourceService;

	public void getAll() {
		String json = resourceService.getResourceTreeToJson();
		this.write(json);
	}
	
	//getter or setter
	public Resource getResource() {
		return resource;
	}
	public void setResource(Resource resource) {
		this.resource = resource;
	}
	public ResourceServiceI getResourceService() {
		return resourceService;
	}
	public void setResourceService(ResourceServiceI resourceService) {
		this.resourceService = resourceService;
	}
}
BaseAction.java
public abstract class BaseAction extends ActionSupport {

	public void write(String json) {
		try {
			ServletActionContext.getResponse().setCharacterEncoding("UTF-8");
			ServletActionContext.getResponse().setContentType("text/json");
			ServletActionContext.getResponse().getWriter().write(json);
			ServletActionContext.getResponse().getWriter().flush();
			ServletActionContext.getResponse().getWriter().close();
		} catch (Exception ex) {
			Debuger.log("BaseAction write json throw Exception!");
			ex.printStackTrace();
		}
	}

	public void write(boolean bool) {
		try {
			String json = "{\"success\":\"" + bool + "\"}";
			ServletActionContext.getResponse().getWriter().write(json);

			ServletActionContext.getResponse().getWriter().flush();
			ServletActionContext.getResponse().getWriter().close();
		} catch (Exception ex) {
			Debuger.log("BaseAction write bool throw Exception!");
			ex.printStackTrace();
		}
	}
}

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
	<package name="priviligemgmt" extends="struts-default" namespace="/privilegemgmt">
		<action name="resourceAction_*" class="com.wzh_1208.sshe.action.ResourceAction" method="{1}">   
			<result name="input">/jsp/privilegemgmt/resourceinfo.jsp</result>
		</action>
	</package>
</struts>
在使用框架提供的類時,我一般將他封裝成自己的一個抽象類,然後需要使用到該類的地方就直接繼承自己的基類,方便拓展和程式碼的重用。由於可能在專案中大多數Action都需要向客戶端寫出json字串,這裡就在BaseAction中封裝了兩個write方法,其他Action繼承BaseAcion即可。getAll方法其實就一個殼子供jsp頁面呼叫,全部的邏輯丟給了業務邏輯層的getResourceTreeToJson方法,該方法將資料庫中的資源封裝成了一顆樹,並以json字串的形式返回。

ResourceServiceImpl.java

import java.util.List;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.wzh_1208.waimai.bean.Resource;
import com.wzh_1208.waimai.dao.ResourceDAOI;
import com.wzh_1208.waimai.services.ResourceServiceI;

public class ResourceServiceImpl implements ResourceServiceI {
	private ResourceDAOI resourceDao;
	
	@Override
	public List<Resource> getAllResource() {
		return resourceDao.findAll();
	}

	@Override
	public String getResourceTreeToJson() {
		return this.createTreeJson(getAllResource());
	}

	/**
	 * 建立一顆樹,以json字串形式返回
	 * @param list 原始資料列表
	 * @return 樹
	 */
	private String createTreeJson(List<Resource> list) {
		JSONArray rootArray = new JSONArray();
		for (int i=0; i<list.size(); i++) {
			Resource resource = list.get(i);
			if (resource.getParentid() == null) {
				JSONObject rootObj = createBranch(list, resource);
				rootArray.add(rootObj);
			}
		}
		
		return rootArray.toString();
	}
	
	/**
	 * 遞迴建立分支節點Json物件
	 * @param list 建立樹的原始資料
	 * @param currentNode 當前節點
	 * @return 當前節點與該節點的子節點json物件
	 */
	private JSONObject createBranch(List<Resource> list, Resource currentNode) {
		/*
		 * 將javabean物件解析成為JSON物件
		 */
		JSONObject currentObj = JSONObject.fromObject(currentNode);
		JSONArray childArray = new JSONArray();
		/*
		 * 迴圈遍歷原始資料列表,判斷列表中某物件的父id值是否等於當前節點的id值,
		 * 如果相等,進入遞迴建立新節點的子節點,直至無子節點時,返回節點,並將該
		 * 節點放入當前節點的子節點列表中
		 */
		for (int i=0; i<list.size(); i++) {
			Resource newNode = list.get(i);
			if (newNode.getParentid()!=null && newNode.getParentid().compareTo(currentNode.getId()) == 0) {
				JSONObject childObj = createBranch(list, newNode);
				childArray.add(childObj);
			}
		}
		
		/*
		 * 判斷當前子節點陣列是否為空,不為空將子節點陣列加入children欄位中
		 */
		if (!childArray.isEmpty()) {
			currentObj.put("children", childArray);
		}
		
		return currentObj;
	}
	
	//getter or setter
	public ResourceDAOI getResourceDao() {
		return resourceDao;
	}

	public void setResourceDao(ResourceDAOI resourceDao) {
		this.resourceDao = resourceDao;
	}
}

在使用easyui的treegrid時,最重要的一點就是伺服器為easyui返回的json資料的格式。easyui在處理從伺服器端返回來的json字串時,會根據節點下面是否包含children欄位及是否含有資料來確定當前節點是否包含子節點。所以在返回json資料時,需要為含有子節點的節點加上children欄位,並將子節點列表賦給該值。ResourceServiceImpl為ResourceServiceI的實現類,實現了getAllResource()和getResourceToJson()方法,getAllResource()方法呼叫資料庫訪問物件ResourceDAOImpl來獲取資料庫中所有的資源列表並返回,getResourceToJson方法為獲取所有的Resource並組織成一顆樹,以json字串的形式返回。

ResourceDAOImpl.java

import java.sql.Timestamp;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wzh_1208.waimai.BaseDAO;
import com.wzh_1208.waimai.bean.Resource;
import com.wzh_1208.waimai.dao.ResourceDAOI;

public class ResourceDAOImpl extends BaseDAO implements ResourceDAOI {
	private static final Logger log = LoggerFactory
			.getLogger(ResourceDAOImpl.class);
	
	protected void initDao() {
		// do nothing
	}

	public List<Resource> findAll() {
		log.debug("finding all Resource instances");
		try {
			//查詢資源記錄,按資源父id和序號的升序排列
			String queryString = "from Resource order by parentid,resourceOrder asc";
			return getHibernateTemplate().find(queryString);
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}
}
BaseDAO.java
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public abstract class BaseDAO extends HibernateDaoSupport {
}
ResourceDAOImpl繼承了BaseDAO類和ResourceDAOI,並實現findAll方法,為業務邏輯層返回按資源父id和序號升序排列的Resource所有記錄。

以上就完成了整個資料展現過程的實現,但工作還未完成,還需配置一大堆配置檔案。這裡就不貼出來了。

Resource.java

import java.sql.Timestamp;

/**
 * Resource entity. @author WZH
 */

public class Resource implements java.io.Serializable {
	//主鍵id
	private Integer id;
	//資源名稱
	private String resourceName;
	//資源訪問路徑
	private String resourceUrl;
	//資源描述
	private String description;
	//資源的圖示
	private String icon;
	//父節點id
	private Integer parentid;
	//資源的型別,選單或按鈕
	private String resourceType;
	//是否可用
	private Integer enable;
	//資源排序序號
	private Integer resourceOrder;
	//建立時間
	private Timestamp createTime;
<span style="white-space:pre">	/** default constructor */
<span style="white-space:pre">	</span>public Resource() {
<span style="white-space:pre">	</span>}</span>
	//getter or setter方法省略
                 ........
}
Resource.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.wzh_1208.sshe.bean.Resource" table="resource">
        <id name="id" type="java.lang.Integer">
            <column name="id" />
            <generator class="native"></generator>
        </id>
        <property name="resourceName" type="java.lang.String">
            <column name="resource_name" length="20" not-null="true">
            </column>
        </property>
        <property name="resourceUrl" type="java.lang.String">
            <column name="resource_url" length="200">
            </column>
        </property>
        <property name="description" type="java.lang.String">
            <column name="description" length="200">
            </column>
        </property>
        <property name="icon" type="java.lang.String">
            <column name="icon" length="200">
            </column>
        </property>
        <property name="parentid" type="java.lang.Integer">
            <column name="parentid">
            </column>
        </property>
        <property name="resourceType" type="java.lang.String">
            <column name="resource_type" length="11" not-null="true">
            </column>
        </property>
        <property name="enable" type="java.lang.Integer">
            <column name="enable" not-null="true">
            </column>
        </property>
        <property name="resourceOrder" type="java.lang.Integer">
            <column name="resource_order">
            </column>
        </property>
        <property name="createTime" type="java.sql.Timestamp">
            <column name="create_time" length="0">
            </column>
        </property>
    </class>
</hibernate-mapping>

執行程式,資料便可以樹的形式展現出來,效果圖如下:

但此時只是將資料庫中的資料原封不動的顯示在了介面上,並未像文章開頭那張圖一樣在節點上顯示設定的圖示、狀態為可用或禁用以及操作欄未顯示操作。此時就需為資料進行格式話處理,即用到easyui的formatter屬性來定義格式化列的顯示函式,來實現資料自定義的顯示。修改之後的html程式碼如下:

<script>
/**
 *格式化資源顯示名稱,easyui在顯示節點欄位時,如果記錄中有iconCls值,easyui會根據該值來相應的圖示,
 *如果沒有則應預設圖示代替。這裡在傳遞過來的json字串中未含有iconCls欄位,但包含icon欄位,這裡只需
 *將icon欄位值賦給iconCls,即可顯示圖示。
**/
function formatName(value, row, index) {
	//在名稱前面顯示圖示就是靠iconCls屬性,iconCls屬性為一個css類,easyui拿到這個屬性值就能顯示相應的圖示了
	//由於傳遞過來的json字串中未包含iconCls屬性,只有icon屬性,所以要想easyui顯示圖示只需將icon的值賦給iconCls
	row.iconCls = row.icon;
	return value;
}

/**
 *格式化父資源的顯示,為0時不顯示父資源
**/
function formatParentId(value, row, index) {
	if (value == 0) {
		return null;
	} 
	return value;
}

/**
 *格式化狀態,如果為1,顯示正常,為0顯示禁用
 */
function formatState(value,row,index) {
	switch (value) {
	case 1:
		return '正常';
	case 0:
		return '禁用';	
	}
}

/**
 *格式化操作,在每行的操作欄顯示編輯和刪除操作
 */
function formatAction(value, row, index) {
	var str = '';

	if (true) {
		str += '<a href="javascript:onUpdate()" >編輯</a>';
	}
	str += ' | ';
	if (true) {
		str += '<a href="javascript:onDelete()">刪除</a>';
	}

	return str;
}
</script>
</head>

<body class="easyui-layout" data-options="fit:true,border:false">
<div>
<span style="white-space:pre">	</span><table id="resourcelist" class="easyui-treegrid"
        data-options="url:'privilegemgmt/resourceAction_getAll.action',idField:'id',treeField:'resourceName',toolbar:'#tb',border:false">
    <thead>
        <tr>
            <th data-options="field:'id',width:40">編號</th>
            <th data-options="field:'resourceName',width:150,formatter:formatName">資源名稱</th>
            <th data-options="field:'resourceUrl',width:200">資源路徑</th>
            <th data-options="field:'resourceOrder',width:50">排序</th>
            <th data-options="field:'icon',width:80">圖示</th>
            <th data-options="field:'resourceType',width:80">資源型別</th>
            <th data-options="field:'parentid',width:80,formatter:formatParentId">上級資源ID</th>
            <th data-options="field:'enable',width:50,formatter:formatState">狀態</th>
            <th data-options="field:'action',width:120,formatter:formatAction">操作</th>
        </tr>
    </thead>
<span style="white-space:pre">	</span></table>
<span style="white-space:pre">	</span><div id="tb">
<span style="white-space:pre">		</span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-add" plain="true" onclick="onAdd()">增加</a>
<span style="white-space:pre">	</span>    <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-edit" plain="true" onclick="onUpdate()">編輯</a>
<span style="white-space:pre">		</span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-remove" plain="true" onclick="onDelete()">刪除</a>  
<span style="white-space:pre">	</span></div>
</div>
</body>
從以上的程式碼可以看出為需要格式化的列的<th>標籤的data-options屬性中增加了formatter屬性以及對應呼叫的格式化函式,具體函式的作用和實現已在程式碼中做出說明。只是需要注意的是,iconCls欄位值為css的一個類,可以使用easyui所提供的圖示,也可使用自定義的圖示,若使用自定義的圖示時,一定要特別注意圖示的路徑,否則也不能正常顯示。

這樣就完成了Treegrid屬性展現資源資料。