使用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地址:https://github.com/riophae/vue-treeselect

官網地址:https://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 方法進行轉換。

具體操作可以參考:https://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組件的時候,有時候需要封裝它為自定義組件,那么normalizer也會作為prop屬性作為配置的,這個時候,可以在自定義組件中定義好默認的normalizer。具體代碼如下所示。

<template>
  <div>
    <div class="flex-container">
      <div class="flex-item">
        <treeselect ref="tree" v-model="svalue" :disabled="disabled" :options="options" :multiple="false" :flat="false"
          :default-expand-level="Infinity" :open-on-click="true" :open-on-focus="true" clearable :max-height="200"
          :placeholder="placeholder" :normalizer="normalizer" />
      </div>
      <div v-if="showcheck" class="flex-item">
        <el-checkbox v-model="isTop" :label="checkboxLable" border @change="checkChange" />
      </div>
    </div>
  </div>
</template>

那么prop中的normalizer的定義如下所示。

使用這個自定義組件的時候,可以指定它的normalizer。

<MyTreeselectTop v-model="editForm.pid" :options="selectTreeData" :normalizer="normalizer" />

 

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

把Bootstrap框架界面改造為Vue+Element前端界面后,

頁面列表效果如下所示。

 編輯界面效果如下所示。

 

為了方便讀者理解,我列出一下前面幾篇隨筆的連接,供參考:

循序漸進VUE+Element 前端應用開發(1)--- 開發環境的准備工作

循序漸進VUE+Element 前端應用開發(2)--- Vuex中的API、Store和View的使用

循序漸進VUE+Element 前端應用開發(3)--- 動態菜單和路由的關聯處理

循序漸進VUE+Element 前端應用開發(4)--- 獲取后端數據及產品信息頁面的處理

循序漸進VUE+Element 前端應用開發(5)--- 表格列表頁面的查詢,列表展示和字段轉義處理

循序漸進VUE+Element 前端應用開發(6)--- 常規Element 界面組件的使用

循序漸進VUE+Element 前端應用開發(7)--- 介紹一些常規的JS處理函數

循序漸進VUE+Element 前端應用開發(8)--- 樹列表組件的使用

循序漸進VUE+Element 前端應用開發(9)--- 界面語言國際化的處理

循序漸進VUE+Element 前端應用開發(10)--- 基於vue-echarts處理各種圖表展示 

循序漸進VUE+Element 前端應用開發(11)--- 圖標的維護和使用

循序漸進VUE+Element 前端應用開發(12)--- 整合ABP框架的前端登錄處理

循序漸進VUE+Element 前端應用開發(13)--- 前端API接口的封裝處理

循序漸進VUE+Element 前端應用開發(14)--- 根據ABP后端接口實現前端界面展示

循序漸進VUE+Element 前端應用開發(15)--- 用戶管理模塊的處理

循序漸進VUE+Element 前端應用開發(16)--- 組織機構和角色管理模塊的處理 

循序漸進VUE+Element 前端應用開發(17)--- 菜單管理

循序漸進VUE+Element 前端應用開發(18)--- 功能點管理及權限控制  

VUE+Element 前端應用開發框架功能介紹 

循序漸進VUE+Element 前端應用開發(19)--- 后端查詢接口和Vue前端的整合

使用代碼生成工具快速生成基於ABP框架的Vue+Element的前端界面

循序漸進VUE+Element 前端應用開發(20)--- 使用組件封裝簡化界面代碼

循序漸進VUE+Element 前端應用開發(21)--- 省市區縣聯動處理的組件使用

循序漸進VUE+Element 前端應用開發(22)--- 簡化main.js處理代碼,抽取過濾器、全局界面函數、組件注冊等處理邏輯到不同的文件中

循序漸進VUE+Element 前端應用開發(23)--- 基於ABP實現前后端的附件上傳,圖片或者附件展示管理 

循序漸進VUE+Element 前端應用開發(24)--- 修改密碼的前端界面和ABP后端設置處理

循序漸進VUE+Element 前端應用開發(25)--- 各種界面組件的使用(1)

循序漸進VUE+Element 前端應用開發(26)--- 各種界面組件的使用(2)

電商商品數據庫的設計和功能界面的處理 

循序漸進VUE+Element 前端應用開發(27)--- 數據表的動態表單設計和數據存儲

循序漸進VUE+Element 前端應用開發(28)--- 附件內容的管理 

循序漸進VUE+Element 前端應用開發(29)--- 高級查詢條件的界面設計

部署基於.netcore5.0的ABP框架后台Api服務端,以及使用Nginx部署Vue+Element前端應用

循序漸進VUE+Element 前端應用開發(30)--- ABP后端和Vue+Element前端結合的分頁排序處理 

循序漸進VUE+Element 前端應用開發(31)--- 系統的日志管理,包括登錄日志、接口訪問日志、實體變化歷史日志

循序漸進VUE+Element 前端應用開發(32)--- 手機短信動態碼登陸處理 

循序漸進VUE+Element 前端應用開發(33)--- 郵件參數配置和模板郵件發送處理 

使用代碼生成工具快速開發ABP框架項目 

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

使用Vue-TreeSelect組件的時候,用watch變量方式解決彈出編輯對話框界面無法觸發更新的問題 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM