vue封装element-ui的el-checkbox实现嵌套多选组件,支持数据回显

个人博客
要实现的效果(2021-07-06更新支持反选)
数据回显需要绑定default-checked-keys(2022-03-24更新)
在这里插入图片描述

如何使用

<multi-check-list :dataList="dataList" :default-checked-keys="['a001','a003']" :invert="false" :isCheckAll='true' @change="handlerDataCheck"></multi-check-list>
import MultiCheckList from './multiCheckList.vue';
data() {
	return {
		dataList:[{
		   code:1001,
		   label:'电商',
		   value:'a001',
		   children:[{
		     code:1002,
		     label:'淘宝',
		     value:'a002'
		   }, {
		     code:1003,
		     label:'京东',
		     value:'a003'
		   }, {
		     code:1004,
		     label:'亚马逊',
		     value:'a002'
		   }]
		 }, {
		   code:1005,
		   label:'生活服务',
		   value:'a005',
		   children:[{
		     code:1006,
		     label:'美团',
		     value:'a006'
		   }, {
		     code:1007,
		     label:'大众点评',
		     value:'a007'
		   }, {
		     code:1008,
		     label:'滴滴',
		     value:'a008'
		   }, {
		     code:1009,
		     label:'饿了么',
		     value:'a009'
		   }]
		 }],
	}
},
methods: {
	handlerDataCheck(parent, child) {
      console.log(parent, child)
    },
}

具体实现

HTML
<template>
    <div class="multi-check" :style="{width:width}">
        <div class="multi-check-item" v-if="isCheckAll">
            <el-checkbox label="全选" :indeterminate="isIndeterminate" v-model="checkAll" @change="handlerChange(0, null, $event)">全选 {{totalLabel}}</el-checkbox>
			<el-checkbox label="反选" v-model="checkInvert" @change="handlerChange(-1, null, $event)">反选</el-checkbox>
        </div>
        <div class="multi-check-item" v-for="(item) in newDataList" :key="item.code">
            <el-checkbox  v-model="item.isChecked" :indeterminate="item.isIndeterminate" :label="item.code" :value="item.value" @change="handlerChange(1, item, $event)">{{item.label}}</el-checkbox>
            <div class="multi-check-item"  style="display:inline-block">
                <el-checkbox v-model="child.isChecked" @change="handlerChange(2, item, $event)" v-for="child in (item.children || [])" :key="child.code" :label="child.value">{{child.label}}</el-checkbox>
            </div>
        </div>
    </div>
</template>
JavaScript
<script>
export default {
    name:'multiCheckList',
    props: {
        isCheckAll: {			//是否显示全选
            type: Boolean,
            default: false
        },
        invert:{		//是否显示反选(2021-07-06更新)
			type:Boolean,
			default:false
		},
        dataList:{			//数据集合
            type: Array,
            required: true
        },
        width:{
            type: String,
            default:'100%'
        },
        defaultCheckedKeys:{		//默认选中项
			type:Array,
			default:() => ([])
		}
    },
    data() {
        return {
            checkAll:false,
            checkInvert:false,	//反选(2021-07-06更新)
            isIndeterminate:false,
            totalLabel:'',
            newDataList:[]	//为了不污染prop dataList
        }
    },
    watch: {
        newDataList: {
            handler() {
				const isChangeSum = this.newDataList.reduce((prev, cur) => {
					return prev + (+(cur.isChange || 0));
				}, 0);
				const isCheckSum = this.newDataList.reduce((prev, cur) => {
					return prev + (+(cur.isChecked || 0));
				}, 0);
				this.checkAll = (isCheckSum && isCheckSum === this.newDataList.length);
				this.isIndeterminate = !!isChangeSum;
				if(isCheckSum && isCheckSum === this.newDataList.length) {
					this.isIndeterminate = false;
				}
                this.totalLabel = isChangeSum === 0 ? "" : `已选择(${isChangeSum})个分类`;
			},
			immediate: true,
			deep:true
        },
        dataList: {
            handler:function(val) {
            	//这里只做简单的拷贝,深拷贝需要自己实现,这里只是为了演示
                this.newDataList = JSON.parse(JSON.stringify(val))
                this.initCheckedKeys()
            },
            immediate:true,
            deep:true
        }
    },
    computed: {
        getDataList() {
            let parentList = []
            let childList = []
            this.newDataList.forEach(item => {
                if(item.isChecked) parentList.push(item.value)
				;(item.children || []).forEach(child => {
					if(child.isChecked) {
						childList.push(child.value);
					}
				})
            })
            return [parentList, childList]
        }
    },
    methods: {
    	initCheckedKeys() {		//初始化默认选中项
            ;(this.newDataList || []).forEach(item => {
                if(!item.children || !Array.isArray(item.children)) item.children = []
                item.children.forEach(child => {
                    if(this.defaultCheckedKeys.includes(child.value)) {
                        this.$set(child, 'isChecked', true);
                    }
                });
                if(item.children.length) {
                    const every = item.children.every(it => it.isChecked)   //子集全部选中
                    const some = item.children.some(it => it.isChecked)   //子集没有全部选中
                    this.$set(item, 'isChecked', every);
                    this.$set(item, 'isIndeterminate', some && !every);
                    this.$set(item, 'isChange', some)
                }
            })
        },
        handlerCheckAll(isChecked) {
            this.newDataList.forEach(item => {
				this.$set(item, 'isChecked', isChecked);
				this.$set(item, 'isChange', isChecked);
				if(isChecked) this.$set(item, 'isIndeterminate', false);
				item.children.forEach(child => {
					this.$set(child, 'isChecked', isChecked);
				})
			});
        },
        handlerInvertCheck() {		//反选(2021-07-06更新)
			this.newDataList.forEach(item => {
				this.$set(item, 'isChange', !item.isChecked);
				this.$set(item, 'isChecked', !item.isChecked);
				item.children.forEach(child => {
					this.$set(child, 'isChecked', !child.isChecked);
					if(child.isChecked) this.$set(item, 'isChange', true);
				})
			});
		},
        handlerChange($type, $row, $event) {
            let isChecked = !$event.target ? $event: $event.target.checked;
            if($type === 0) {		//全选
                this.handlerCheckAll(isChecked)
                this.$emit('change', this.getDataList[0],this.getDataList[1])
                return
            }
            if($type === 1) {
                $row.children.forEach((item) => {
                    this.$set(item, 'isChecked', isChecked);
                })
            }
            if($type === -1) {	//反选(2021-07-06更新)
				this.handlerInvertCheck()
				this.$emit('change', this.getDataList[0],this.getDataList[1])
				return
			}
            const checkCount = $row.children.reduce((prev, cur)=> {
				let check = 0;
				if(cur.isChecked === undefined) {
					check = 0;
				}else {
					check = +cur.isChecked
				}
				return prev + (+check);
            },0);	//统计选择的次数
            this.$set($row, 'isChange', checkCount === 0 ? false: true);
            this.$set($row, 'isChecked', (checkCount && checkCount === $row.children.length) ? true: false);
            this.$set($row, 'isIndeterminate', (checkCount && checkCount < $row.children.length) ? true:false);

            this.$emit('change', this.getDataList[0],this.getDataList[1])
        }
    }
}
</script>
css
<style lang="scss" scoped>
.multi-check {
    position: relative;
    text-align:left;
    
    .multi-check-item {
        width: 100%;
        background-color: #F9FAFB;
        padding: 10px 0;
        padding-left: 25px;
        margin-bottom: 10px;
        border-radius: 6px;
    }
}
</style>
Logo

前往低代码交流专区

更多推荐