最近在改造原有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>
  <divid="app">
    <treeselectv-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-itemlabel="所属公司"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-itemlabel="默认部门"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-itemlabel="所属经理"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) {returnrequest({
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) {deletenode.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.resultif(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) {deletenewData.children;
}
item.push(newData);
});
returnitem;
}
this.selectTreeData =ass(list)
});
},

以上就是数据层次结构相同,属性名称不同的时候,进行转换处理的另外一种方式。

当然,我们定义返回列表数据的时候,如果需要用来绑定在树列表中的,也可以在后端WebAPI进行定义好符合格式的数据,避免在前端额外的代码转换。

        /// <summary>
        ///根据用户获取对应人员层次(给树控件显示的下拉列表)(值为ID)/// </summary>
        /// <param name="deptId">用户所在部门</param>
        /// <returns></returns>
        public List<TreeNodeItem> GetUserDictJson(intdeptId)
{
var itemList = new List<TreeNodeItem>();
itemList.Insert(
0, new TreeNodeItem("-1", ""));var list = BLLFactory<User>.Instance.FindByDept(deptId);foreach (var info inlist)
{
itemList.Add(
newTreeNodeItem(info.ID, info.FullName));
}
returnitemList;
}

其中 TreeNodeItem 类定义了Id, Label,Children的属性,这样前端就可以直接绑定使用了。

另外,在提一下,使用Vue-TreeSelect组件的时候,有时候需要封装它为自定义组件,那么normalizer也会作为prop属性作为配置的,这个时候,可以在自定义组件中定义好默认的normalizer。具体代码如下所示。

<template>
  <div>
    <divclass="flex-container">
      <divclass="flex-item">
        <treeselectref="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>
      <divv-if="showcheck"class="flex-item">
        <el-checkboxv-model="isTop":label="checkboxLable"border @change="checkChange" />
      </div>
    </div>
  </div>
</template>

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

使用这个自定义组件的时候,可以指定它的normalizer。

<MyTreeselectTopv-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变量方式解决弹出编辑对话框界面无法触发更新的问题

标签: none

添加新评论