最近编写了一个vue组件,基于elementUi的用户树弹层组件。其中用到了el-select、el-tree等组件。弹层内部支持基于工号和姓名的搜索。
弹层展示:
使用页面:
模拟接口数据:
[
[
{
"id": null,
"deptId": "1",
"pDeptId": "0",
"layer": 1,
"jgdm": "1000",
"deptName": "测试医院(389)",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": "",
"children": [
{
"id": null,
"deptId": "2",
"pDeptId": "1",
"layer": 2,
"jgdm": "1000",
"deptName": "职能科室(3)",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": "",
"children": [
{
"id": null,
"deptId": "70",
"pDeptId": "2",
"layer": 3,
"jgdm": "1000",
"deptName": "护理部(3)",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": "1,5",
"children": [
{
"id": null,
"deptId": "70-1",
"pDeptId": "70",
"layer": null,
"jgdm": null,
"deptName": "医生",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": null,
"children": [
{
"id": null,
"deptId": "70-1-0095",
"pDeptId": "70-1",
"layer": null,
"jgdm": null,
"deptName": "邓玉霞",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": null,
"children": [],
"treeType": 2,
"userId": "1bbb3f69f22e44f0a89a37c40132005f",
"account": "0095",
"userName": null,
"deptIdWithName": "护理部-70",
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": null,
"isHaveDeptNum": null,
"userTreeUserId": "1bbb3f69f22e44f0a89a37c40132005f",
"parentId": "70-1",
"curId": "70-1-0095"
}
],
"treeType": 2,
"userId": null,
"account": null,
"userName": null,
"deptIdWithName": null,
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": null,
"isHaveDeptNum": null,
"userTreeUserId": "70-1",
"parentId": "70",
"curId": "70-1"
},
{
"id": null,
"deptId": "70-5",
"pDeptId": "70",
"layer": null,
"jgdm": null,
"deptName": "护理部主任",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": null,
"children": [
{
"id": null,
"deptId": "70-5-0208",
"pDeptId": "70-5",
"layer": null,
"jgdm": null,
"deptName": "杨飞",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": null,
"children": [],
"treeType": 2,
"userId": "37bdd63a761641b484b5a9696c537667",
"account": "0208",
"userName": null,
"deptIdWithName": "护理部-70",
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": null,
"isHaveDeptNum": null,
"userTreeUserId": "37bdd63a761641b484b5a9696c537667",
"parentId": "70-5",
"curId": "70-5-0208"
},
{
"id": null,
"deptId": "70-5-0376",
"pDeptId": "70-5",
"layer": null,
"jgdm": null,
"deptName": "肖阳",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": null,
"children": [],
"treeType": 2,
"userId": "b68529af024146e9a518ca60e2c70265",
"account": "0376",
"userName": null,
"deptIdWithName": "护理部-70",
"isClick": null,
"defineDept": 1,
"techOrderId": null,
"deptOrderId": null,
"isHaveDeptNum": null,
"userTreeUserId": "b68529af024146e9a518ca60e2c70265",
"parentId": "70-5",
"curId": "70-5-0376"
}
],
"treeType": 2,
"userId": null,
"account": null,
"userName": null,
"deptIdWithName": null,
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": null,
"isHaveDeptNum": null,
"userTreeUserId": "70-5",
"parentId": "70",
"curId": "70-5"
}
],
"treeType": 1,
"userId": null,
"account": null,
"userName": null,
"deptIdWithName": null,
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": 200,
"isHaveDeptNum": true,
"userTreeUserId": "70",
"parentId": "2",
"curId": "70"
}
],
"treeType": 1,
"userId": null,
"account": null,
"userName": null,
"deptIdWithName": null,
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": 134,
"isHaveDeptNum": null,
"userTreeUserId": "2",
"parentId": "1",
"curId": "2"
}
],
"treeType": 1,
"userId": null,
"account": null,
"userName": null,
"deptIdWithName": null,
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": 0,
"isHaveDeptNum": null,
"userTreeUserId": "1",
"parentId": "0",
"curId": "1"
},
{
"id": null,
"deptId": "98",
"pDeptId": "0",
"layer": 1,
"jgdm": "1000",
"deptName": "16病区",
"orderId": null,
"deleteBit": null,
"deptType": null,
"checked": null,
"jobIds": "",
"children": [],
"treeType": 1,
"userId": null,
"account": null,
"userName": null,
"deptIdWithName": null,
"isClick": null,
"defineDept": null,
"techOrderId": null,
"deptOrderId": 245,
"isHaveDeptNum": null,
"userTreeUserId": "98",
"parentId": "0",
"curId": "98"
}
]
]组件代码:
<template>
<div>
<el-select
v-if="userSelectShow"
ref="userSelect"
v-model="dropDownCheckedUserIdArray"
:clearable="clearable"
:style="{width:width+' !important'}"
:multiple="true"
:placeholder="placeholder"
popper-class="customSelect"
@clear="userClear"
@remove-tag="dropDownRemoveTag"
@click.native="userSelectClick"
>
<el-option v-for="(item,index) in userSelectList" :key="index" :label="item.userName" :value="item.userId" />
</el-select>
<div v-if="userTreeShow" class="custom-modal" />
<el-dialog
v-if="userTreeShow"
:modal="false"
:close-on-click-modal="false"
:title="title"
append-to-body
:visible="userTreeShow"
width="500px"
center
@close="userTreeClose"
>
<div class="searchClass">
<el-input v-model="search" placeholder="请输入工号或者姓名" @change="searchChange()">
<i slot="suffix" class="el-input__icon el-icon-search" @click="searchChange()" />
</el-input>
</div>
<div class="d-flex a-center flex-nowrap userTreeSelect">
<el-scrollbar class="scrollbar">
<el-tree
ref="userTree"
v-loading="userTreeLoading"
:default-checked-keys="userDefaultChecked"
:check-strictly="checkStrictly"
:check-on-click-node="checkOnClickNode"
:default-expand-all="userDefaultChecked.length> 0 ? false : true"
:default-expanded-keys="userDefaultChecked"
:data="userData"
:props="defaultProps"
node-key="userTreeUserId"
show-checkbox
:filter-node-method="filterNode"
@check="getCheckedNodes"
@node-click="userNodeClick"
/>
</el-scrollbar>
</div>
<div slot="footer">
<el-button @click.native.prevent="confirmFun">确定</el-button>
<el-button @click.native.prevent="cancelFun">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getOrgPersonTree } from '@/api/power'
export default {
name: 'UserTreeSelect',
props: {
value: { // 用户id值
type: String,
default: ''
},
names: { // 用户名称值
type: String,
default: ''
},
deptId: { // 科室id
type: String,
default: ''
},
checkedLimitCount: {// 选择限制数量 0表示不限制
type: Number,
default: 0
},
title: { // 弹窗标题
type: String,
default: '选择用户'
},
checkOnClickNode: { // 点击节点是否选中复选框
type: Boolean,
default: false
},
clearable: { // 是否支持清除
type: Boolean,
default: false
},
width: { // 下拉框宽度
type: String,
default: '100%'
},
placeholder: { // 下拉框提示信息
type: String,
default: '请选择用户'
}
},
data() {
return {
userTreeLoading: false,
modal: true,
defaultProps: {
children: 'children',
label: 'deptName'
},
checkStrictly: false, // 父子结构不相关联
userTreeShow: false,
userSelectShow: true,
dropDownCheckedUserIdArray: [], // 下拉框中的已选用户id
selectUserMap: {}, // 已选用户的map
search: '',
userSelectList: [], // 用户下拉框数据
userDefaultChecked: [], // 默认选择中的用户id
userData: [] // 用户数据
}
},
watch: {
/**
* 监听值变化
*/
value: {
handler(val) {
// 需要先隐藏下拉框,再显示,否则当数据量较大时会出现卡顿情况
this.userSelectShow = false
// 置空下拉框数据
this.userSelectList = []
// 置空数据
// this.dropDownCheckedUserIdArray = []
// 已选用户id数组
let userIdArray = []
// 已选用户名称数组
let userNameArray = []
// 转换字符串为数组
if (val && val.length) {
userIdArray = val.split(',')
}
// 转换字符串为数组
if (this.names && this.names.length) {
userNameArray = this.names.split(',')
}
// 如果长度一致
if (userIdArray.length === userNameArray.length) {
// 临时下拉框数据
const tempUserSelectList = []
// 遍历数据生成已选用户列表
userIdArray.forEach((item, index) => {
// 创建用户对象
const userObj = {}
userObj.userId = item
userObj.userName = userNameArray[index]
// 添加到用户数组中
tempUserSelectList.push(userObj)
})
this.$nextTick(() => {
// 设置选中用户id
this.dropDownCheckedUserIdArray = userIdArray
// 赋值
this.userSelectList = tempUserSelectList
// 设置用户树默认选中值
this.userDefaultChecked = userIdArray
this.userSelectShow = true
})
}
},
immediate: true, // 立即触发一次
deep: true // 可以深度检测到 对象的属性值的变化
}
},
mounted() {
},
methods: {
userSelectClick() {
this.userTreeShow = true
const params = {}
if (this.deptId) {
params.deptId = this.deptId
}
if (!Object.keys(this.userData).length) {
// 开启加载树loading
this.userTreeLoading = true
getOrgPersonTree(params).then(res => {
if (res.data && res.data.data && res.data.data.deptTree && res.data.data.deptTree.length && res.data.data.deptTree[0]) {
this.userData = res.data.data.deptTree[0]
}
// 关闭loading
this.userTreeLoading = false
}).catch(e => {
this.userTreeLoading = false
})
}
},
userTreeClose() {
this.userTreeShow = false
},
getCheckedNodes() {
},
userNodeClick(data, node) {
},
userClear() {
this.userSelectShow = false
// 重置数据
this.userDefaultChecked = []
this.dropDownCheckedUserIdArray = []
this.userSelectList = []
// 调用修改v-model对应值方法
this.$emit('input', '')
// 修改names属性
this.$emit('update:names', '')
// 设置change方法
this.$emit('change', '')
this.userSelectShow = true
},
/**
* 监听下拉框删除标签
*/
dropDownRemoveTag(value) {
// 获取已选标签 在默认选中数组中的下标
const tagIndex = this.userDefaultChecked.indexOf(value)
// 删除该下标
this.userDefaultChecked.splice(tagIndex, 1)
// 返回值
let returnValue = ''
// 返回名称
let returnName = ''
// 传入值和名称不为空
if (this.value && this.names) {
// 转换用户id数组
const valueArray = this.value.split(',')
// 转换用户名称数组
const nameArray = this.names.split(',')
// 获取当前值在value数组中的下标
const valueIndex = valueArray.indexOf(value)
// 删除该下标
valueArray.splice(valueIndex, 1)
nameArray.splice(valueIndex, 1)
// 转换值
returnValue = valueArray.join(',')
returnName = nameArray.join(',')
}
// 调用修改v-model对应值方法
this.$emit('input', returnValue)
// 修改names属性
this.$emit('update:names', returnName)
// 设置change方法
this.$emit('change', returnValue)
},
/**
*@param value 当前值
* @param data 当前数据
* @param node 当前节点
*/
filterNode(value, data, node) {
// 如果当前数据中的姓名 或者 工号包含当前输入的值 则显示数据
if (data && ((data.deptName && data.deptName.indexOf(value) !== -1) || (data.account && data.account.indexOf(value) !== -1))) {
// 显示
return true
} else {
// 否则不显示
return false
}
},
searchChange() {
this.$refs.userTree.filter(this.search.trim())
},
// 确认按钮方法
confirmFun() {
// 获取选择节点
let checkedKeys = this.$refs.userTree.getCheckedKeys()
// 过滤数据(科室、岗位不显示),仅显示用户id数据
checkedKeys = checkedKeys.filter(item => {
const filterFlag = item.length === 32
return filterFlag
})
// 如果限制数量大于0
if (this.checkedLimitCount > 0) {
if (checkedKeys.length > this.checkedLimitCount) {
this.$message({ type: 'warning', message: '当前选择用户数量为' + checkedKeys.length + ',不能超过' + this.checkedLimitCount })
return
}
}
// 调用获取名称
const names = this.getNames(checkedKeys)
// 调用修改v-model对应值方法
this.$emit('input', checkedKeys.join(','))
// 修改names属性
this.$emit('update:names', names)
// 设置change方法
this.$emit('change', checkedKeys.join(','))
// 置空搜索值
this.search = ''
// 关闭弹层
this.userTreeClose()
},
/**
* 取消按钮方法
*/
cancelFun() {
this.userTreeClose()
},
// 获取选择名称
getNames(checkedKeys) {
// 重置选择名称map
this.selectUserMap = {}
// 递归获取已选名称
if (this.userData[0].children.length > 0) {
this.convertTreeNames(this.userData[0].children, checkedKeys)
} else {
// 获取对象
const obj = this.userData[0]
// 如果已选列表中存当前值
if (checkedKeys.includes(obj.userTreeUserId)) {
// 设置属性到map中
this.selectUserMap[obj.userTreeUserId] = obj.deptName
}
}
// 创建已选用户名称数组
const selectUserNames = []
// 获取的userId不为空
if (checkedKeys && checkedKeys.length) {
checkedKeys.forEach(item => {
// 从map中获取userId对应的map 设置到名称数组中
selectUserNames.push(this.selectUserMap[item])
})
}
// 返回数组转换字符串
return selectUserNames.join(',')
},
/**
* 递归获取树名称
* @param treeDataList
* @param checkedKeys
*/
convertTreeNames(treeDataList, checkedKeys) {
// 递归获取已选项目名称
for (let i = 0; i < treeDataList.length; i++) {
const treeData = treeDataList[i]
// 如果已选列表中存当前值
if (checkedKeys.includes(treeData.userTreeUserId)) {
// 设置属性到map中
this.selectUserMap[treeData.userTreeUserId] = treeData.deptName
}
if (treeData.children.length > 0) {
// 递归调用
this.convertTreeNames(treeData.children, checkedKeys)
}
}
}
}
}
</script>
<style lang="scss">
.el-select-dropdown.el-popper.customSelect{
display: none !important;
}
</style>
<style lang="scss" scoped>
.userTreeSelect{
/deep/.scrollbar{
height:400px;
width: 100%;
}
}
/deep/.searchClass{
margin-bottom: 2px;
}
/deep/.custom-modal{
z-index: 2003;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: .5;
background: #000;
}
</style>调用组件代码:
<UserTreeSelect ref="leaderUserTree" v-model="qualityCheckPlanSaveDto.qualityCheckPlan.leaderIds" :clearable="true" title="选择检查组长" :names.sync="qualityCheckPlanSaveDto.qualityCheckPlan.leaderNames" width="99%" placeholder="请选择小组组长"/>




发表评论