最近编写了一个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="请选择小组组长"/>
发表评论