使用Vue-TreeSelect組件實現公司-部門-人員級聯下拉列表的處理
- 2021 年 4 月 20 日
- 筆記
- 循序漸進VUE+Element
最近在改造原有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
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組件實現公司-部門-人員級聯下拉列表的功能操作,希望大家不吝賜教。