使用Vue-TreeSelect組件實現公司-部門-人員級聯下拉列表的處理

最近在改造原有Bootstrap開發框架,增加一個Vue&Element前端的時候,發現需要處理一個級聯更新的過程,就是選擇公司,然後更新部門,選擇部門,或者人員列表,選擇作為主管的一個實現,不同於Bootstrap使用Select2的插件,這裡前端是Vue&Element,那麼我們可以選擇下拉列表的方式展現,在Element中可以考慮使用Cascader 級聯選擇器,也可以考慮使用封裝Tree 樹形控制項,或者使用第三方組件Vue-TreeSelect組件。本篇隨筆介紹使用Vue-TreeSelect組件實現公司-部門-人員級聯下拉列表的處理過程。

1、Vue-TreeSelect組件的使用

在我早期隨筆《循序漸進VUE+Element 前端應用開發(8)— 樹列表組件的使用》中也大概介紹了一下Vue-TreeSelect組件,這裡我們再來回顧一下它的用法。

GitHub地址://github.com/riophae/vue-treeselect

官網地址://vue-treeselect.js.org/

NPM安裝:

npm install --save @riophae/vue-treeselect

介面程式碼如下所示。

<template>
  <div id="app">
    <treeselect v-model="value" :multiple="true" :options="options" />
  </div>
</template>

這裡的value就是選中的集合,options則是樹列表的節點數據,和Element中的Tree組件一樣,options的格式也包含id, lable, children這幾個屬性。

如果常規的數據提供,我們只要準備這些數據格式給options即可。

如下面的數據格式。

      treedata: [// 初始化樹列表
        { // 默認數據
          label: '一級 1',
          children: [{
            label: '二級 1-1'
          }]
        }
      ]

不過我們一般數據是動態從後端介面中提取的,不是靜態的,所以需要使用相應的方法來獲取,並設置。

如果是後端介面無法滿足特定的屬性名稱,那麼Vue-TreeSelect組件也提供了一個 normalizer 屬性方法用來重定義節點屬性名稱

 類似下面的javascript程式碼

export default {
  data: () => ({
    value: null,
    options: [ {
      key: 'a',
      name: 'a',
      subOptions: [ {
        key: 'aa',
        name: 'aa',
      } ],
    } ],
    normalizer(node) {
      return {
        id: node.key,
        label: node.name,
        children: node.subOptions,
      }
    },
  }),
}

通過normalizer 屬性方法可以把數據源的屬性映射到樹列表中去。有時候我們對於空列表,可能還需要判斷為空,並移除這個屬性,程式碼如下所示。

  normalizer (node) {
    if (node.children && !node.children.length) {
      delete node.children
    }
    return {
      id: node.key,
      label: node.name,
      children: node.children,
    }
  },

另外,有時候需要在列表值變化的時候,觸發級聯更新,那麼就需要處理@input事件了。

  <treeselect
    :options="options"
    :value="value"
    :searchable="false"
    @input="updateValue"
    />

 

2、公司-部門-人員級聯下拉列表的處理

綜合上面的幾個特點,我們公司-部門-人員級聯下拉列表的處理就需要上面的知識點來處理。

 在上面的彈出對話框中,選擇所屬公司,默認部門,所屬經理的操作,級聯處理過程效果如下所示。

 介面程式碼如下所示

  <el-col :span="12">
    <el-form-item label="所屬公司" prop="company_ID">
      <treeselect :options="myGroupCompany" v-model="addForm.company_ID" :searchable="false"
        :default-expand-level="Infinity" :open-on-click="true" :open-on-focus="true"
        @input="updateGroupCompany" placeholder="所屬公司" />
    </el-form-item>
  </el-col>
  <el-col :span="12">
    <el-form-item label="默認部門" prop="dept_ID">
      <treeselect :options="myDeptTree" v-model="addForm.dept_ID" :searchable="false"
        :default-expand-level="Infinity" :open-on-click="true" :open-on-focus="true" @input="updateDeptUser"
        :normalizer="normalizer" placeholder="所屬部門" />
    </el-form-item>
  </el-col>
  <el-col :span="12">
    <el-form-item label="所屬經理" prop="pid">
      <treeselect :options="myDeptUser" v-model="addForm.pid" :searchable="false"
        :default-expand-level="Infinity" :open-on-click="true" :open-on-focus="true"
        :normalizer="normalizer" placeholder="所屬經理" />
    </el-form-item>
  </el-col>

如第一項公司列表,我們獲取列表後設置options的對象即可。這裡面需要定義幾個變數 myGroupCompany、myDeptTree、myDeptUser的集合屬性。

這裡保留了normalizer 映射新屬性的做法,不過由於屬性名稱默認和樹控制項的屬性一致,也可以省略。

在其中更新處理,用到了 @input=”updateGroupCompany” 、@input=”updateDeptUser” 用於觸發更新其他關聯內容的事件。

另外一點,我們的新增或者編輯框中v-modal中關聯的值,需要設置為null即可。

  addForm: {// 新建表單
    id: '',
    pid: null,
    dept_ID: null,
    company_ID: null,
    ................
  },

在顯示彈出對話框,打開新增用戶的時候,需要觸發獲取公司資訊列表,如下所示。

    showAdd () {
      this.resetForm('addForm')
      this.initData() //打開新增窗體的時候,初始化公司列表
      this.isAdd = true
    },

而其中initData的函數操作如下所示。

    async initData () {
      var param = {}
      await ou.GetMyGroupCompany(param).then(data => {
        console.log(data.result)
        var newTreedata = getJsonTree(data.result, {
          id: 'id',
          pid: 'pid',
          children: 'children',
          label: 'name'
        });
        this.myGroupCompany = newTreedata
      })
    },

