首先在项目下新建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>();

最终执行这个方法后得到的数据为