用vue实现封装用户树形选择和部门树形选择控件,采用el-tree。方便各个功能模块的使用和以后的开发。
一、封装用户树形选择控件(userTree.vue)
<template>
<div style="padding: 10px;">
<!-- 选择人员对话框 -->
<el-input placeholder="输入关键字进行过滤" v-model="filterText">
</el-input>
<div v-loading="loading" style="padding:0px 20px;overflow-y: auto;height: 50vh;margin-top: 10px;">
<el-tree class="filter-tree" :data="treeData" show-checkbox :props="defaultProps"
:filter-node-method="filterNode" :default-checked-keys="checkedKeys" :check-strictly="singleSelection"
:default-expanded-keys="checkedKeys" @check="handleCheckChange" ref="tree" node-key="id">
</el-tree>
<!-- -->
</div>
<div slot="footer" class="dialog-footer" style="text-align: right;padding-top: 20px;">
<el-button class="border_buttom" size="small" plain @click="cancel">关 闭</el-button>
<el-button class="press_button" size="small" @click="handleAddDept">确 定</el-button>
</div>
</div>
</template>
<script>
import {
allListUser
} from "@/api/system/user";
import {
listDept
} from "@/api/system/dept";
export default {
name: "userTree",
props: {
//选中数据回显
checkedKeys: {
type: Array,
default: () => {
return [];
}
},
//是否开启单选
singleSelection: {
type: Boolean,
default: () => {
return false;
}
},
},
data() {
return {
loading:true,
//是否已勾选判断
checkIf:false,
services: [],
//选人弹窗
open: false,
//选人过滤
filterText: '',
//树控件数据
treeData: null,
defaultProps: {
children: 'children',
label: 'label'
},
selectedMumberList: [],
}
},
inject: ["selectUser", "closeUserTree"],
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
created() {
this.getUserTree();
},
methods: {
cancel() {
this.closeUserTree()
this.$refs.tree.setCheckedKeys([]);
},
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
handleCheckChange(node, list) {
if (this.singleSelection) {
if (node.uid != null) {
this.checkIf=true
this.selectedMumberList = [];
//选中事件在选中后执行,当lis中有两个选中时,使用setCheckedKeys方法,选中一个节点
if (list.checkedKeys.length == 2) {
this.$refs.tree.setCheckedKeys([node.id]);
}
this.selectedMumberList.push({
uid: node.id,
label: node.label,
phonenumber:node.phonenumber
})
} else {
this.$message({
message: '请选择用户数据',
type: 'warning'
});
}
} else {
// 获取选中的子节点列表
this.checkIf=true
this.selectedMumberList = this.$refs.tree.getCheckedNodes(true, false);
this.selectedMumberList = this.selectedMumberList.filter(obj => {
return obj.uid != null
})
}
},
/** 确定选择人员 */
handleAddUser() {
let arr = []
let userIds = []
let phonenumbers = []
let form = {
userNames: null,
userIds: null,
phonenumbers:null,
}
if(this.checkIf){
this.selectedMumberList.forEach((obj) => {
arr.push(obj.label);
userIds.push(obj.uid.replace('u_', ''));
phonenumbers.push(obj.phonenumber)
})
form.userNames = arr.toString()
form.userIds = userIds.toString()
form.phonenumbers = phonenumbers.toString()
this.checkIf=false
this.selectUser(form)
this.$refs.tree.setCheckedKeys([])
}else{
this.checkIf=false
this.closeUserTree()
}
},
/** 构建树形数据结构 */
getUserTree() {
this.loading=true
//获取所有部门的数据接口
listDept().then(res => {
let deptTree = res.data
//获取所有用户数据
allListUser().then((res) => {
this.userList = []
for (let i in res.rows) {
const obj = {
pid: null,
id: null,
uid: null,
label: null,
phonenumber:null,
}
if (res.rows[i].deptId == null || res.rows[i].deptId == "") {
obj.pid = -1
} else {
obj.pid = res.rows[i].deptId
}
//加‘u_’是为了区分部门id和用户id
obj.id = "u_" + res.rows[i].userId
obj.uid = "u_" + res.rows[i].userId
obj.label = res.rows[i].userName
//可以按需求补充其他字段,如一下为补充电话号码
obj.phonenumber= res.rows[i].phonenumber||'-'
this.userList.push(obj)
}
this.mapTree(deptTree)
this.treeData = deptTree
this.loading=false
});
})
},
mapTree(data) {
data.forEach(ditem => { //遍历树 拼入相应的user
if (this.singleSelection && !ditem.uid) {
ditem['disabled'] = true
}
this.userList.forEach(uitem => {
if (ditem.id == uitem.pid && ditem.uid == null) {
if (!ditem.children) {
ditem.children = []
}
ditem.children.push(uitem)
}
})
if (ditem.children) {
this.mapTree(ditem.children)
}
})
},
}
};
</script>
<style>
</style>
二、封装部门选择控件(deptTree.vue)
<template>
<div style="padding: 10px;">
<!-- 选择部门对话框 -->
<el-input placeholder="输入关键字进行过滤" v-model="filterText"> </el-input>
<div v-loading="loading" style="padding:0px 20px;overflow-y: auto;height: 50vh;margin-top: 10px;">
<el-tree class="filter-tree" :data="treeData" show-checkbox :props="defaultProps"
:filter-node-method="filterNode" :default-checked-keys="checkedDeptKeys" default-expand-all
:check-strictly="singleSelection" :default-expanded-keys="checkedDeptKeys" @check="handleCheckChange"
ref="tree" node-key="id">
</el-tree>
</div>
<div slot="footer" class="dialog-footer" style="text-align: right;padding-top: 20px;">
<el-button class="border_buttom" size="small" plain @click="cancel">关 闭</el-button>
<el-button class="press_button" size="small" @click="handleAddDept">确 定</el-button>
</div>
</div>
</template>
<script>
import {
listDept
} from "@/api/system/dept";
export default {
name: "deptTree",
props: {
//选中回显
checkedDeptKeys: {
type: Array,
default: () => {
return [];
}
},
//是否单选
singleSelection: {
type: Boolean,
default: () => {
return false;
}
},
},
data() {
return {
loading:true,
services: [],
//选人弹窗
open: false,
//选人过滤
filterText: '',
//树控件数据
treeData: null,
defaultProps: {
children: 'children',
label: 'label'
},
selectedMumberList: [],
//是否有选择
checkIf:false,
}
},
inject: ["selectDept", "closeDeptTree"],
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
created() {
this.getDeptTree();
},
methods: {
cancel() {
this.closeDeptTree()
this.$refs.tree.setCheckedKeys([]);
},
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
handleCheckChange(node, list) {
if (this.singleSelection) {
this.checkIf=true
this.selectedMumberList = [];
//node 该节点所对应的对象、list 树目前的选中状态对象
//选中事件在选中后执行,当lis中有两个选中时,使用setCheckedKeys方法,选中一个节点
if (list.checkedKeys.length == 2) {
//单选实现
this.$refs.tree.setCheckedKeys([node.id]);
}
this.selectedMumberList.push({
id: node.id,
label: node.label
})
} else {
this.checkIf=true
// 获取选中的子节点列表
this.selectedMumberList = this.$refs.tree.getCheckedNodes(true, false);
}
},
/** 选择部门 */
handleAddDept() {
let arr = []
let deptIds = []
this.selectedMumberList.forEach((obj) => {
arr.push(obj.label);
deptIds.push(obj.id);
})
let form = {
deptNames: null,
deptIds: null
}
if(this.checkIf){
form.deptNames = arr.toString()
form.deptIds = deptIds.toString()
this.checkIf=false
this.selectDept(form)
this.$refs.tree.setCheckedKeys([]);
}else{
this.checkIf=false
this.closeDeptTree()
}
},
/** 构建树形数据结构 */
getDeptTree() {
this.loading=true
listDept().then(res => {
this.treeData = res.data
this.loading=false
})
},
}
};
</script>
<style>
</style>
三、控件使用
<template>
<div>
<div>
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="用户1" prop="u1">
<el-input v-model="form.u1Name" readonly @click.native="handleUser(1)" placeholder="请选择用户1">
<template slot="append">
<span class="inco">
<i class="el-icon-user"></i>
</span>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门1" prop="d1Name">
<el-input v-model="form.d1Name" readonly @click.native="handleDept(1)" placeholder="请输入部门1">
<template slot="append">
<span class="inco">
<i class="el-icon-user"></i>
</span>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="用户2" prop="u2">
<el-input v-model="form.u2Name" readonly @click.native="handleUser(2)" placeholder="请选择用户2">
<template slot="append">
<span class="inco">
<i class="el-icon-user"></i>
</span>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="用户3" prop="u3">
<el-input v-model="form.u3Name" readonly @click.native="handleUser(3)" type="text"
placeholder="请选择用户3">
<template slot="append">
<span class="inco">
<i class="el-icon-user"></i>
</span>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="用户4" prop="u4">
<el-input v-model="form.u4Name" readonly @click.native="handleUser(4)" placeholder="请选择用户4">
<template slot="append">
<span class="inco">
<i class="el-icon-user"></i>
</span>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<el-dialog title="选择人员" :visible.sync="openUserTree" width="500px" append-to-body>
<userTree v-if="openUserTree" :singleSelection="singleSelection" :checkedKeys="checkedKeys"></userTree>
</el-dialog>
<el-dialog title="选择部门" :visible.sync="openDeptTree" width="500px" append-to-body>
<deptTree :singleSelection="true" :checkedDeptKeys="checkedDeptKeys"></deptTree>
</el-dialog>
</div>
</template>
<script>
//引用用户选择和部门选择树形控件
import userTree from "/src/components/deptUserTree/userTree.vue"
import deptTree from "/src/components/deptUserTree/deptTree.vue"
export default {
name: "myPage",
components: {
userTree,
deptTree,
},
data() {
return {
//用户tree打开
openUserTree: false,
//部门tree打开
openDeptTree: false,
//用户tree数据回显
checkedKeys: [],
//部门tree数据回显
checkedDeptKeys: [],
// 表单参数
form: {},
//选人控件是否单选
singleSelection: false,
};
},
created() {
},
//父子组件方法触发的关键
provide() {
return {
selectUser: this.selectUser,
closeUserTree: this.closeUserTree,
selectDept: this.selectDept,
closeDeptTree: this.closeDeptTree,
};
},
methods: {
//点击选人输入框,通过i区分不同的字段的处理
handleUser(i) {
this.userFlag = i
this.checkedKeys = []
this.singleSelection = false
if (this.userFlag == 1) {
this.singleSelection = true
if (this.form.u1Name != null) {
this.checkedKeys = this.form.u1.split(',')
}
} else if (this.userFlag == 2) {
this.singleSelection = true
if (this.form.u2Name != null) {
this.checkedKeys = this.form.u2.split(',')
}
} else if (this.userFlag == 3) {
if (this.form.u3Name != null) {
this.checkedKeys = this.form.u3.split(',')
}
} else if (this.userFlag == 4) {
if (this.form.u4Name != null) {
this.checkedKeys = this.form.u4.split(',')
}
}
//处理数据回显赋值
if (this.checkedKeys != []) {
this.checkedKeys.forEach((item, index) => {
this.checkedKeys[index] = "u_" + this.checkedKeys[index]
})
}
this.openUserTree = true
},
//选人确定赋值
selectUser(e) {
if (this.userFlag == 1) {
this.form.u1 = e.userIds
this.form.u1Name = e.userNames
} else if (this.userFlag == 2) {
this.form.u2 = e.userIds
this.form.u2Name = e.userNames
//赋值联系方式
this.form.u2Way = e.phonenumbers
} else if (this.userFlag == 3) {
this.form.u3 = e.userIds
this.form.u3Name = e.userNames
} else if (this.userFlag == 4) {
this.form.u4 = e.userIds
this.form.u4Name = e.userNames
}
this.openUserTree = false
},
//关闭组件
closeUserTree() {
this.openUserTree = false
},
//点击选部门输入框
handleDept(i) {
this.deptFlag = i
this.checkedDeptKeys = []
if (this.deptFlag == 1) {
if (this.form.d1Name != null) {
//处理部门回显选中的到控件里
this.checkedDeptKeys = this.form.d1.split(',')
}
}
this.openDeptTree = true
},
//选择部门
selectDept(e) {
if (this.deptFlag == 1) {
this.form.d1 = e.deptIds
this.form.d1Name = e.deptNames
}
this.openDeptTree = false
},
//关闭部门选择组件
closeDeptTree() {
this.openDeptTree = false
},
// 表单重置
reset() {
this.form = {
u1: null,
u1Name: null,
u2: null,
u2Name: null,
u3: null,
u3Name: null,
u4: null,
u4Name: null,
d1: null,
d1Name: null,
};
this.resetForm("form");
},
}
};
</script>
<style scoped lang="scss">
</style>
三、代码分析
自己看代码的注释吧,重点主要是
1、数据结构的构建,获取用户数据和部门数据接口时按照自己的相关字段调整,还有一些数据权限问题按自己需求规避。
2、父子组件方法的调用,因为会存在各种前端标签包裹问题,目前代码只是简单样例,因此采用了provide()来处理。
3、其他应该没有技术难点。