Antd Checkbox CheckboxGroup Tab 實(shí)現(xiàn)多組多選聯(lián)動(dòng)

《加班搞》 第一篇 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)同廉。

如圖所示:
功能圖

數(shù)據(jù)格式:
數(shù)據(jù)格式
  1. Initdata 初始數(shù)據(jù)
  2. activeKey tab當(dāng)前頁(yè)的key
  3. convert_values() 處理初始數(shù)據(jù)(可忽略只針對(duì)我這個(gè)環(huán)境做的處理)
  4. convert_data()處理保存時(shí)的數(shù)據(jù)格式(可忽略只針對(duì)我這個(gè)環(huán)境做的處理)
  5. tab_convert()處理 tab數(shù)據(jù)格式(可忽略只針對(duì)我這個(gè)環(huán)境做的處理)

checkedList 的數(shù)據(jù)格式

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)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末近迁,一起剝皮案震驚了整個(gè)濱河市艺普,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖歧譬,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岸浑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瑰步,警方通過查閱死者的電腦和手機(jī)矢洲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缩焦,“玉大人读虏,你說我怎么就攤上這事∩嘟纾” “怎么了掘譬?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)呻拌。 經(jīng)常有香客問我葱轩,道長(zhǎng),這世上最難降的妖魔是什么藐握? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任靴拱,我火速辦了婚禮,結(jié)果婚禮上猾普,老公的妹妹穿的比我還像新娘袜炕。我一直安慰自己,他們只是感情好初家,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布偎窘。 她就那樣靜靜地躺著,像睡著了一般溜在。 火紅的嫁衣襯著肌膚如雪陌知。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天掖肋,我揣著相機(jī)與錄音仆葡,去河邊找鬼。 笑死志笼,一個(gè)胖子當(dāng)著我的面吹牛沿盅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纫溃,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼腰涧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了紊浩?” 一聲冷哼從身側(cè)響起窖铡,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤揍很,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后万伤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呜袁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年敌买,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阶界。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虹钮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膘融,到底是詐尸還是另有隱情芙粱,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布氧映,位于F島的核電站春畔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岛都。R本人自食惡果不足惜律姨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臼疫。 院中可真熱鬧择份,春花似錦、人聲如沸烫堤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸽斟。三九已至拔创,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間湾盗,已是汗流浹背伏蚊。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留格粪,地道東北人躏吊。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像帐萎,于是被迫代替她去往敵國(guó)和親比伏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容