需求:在前端将组织架构显示为可选择的树形结构,并在选择之后可以回填组织名称,并获取到对应组织id
实现:系统框架使用kendoui作为前端框架,那么就寻找相关组件,并且在后台java代码中组装数据,设置属性.
表结构如下
1.建立TreeVo对象
package com.fsl.lcp.demo.vo;
import java.util.List;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
public class TreeVo {
@JsonSerialize(using=ToStringSerializer.class)
private Long id;
private String text;
private Boolean expanded=false;
private Boolean checked=false;
private List<TreeVo> items;
public Boolean getChecked() {
return checked;
}
public void setChecked(Boolean checked) {
this.checked = checked;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Boolean getExpanded() {
return expanded;
}
public void setExpanded(Boolean expanded) {
this.expanded = expanded;
}
public List<TreeVo> getItems() {
return items;
}
public void setItems(List<TreeVo> items) {
this.items = items;
}
}
2.组装数据
查找到的组织数据sql就是一个select *,组装数据代码如下
package com.fsl.lcp.demo.service.impl;
import com.fsl.lcp.demo.dto.SalesActivityDemo;
import com.fsl.lcp.demo.service.ISalesActivityDemoService;
import com.fsl.lcp.demo.vo.TreeVo;
import com.hand.hap.hr.dto.HrOrgUnit;
import com.hand.hap.hr.mapper.OrgUnitMapper;
import com.hand.hap.system.service.impl.BaseServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class SalesActivityDemoServiceImpl extends BaseServiceImpl<SalesActivityDemo> implements ISalesActivityDemoService{
@Autowired
OrgUnitMapper orgUnitMapper;
@Override
public List<TreeVo> unitTreeGet() {
List<HrOrgUnit> selectAllUnitAndCharge = orgUnitMapper.selectAllUnitAndCharge(new HrOrgUnit());
List<TreeVo> treeList=new ArrayList<TreeVo>();
//根节点
List<HrOrgUnit> collect = selectAllUnitAndCharge.stream().filter((e)->null==e.getParentId()).collect(Collectors.toList());
collect.stream().forEach((e)->{
TreeVo vo=new TreeVo();
vo.setId(e.getUnitId());
vo.setText(e.getName());
vo.setExpanded(true);
treeList.add(vo);
});
//子节点
getChildNodes(selectAllUnitAndCharge, treeList);
return treeList;
}
public void getChildNodes(List<HrOrgUnit> unitsAll,List<TreeVo> treeList) {
treeList.stream().forEach((a) -> {
List<TreeVo> childNodes = new ArrayList<>();
unitsAll.forEach((e) -> {
if (null != e.getParentId()) {
if (e.getParentId().equals(a.getId())) {
//子节点-添加
TreeVo child=new TreeVo();
child.setId(e.getUnitId());
child.setText(e.getName());
if(a.getExpanded()){
child.setExpanded(a.getExpanded());
}
if(a.getChecked()){
child.setChecked(a.getChecked());
}
if(e.getName().equals("办公室")){
child.setChecked(true);
}
childNodes.add(child);
}
}
});
if(childNodes.size()>0){
a.setItems(childNodes);
getChildNodes(unitsAll, childNodes);
}
});
}
}
3.前端渲染
<#include "../include/header.html">
<body>
<script src="${base.contextPath}/lib/kendoui/js/jquery.min.js"></script>
<script src="${base.contextPath}/lib/kendoui/js/jszip.min.js"></script>
<!--<script src="${base.contextPath}/resources/console.js"></script>-->
<script src="${base.contextPath}/resources/kendo.all.min.js"></script>
<link href="${base.contextPath}/resources/kendo.common.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/kendo.rtl.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/kendo.default.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/kendo.default.mobile.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/examples-offline.css" rel="stylesheet" type="text/css"/>
<div id="treeList">
<div class="demo-section k-content">
<div id="dialog">
<div class="dialogContent">
<input id="filterText" type="text" placeholder="请输入" />
<div class="selectAll">
<input type="checkbox" id="chbAll" class="k-checkbox" onchange="chbAllOnChange()" />
<label class="k-checkbox-label" for="chbAll">所有</label>
<span id="result">未选择</span>
</div>
<div id="treeview"></div>
</div>
</div>
<select id="multiselect"></select>
<br />
<button id="openWindow" class="k-primary">请选择</button>
</div>
<script>
var dataTree;
var myDataSource;
$.ajax({
type: "get",
url: "${base.contextPath}/lcp/sales/activity/demo/audit/line/getTreeUnit",
/* data: kendo.stringify(data), */
dataType: "json",
contentType: 'application/json',
success: function (datas) {
if(datas){
dataTree=datas;
myDataSource = new kendo.data.HierarchicalDataSource({
data: dataTree
});
$("#treeview").kendoTreeView({
checkboxes: {
checkChildren: true
},
dataSource: myDataSource,
check: onCheck
});
}
}
});
$("#multiselect").kendoMultiSelect({
dataTextField: "text",
dataValueField: "id"
});
$(document).ready(function () {
var dialog = $("#dialog");
var multiSelect = $("#multiselect").data("kendoMultiSelect");
$("#openWindow").kendoButton();
multiSelect.readonly(true);
$("#openWindow").click(function () {
dialog.data("kendoDialog").open();
$(this).fadeOut();
var nodes = $("#treeview").data("kendoTreeView").dataSource.view();
var checkedNodes = [];
getCheckedNodes(nodes, checkedNodes);
setMessage(checkedNodes.length);
});
dialog.kendoDialog({
width: "400px",
title: "组织架构",
visible: false,
actions: [
{
text: '取消',
primary: false,
action: onCancelClick
},
{
text: '确定',
primary: true,
action: onOkClick
}
],
close: onClose
}).data("kendoDialog").close();
});
function onCancelClick(e) {
e.sender.close();
}
function onOkClick(e) {
debugger;
var checkedNodes = [];
var treeView = $("#treeview").data("kendoTreeView");
getCheckedNodes(treeView.dataSource.view(), checkedNodes);
populateMultiSelect(checkedNodes);
e.sender.close();
}
function onClose() {
$("#openWindow").fadeIn();
}
function populateMultiSelect(checkedNodes) {
var multiSelect = $("#multiselect").data("kendoMultiSelect");
multiSelect.dataSource.data([]);
var multiData = multiSelect.dataSource.data();
if (checkedNodes.length > 0) {
var array = multiSelect.value().slice();
for (var i = 0; i < checkedNodes.length; i++) {
multiData.push({ text: checkedNodes[i].text, id: checkedNodes[i].id });
array.push(checkedNodes[i].id.toString());
}
multiSelect.dataSource.data(multiData);
multiSelect.dataSource.filter({});
multiSelect.value(array);
}
}
function checkUncheckAllNodes(nodes, checked) {
debugger;
for (var i = 0; i < nodes.length; i++) {
nodes[i].set("checked", checked);
if (nodes[i].hasChildren) {
checkUncheckAllNodes(nodes[i].children.view(), checked);
}
}
}
function chbAllOnChange() {
debugger;
var checkedNodes = [];
var treeView = $("#treeview").data("kendoTreeView");
var isAllChecked = $('#chbAll').prop("checked");
checkUncheckAllNodes(treeView.dataSource.view(), isAllChecked)
if (isAllChecked) {
setMessage($('#treeview input[type="checkbox"]').length);
}
else {
setMessage(0);
}
}
function getCheckedNodes(nodes, checkedNodes) {
var node;
for (var i = 0; i < nodes.length; i++) {
node = nodes[i];
if (node.checked) {
checkedNodes.push({ text: node.text, id: node.id });
}
if (node.hasChildren) {
getCheckedNodes(node.children.view(), checkedNodes);
}
}
}
function onCheck() {
var checkedNodes = [];
var treeView = $("#treeview").data("kendoTreeView");
getCheckedNodes(treeView.dataSource.view(), checkedNodes);
setMessage(checkedNodes.length);
}
function setMessage(checkedNodes) {
var message;
if (checkedNodes > 0) {
message = "已选择:"+checkedNodes ;
}
else {
message = "未选择";
}
$("#result").html(message);
}
$("#filterText").keyup(function (e) {
var filterText = $(this).val();
if (filterText !== "") {
$(".selectAll").css("visibility", "hidden");
$("#treeview .k-group .k-group .k-in").closest("li").hide();
$("#treeview .k-group").closest("li").hide();
$("#treeview .k-group .k-group .k-in:contains(" + filterText + ")").each(function () {
$(this).parents("ul, li").each(function () {
$(this).show();
});
});
$("#treeview .k-group .k-in:contains(" + filterText + ")").each(function () {
$(this).parents("ul, li").each(function () {
$(this).show();
});
});
}
else {
$("#treeview .k-group").find("li").show();
$(".selectAll").css("visibility", "visible");
}
});
var removeUnit = function(text,id){
var multiSelect = $("#multiselect").data("kendoMultiSelect");
var multiData = multiSelect.dataSource.data();
var array = multiSelect.value().slice();
multiData.remove({ text: text ,id: id});
var index = array.indexOf(id.toString());
if (index > -1) {
array.splice(index, 1);
}
multiSelect.dataSource.data(multiData);
multiSelect.dataSource.filter({});
multiSelect.value(array);
debugger;
var nodes = $("#treeview").data("kendoTreeView").dataSource.view();
checkedToFalse(nodes,text,id);
debugger;
var checkedNodes = [];
getCheckedNodes(nodes, checkedNodes);
setMessage(checkedNodes.length);
$('#chbAll').prop("checked",false);
}
function dealfalse(nodes, checked) {
/* nodes.set("checked", checked);
if (nodes.hasChildren) {
dealfalse(nodes.children.view(), checked);
}*/
for (var i = 0; i < nodes.length; i++) {
nodes[i].set("checked", checked);
if (nodes[i].hasChildren) {
checkUncheckAllNodes(nodes[i].children.view(), checked);
}
}
}
function checkedToFalse(treeView,text,id){
var node;
for (var i = 0; i < treeView.length; i++) {
node = treeView[i];
if (node.text==text&&node.id==id) {
debugger;
var nodes=new Array();
nodes[0]=node;
dealfalse(nodes,false);
}
if (node.hasChildren) {
checkedToFalse(node.children.view(), text,id);
}
}
}
</script>
<style>
html .k-dialog .k-window-titlebar {
padding-left: 17px;
}
.k-dialog .k-content {
padding: 17px;
}
#filterText {
width: 100%;
box-sizing: border-box;
padding: 6px;
border-radius: 3px;
border: 1px solid #d9d9d9;
}
.selectAll {
margin: 17px 0;
}
#result {
color: #9ca3a6;
float: right;
}
#treeview {
height: 300px;
overflow-y: auto;
border: 1px solid #d9d9d9;
}
#openWindow {
min-width: 180px;
}
</style>
</div>
</body>
</html>
效果如下图:
注意:
kendoUi的版本是 v2017.1.223
此版本的可选择树形组件有点问题,就是在选择了之后回填数据之后没有X掉数据,下图KendoUI的demo
修复:打开kendo.all.min.js,搜索如下代码
<li class="k-button" deselectable="on"><span deselectable="on">'
如下图更改
var text = '\'' + o(e) +'\'';
var $kendoOutput,
$kendoHtmlEncode = kendo.htmlEncode;
$kendoOutput=''+$kendoHtmlEncode(e.id)+'';
onclick="removeUnit( ' + text + ',\''+$kendoOutput+'\')"
removeUnit在前端代码中有对应函数
补充:
上面的代码无法实现上图的级联移除,修复如下,更改removeUnit方法,添加如下注释的几个函数
<#include "../include/header.html">
<body>
<script src="${base.contextPath}/lib/kendoui/js/jquery.min.js"></script>
<script src="${base.contextPath}/lib/kendoui/js/jszip.min.js"></script>
<!--<script src="${base.contextPath}/resources/console.js"></script>-->
<script src="${base.contextPath}/resources/kendo.all.min.js"></script>
<link href="${base.contextPath}/resources/kendo.common.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/kendo.rtl.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/kendo.default.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/kendo.default.mobile.min.css" rel="stylesheet" type="text/css"/>
<link href="${base.contextPath}/resources/examples-offline.css" rel="stylesheet" type="text/css"/>
<div id="treeList">
<div class="demo-section k-content">
<div id="dialog">
<div class="dialogContent">
<input id="filterText" type="text" placeholder="请输入" />
<div class="selectAll">
<input type="checkbox" id="chbAll" class="k-checkbox" onchange="chbAllOnChange()" />
<label class="k-checkbox-label" for="chbAll">所有</label>
<span id="result">未选择</span>
</div>
<div id="treeview"></div>
</div>
</div>
<select id="multiselect"></select>
<br />
<button id="openWindow" class="k-primary">请选择</button>
</div>
<script>
var dataTree;
var myDataSource;
$.ajax({
type: "get",
url: "${base.contextPath}/lcp/sales/activity/demo/audit/line/getTreeUnit",
/* data: kendo.stringify(data), */
dataType: "json",
contentType: 'application/json',
success: function (datas) {
if(datas){
dataTree=datas;
myDataSource = new kendo.data.HierarchicalDataSource({
data: dataTree
});
$("#treeview").kendoTreeView({
checkboxes: {
checkChildren: true
},
dataSource: myDataSource,
check: onCheck
});
}
}
});
$("#multiselect").kendoMultiSelect({
dataTextField: "text",
dataValueField: "id"
});
$(document).ready(function () {
var dialog = $("#dialog");
var multiSelect = $("#multiselect").data("kendoMultiSelect");
$("#openWindow").kendoButton();
multiSelect.readonly(true);
$("#openWindow").click(function () {
dialog.data("kendoDialog").open();
$(this).fadeOut();
var nodes = $("#treeview").data("kendoTreeView").dataSource.view();
var checkedNodes = [];
getCheckedNodes(nodes, checkedNodes);
setMessage(checkedNodes.length);
});
dialog.kendoDialog({
width: "400px",
title: "组织架构",
visible: false,
actions: [
{
text: '取消',
primary: false,
action: onCancelClick
},
{
text: '确定',
primary: true,
action: onOkClick
}
],
close: onClose
}).data("kendoDialog").close();
});
function onCancelClick(e) {
e.sender.close();
}
function onOkClick(e) {
var checkedNodes = [];
var treeView = $("#treeview").data("kendoTreeView");
getCheckedNodes(treeView.dataSource.view(), checkedNodes);
populateMultiSelect(checkedNodes);
e.sender.close();
}
function onClose() {
$("#openWindow").fadeIn();
}
function populateMultiSelect(checkedNodes) {
var multiSelect = $("#multiselect").data("kendoMultiSelect");
multiSelect.dataSource.data([]);
var multiData = multiSelect.dataSource.data();
if (checkedNodes.length > 0) {
var array = multiSelect.value().slice();
for (var i = 0; i < checkedNodes.length; i++) {
multiData.push({ text: checkedNodes[i].text, id: checkedNodes[i].id });
array.push(checkedNodes[i].id.toString());
}
multiSelect.dataSource.data(multiData);
multiSelect.dataSource.filter({});
multiSelect.value(array);
}
}
function checkUncheckAllNodes(nodes, checked) {
for (var i = 0; i < nodes.length; i++) {
nodes[i].set("checked", checked);
if (nodes[i].hasChildren) {
checkUncheckAllNodes(nodes[i].children.view(), checked);
}
}
}
function chbAllOnChange() {
var checkedNodes = [];
var treeView = $("#treeview").data("kendoTreeView");
var isAllChecked = $('#chbAll').prop("checked");
checkUncheckAllNodes(treeView.dataSource.view(), isAllChecked)
if (isAllChecked) {
setMessage($('#treeview input[type="checkbox"]').length);
}
else {
setMessage(0);
}
}
function getCheckedNodes(nodes, checkedNodes) {
var node;
for (var i = 0; i < nodes.length; i++) {
node = nodes[i];
if (node.checked) {
checkedNodes.push({ text: node.text, id: node.id });
}
if (node.hasChildren) {
getCheckedNodes(node.children.view(), checkedNodes);
}
}
}
function onCheck() {
var checkedNodes = [];
var treeView = $("#treeview").data("kendoTreeView");
getCheckedNodes(treeView.dataSource.view(), checkedNodes);
setMessage(checkedNodes.length);
}
function setMessage(checkedNodes) {
var message;
if (checkedNodes > 0) {
message = "已选择:"+checkedNodes ;
}
else {
message = "未选择";
}
$("#result").html(message);
}
$("#filterText").keyup(function (e) {
var filterText = $(this).val();
if (filterText !== "") {
$(".selectAll").css("visibility", "hidden");
$("#treeview .k-group .k-group .k-in").closest("li").hide();
$("#treeview .k-group").closest("li").hide();
$("#treeview .k-group .k-group .k-in:contains(" + filterText + ")").each(function () {
$(this).parents("ul, li").each(function () {
$(this).show();
});
});
$("#treeview .k-group .k-in:contains(" + filterText + ")").each(function () {
$(this).parents("ul, li").each(function () {
$(this).show();
});
});
}
else {
$("#treeview .k-group").find("li").show();
$(".selectAll").css("visibility", "visible");
}
});
//移除树以及数量得更改
var removeUnit = function(text,id){
var nodes = $("#treeview").data("kendoTreeView").dataSource.view();
removeInput(text,id,nodes);
checkedToFalse(nodes,text,id);
var checkedNodes = [];
getCheckedNodes(nodes, checkedNodes);
setMessage(checkedNodes.length);
$('#chbAll').prop("checked",false);
}
//移除关联节点-1.如果存在子节点,则移除所有子节点在div中得显示2.如果是父节点得唯一一个被选中得节点,那么移除父节点
function removeInput(text,id,nodes){
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.text==text&&node.id==id) {
removeOne(node.text,node.id);
if (node.hasChildren) {
var childs=node.items;
removeNodes(childs);
}
} else{
if (node.hasChildren) {
var childs=node.items;
removeInput(text,id,childs);
}
}
}
}
function removeNodes(nodes){
for (var i = 0; i < nodes.length; i++) {
var node=nodes[i];
if (node.hasChildren) {
var childs=node.items;
removeOne(node.text,node.id);
removeNodes(childs);
}else{
removeOne(node.text,node.id);
}
}
}
//移除某个节点
function removeOne(text,id){
var multiSelect = $("#multiselect").data("kendoMultiSelect");
var multiData = multiSelect.dataSource.data();
var array = multiSelect.value().slice();
multiData.remove({ text: text ,id: id});
var index = array.indexOf(id.toString());
if (index > -1) {
array.splice(index, 1);
}
multiSelect.dataSource.data(multiData);
multiSelect.dataSource.filter({});
multiSelect.value(array);
var nodes = $("#treeview").data("kendoTreeView").dataSource.view();
removePar(text,id,nodes,"");
}
//获取-父节点等
function removePar(text,id,nodes,nodePar){
for (var i = 0; i < nodes.length; i++) {
var node=nodes[i];
if (node.text==text&&node.id==id) {
removeReal(nodePar,text,id);
}else{
if (node.hasChildren) {
var childs=node.items;
removePar(text,id,childs,node);
}
}
}
}
//判断-父节点-移除
function removeReal(nodePar,text,id){
if(""!=nodePar){
var nodes=nodePar.items;
var boolean=new Boolean(true);
for (var i = 0; i < nodes.length; i++) {
var node=nodes[i];
if (!(node.text==text&&node.id==id)) {
if(node.checked){
boolean=false;
}else{
boolean=true;
}
}
}
if(boolean){
removeOne(nodePar.text,nodePar.id)
}
}
}
function dealfalse(nodes, checked) {
for (var i = 0; i < nodes.length; i++) {
nodes[i].set("checked", checked);
if (nodes[i].hasChildren) {
checkUncheckAllNodes(nodes[i].children.view(), checked);
}
}
}
function checkedToFalse(treeView,text,id){
var node;
for (var i = 0; i < treeView.length; i++) {
node = treeView[i];
if (node.text==text&&node.id==id) {
var nodes=new Array();
nodes[0]=node;
dealfalse(nodes,false);
}
if (node.hasChildren) {
checkedToFalse(node.children.view(), text,id);
}
}
}
</script>
<style>
html .k-dialog .k-window-titlebar {
padding-left: 17px;
}
.k-dialog .k-content {
padding: 17px;
}
#filterText {
width: 100%;
box-sizing: border-box;
padding: 6px;
border-radius: 3px;
border: 1px solid #d9d9d9;
}
.selectAll {
margin: 17px 0;
}
#result {
color: #9ca3a6;
float: right;
}
#treeview {
height: 300px;
overflow-y: auto;
border: 1px solid #d9d9d9;
}
#openWindow {
min-width: 180px;
}
</style>
</div>
</body>
</html>