這裡調用ou的api進行獲取公司資訊的操作

import request from '@/utils/request'

import BaseApi from '@/api/base-api'
// 業務類自定義介面實現, 通用的介面已經在BaseApi中定義
class Api extends BaseApi {
  // 獲取集團公司列表。如果是超級管理員,返回集團+公司節點;如果是公司管理員,返回其公司節點
  GetMyGroupCompany(data) {
    return request({
      url: this.baseurl + 'GetMyGroupCompany',
      method: 'get',
      params: data
    })
  }    
  ..........
}

而公司資訊觸發部門更新,我們用如下函數來處理變化。

    async updateGroupCompany (value, instanceId) {
      // console.log(value + '~' + instanceId)

      this.addForm.dept_ID = null //置空控制項內容
      if (!this.isEmpty(value)) {
        var param = { parentId: value }
        await user.GetDeptJsTreeJson(param).then(data => {
          this.myDeptTree = data.result
        })
      }
    },

由於User的API中 GetDeptJsTreeJson返回的是符合樹控制項節點屬性名稱的,因此可以直接賦值給vue-TreeSelect的opition值。

<treeselect :options="myDeptTree" v-model="addForm.dept_ID" :searchable="false"
   :default-expand-level="Infinity" :open-on-click="true" :open-on-focus="true" @input="updateDeptUser"
   :normalizer="normalizer" placeholder="所屬部門" />

而部門選擇後,則觸發部門用戶列表的更新,如下程式碼所示。

    async updateDeptUser (value, instanceId) {
      // console.log(value + '~' + instanceId)
      this.addForm.pid = null //置空控制項內容
      if (!this.isEmpty(value)) {
        var param = { deptId: value }
        await user.GetUserDictJson(param).then(data => {
          this.myDeptUser = data.result
        })
      }
    },

同樣,由於由於User的API中 GetUserDictJson 返回的是符合樹控制項節點屬性名稱的,因此可以直接賦值給vue-TreeSelect的opition值。

<treeselect :options="myDeptUser" v-model="addForm.pid" :searchable="false"
   :default-expand-level="Infinity" :open-on-click="true" :open-on-focus="true"
   :normalizer="normalizer" placeholder="所屬經理" />

3、特殊處理的內容

 前面我們介紹了,如果獲取內容和樹控制項的屬性不一致,需要進行轉義映射,如下程式碼所示。

      normalizer (node) {
        if (node.children && !node.children.length) {
          delete node.children
        }
        return {
          id: node.id,
          label: node.label,
          children: node.children,
        }
      },

並在介面程式碼上指定normalizer處理。

:normalizer="normalizer"

有時候,我們返回的對象集合可能是一個二維列表內容,它本身有id,pid來標識它的層次關係,那麼如果我們轉換為嵌套列表的話,就可以使用getJsonTree 方法進行轉換。

具體操作可以參考://blog.csdn.net/unamattin/article/details/77152451 

使用的時候,導入這個類方法即可。

import { getJsonTree } from '@/utils/json-tree.js' // 轉換二維表數據為樹列表數據的輔助類

如果前面介紹的

    async initData () {
      var param = {}
      await ou.GetMyGroupCompany(param).then(data => {
        console.log(data.result)
        var newTreedata = getJsonTree(data.result, {
          id: 'id',
          pid: 'pid',
          children: 'children',
          label: 'name'
        });
        this.myGroupCompany = newTreedata
      })
    },

如果兩個都是嵌套結構的樹列表,但是屬性名稱不同,那麼也可以通過map的操作方法,定義一個js函數進行轉換即可,轉換的程式碼如下所示。

    getTree () { // 樹列表數據獲取
      var param = {}
      user.GetMyDeptJsTreeJson(param).then(data => {
        // console.log(data)
        this.treedata = [];// 樹列表清空
        var list = data.result
        if (list) {
          this.treedata = list
        }

        //修改另一個Treedata
        const ass = (data) => {
          let item = [];
          data.map((list, i) => {
            let newData = {};
            newData.id = list.id;
            newData.label = list.label;
            newData.children = list.children ? ass(list.children) : null;    //如果還有子集,就再次調用自己
            //如果列表為空,則移除children
            if (list.children && !list.children.length) {
              delete newData.children;
            }
            item.push(newData);
          });
          return item;
        }
        this.selectTreeData = ass(list)
      });
    },

以上就是數據層次結構相同,屬性名稱不同的時候,進行轉換處理的另外一種方式。

當然,我們定義返回列表數據的時候,如果需要用來綁定在樹列表中的,也可以在後端WebAPI進行定義好符合格式的數據,避免在前端額外的程式碼轉換。

        /// <summary>
        /// 根據用戶獲取對應人員層次(給樹控制項顯示的下拉列表)(值為ID)
        /// </summary>
        /// <param name="deptId">用戶所在部門</param>
        /// <returns></returns>
        public List<TreeNodeItem> GetUserDictJson(int deptId)
        {
            var itemList = new List<TreeNodeItem>();
            itemList.Insert(0, new TreeNodeItem("-1", ""));

            var list = BLLFactory<User>.Instance.FindByDept(deptId);
            foreach (var info in list)
            {
                itemList.Add(new TreeNodeItem(info.ID, info.FullName));
            }

            return itemList;
        }

其中 TreeNodeItem 類定義了Id, Label,Children的屬性,這樣前端就可以直接綁定使用了。

 

 以上就是前後端樹列表的綁定處理,以及使用Vue-TreeSelect組件實現公司-部門-人員級聯下拉列表的功能操作,希望大家不吝賜教。