React实现checkbox group多组选项和标签组显示的联动

实现功能:勾选checkbox项,确定后,已勾选的checkbox项以tag标签的形式展示,tag标签可快捷删除。

checkbox group
tags

实现过程:

  • 使用React。
  • 使用Ant Design的Checkbox、Tag组件。
  • 整个组件主要分为两个部分:多选框组和Tag标签组。

1. 多选框组

class AddInfo extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            checkedList: [], // checkbox已选择的选项
            indeterminate: [], // 全选框-已有选择非全选
            checkAll: {}, // checkbox group 的全部title状态true/false
            tempList: [], // 临时存储checkbox已选择的选项
            checkTitle: {} // checkbox group中已选择的title(全选)
        };
    }

    /* 确定勾选 */
    handleOk = () => {
        if (this.state.tempList.length > 0) {
            // 将已选择信息传给Tags组件
            this.props.getChecked({
                checkedItem: this.state.tempList,
                checkAll: this.state.checkAll,
                indeterminate: this.state.indeterminate,
                checkTitle: this.state.checkTitle
            });
        }
    }

    /* checkbox单选 */
    onChange = (allCheckArr, checkedList) => {
        let checkAll = this.state.checkAll;
        let indeterminate = [];
        let checkTitle = {};
        Object.keys(allCheckArr).forEach((title) => {
            checkTitle[title] = 0;
            for (let checkedItem of checkedList || []) {
                if (allCheckArr[title].includes(checkedItem)) {
                    checkTitle[title]++;
                    checkAll[title] = checkTitle[title] === allCheckArr[title].length;
                    indeterminate[title] = !!checkTitle[title] && (checkTitle[title] < allCheckArr[title].length);
                }
            }
            if (checkTitle[title] === 0) { // 选项组下仅有一个选项时取消选中
                checkAll[title] = false;
            }
        });
        this.setState({
            checkedList,
            tempList:checkedList,
            indeterminate: indeterminate,
            checkAll: checkAll,
            checkTitle: checkTitle
        });
    }

    /* checkbox全选 */
    onCheckAllChange = (allCheckArr, title, e) => {
        this.state.checkAll[title] = e.target.checked;
        let checkedListT = [];
        checkedListT.push(...this.state.checkedList);
        let indeterminate = this.state.indeterminate || [];
        let checkTitle = this.state.checkTitle || {};
        if (e.target.checked === true) { // 全选
            checkedListT.push(...allCheckArr[title]);
            checkedListT = Array.from(new Set(checkedListT)); // 去重(原先indeterminate为true的情况)
            checkTitle[title] = allCheckArr[title].length;
        } else { // 取消全选
            let common = checkedListT.filter(v => allCheckArr[title].includes(v));
            checkedListT = checkedListT.concat(common).filter(v => checkedListT.includes(v) && !common.includes(v));
            checkTitle[title] = 0;
        }
        indeterminate[title] = false;
        this.setState({
            tempList: checkedListT,
            checkedList: checkedListT,
            indeterminate: indeterminate,
            checkTitle: checkTitle
        });
    }

    render() {
        const { checkedList, checkAll, indeterminate } = this.state;
        const { allCheckArr } = this.props;
        return (
            <div className={styles.modalcontent} >
                {
                    allCheckArr.map( ({ title, value }, key ) => (
                        <div className={styles.checksgroup}>
                            <div>
                                <Checkbox
                                indeterminate={indeterminate[title]}
                                onChange={this.onCheckAllChange.bind(this, allCheckArr, title)}
                                checked={checkAll[title]}
                                >
                                    {title}
                                </Checkbox>
                            </div>
                            <br />
                            <CheckboxGroup className={styles.contents} options={value} value={checkedList} onChange={this.onChange.bind(this, allCheckArr)} />
                        </div>
                ))}
            </div>
        );
    }
}

