運行環境:springMVC+mybatis
一、建表
說明:0表示此節點為非葉子節點,即此節點還包括了子節點;1表示此節點為葉子節點,即此節點沒有子節點。;關於圖標iconCls是從Extjs的文件的icons文件夾找的。命名方式是把找到的圖標名去掉下划線,然后首字母大寫即可。
二、運用mybatis 的generator插件自動生成pojo、映射文件及訪問接口並做適當的添加修改。
1.pojo
package com.shyy.web.entity; import java.util.List; public class Tree { private String id; private String text; private String iconCls; private Boolean leaf; private String fatherId; private List<Tree> children; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getIconCls() { return iconCls; } public void setIconCls(String iconCls) { this.iconCls = iconCls; } public Boolean getLeaf() { return leaf; } public void setLeaf(Boolean leaf) { this.leaf = leaf; } public String getFatherId() { return fatherId; } public void setFatherId(String fatherId) { this.fatherId = fatherId; } public List<Tree> getChildren() { return children; } public void setChildren(List<Tree> children) { this.children = children; } @Override public String toString() { return "Tree{" + "id='" + id + '\'' + ", text='" + text + '\'' + ", iconCls='" + iconCls + '\'' + ", leaf=" + leaf + ", fatherId='" + fatherId + '\'' + ", children=" + children + '}'; } }
2.映射文件:
需要注意的是, <result property="leaf" column="leaf" jdbcType="TINYINT" javaType="Boolean" />這樣的話,mybatis就會自動將數據庫中的TINYINT類型的leaf轉化成布爾類型,0會轉化為false,1會轉化為true。這里一定要有這個,不然在執行時會報異常。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.shyy.web.service.TreeMapper" > <resultMap id="BaseResultMap" type="com.shyy.web.entity.Tree" > <id column="id" property="id" jdbcType="VARCHAR" /> <result column="text" property="text" jdbcType="VARCHAR" /> <result column="iconCls" property="iconCls" jdbcType="VARCHAR" /> <result column="leaf" property="leaf" jdbcType="TINYINT" javaType="Boolean" /> <result column="fatherId" property="fatherId" jdbcType="VARCHAR" /> </resultMap> <sql id="Base_Column_List" > id, text, iconCls, leaf, fatherId </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" > select <include refid="Base_Column_List" /> from t_tree where id = #{id,jdbcType=VARCHAR} </select> <select id="getFather" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List" /> FROM t_tree WHERE fatherId IS NULL </select> <select id="getChildren" resultMap="BaseResultMap" parameterType="java.lang.String"> SELECT <include refid="Base_Column_List" /> FROM t_tree WHERE fatherId = #{fatherId} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.String" > delete from t_tree where id = #{id,jdbcType=VARCHAR} </delete> <insert id="insert" parameterType="com.shyy.web.entity.Tree" > insert into t_tree (id, text, iconCls, leaf, fatherId) values (#{id,jdbcType=VARCHAR}, #{text,jdbcType=VARCHAR}, #{iconCls,jdbcType=VARCHAR}, #{leaf,jdbcType=TINYINT}, #{fatherId,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.shyy.web.entity.Tree" > insert into t_tree <trim prefix="(" suffix=")" suffixOverrides="," > <if test="id != null" > id, </if> <if test="text != null" > text, </if> <if test="iconCls != null" > iconCls, </if> <if test="leaf != null" > leaf, </if> <if test="fatherId != null" > fatherId, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="id != null" > #{id,jdbcType=VARCHAR}, </if> <if test="text != null" > #{text,jdbcType=VARCHAR}, </if> <if test="iconCls != null" > #{iconCls,jdbcType=VARCHAR}, </if> <if test="leaf != null" > #{leaf,jdbcType=TINYINT}, </if> <if test="fatherId != null" > #{fatherId,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.shyy.web.entity.Tree" > update t_tree <set > <if test="text != null" > text = #{text,jdbcType=VARCHAR}, </if> <if test="iconCls != null" > iconCls = #{iconCls,jdbcType=VARCHAR}, </if> <if test="leaf != null" > leaf = #{leaf,jdbcType=TINYINT}, </if> <if test="fatherId != null" > fatherId = #{fatherId,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=VARCHAR} </update> <update id="updateByPrimaryKey" parameterType="com.shyy.web.entity.Tree" > update t_tree set text = #{text,jdbcType=VARCHAR}, iconCls = #{iconCls,jdbcType=VARCHAR}, leaf = #{leaf,jdbcType=TINYINT}, fatherId = #{fatherId,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} </update> </mapper>
3.訪問映射文件的接口(沒有實現類,直接由該接口訪問對應的映射文件)
package com.shyy.web.service; import com.shyy.web.entity.Tree; import java.util.List; public interface TreeMapper { int deleteByPrimaryKey(String id); int insert(Tree record); int insertSelective(Tree record); Tree selectByPrimaryKey(String id); List<Tree> getFather(); List<Tree> getChildren(String id); int updateByPrimaryKeySelective(Tree record); int updateByPrimaryKey(Tree record); }
4.控制層代碼
package com.shyy.web.controller.anntation; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.shyy.web.entity.Tree; import com.shyy.web.service.TreeMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.shyy.web.controller.response.EmptyResponse; import com.shyy.web.controller.response.NormalResponse; import com.shyy.web.controller.response.Response; import com.shyy.web.entity.Privilege; import com.shyy.web.service.PrivilegeService; import com.shyy.web.service.impl.PrivilegeServiceImpl; @Controller @RequestMapping("/menu/") public class PrivilegeController { Logger logger = LoggerFactory.getLogger(PrivilegeController.class); @Autowired private TreeMapper treeMapper; @SuppressWarnings("unused") @RequestMapping("showmyMenu") @ResponseBody public List<Tree> myMenus(HttpServletRequest req, HttpServletResponse resp) { System.out.println("-aaa--"); List<Tree> pris = treeMapper.getFather();//一級菜單 List<Tree> menus = new ArrayList<Tree>(); for(Tree pri:pris){//迭代一級菜單找二級菜單 List<Tree> prisNode = treeMapper.getChildren(pri.getId());//二級菜單 pri.setChildren(prisNode); for (Tree privilege : prisNode) {//迭代二級菜單找三級菜單 List<Tree> childNode = treeMapper.getChildren(privilege.getId());//三級菜單 privilege.setChildren(childNode); //如果有四級菜單,則需迭代三級菜單找四級菜單 } menus.add(pri); } System.out.println(menus); if (menus != null || menus.size() > 0) { return menus; } else { return null; } } public String test(){ return "wang"; } }
js代碼:
Ext.onReady(function(){ var model = Ext.define("TreeModel", { // 定義樹節點數據模型 extend : "Ext.data.Model", fields : [{name : "id",type : "string"}, {name : "text",type : "string"}, {name : "iconCls",type : "string"}, {name : "leaf",type : "boolean"}, {name : 'url',type:"string"}, {name : 'description',type:"string"}] }); var store = Ext.create('Ext.data.TreeStore', { model : model,//定義當前store對象的Model數據模型 // autoLoad : true, proxy : { type : 'ajax', url : '../menu/showmyMenu',//請求 reader : { type : 'json', // root : 'data'//數據 } }, root : {//定義根節點,此配置是必須的 // text : '管理菜單', expanded : true } }); Ext.create('Ext.tree.Panel', { title: 'Simple Tree', width: 200, height: 550, store: store, rootVisible: false,//隱藏根節點 renderTo: Ext.getBody() }); });
說明:在http://liuchangming1993-126-com.iteye.com/blog/1938482文章中的博主用的struts2,返回形式與本文的springMVC不同,因此這里沒有指定root屬性。
下面看一下js中的ajax請求返回的數據(片段):
[Tree{id='101', text='圖像報表', iconCls='Chartbar', leaf=false, fatherId='null',
children=[Tree{id='1001', text='餅狀圖', iconCls='Chartpie', leaf=false, fatherId='101', children=[Tree{id='10001', text='餅狀圖一', iconCls='Chartpieadd', leaf=true, fatherId='1001', children=null}, Tree{id='10002', text='餅狀圖二', iconCls='Chartpiedelete', leaf=true, fatherId='1001', children=null}]},
Tree{id='1002', text='線狀圖', iconCls='Chartline', leaf=false, fatherId='101', children=[Tree{id='10003', text='線狀圖一', iconCls='Chartcurveadd', leaf=true, fatherId='1002', children=null}, Tree{id='10004', text='線狀圖二', iconCls='Chartcurvedelete', leaf=true, fatherId='1002', children=null}]}]}]
可以看出是reader 將對象的數據轉化成了json格式。
運行效果:
方法二
這里的邏輯是一次性加載完所有的菜單返回至前台,在http://liuchangming1993-126-com.iteye.com/blog/1938482的博客中用到的是另一種方式,即根據用戶點擊的菜單展開子菜單。
現在用這種方式實現一下:
說明:因為之前建的表t_tree與pojo的主鍵都是id,發現與Extjs沖突,所以這次將表的主鍵改為tid,然后自動生成相應代碼。
一、在映射文件中加一個sql
<select id="findLeaf" parameterType="String" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List" /> FROM t_tree WHERE 1=1 <choose> <when test="_parameter!=null and _parameter!=''"> and fatherId = #{fatherId} </when> <otherwise> and fatherId IS NULL </otherwise> </choose> </select>
二、修改訪問映射文件的接口,即增加訪問上面sql的方法
package com.shyy.web.service; import com.shyy.web.entity.Tree; import java.util.List; public interface TreeMapper { int deleteByPrimaryKey(String tid); int insert(Tree record); int insertSelective(Tree record); Tree selectByPrimaryKey(String tid); List<Tree> getFather(); List<Tree> getChildren(String id); List<Tree> findLeaf(String id); int updateByPrimaryKeySelective(Tree record); int updateByPrimaryKey(Tree record); }
三、修改控制器,增加一個方法
@SuppressWarnings("unused") @RequestMapping("showmyMenu2") @ResponseBody public List<Tree> myMenus2(HttpServletRequest req, HttpServletResponse resp,@RequestParam String tid) { System.out.println("tid:"+tid); List<Tree> menus = new ArrayList<Tree>(); menus = treeMapper.findLeaf(tid); if (menus != null || menus.size() > 0) { return menus; } else { return null; } }
四、修改js
Ext.onReady(function(){ var store = Ext.create('Ext.data.TreeStore', { autoLoad : true, proxy : { type : 'ajax', url : '../menu/showmyMenu2',//請求 reader : { type : 'json', // root : 'menuList'//數據 }, //傳參 extraParams : { tid : '' } }, root : { // text : '管理菜單', expanded : true }, listeners : { 'beforeexpand' : function(node,eOpts){ //點擊父親節點的菜單會將節點的id通過ajax請求,將到后台 this.proxy.extraParams.tid = node.raw.tid; } } }); Ext.create('Ext.tree.Panel', { renderTo : Ext.getBody(), title : '動態加載TreePanel', width : 300, height : 500, useArrows : true, rootVisible: false, store : store }); });
注:
1.autoLoad 這個配置默認是false,但是即使注釋掉此配置也是沒有影響的;
2.可以看出上面的js已去掉了model 配置,是沒有影響的,在Ext.data.TreeStore的API中有這樣的說明:
Using Models 用例模型
如果未指定模型Model,將創建實現於Ext.data.NodeInterface的一種隱式模型。 標准的Tree字段列表也將被復制到Model上來保持自身的狀態。 在Ext.data.NodeInterface文檔中列出了這些字段。
那么model是干嘛的(如上面的創建的繼承於Ext.data.Model的實例"TreeModel"),NodeInterface又是干嘛的?下面看一下Ext.data.NodeInterface的API簡介:
本類是一個應用到Model的原型上的方法集合,使其具有Node API。這意味着樹狀模型 具有所有與樹相關的方法。通常情況下, 開發者不會直接使用本類。為了保存樹的狀態和UI, 本類會在模型上創建一些額外的字段(如果它們不存在)。 這些字段記錄為config選項。
到這里model的作用就明確了,舉例來說,像本文定義的model,比如它有一個字段名為“leaf”,那么由於在Ext.data.NodeInterface中也具有這個配置屬性,所以這個leaf的值會在加載時起作用。其他定義的字段在樹面板上起到什么作用則均可在Ext.data.NodeInterface找到答案。
在api中可以看出Ext.data.TreeStore繼承了Ext.data.NodeStore,而Ext.data.NodeStore的接口正是Ext.data.NodeInterface。
3.上面js中listeners 定義的‘beforeexpand’事件也來源於Ext.data.NodeInterface。在本節點展開前觸發。
4.reader 中的root配置,在Ext.data.TreeStore的API中有這樣的說明:
Reading Nested Data 讀取內嵌數據
對於樹讀取內嵌數據,在Ext.data.reader.Reader中必須配置一個root屬性, 這樣reader可以找到關於每個節點的內嵌數據。 如果未指定root,那么它將默認為'children'。
這里介紹它的作用是“讀取內嵌數據”,我並未指定,然而它說默認為‘children’,剛好我在設計菜單的pojo時對於子菜單屬性的引用名就是children:
private List<Tree> children;
所以如果上面的引用是別的什么,那么就要指定了?——這里不作測試了。
運行效果: