《加班搞》 第一篇 Antd Checkbox CheckboxGroup Tab多组多选联动
如图所示:功能介绍:选择角色权限 ,有多个Tab,每个Tab里都有三级多选 ,我们抽象的可以理解共有三级,第一级全选当前tab所有Checkbox ,二级全选右侧所有Checkbox,三级多选联动。
- Initdata 初始数据
- activeKey tab当前页的key
- convert_values() 处理初始数据(可忽略只针对我这个环境做的处理)
- convert_data()处理保存时的数据格式(可忽略只针对我这个环境做的处理)
- tab_convert()处理 tab数据格式(可忽略只针对我这个环境做的处理)
checkedList 的数据格式
1.Tab PermissionTabs
const PermissionTabs = ({ permission, defaultCheckedKeys, inputOnChange }) => {
const [activeKey, setActiveKey] = React.useState('node') //选择tab的Key
const [checkedList, setCheckedList] = React.useState(convert_values(defaultCheckedKeys)) // 所有选中数据
const Initdata=convert_values(defaultCheckedKeys) // 存储初始数据
const tab_convert = (keys, sign) => { // 处理数据 可忽略
if (!keys) return []
let tabs = []
keys.map(key => {
permission.map(tab => {
if (sign) {
// if (tab.key.toLowerCase() != key.toLowerCase()) tabs.push(tab)
} else {
if (tab.key.toLowerCase() == key.toLowerCase()) tabs.push(tab)
}
})
})
return tabs
}
const convert_data = (checkedList) => { // 转换保存时的数据格式
if (JSON.stringify(checkedList) == '{}') return []
const data = Object.keys(checkedList).map(item => checkedList[item].concat(item))
return _.flattenDeep(data)
}
const convert_values = (defaultCheckedKeys = []) => { //处理初始数据
const arr = defaultCheckedKeys.reduce((prev, cur, index, arr) => {
if (cur.indexOf('.') != -1 && cur.indexOf('-') == -1) {
const key_code = cur.split('.')
prev[key_code[0]] = arr.filter(key => key.indexOf(key_code[0] + '.') != -1)
}
return prev
}, {})
return arr
}
React.useEffect(() => { // 存储数据
inputOnChange(convert_data(checkedList))
}, [checkedList])
const callback = (key) => setActiveKey(key)
return (
<Tabs activeKey={activeKey} onChange={callback}>
{tabs.map(tab => (
<TabPane tab={tab.name} key={tab.key}>
<CheckboxRow tab={tab}
activeKey={activeKey}
checkedList={checkedList}
setCheckedList={setCheckedList}
Initdata={Initdata}
/>
</TabPane>
))}
</Tabs>
)
}
1.主要由Antd Checkbox的API ,checkedValue、checked,indeterminate几个状态控制CheckboxGroup与单个的checkbox的全勾选、半勾选、无勾选几种情况的联动。Checkbox多选框
2.checkAll 当前Tab的二级状态,也就是指每一行的左侧多选
3.activeKeyCheAll 当前Tab的一级全选状态
4.checkedList 所有选中的数据
5.indeterminate 当前tab里二级半选中状态
6.allInte 当前tab里一级半选中状态
2. 多选组 CheckboxRow 主要逻辑
const CheckboxRow = ({ tab, checkedList, setCheckedList, activeKey, Initdata }) => {
const [ indeterminate, setIndeterminate ] = React.useState([]) //当前tab二级控非选中状态样式
const [ allInte, setAllInte ] = React.useState({}) //当前tab一级级非选中状态样式
const [ checkAll, setCheckAll ] = React.useState({}) //当前tab二级选中状态
const [ activeKeyCheAll, setActiveKeyCheAll ] = React.useState({}) //当前tab一级状态
// 当前tab面板数据
const tabpanel = tabs.filter((item) => item.key == activeKey)[0] || []
const checks = tabpanel && tabpanel.child
// 单选
const onChange = (cdAllKey, checkedkeys, parentKey) => {
let checkKey = {};
let checked_list = {}
checked_list[parentKey] = checkedkeys
Object.keys(cdAllKey).forEach((key) => {
checkKey[key] = 0;
for (let checkedItem of checkedkeys || []) {
if (cdAllKey[key].indexOf(checkedItem) != -1) {
checkKey[key]++;
checkAll[key] = checkKey[key] === cdAllKey[key].length;
indeterminate[key] = !!checkKey[key] && (checkKey[key] < cdAllKey[key].length);
}
}
if (checkKey[key] === 0) { // 选项组下仅有一个选项时取消选中
checkAll[key] = false;
indeterminate[key] = false
}
})
setCheckedList({ ...checkedList, ...checked_list })
setIndeterminate(indeterminate)
setCheckAll(checkAll)
}
// 初始化
React.useEffect(() => {
if (JSON.stringify(Initdata) != '{}') {
let Indet_erminate = {}, check_All = {}, currPanel = {}
// 以下为初始值与当前tab页数据对比处理
const Initdatas = Object.keys(Initdata).reduce((prev, cur) => {
prev[cur] = Initdata[cur]
return prev
}, {})
checks.forEach(item => { currPanel[item.key] = item.children.map(k => k.key) })
// 初始值验证数组
Object.keys(Initdatas).forEach(key => {
const currlength = currPanel[key] && currPanel[key].length
const Initlength = Initdatas[key] && Initdatas[key].length
if (currlength <= Initlength) {
check_All[key] = true
} else if (Initlength < currlength) {
Indet_erminate[key] = true
}
})
// 当前tab页二级选项选中 vs 当前原始数据 控制一级全选
const check_AllKeys = Object.keys(check_All)
const currPanelKeys = Object.keys(currPanel)
const active_KeyCheAll = {}, All_Inte = {}
if (check_AllKeys.length == currPanelKeys.length) {
active_KeyCheAll[activeKey] = true
} else if (check_AllKeys.length < currPanelKeys.length) {
All_Inte[activeKey] = true
}
setIndeterminate(Indet_erminate)
setCheckAll(check_All)
setAllInte(All_Inte)
setActiveKeyCheAll(active_KeyCheAll)
}
}, [])
// 监测当前tab数据变化改变一级 二级状态
React.useEffect(() => {
const All = Object.keys(checkAll).filter(key => checkAll[key]) || []
const Checks = checks.map(item => item.key) || []
const indeterminateaArr = Object.keys(indeterminate).filter(key => indeterminate[key] === true) || []
if (All.length === Checks.length) {
allInte[activeKey] = false
let activeKey_CheAll = {}
activeKey_CheAll[activeKey] = true
setActiveKeyCheAll({ ...activeKeyCheAll, ...activeKey_CheAll })
setAllInte(allInte)
} else if (((All.length > 0) && (All.length < Checks.length)) || indeterminateaArr.length) {
allInte[activeKey] = true
setAllInte(allInte)
setActiveKeyCheAll(_.omit(activeKeyCheAll, activeKey))
}
}, [ checkedList ])
//二级全选
const onCheckAllChange = (e, plainOptions, parentKey) => {
const checked = e.target.checked
if (checked === true) {
let checked_list = {}
checked_list[parentKey] = plainOptions.map(item => item.value)
checkAll[parentKey] = true
setCheckedList({ ...checkedList, ...checked_list })
setIndeterminate(_.omit(indeterminate, parentKey))
setCheckAll(checkAll)
} else {
setCheckedList(_.omit(checkedList, parentKey))
setCheckAll(_.omit(checkAll, parentKey))
}
}
// 一级选项
const onAllChange = (e) => {
const checked = e.target.checked
let checkData = {}
if (checked === true) {
checks.forEach(item => {
checkData[item.key] = item && item.children && item.children.map(check => check.key)
checkAll[item.key] = true
indeterminate[item.key] = false
})
activeKeyCheAll[activeKey] = true
setCheckedList({ ...checkedList, ...checkData })
setIndeterminate(indeterminate)
setCheckAll(checkAll)
setActiveKeyCheAll(activeKeyCheAll)
} else {
checks.forEach(item => {
checkedList = _.omit(checkedList, item.key)
checkAll[item.key] = false
indeterminate[item.key] = false
})
activeKeyCheAll[activeKey] = false
setCheckedList({ ...checkedList })
setIndeterminate(indeterminate)
setCheckAll(checkAll)
setActiveKeyCheAll(activeKeyCheAll)
}
}
return tab.child && tab.child.length > 0 ?
<div><Checkbox style={{ margin: '0px 0px 10px 10px' }} indeterminate={allInte[activeKey]}
checked={activeKeyCheAll[activeKey]} onChange={onAllChange}>全选</Checkbox> {
tab.child.map(cd => (
<Row gutter={[ 3, 16 ]} >
<Col span={6}>
<div className="Tabs-Col" >
<Checkbox key={cd.key} indeterminate={indeterminate[cd.key]} checked={checkAll[cd.key]}
onChange={(e) => onCheckAllChange(e, toLable(cd && cd.children || []), cd.key)}>
{cd.title}
</Checkbox></div>
</Col>
<Col span={18} >
<div className="Tabs-Col Tabs-Col2">
{cd && cd.children && cd.children.length > 0 ? <CheckboxCol cd={cd} checkedList={checkedList} onChange={onChange} /> : null}
</div>
</Col>
</Row>
))}</div> : <Empty image={require('./../../../icon/homeIcon/noData.png')} imageStyle={{ height: 100 }} description="暂无数据"></Empty>
}
每次的OnChange都会把当前勾选的父级key(parentKey)、选中的key(checkedValue)、当前行的数据(cdAllKey)传给执行函数更加方便处理
3.CheckboxCol 每一行的右侧 Checkbox
const toLable = (data) => {
if (data && data.length > 0) {
return data.map(da => ({ label: da.title, value: da.key }))
}
return []
}
const CheckboxCol = (props) => {
const { checkedList, onChange, cd: { key, children } } = props
const options = toLable(children) // 转换数据格式 { label:'',value:'' }
const cdAllKey = { [key]: options.map(item => item.value) } //点击行数据 key:[...]
return <CheckboxGroup options={options} value={checkedList[key]} onChange={(checkedList) => onChange(cdAllKey, checkedList, key)} />
}
4.小结
这些就是本案例代码的核心部分,代码还有可优化空间 ,目前是最方便大家容易看懂的代码结构,后期优化好还会再次更新,希望对大家有所帮助。
5.写作感悟
《加班搞》作为我的第一个板块的第一期,和我第一次写作(处女作),还是想说点什么,哈哈。首先谢谢大家能看到这里 ,确实没有想到写作也是这么挺费神的,先要整理代码,梳理思路,怎么让大家清晰的看明白,这些都要考虑,不管怎么说也算完成了 ,有不足之处望大家多多包涵,多提宝贵意,在这里恳请各位在收藏的同时也点一点赞 (๑o๑) ,给我一点鼓励让我继续写下去~~谢谢大家捧场!