《加班搞》 第一篇 Antd Checkbox CheckboxGroup Tab多組多選聯(lián)動(dòng)
如圖所示:功能介紹:選擇角色權(quán)限 哈肖,有多個(gè)Tab谱秽,每個(gè)Tab里都有三級(jí)多選 ,我們抽象的可以理解共有三級(jí)田藐,第一級(jí)全選當(dāng)前tab所有Checkbox 宇植,二級(jí)全選右側(cè)所有Checkbox,三級(jí)多選聯(lián)動(dòng)同廉。
- Initdata 初始數(shù)據(jù)
- activeKey tab當(dāng)前頁(yè)的key
- convert_values() 處理初始數(shù)據(jù)(可忽略只針對(duì)我這個(gè)環(huán)境做的處理)
- convert_data()處理保存時(shí)的數(shù)據(jù)格式(可忽略只針對(duì)我這個(gè)環(huán)境做的處理)
- tab_convert()處理 tab數(shù)據(jù)格式(可忽略只針對(duì)我這個(gè)環(huán)境做的處理)
checkedList 的數(shù)據(jù)格式
1.Tab PermissionTabs
const PermissionTabs = ({ permission, defaultCheckedKeys, inputOnChange }) => {
const [activeKey, setActiveKey] = React.useState('node') //選擇tab的Key
const [checkedList, setCheckedList] = React.useState(convert_values(defaultCheckedKeys)) // 所有選中數(shù)據(jù)
const Initdata=convert_values(defaultCheckedKeys) // 存儲(chǔ)初始數(shù)據(jù)
const tab_convert = (keys, sign) => { // 處理數(shù)據(jù) 可忽略
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) => { // 轉(zhuǎn)換保存時(shí)的數(shù)據(jù)格式
if (JSON.stringify(checkedList) == '{}') return []
const data = Object.keys(checkedList).map(item => checkedList[item].concat(item))
return _.flattenDeep(data)
}
const convert_values = (defaultCheckedKeys = []) => { //處理初始數(shù)據(jù)
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(() => { // 存儲(chǔ)數(shù)據(jù)
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幾個(gè)狀態(tài)控制CheckboxGroup與單個(gè)的checkbox的全勾選锅劝、半勾選、無(wú)勾選幾種情況的聯(lián)動(dòng)蟆湖。Checkbox多選框
2.checkAll 當(dāng)前Tab的二級(jí)狀態(tài)故爵,也就是指每一行的左側(cè)多選
3.activeKeyCheAll 當(dāng)前Tab的一級(jí)全選狀態(tài)
4.checkedList 所有選中的數(shù)據(jù)
5.indeterminate 當(dāng)前tab里二級(jí)半選中狀態(tài)
6.allInte 當(dāng)前tab里一級(jí)半選中狀態(tài)
2. 多選組 CheckboxRow 主要邏輯
const CheckboxRow = ({ tab, checkedList, setCheckedList, activeKey, Initdata }) => {
const [ indeterminate, setIndeterminate ] = React.useState([]) //當(dāng)前tab二級(jí)控非選中狀態(tài)樣式
const [ allInte, setAllInte ] = React.useState({}) //當(dāng)前tab一級(jí)級(jí)非選中狀態(tài)樣式
const [ checkAll, setCheckAll ] = React.useState({}) //當(dāng)前tab二級(jí)選中狀態(tài)
const [ activeKeyCheAll, setActiveKeyCheAll ] = React.useState({}) //當(dāng)前tab一級(jí)狀態(tài)
// 當(dāng)前tab面板數(shù)據(jù)
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) { // 選項(xiàng)組下僅有一個(gè)選項(xiàng)時(shí)取消選中
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 = {}
// 以下為初始值與當(dāng)前tab頁(yè)數(shù)據(jù)對(duì)比處理
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) })
// 初始值驗(yàn)證數(shù)組
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
}
})
// 當(dāng)前tab頁(yè)二級(jí)選項(xiàng)選中 vs 當(dāng)前原始數(shù)據(jù) 控制一級(jí)全選
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)
}
}, [])
// 監(jiān)測(cè)當(dāng)前tab數(shù)據(jù)變化改變一級(jí) 二級(jí)狀態(tài)
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 ])
//二級(jí)全選
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))
}
}
// 一級(jí)選項(xiàng)
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="暫無(wú)數(shù)據(jù)"></Empty>
}
每次的OnChange都會(huì)把當(dāng)前勾選的父級(jí)key(parentKey)、選中的key(checkedValue)隅津、當(dāng)前行的數(shù)據(jù)(cdAllKey)傳給執(zhí)行函數(shù)更加方便處理
3.CheckboxCol 每一行的右側(cè) 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) // 轉(zhuǎn)換數(shù)據(jù)格式 { label:'',value:'' }
const cdAllKey = { [key]: options.map(item => item.value) } //點(diǎn)擊行數(shù)據(jù) key:[...]
return <CheckboxGroup options={options} value={checkedList[key]} onChange={(checkedList) => onChange(cdAllKey, checkedList, key)} />
}
4.小結(jié)
這些就是本案例代碼的核心部分诬垂,代碼還有可優(yōu)化空間 ,目前是最方便大家容易看懂的代碼結(jié)構(gòu)伦仍,后期優(yōu)化好還會(huì)再次更新结窘,希望對(duì)大家有所幫助。
5.寫作感悟
《加班搞》作為我的第一個(gè)板塊的第一期呢铆,和我第一次寫作(處女作)晦鞋,還是想說點(diǎn)什么,哈哈棺克。首先謝謝大家能看到這里 悠垛,確實(shí)沒有想到寫作也是這么挺費(fèi)神的,先要整理代碼娜谊,梳理思路确买,怎么讓大家清晰的看明白,這些都要考慮纱皆,不管怎么說也算完成了 湾趾,有不足之處望大家多多包涵,多提寶貴意派草,在這里懇請(qǐng)各位在收藏的同時(shí)也點(diǎn)一點(diǎn)贊 (?o?) 搀缠,給我一點(diǎn)鼓勵(lì)讓我繼續(xù)寫下去~~謝謝大家捧場(chǎng)!