VUE-ElementUI 对话框多层级树形控件,穿越框复杂业务实现
请忽略css,主要是逻辑,看图,图片为子组件,左侧树形控件,中间选择框,右侧展示框
父组件
<template>
<div class="box">
<div class="ad-primary choose-btn" @click="chooseOne">请选择人员</div>
<div class="ad-primary choose-btn" @click="chooseTwo">请选择管理员</div>
<div>选择人员{{attendanceOfficerNameList}}</div>
<div>选择管理员{{attendanceOfficerNameListF}}</div>
<select-officer
@dialogFun="dialogOne"
:post-visible="dialogVisibleOne"
:post-person="kqPerson"
:post-frame="kqCount"
:post-user="kqCountu"></select-officer>
<select-officer
@dialogFun="dialogTwo"
:post-visible="dialogVisibleTwo"
:post-person="labelPerson"
:post-frame="labelCount"
:post-user="labelCountu"></select-officer>
</div>
</template>
<script>
import attendanceOfficer from "@/pages/common/attendanceOfficer"
export default {
name: 'attendanceAdd',
components: {
'select-officer': attendanceOfficer
},
data() {
return {
dialogVisibleOne: false,
dialogVisibleTwo: false,
kqPerson: [],
kqCount: 0,
kqCountu: 0,
labelPerson: [],
labelCount: 0,
labelCountu: 0,
attendanceOfficerIds: [],
attendanceOfficerNameList: "",
attendanceOfficerIdsF: [],
attendanceOfficerNameListF: "",
}
},
created() {
this.getDatas();
},
methods: {
/** 考勤类型列表 */
getDatas() {
function getQuery (variable) {
let code = window.location.search.substring(1)
//let enUrl = 'user=dev-test&pwd=11111111'
//let enUrls = encodeURIComponent(enUrl)
//console.log(enUrls)
let codes = decodeURIComponent(code)
let vars = codes.split("&");
for (let i = 0; i < vars.length; i++) {
let pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return (false);
}
let that = this;
let u = getQuery('kqId');
//console.log('kq = '+u)
if (u) {
let params_show = {
//"requestId":"4333532244114",
"authToken": "12312",
"userToken": "26cea5f746ae4c7fbf7c3a4018b23f28",
"data": {
"kqId": u
}
}
this.http.post('www.centby.com/show', params_show).then(
res => {
if (res.data.kqPersons != null) {
that.kqPerson = res.data.kqPersons;
let j = 0;
let k = 0;
res.data.kqPersons.forEach(function (item) {
if (nodeType == 0) {
j++;
} else if (nodeType == 1) {
k++
}
});
that.kqCount = j;
that.kqCountu = k;
}
if (res.data.kqLiablePersons != null) {
that.labelPerson = res.data.kqLiablePersons;
let m = 0;
let n = 0;
res.data.kqLiablePersons.forEach(function (item) {
if (nodeType == 0) {
m++;
} else if (nodeType == 1) {
n++
}
});
that.labelCount = m;
that.labelCountu = n;
}
//console.log(JSON.stringify(res))
},
error => {
console.log(error.message)
}
)
}
},
chooseOne(){
this.dialogVisibleOne = true;
},
chooseTwo(){
this.dialogVisibleTwo = true;
},
dialogOne(data){
//console.log(data)
this.dialogVisibleOne = data.emita;
this.attendanceOfficerIds = data.emitb;
this.attendanceOfficerNameList = data.emitc;
},
dialogTwo(data){
//console.log(data)
this.dialogVisibleTwo = data.emita;
this.attendanceOfficerIdsF = data.emitb;
this.attendanceOfficerNameListF = data.emitc;
}
}
}
</script>
<style scoped>
.choose-btn {
width: 80px;
height: 30px;
line-height: 30px;
padding: 0 20px;
margin-bottom: 10px;
font-size: 14px;
font-weight: 600;
border-radius: 4px;
cursor: pointer;
}
</style>
子组件
<template>
<el-dialog
title="选择人员"
align="left"
:visible.sync="dialogVisible"
width="50%"
:before-close="handleClose">
<div class="chosen-modal">
<div class="chosen-modal-left">
<div class="chosen-modal-hd">
<div class="chosen-modal-hd-search">
<i class="el-icon-search" @click="searchSelect"></i>
<input type="text" class="aui-input" placeholder="搜索" v-model="searchWord" id="chosenSearch">
</div>
</div>
<div class="chosen-modal-bd" style="position: relative;">
<div class="chosen-left" style="width: 200px;">
<el-tree :data="treeData" :props="defaultProps" node-key="id" @node-click="handleNodeClick">
<span class="custom-tree-node" slot-scope="{ node, data }">
<i class="el-icon-folder-opened" v-show="data.nodeType == 0 ? true : false"></i>
<i class="el-icon-s-custom" v-show="data.nodeType == 0 ? false : true"></i>
<span>{{ node.label }}</span>
</span>
</el-tree>
</div>
<div class="chosen-right">
<el-checkbox v-model="checkAll" @change="handleCheckAllChange" :disabled="forbid">全选</el-checkbox>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkedCities" @change="handleCheckedCitiesChange">
<el-checkbox v-for="item in category" :label="item.id" :key="item.id" :disabled="forbid">
<i class="el-icon-folder-opened" v-show="item.nodeType == 0 ? true : false"></i>
<i class="el-icon-s-custom" v-show="item.nodeType == 0 ? false : true"></i>
{{item.name}}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
<div class="chosen-modal-right">
<div class="chosen-modal-right-hd">
<p id="tipText">已选{{frameCount}}个架构,{{userCount}}名用户</p>
</div>
<div class="chosen-modal-right-bd">
<div class="chosen-person-org" v-for="items in selectList" :key="items.id" @click="delList(items.id)">
<i class="el-icon-folder-opened" v-show="items.nodeType == 0 ? true : false"></i>
<i class="el-icon-s-custom" v-show="items.nodeType == 0 ? false : true"></i>
<span class="js-chosen-rs-text">{{items.name}}</span>
<i class="el-icon-close"></i>
</div>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="handleClose">取 消</el-button>
<el-button type="primary" class="ad-primary" @click="next">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
//centby.com
export default {
name: 'attendanceOfficer',
props: {
postFrame: {
type: Number
},
postUser: {
type: Number
},
postVisible: {
type: Boolean
},
postPerson: {
type: Array,
default: () => []
}
},
data () {
return {
searchWord: "",
defaultProps: {
children: 'children',
label: 'label'
},
treeData: [],//树形渲染
category: [],//选中框渲染
selectList: [],//展示框渲染
checkAll: false,//全选按钮状态
checkedCities: [],//选中框选中的 id 列表
listId:[],//选中框所有 id 列表
parentList: [],//当前树形节点所有父级 id 列表包含自己
forbid: false,//选中框所有禁选状态,默认可选
frameCount: 0,
userCount: 0,
attendanceOfficerIds: [],//选中的 id 传给父组件
attendanceOfficerNameList: "",//选中字符串传给父组件,做展示
reviewData: '',//展示框请求回显数据
dialogVisible: false//对话框打开或关闭状态
}
},
watch: {
postFrame: {
handler(newName, oldName) {
this.frameCount = newName;
},
immediate: true,
deep: true
},
postUser: {
handler(newName, oldName) {
this.userCount = newName;
},
immediate: true,
deep: true
},
postPerson: {
handler(newName, oldName) {
this.selectList = newName;
},
immediate: true,
deep: true
},
postVisible: {
handler(newName, oldName) {
this.dialogVisible = newName;
},
type: Boolean
}
},
created(){
this.treePool();
},
methods: {
handleClose(){
this.dialogVisible = false;
this.$emit('dialogFun', this.dialogVisible);
},
next() {
let list = this.selectList;
let arr = [];
list.forEach(item => {
this.attendanceOfficerIds.push(item.id);
arr.push(item.name);
})
this.attendanceOfficerNameList = arr.join(",");
console.log(this.attendanceOfficerIds)
console.log(this.attendanceOfficerNameList)
this.dialogVisible = false;
//对话框打开关闭状态,选中 id 列表,选中 name 列表返回给父组件
this.$emit('dialogFun', {emita:this.dialogVisible,emitb:this.attendanceOfficerIds,emitc:this.attendanceOfficerNameList});
},
treePool () {
//展示框数据
// this.selectList = [{
// id: '7387601',
// name: '科技部',
// nodeType: '0'
// }]
//树形数据
/*
[{
"id": "0",
"label": "学生",
"nodeType": 0,
"orgType": 1,
"children": [
{
"id": "3736461431145987660",
"label": "初中部",
"nodeType": 0,
"orgType": 1,
"children": [
{
"id": "3736461431145987661",
"label": "2011级",
"nodeType": 0,
"orgType": 1,
"children": [
{
"id": "3736461431145987662",
"label": "3班",
"nodeType": 0,
"orgType": 1,
"children": [
{
"id": "3736461431145987663",
"label": "小牛奶",
"nodeType": 1,
"orgType": 1,
"children": null
},
{
"id": "3736461431145987663",
"label": "小牛奶",
"nodeType": 1,
"orgType": 1,
"children": null
}
]
}
]
}
]
}
]
}]
*/
let that = this;
let params = {
//"requestId":"4333532244114",
"authToken":"12312",
"userToken":"26cea5f746ae4c7fbf7c3a4018b23f28",
"data":{
}
}
this.http.post('www.centby.com/whole', params).then(
res => {
that.treeData = res.data;
//console.log(JSON.stringify(treeData))
},
error => {
console.log(error.message)
}
)
},
searchSelect() {
console.log(this.searchWord);
let that = this;
let keyWord = this.searchWord;
let params = {
//"requestId":"userToken1574677356105xRx1",
"authToken":"666",
"userToken":"666",
"data":{
"keyWord": keyWord
}
}
this.http.post('www.centby.com/word', params).then(
res => {
console.log(JSON.stringify(res))
that.treeData = res.data
},
error => {
console.log(error.message)
}
)
},
handleCheckAllChange(val) {
let all = JSON.parse(JSON.stringify(this.listId));
//console.log("all === "+all)
this.checkedCities = val ? all : [];
//this.isIndeterminate = false;
//console.log("list === "+this.checkedCities);
let that = this;
let categorys = JSON.parse(JSON.stringify(this.category));
if(val === true){
//全部展示
categorys.forEach(function(item){
categorys.find(function(s){
if(s.id === item.id){
//console.log(s)
that.selectList = that.selectList.concat([s])
//放入展示框,并去重
let k = {};
that.selectList = that.selectList.reduce(function (subitem, next) {
k[next.id] ? '' : k[next.id] = true && subitem.push(next);
return subitem;
}, []);
}
})
})
}else{
//取消展示
categorys.forEach(function(item){
for(let j = 0;j < that.selectList.length;j++){
if(that.selectList[j].id === item.id){
that.selectList.splice(j,1);
}
}
})
}
//计算展示框数量
let j = 0;
let k = 0;
that.selectList.forEach(function(item){
if(item.nodeType == 0){
j++;
}else if(item.nodeType == 1){
k++
}
});
that.frameCount = j;
that.userCount = k;
},
handleCheckedCitiesChange(value) {
//console.log("sub === "+JSON.stringify(value))
/*
//选中 id 列表数据示例
//checkedCities = [3736461431145987743]
//选择框数据示例
category = [
{
"id": "3736461431145987660",
"name": "初中部",
"nodeType": 0
},
{
"id": "3736461431145987743",
"name": "小学部",
"nodeType": 0
}
]
* * */
let categorys = this.category;
console.log("value === " + this.checkedCities )
let checkedCount = value.length;
this.checkAll = checkedCount === this.category.length;
//this.isIndeterminate = checkedCount > 0 && checkedCount < this.category.length;
let that = this;
//console.log('list = '+this.listId)
let deepCopy = JSON.stringify(this.listId);
let unChecked = JSON.parse(deepCopy);
//选中ID展示
value.forEach(function (item) {
//console.log(item)
//添加到展示框
categorys.find(function(s){
if(s.id === item){
//console.log(s)
that.selectList = that.selectList.concat([s])
//放入展示框,并去重
let k = {};
that.selectList = that.selectList.reduce(function (subitem, next) {
k[next.id] ? '' : k[next.id] = true && subitem.push(next);
return subitem;
}, []);
}
})
//console.log('uncheck = '+JSON.stringify(unChecked))
//获取不选中的id
for(let i = 0;i < unChecked.length;i++){
// /console.log(item)
if(unChecked[i] == item){
unChecked.splice(i,1);
}
}
})
//console.log('res = '+JSON.stringify(unChecked))
//把未选中的从展示框删除
unChecked.forEach(function(item){
for(let j = 0;j < that.selectList.length;j++){
if(that.selectList[j].id == item){
that.selectList.splice(j,1);
}
}
})
//计算展示框数量
let j = 0;
let k = 0;
that.selectList.forEach(function(item){
if(item.nodeType == 0){
j++;
}else if(item.nodeType == 1){
k++
}
});
that.frameCount = j;
that.userCount = k;
},
delList(val){
console.log('del '+JSON.stringify(val))
//console.log('forbid = '+ this.forbid)
let that = this;
//let checkedMenu = JSON.parse(JSON.stringify(this.checkedCities));
let categorys = JSON.parse(JSON.stringify(that.category));
//第一步先从展示框删除
for(let j = 0;j < that.selectList.length;j++){
if(that.selectList[j].id == val){
that.selectList.splice(j,1);
}
}
let permission = this.forbid;
if(permission === true){
//禁止选择状态
//第二步拿删除当前id后的展示框列表 和 所有树形父级 id 列表对比
let hasOne = false;
let parent = JSON.parse(JSON.stringify(that.parentList));
let showList = JSON.parse(JSON.stringify(that.selectList));
console.log('after == '+ JSON.stringify(showList))
console.log('parent == '+ JSON.stringify(parent))
parent.forEach(function(item){
showList.forEach(function(show){
if(show.id == item){
hasOne = true;
}
})
})
//没有父级,改变禁选状态为可选,并重新渲染选择框列表
if(hasOne === false){
//改变禁选状态为可选
that.forbid = false;
//从展示框删除
for(let j = 0;j < that.selectList.length;j++){
if(that.selectList[j].id == val){
that.selectList.splice(j,1);
}
}
//重新渲染选中框
that.checkAll = false;
that.checkedCities = [];
that.selectList.forEach(function (item) {
//console.log(item)
categorys.find(function(s){
if(s.id === item.id){
//console.log(s)
that.checkedCities.push(item.id)
//选中等于当前所有选中框列表全选操作
if(that.checkedCities.length == categorys.length){
that.checkAll = true;
}
}
})
})
}
}else{
//非禁选状态
//重新渲染选中框
for(let j = 0;j < that.checkedCities.length;j++){
if(that.checkedCities[j] == val){
that.checkedCities.splice(j,1);
that.checkAll = false;
}
}
}
//计算展示框数量
let j = 0;
let k = 0;
that.selectList.forEach(function(item){
if(item.nodeType == 0){
j++;
}else if(item.nodeType == 1){
k++
}
});
that.frameCount = j;
that.userCount = k;
},
handleNodeClick(data) {
//点击树状列表
//console.log(data);
/*
//选择框数据示例
{
"code": 0,
"message": "调用成功",
"data": {
"parentIds": [],
"nodes": [
{
"id": "3736461431145987660",
"name": "初中部",
"nodeType": 0
},
{
"id": "3736461431145987743",
"name": "小学部",
"nodeType": 0
}
]
}
}
//选中 id 数据示例
checkedCities = [3736461431145987743]
//选择框展示数据示例,展示框数据结构和选择框数据结构相同,选择框选中的列表为数组结构 id 列表
category = [
{
"id": "3736461431145987660",
"name": "初中部",
"nodeType": 0
},
{
"id": "3736461431145987743",
"name": "小学部",
"nodeType": 0
}
]
* * */
let that = this;
let idx = data.id;
let types = data.orgType;
let params = {
//"requestId":"userToken1574677356105xRx1",
"authToken":"666",
"userToken":"666",
"data":{
"orgId": idx,
"orgType": types
}
}
this.http.post('www.centby.com/info', params).then(
res => {
//console.log(res)
that.category = res.data.nodes;
//提取所有ID并保存
let arr = [];
that.category.forEach(function(item){
//console.log(item.id)
arr.push(item.id)
});
that.listId = arr;
//所有id保存,that.listId = ['3736461431145987600','3736461431145987601','3736461431145987602'];
//初始化并保存当前所有树形父级 id 列表包含自己,展示框删除操作会调用此数据
that.parentList = JSON.parse(JSON.stringify(res.data.parentIds));
console.log('parent = '+ JSON.stringify(that.parentList));
//初始化禁选判断,禁选,全选按钮,已选中
let permission = false;
that.forbid = false;
that.checkAll = false;
that.checkedCities = [];
//使用JSON转换解决深拷贝问题
let parent = JSON.parse(JSON.stringify(that.parentList));
let showList = JSON.parse(JSON.stringify(that.selectList));
//每次点击树状列表,拿当前所有树形父级 id 列表(parentIds)包含自己,和展示框列表 id 对比,如果有,选择框展示全部全选并且禁止状态
//展示框删除动作,会取此禁选状态做展示框删除动作
parent.forEach(function(item){
showList.forEach(function(show){
if(show.id == item){
permission = true;
}
})
})
if(permission == true){
//禁止选择操作
that.checkedCities = that.listId;
that.checkAll = true;
that.forbid = true;
// that.selectList.forEach(function (item) {
// if(item.id == idx){
// that.checkedCities = that.listId;
// that.checkAll = true;
// that.forbid = true;
// }
// })
}else{
//不禁止状态回显渲染
that.selectList.forEach(function (item) {
//console.log(item)
that.category.find(function(s){
if(s.id === item.id){
//console.log(s)
that.checkedCities.push(item.id)
//选中等于当前所有选中框列表全选操作
if(that.checkedCities.length == that.listId.length){
that.checkAll = true;
}
}
})
})
}
},
error => {
console.log(error.message)
}
)
},
}
}
</script>
<style scoped lang="scss">
.box >>> .el-checkbox {
display: block;
}
.chosen-modal {
display: flex;
height: 598px;
padding: 15px;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
.chosen-modal-left {
width: 430px;
}
.chosen-modal-hd {
padding: 20px;
display: flex;
border-top: 1px solid #dddee1;
border-right: 1px solid #dddee1;
border-left: 1px solid #dddee1;
}
.chosen-modal-hd .chosen-modal-hd-search {
position: relative;
flex: 1;
}
.chosen-modal-hd .chosen-modal-hd-search i {
width: 32px;
height: 32px;
line-height: 32px;
font-size: 16px;
text-align: center;
color: #80848f;
position: absolute;
right: 0;
z-index: 1;
cursor: pointer;
}
.aui-input {
display: inline-block;
width: 100%;
height: 32px;
line-height: 1.5;
padding: 4px 32px 4px 7px;
box-sizing: border-box;
font-size: 12px;
border: 1px solid #dddee1;
border-radius: 4px;
color: #6e7d8f;
background-color: #fff;
background-image: none;
position: relative;
cursor: text;
transition: border .2s ease-in-out,background .2s ease-in-out,box-shadow .2s ease-in-out;
}
.chosen-modal-bd {
display: flex;
height: 526px;
border: 1px solid #dddee1;
}
.chosen-left {
margin-top: -1px;
width: 200px;
min-width: 200px;
height: 525px;
overflow-y: auto;
position: absolute;
background: #fff;
z-index: 2;
overflow-x: hidden;
border-top: 1px solid #dddee1;
border-right: 1px solid #dddee1;
}
.chosen-left-device {
margin-top: -1px;
width: 200px;
min-width: 200px;
height: 525px;
overflow-y: auto;
position: absolute;
background: #fff;
z-index: 2;
overflow-x: hidden;
border-top: 1px solid #dddee1;
}
.chosen-right {
flex: 1;
padding-left: 200px;
z-index: 1;
overflow-y: scroll;
}
.chosen-right .el-checkbox {
padding: 10px 15px;
color: #0cb181;
}
.aui-checkbox-label {
align-items: center;
width: 100%;
white-space: normal;
word-wrap: break-word;
word-break: break-all;
margin-bottom: 0;
line-height: 1.3;
}
.chosen-modal-right {
flex: 1;
border: 1px solid #dddee1;
margin-left: 20px;
}
.chosen-modal-right-hd {
padding: 20px;
}
.chosen-modal-right-bd {
height: 538px;
overflow: auto;
}
.chosen-person-org {
display: flex;
padding: 6px 20px;
height: 20px;
align-items: center;
}
.chosen-modal-right-bd>div>i {
font-size: 12px;
color: #0cb181;
margin-right: 5px;
align-self: baseline;
position: relative;
top: 6px;
}
.chosen-modal-right-bd>div>span {
flex: 1;
word-wrap: break-word;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
word-break: break-all;
overflow: hidden;
}
</style>