首先在项目下新建components目录存放公共组件,在目录下新建LeftCheckTree目录,并在此目录下新建index.vue用来实现公共部门树组件。
在页面上需要一个模糊搜索的输入框和el-tree控件
<template>
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
<div class="head-container">
<el-tree
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
default-expand-all
show-checkbox
@check="handleCheck"
</template>
然后需要引入一些样式组件和方法等
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { treeselect } from "@/api/system/dept";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
这里引用的 import { treeselect } from
"@/api/system/dept";是获取部门数据的方法
所以在api/system/dept.js中的treeselect方法中会请求后台查询部门数据
// 查询部门下拉树结构
export function treeselect() {
return request({
url: '/system/dept/treeselect',
method: 'get'
这里省略封装axios请求的过程,请求后台数据部门下面介绍。
为了实现在此页面一加载完就查询部门数据,在created函数中执行请求数据的方法
created() {
this.getTreeselect();
methods: {
/** 查询部门下拉树结构 */
getTreeselect() {
treeselect().then(response => {
this.deptOptions = response.data;
请求后台获取的数据赋值给部门树选项数组,此数组需要提前声明
data() {
return {
// 部门树选项
deptOptions: [],
然后通过 :data="deptOptions"将数据绑定给el-tree控件。
控件还添加了filter-node-method对树节点进行筛选时执行的方法,返回true表示这个节点可以显示,返回false则表示这个节点会被隐藏。
:filter-node-method="filterNode"
filterNode是个函数
// 筛选节点
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
此组件完整示例代码
<template>
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
<div class="head-container">
<el-tree
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
default-expand-all
show-checkbox
@check="handleCheck"
</template>
<script>
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { treeselect } from "@/api/system/dept";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "leftCheckTree",
components: { Treeselect },
props: {},
data() {
return {
// 部门名称
deptName: undefined,
defaultProps: {
children: "children",
label: "label"
// 部门树选项
deptOptions: [],
watch: {
// 根据名称筛选部门树
deptName(val) {
this.$refs.tree.filter(val);
created() {
this.getTreeselect();
methods: {
/** 查询部门下拉树结构 */
getTreeselect() {
treeselect().then(response => {
this.deptOptions = response.data;
// 筛选节点
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
//多选
handleCheck(data, checked){
let deptIdList = [];
for(let i = 0;i<checked.checkedNodes.length;i++){
if(!checked.checkedNodes[i].children){
deptIdList.push(checked.checkedNodes[i].id)
this.$emit('handleCheck', deptIdList)
</script>
<style lang="scss" scoped>
</style>
export default {
name: "leftCheckTree",
就可以将此组件暴露并且名字为leftCheckTree
那么我们在需要的页面就可以引用这个组件了。
首先在页面中添加组件
<template>
<div class="app-container">
<el-row :gutter="20">
<!--部门数据-->
<el-col :span="4" :xs="24">
<left-check-tree @handleCheck="handleCheck"></left-check-tree>
</el-col>
在此页面我们需要获取到选中的部门的id的数组。
在上面的树组件中的
this.$emit('handleCheck', deptIdList)
就是实现子组件向父组件传值,名字叫handleCheck,值时deptList即多选时选中的部门id,即多选选中的节点的部门id属性。
那么在父页面即引用这个数组件的页面中就可以通过@handleCheck="handleCheck"
并且在handleCheck方法中
handleCheck(deptIdList) {
this.queryParams.bmids = deptIdList;
console.log(this.queryParams.bmids);
获取到选中的部门id的数组并且将其赋值给父页面即调用树控件页面的对象的数组属性,即查询参数的部门id数组属性
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
bmids: [],
这样就能获取到要查询那几个部门的数据的部门id的数组。
在对应SpringBoot后台接口中,使用Post接受请求参数,因为部门数组的查询参数使用get请求的话会有长度限制。
@PostMapping("/selectListBySx")
public TableDataInfo selectKqKqryszList(@RequestBody() KqKqrysz kqKqrysz)
List<KqKqrysz> list = kqKqryszService.selectKqKqryszListBySx(kqKqrysz);
return getDataTable(list);
接受参数的实体类中需要添加一个
/****
* 部门id数组传参用
private Long[] bmids;
部门id的数组属性以及get和set方法。
在接受到参数后一直传递到mapper层对应的xml里面
<!--根据筛选条件查询-->
<select id="selectListBySx" parameterType="KqKqrysz" resultMap="KqKqryszResult">
SELECT *
from table1
<where>
<if test="bmids != null and bmids.length >0">
and j.bmid in
<foreach collection="bmids" item="item" open="(" separator="," close=")">
${item}
</foreach>
</where>
</select>
这样就可以查询部门id是不是包含在传递的参数数组中的数据。
对应的部门数据库的设计
主要是要有部门id和父级部门id和部门名称这几个字段,通过父级id就能构建出父子级的关系。
比如可以这样添加数据
第一个测试部门的父级部门是0,则代表它是顶级部门,下面的父级部门的id是上面的顶级部门的id,所以这样就能构造出父子级部门的关系。
然后再说怎样将后台父子级的数据构造成前端需要的树控件的数据。
前面在封装公共控件时
请求后台数据对应的接口
* 获取部门下拉树列表
@GetMapping("/treeselect")
public AjaxResult treeselect(SysDept dept)
List<SysDept> depts = deptService.selectDeptList(dept);
return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
首先是查询出数据库中存储的所有的部门数据deptService.selectDeptList(dept);
字段信息和上面设计数据库时对应。
然后将其构建成前端需要的数据源的形式通过buildDeptTreeSelect。
首先是请求数据,在对应的mapper层
<select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult">
<include refid="selectDeptVo"/>
where d.del_flag = '0'
<if test="parentId != null and parentId != 0">
AND parent_id = #{parentId}
<if test="deptName != null anddeptName != ''">
AND dept_name like concat('%', #{deptName}, '%')
<if test="status != null andstatus != ''">
AND status = #{status}
order by d.parent_id, d.order_num
</select>
最终将数据库中的数据以父级id和排序号排序查询出来。查询出数据库中所有的对象的list
public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts)
List<SysDept> deptTrees = buildDeptTree(depts);
return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
又调用了buildDeptTree方法,最终是要构建成父级节点要有children这个属性,即符合el-tree赋值的标准。
el-tree官方示例赋值代码
<el-tree
:data="data"
show-checkbox
node-key="id"
:default-expanded-keys="[2, 3]"
:default-checked-keys="[5]">
</el-tree>
<script>
export default {
data() {
return {
data: [{
id: 1,
label: '一级 2',
children: [{
id: 3,
label: '二级 2-1',
children: [{
id: 4,
label: '三级 3-1-1'
id: 5,
label: '三级 3-1-2',
disabled: true
id: 2,
label: '二级 2-2',
disabled: true,
children: [{
id: 6,
label: '三级 3-2-1'
id: 7,
label: '三级 3-2-2',
disabled: true
defaultProps: {
children: 'children',
label: 'label'
</script>
所在在上面的buildDeptTree方法中
* 构建前端所需要树结构
* @param depts 部门列表
* @return 树结构列表
@Override
public List<SysDept> buildDeptTree(List<SysDept> depts)
List<SysDept> returnList = new ArrayList<SysDept>();
List<Long> tempList = new ArrayList<Long>();
for (SysDept dept : depts)
tempList.add(dept.getDeptId());
for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext();)
SysDept dept = (SysDept) iterator.next();
// 如果是顶级节点, 遍历该父节点的所有子节点
if (!tempList.contains(dept.getParentId()))
recursionFn(depts, dept);
returnList.add(dept);
if (returnList.isEmpty())
returnList = depts;
return returnList;
这其中有用到了递归函数recursionFn
* 递归列表
private void recursionFn(List<SysDept> list, SysDept t)
// 得到子节点列表
List<SysDept> childList = getChildList(list, t);
t.setChildren(childList);
for (SysDept tChild : childList)
if (hasChild(list, tChild))
// 判断是否有子节点
Iterator<SysDept> it = childList.iterator();
while (it.hasNext())
SysDept n = (SysDept) it.next();
recursionFn(list, n);
注意为了构建每个节点的children属性,所以在SysDept这个实体类中要比数据库多一个children属性,并且是一个list
/** 部门ID */
private Long deptId;
/** 父部门ID */
private Long parentId;
/** 祖级列表 */
private String ancestors;
/** 部门名称 */
private String deptName;
/** 显示顺序 */
private String orderNum;
/** 负责人 */
private String leader;
/** 联系电话 */
private String phone;
/** 邮箱 */
private String email;
/** 部门状态:0正常,1停用 */
private String status;
/** 删除标志(0代表存在 2代表删除) */
private String delFlag;
/** 父部门名称 */
private String parentName;
/** 子部门 */
private List<SysDept> children = new ArrayList<SysDept>();
最终执行这个方法后得到的数据为