1. 程式人生 > 實用技巧 >ElementUI中el-tree控制元件封裝公共控制元件(部門列表)並請求後臺資料獲取父子級資料並構建成前端資料結構資料

ElementUI中el-tree控制元件封裝公共控制元件(部門列表)並請求後臺資料獲取父子級資料並構建成前端資料結構資料

場景

在人員管理系統中,有不少頁面需要選擇目前的部門樹形結構中的部門進而作為篩選條件進行查詢。

怎樣藉助ElementUI的el-tree控制元件封裝成公共控制元件並請求SpringBoot後臺資料獲取部門資料並封裝成前端需要的樹形結構資料。

注:

部落格:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程式猿
獲取程式設計相關電子書、教程推送與免費下載。

實現

前端實現

首先在專案下新建components目錄存放公共元件,在目錄下新建LeftCheckTree目錄,並在此目錄下新建index.vue用來實現公共部門樹元件。

在頁面上需要一個模糊搜尋的輸入框和el-tree控制元件

<template>
  <div>
  <div class="head-container">
    <el-input
      v-model="deptName"
      placeholder="請輸入部門名稱"
      clearable
      size="small"
      prefix-icon="el-icon-search"
      style="margin-bottom: 20px"
    />
  </div>
  <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" /> </div> </div> </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>
  <div class="head-container">
    <el-input
      v-model="deptName"
      placeholder="請輸入部門名稱"
      clearable
      size="small"
      prefix-icon="el-icon-search"
      style="margin-bottom: 20px"
    />
  </div>
  <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"
    />
  </div>
  </div>
</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>
             </if>
        </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>
  <if test="deptName != null anddeptName != ''">
   AND dept_name like concat('%', #{deptName}, '%')
  </if>
  <if test="status != null andstatus != ''">
   AND status = #{status}
  </if>
  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>();

最終執行這個方法後得到的資料為