组件名:checkbox-select
组件截图:
文件内容:
src/pages/checkbox-select/index.js
import CheckboxSelect from 'components/checkbox-select'; function Page() { // 演示数据 const testList = [ { label: '#演示项目 1', value: 1 }, { label: '#演示项目 2', value: 2 }, { label: '#演示项目 3', value: 3 }, { label: '#演示项目 4', value: 4 }, { label: '#演示项目 5', value: 5 }, { label: '#演示项目 6', value: 6 }, { label: '#演示项目 7', value: 7 }, { label: '#演示项目 8', value: 8 }, ]; // 选择返回数据 const changeCallback = (data) => { console.log('callback data---->', data); }; return ( <> <CheckboxSelect optionsList={testList} changeCallback={changeCallback} /> </> ); } export default Page;
src/components/checkbox-select/index.js
/** * 包含多选框的下拉列表,含全选和全不选操作 * */ import { useEffect, useState, useCallback, useRef } from 'react'; import PropTypes from 'prop-types'; import { Checkbox, Button } from 'antd'; import 'less/components/checkbox-select/index.less'; function CheckboxSelect(props) { const { optionsList, changeCallback } = props; const cancelBtn = useRef(); const [showText, setShowText] = useState('全部'); const [isActive, setIsActive] = useState(false); const [checkAll, setCheckAll] = useState(true); const [indeterminate, setIndeterminate] = useState(false); const [checkboxValList, setCheckboxValList] = useState([]); const [checkboxOldValList, setCheckboxOldValList] = useState([]); // 多选框列表被触发的时候 const checkboxChange = (list) => { setCheckboxValList(list); setIndeterminate(!!list.length && list.length < optionsList.length); }; // 确定操作 const confirm = () => { setCheckboxOldValList(checkboxValList); setIsActive(false); // 返回最终数据 changeCallback(checkboxValList); // 把选择的文本显示在框框内 if (checkboxValList.length === optionsList.length) { setShowText('全部'); return; } const textArr = []; optionsList.forEach((item1) => { checkboxValList.forEach((item2) => { if (item1.value === item2) { textArr.push(item1.label); } }); }); setShowText(textArr.join(',')); }; // 取消操作 const cancel = () => { setCheckboxValList(checkboxOldValList); setIsActive(false); }; // 点击操作 const toggleClick = (ev) => { // 阻止事件冒泡 ev.nativeEvent.stopImmediatePropagation(); const status = !isActive; setIsActive(status); }; // 全选/全不选 操作 const onCheckAllChange = () => { const status = !checkAll; setCheckAll(status); // 判断是全选还是全不选 const listArr = []; if (status) { optionsList.forEach((item) => { listArr.push(item.value); }); } setCheckboxValList(listArr); setIndeterminate(false); }; // 触发内容区域 const documentEvent = () => { cancelBtn.current.click(); }; // 监听全选/全不选状态 useEffect(() => { setCheckAll(optionsList.length === checkboxValList.length); setIndeterminate(!!checkboxValList.length && checkboxValList.length < optionsList.length); }, [checkboxValList]); // 组件卸载 const unComponent = () => { // console.log('组件卸载'); document.removeEventListener('click', documentEvent); }; // 初始操作 const init = useCallback(() => { // 设置checkbox默认全选 const listArr = []; optionsList.forEach((item) => { listArr.push(item.value); }); setCheckboxValList(listArr); setCheckboxOldValList(listArr); // 空白文档处被点击 document.addEventListener('click', documentEvent); }, []); useEffect(() => { init(); return unComponent; }, []); return ( <> <div className={`checkbox-select ${isActive ? 'checkbox-select-active' : ''}`}> <div className="cs__input-wrap" onClick={toggleClick}> <div className="cs__input-text">{showText}</div> <span className="cs__input-icon"></span> </div> <div className="cs__tool" onClick={(ev) => { ev.nativeEvent.stopImmediatePropagation(); }} > <Checkbox.Group onChange={checkboxChange} value={checkboxValList}> <ul className="cs__checkbox-list"> {optionsList.length ? ( optionsList.map((item) => { return ( <li key={item.value}> <Checkbox value={item.value}>{item.label}</Checkbox> </li> ); }) ) : ( <li className="empty">暂无数据</li> )} </ul> </Checkbox.Group> <div className="cs__button" style={{ display: optionsList.length ? '' : 'none' }}> <Checkbox indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}> 全选/全不选 </Checkbox> <Button type="primary" size="small" onClick={confirm}> 确定 </Button> <Button size="small" onClick={cancel} ref={cancelBtn}> 取消 </Button> </div> </div> </div> </> ); } CheckboxSelect.propTypes = { //下拉列表数据 optionsList: PropTypes.array, // 确定按钮触发返回函数:return array changeCallback: PropTypes.func, }; CheckboxSelect.defaultProps = { optionsList: [], }; export default CheckboxSelect;
src/less/components/checkbox-select/index.less
* { padding: 0; margin: 0; list-style: none; } // --------------------------------------- 组件样式开始 --------------------------------- @keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } } .checkbox-select { margin: 0px auto; top: 100px; max-width: 260px; position: relative; .cs__input-wrap { position: relative; cursor: pointer; .cs__input-text { width: 100%; padding: 0 30px 0 10px; height: 34px; line-height: 34px; border: 1px solid #ddd; user-select: none; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } .cs__input-icon { position: absolute; display: block; right: 1px; top: 1px; height: 32px; width: 30px; &::after { content: ' '; display: block; width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 8px solid #555; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } } } .cs__tool { display: none; position: absolute; width: 100%; left: 0; top: 34px; background-color: #fff; border: 1px solid #f1f1ff; animation: fadeIn 0.5s 0s; .cs__checkbox-list { margin-bottom: 0; max-height: 200px; overflow: hidden auto; label { padding: 5px 10px; &:hover { background-color: #f1f1f1; } } .ant-checkbox-wrapper { width: 100%; } .empty { text-align: center; min-height: 100px; line-height: 100px; font-size: 12px; color: #999; } } .ant-checkbox-group { padding-top: 5px; width: 100%; } .cs__button { width: 100%; border-top: 1px solid #f1f1f1; padding: 5px 10px; display: inline-block; label { font-size: 12px; user-select: none; color: #555; } button { margin: 0 5px; float: right; } .ant-btn-primary { margin-right: 0; } } } } .checkbox-select-active { .cs__tool { display: block; } }