其实写这种树结构选择的公共组件,如果项目是用的jsp, 这个倒是蛮好写的,直接用自定义标签就可以实现了,jsp页面调用自定义标签,然后在自定义标签.tag里面写操作数结构的共性代码
本来我也想直接套用之前项目里面的自定义标签来实现的,但是现在这个项目全都是用的html+veloctity,所有这里jsp的自定义标签.tag就无法使用了,以前没用过veloctity这玩意,思考了下,最后根据jsp里面的自定义标签的方式在html里面用自定义属性实现了公共树结构的选中
注:demo代码用的springmvc+html+jquery+layer+zTree+velocity来实现的
springmvc(java)、html(页面)、jquery(js框架)、layer(弹出层组件)、zTree(树插件)、velocity(模板引擎)
1.写一个子父级关系的entity
public class TestMenu{ //菜单id private Long menuId; //菜单名称 private String menuName; //父亲级id private Long menuParentId; //子集集合 private ArrayList<TestMenu> subList; public TestMenu(){ super(); } public TestMenu(Long menuId,Long menuParentId){ super(); this.menuId = menuId; this.menuParentId = menuParentId; } //生成对应的get set方法 }
2.service还得写接口和实现还得写mybatis和dao或者Mapper,太麻烦了,这里就不写接口和查询的代码了(只需递归查询出一个数据集合就可以了)
3.写一个TestController类,zTree通过ajax来拿数据
@Controller @RequestMapping("/test") public class TestController { @Autowired private TestService service; @RequestMapping("/menuTree") @ResponseBody public Object menuTree() { //调用service查询出树的集合 List<TestMenu> list = service.findList(); return list; } }
4.写一个需要树选择操作的测试页面test.html(该页面用的自定义元素属性)
<!-- 引用公共代码页面 --> #parse("/treeSelect.html") <!-- 添加菜单对话框开始 --> <div id="menu_add_dialog" class="ibox-content" style="display: none;"> <form class="form-horizontal m-t"> <div class="form-group"> <label class="col-sm-3 control-label">选择上级菜单:<span class="text-danger">*</span></label> <div class="col-sm-8"> <input type="hidden" id="menuParentId" name="menuParentId" /> <input readonly="readonly" type="text" id="menuParentName" class="form-control" placeholder="点击选择上级菜单" tagName="treeSelect" treeId="menuId" treePid="menuParentId" treeName="menuName" subList="subList" toInput="menuParentId" treeUrl="/test/menuTree.htm" selectMultiple="false" /> </div> </div> </form> </div>
元素属性解释看下图(图片html代码对应上面test.html页面,js代码对应下面treeSelect.html页面)
5.写一个公共的树选择的页面treeSelect.html(弹出窗口及操作树选择代码都写在这个页面),该页面供需要树选择操作的页面引用
<!-- 选择菜单对话框开始 --> <div id="trre_data_init_dialog" class="ibox-content" style="display: none;"> <form class="form-horizontal m-t"> <div class="form-group"> <div class="col-sm-8"> <div class="zTreeDemoBackground left"> <ul id="tree_ul_id" class="ztree"></ul> </div> </div> </div> </form> </div> <!-- 选择菜单对话框开始 --> <script type="text/javascript"> jQuery(function () { //接收点击元素对象 var objDocument = null; //单选或多选 var checkEnable = false; //点击该元素,弹出tree选中窗口 $("input[tagName='treeSelect']").click(function(){ loadTreeData($(this)); }); //加载数据 function loadTreeData(obj) { //回调 var cb = {}; //判断是单选还是多选,单选时设置回调里面的单击事件 if(obj.attr("selectMultiple")=="true"){ checkEnable = true; }else{ cb = {onClick: zTreeOnClick}; } //配置seting var setting = { view: { selectedMulti: false }, check: { enable: checkEnable}, data: { key : { children : obj.attr("subList"), name : obj.attr("treeName") }, simpleData: { enable: true, idKey : obj.attr("treeId"), pIDKey : obj.attr("treePid") } }, callback: cb }; objDocument = obj; // 加载数据 ajax(basePath + obj.attr("treeUrl"), 'POST', {}, function(result) { if(result.code == 0) { var zNodes = result.data; //将数据绑定到ztree $.fn.zTree.init($("#tree_ul_id"), setting, zNodes); //弹出树的选中窗口 openTreeDialog(); } else { layer.alert("树结构加载失败"); } }); } //点击节点 function zTreeOnClick(event, treeId, treeNode) { //将点击的节点id和name赋值给需要绑定的元素 $("#"+objDocument.attr("id")).val(treeNode[objDocument.attr("treeName")]); $("#"+objDocument.attr("toInput")).val(treeNode[objDocument.attr("treeId")]); //执行关闭 (关闭layer弹出窗口) layer.close($("#trre_data_init_dialog").parent().parent().attr("times")); } //弹出窗口 function openTreeDialog() { //获取隐藏的tree div var div = jQuery('#trre_data_init_dialog'); var treeObj = $.fn.zTree.getZTreeObj("tree_ul_id"); var nodes = treeObj.getNodes(); //多选时回显(将之前选择的节点勾选上) var ids = $("#"+objDocument.attr("toInput")).val(); var idArr = ids.split(","); var arrayNodes = treeObj.transformToArray(nodes); if(checkEnable){ for (var i = 0; i < arrayNodes.length; i++) { for(var j = 0; j < idArr.length; j++){ if(arrayNodes[i][objDocument.attr("treeId")]==idArr[j]){ treeObj.checkNode(arrayNodes[i],true,false); } } } } for (var i = 0; i < nodes.length; i++) { //设置节点展开 treeObj.expandNode(nodes[i], true, false, true); } //判断弹出单选还是多选 if(checkEnable){ layer.open({ type: 1, shift: 5, title: '请选择', area: ['350px', '450px'], skin : 'layui-layer-lan', content: div, zIndex : 108, btn : [ '确定','取消'], yes : function(index, layero) { nodes = treeObj.getCheckedNodes(true); if(nodes.length==0){ layer.msg('请选择对应节点'); }else{ var zId = "",zName=""; for (var i = 0; i < nodes.length; i++) { if(i < nodes.length-1){ zId+=nodes[i][objDocument.attr("treeId")]+","; zName+=nodes[i][objDocument.attr("treeName")]+","; }else{ zId+=nodes[i][objDocument.attr("treeId")]; zName+=nodes[i][objDocument.attr("treeName")]; } } //将选中的节点id和name赋值给需要绑定的元素 $("#"+objDocument.attr("id")).val(zName); $("#"+objDocument.attr("toInput")).val(zId); //执行关闭 (关闭layer弹出窗口) layer.close(index); } } }); }else{ //单选时,直接点击对应节点,就执行回调里面的点击事件,所以不需要上面多选里面的yes函数 layer.open({ type: 1, shift: 5, title: '请选择', area: ['350px', '450px'], skin : 'layui-layer-lan', content: div, zIndex : 101, btn : [ '取消'] }); } } }); </script>
有了treeSelect.html的共性代码,其他需要操作树结构选择的页面,只需要按照以下代码写,就能给页面添加上树结构的选择了(且下面的元素和属性值都可以动态配置了,这样就成了公共的树结构选择了,不管是菜单表或者是地区这些有子父级关系表,都可以直接按照下面模板代码写了,只需要配置各自的属性名和查询的url就行了)
<!-- 引用公共代码页面 -->
#parse("/treeSelect.html")
<!-- 添加菜单对话框开始 -->
<div id="menu_add_dialog" class="ibox-content" style="display: none;">
<form class="form-horizontal m-t">
<div class="form-group">
<label class="col-sm-3 control-label">上级菜单:<span class="text-danger">*</span></label>
<div class="col-sm-8">
<input type="hidden" id="menuParentId" name="menuParentId" />
<input readonly="readonly" type="text" id="menuParentName" class="form-control" placeholder="点击选择菜单"
tagName="treeSelect" treeId="menuId" treePid="menuParentId" treeName="menuName"
subList="subList" toInput="menuParentId"
treeUrl="/system/menu/menuTree.htm" selectMultiple="false" />
</div>
</div>
</form>
</div>