export default AddInfo;

  • 由于Ant Design官网上checkbox group的示例代码只有一个check group,本组件是可以有多组的情况,因此主要通过checkedList,checkAll,indeterminate,checkTitle几个状态控制checkbox group与单个的checkbox的全勾选、半勾选、无勾选几种情况的联动。
  • checkbox单选的操作是传入当前选择的所有的选项,然后与原先的可选项对比,计算出checkAll,indeterminate,checkTitle的值。每次要先将checkAll,indeterminate,checkTitle置空,遍历所有的已选项和待选项。
  • checkbox全选的函数本来是可以复用单选的操作,但是全选之后得出checkAll,indeterminate,checkTitle的值的过程比单选更简单一些,不用遍历选项数组,所以重写了全选的逻辑,没有复用单选的函数,虽然代码量多几行,但是执行过程更简单一些。

2. Tag标签组

import React from 'react';
import { Tag } from 'antd';
import styles from './index.less';

class Tags extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            items: this.props.items, // 需要显示的tag数组
            checkAll: this.props.checkAll, // 该tag所在的数组元素是否全部作为tag存在
            indeterminate: this.props.indeterminate, // 该tag所在的数组元素是否部分作为tag存在
            allCheckArr: this.props.allCheckArr, // 该tag所在的数组
            checkTitle: this.props.checkTitle // 该tag所在的数组元素作为tag存在的数量
        };
    }
    componentWillReceiveProps = ( value, nextState) => {
        this.setState({
            items: value.items,
            checkAll: value.checkAll,
            indeterminate: value.indeterminate,
            allCheckArr: value.allCheckArr,
            checkTitle: value.checkTitle
        });
    }

    delete = (key, value, e) => {
        e.preventDefault();
        let items = this.state.items;
        let checkAll = this.state.checkAll;
        let indeterminate = this.state.indeterminate;
        let allCheckArr = this.state.allCheckArr;
        let checkTitle = this.state.checkTitle;
        items.splice(key, 1);
        for (let title in allCheckArr) {
            for (let item of allCheckArr[title]) {
                if (item === value) {
                    checkTitle[title]--;
                    checkAll[title] = false;
                    if (checkTitle[title] === 0) { // 该选项组下的选项全部删除
                        indeterminate[title] = false;
                    } else {
                        indeterminate[title] = true;
                    }
                }
            }
        }
        this.setState({
            items: items,
            checkAll: checkAll,
            indeterminate: indeterminate,
            checkTitle: checkTitle
        });
        this.props.changeCheckeditems(items);
    }
    render() {
        const items = this.state.items?this.state.items:[];
        return (
            <div>
                {
                    items.map((value, key) => (
                        <Tag className={styles.singletag} closable key={key} onClose={this.delete.bind(this, key, value)}>{value}</Tag>
                    ))}
            </div>
        );
    }
}

export default Tags;

在多选框组对选项勾选之后,将选择结果传入Tags标签组件,该组件以Tag标签将已勾选的选项展示出来。Tag标签可以点击右边的“x”快捷删除,删除后,多选框组中相应的选项也会取消勾选。

3. 组件使用

这两个组件放在同一个父组件中使用,实现值传递。

class parent extends React.Component {
    /* 获取待选择选项 */
    getAllCheckArr = () => {
       ...
                    this.setState({
                        allCheckArr 
                        ...
                    });
         ...
    }
 
    /* 获取checkbox选择的选项 */
    getChecked = (val) => {
        this.setState({
            checkedItem: val.checkedItem,
            checkAll: val.checkAll,
            indeterminate: val.indeterminate,
            checkTitle: val.checkTitle
        });
    }
  
    /* 获取tags删除后的选项 */
    changeChecked = (val) => {
        ...
        this.setState({
            changedItem: val
        });
        ...
    }
    render() {
        const { checkedItem, changedItem,, checkAll, indeterminate, checkTitle } = this.state;
        return (
            ...
                <AddInfo
                       checkList={this.state.checkedItem}
                       allCheckArr={this.state.allCheckArr|| []}
                       getChecked={this.getChecked.bind(this)}
                 />
                 <Tags
                       allCheckArr={this.state.allCheckArr|| []}
                       checkAll={checkAll}
                       checkTitle={checkTitle}
                       indeterminate={indeterminate}
                       items={checkedItem}
                       changeChecked={this.changeChecked.bind(this)}
                 />
          ...
        );
    }
}

代码经过了比较大程度的删改,删除了许多无关功能,只保留了组件功能的核心部分,因此直接copy可能会有报错。。。

posted @ 2018-11-02 17:08  朝曦Z  阅读(8617)  评论(4编辑  收藏  举报