Ant design vue table 单击行选中 勾选checkbox教程
最近了解ant design 设计table 单击行选中checkedbox功能,相比于element的 @row-click 再触发togglerowselection,ant design的api就没那么清晰了,言归正传
期望:ant design table 单击行选中 勾选checkedbox
实现:
单选:
onclickrow(record) { return { on: { click: () => { let keys = []; keys.push(record.id); this.selectedrowkeys = keys; } } } }
多选:
onclickrowmulti(record) { return { on: { click: () => { let rowkeys=this.selectedrowkeys if(rowkeys.length>0 && rowkeys.includes(record.id)){ rowkeys.splice(rowkeys.indexof(record.id),1) }else{ rowkeys.push(record.id) } this.selectedrowkeys = rowkeys; } } } }
补充知识:使用ant design的table和checkbox模拟tree
一、小功能大需求
先看下设计图:
需求如下:
1、一级选中(取消选中),该一级下的二级全部选中(取消选中)
2、二级全选,对应的一级选中,二级未全选中,对应的一级不选中
3、支持搜索,只搜索二级数据,并且只展示搜索到的数据以及对应的一级title,如:搜索“店员”,此时一级只展示咖啡厅....其他一级隐藏,二级只展示店员,其他二级隐藏
4、搜索出来的数据,一级不可选中,即不允许全选,搜索框清空时,回归初始化状态
5、搜索后,自动展开所有二级,默认情况下收起所有二级
看到图的时候,第一反应就是使用tree就能搞定,但是翻阅了文档后,发现tree并不能全部完成,所以就只能使用其他组件进行拼装,最后发现使用table和checkbox可以完美实现。
二、逐步完成需求
如果不想看这些,可直接到最后,有完整代码。。。。。。
1、页面构建
这个就不用多说,只是一个简单的table嵌套checkbox,具体可去查看文档,直接贴代码,因为是布局,所有可以忽略代码中的事件。
注意一点:因为搜索时,会改变数据,所以需要将初始化的数据进行保存。
import react, { usestate, useref, useeffect } from "react"; import { table, input, checkbox } from "antd"; const { search } = input; export default () => { const initialdata: any = useref([]); //使用useref创建initialdata const [data, setdata] = usestate([ { key: 1, title: "普通餐厅(中餐/日料/西餐厅)", checkboxdata: [ { key: 12, title: "普通服务员" }, { key: 13, title: "收银" }, { key: 14, title: "迎宾/接待" }, ], }, { key: 2, title: "零售/快消/服装", checkboxdata: [ { key: 17, title: "基础店员" }, { key: 19, title: "收银员" }, { key: 20, title: "理货员" }, ], }, ]); useeffect(() => { initialdata.current = [...data]; //设置初始化值 }, []); const [checkedjob, setcheckedjob] = usestate([]); //设置子级中选择的类 const [selectedrowkeys, setselectedrowkeys] = usestate<any>([]); //设置选择的行 const expandedrowrender = (record: any) => { return ( <div style={{ paddingleft: 50, boxsizing: "border-box" }}> <p>请选择岗位,或勾选类别全选岗位</p> <div> <checkbox.group value={checkedjob}> {record.checkboxdata.map((item: any) => { return ( <checkbox value={item.key} key={item.key} onchange={checkchange} > {item.title} </checkbox> ); })} </checkbox.group> </div> </div> ); }; const rowselection = { selectedrowkeys, }; return ( <div style={{ background: "#fff", padding: 24, boxsizing: "border-box", width: 982, }} > <search placeholder="请输入岗位名称" onsearch={(value) => { console.log(loop(value)); }} /> <table showheader={false} columns={columns} expandable={{ expandedrowrender, }} datasource={data} pagination={false} rowselection={rowselection} /> </div> ); }; const columns = [{ title: "title", dataindex: "title", key: "title" }];
2、一级选中(取消全选)
当一级选中(取消全选)时,需要更新对应二级选项的状态。在antd文档中,使用rowselection的onselect,可以设置选择/取消选择某行的回调。
onselect:(record,selected)=> record:操作当前行的数据,selected:true:全选,false:取消全选
注意:当全选时,不能直接添加当前一级下的所有二级,需要过滤掉当前已经选中的二级
具体逻辑如下代码:
//首选在rowselection配置中添加onselectconst rowselection = { selectedrowkeys, onselect }; //一级全选或者取消的逻辑 const onselect = (record: any, selected: any) => { //因为存在搜索,所以需要使用我们的初始化数据,找到当前record.key在初始化数据中对应的数据 let initialparent = initialdata.current.find( (d: any) => d.key === record.key ); //初始化数据中对应的二级数据 let selectparentdata = initialparent.checkboxdata ? initialparent.checkboxdata.map((d: any) => d.key) : []; if (selected) { //全选 //向selectrowkeys添加选中的值 setselectedrowkeys([...selectedrowkeys, record.key]); //更新child数组,将selectparentdata中的数据全部过滤添加 setcheckedjob(array.from(new set([...checkedjob, ...selectparentdata]))); } else { //取消全选 //从父级数组中移除key值 setselectedrowkeys( [...selectedrowkeys].filter((d: any) => d !== record.key) ); //更新child数组,将selectparentdata中的数据全部过滤掉 let newarr: any = []; [...checkedjob].foreach((v) => { if (selectparentdata.indexof(v) === -1) { newarr.push(v); } }); setcheckedjob(newarr); } };
3、二级选中或取消选中逻辑
二级选中或者取消比较简单,只要注意在选中时,如何去考虑是否所有二级全部选中即可。具体代码如下。
//判断b数组中的数据是否全部在a数组中 const iscontained = (a: any, b: any) => { if (!(a instanceof array) || !(b instanceof array)) return false; if (a.length < b.length) return false; var astr = a.tostring(); for (var i = 0, len = b.length; i < len; i++) { if (astr.indexof(b[i]) == -1) return false; } return true; }; //设置checkbox的onchange事件 const checkchange = (e: any) => { let praentrowskey: any; //找到选中的二级对应的父级key initialdata.current.foreach((v: any) => { if (v.checkboxdata.find((d: any) => d.key === e.target.value)) { praentrowskey = v.key; } }); if (e.target.checked) { //选中时 设置当前的check数组 let newcheckedjob = [...checkedjob, e.target.value]; setcheckedjob(newcheckedjob); //判断当前二级的内容是否全部被选中,如果全部选中,则需要设置selectedrowkeys //praentrowskey下的所有子元素 let childarr = initialdata.current .find((d: any) => d.key === praentrowskey) ?.checkboxdata?.map((i: any) => i.key); // 为当前选择之后的新数组 if (iscontained(newcheckedjob, childarr)) { //全部包含,设置父级 setselectedrowkeys([...selectedrowkeys, praentrowskey]); } } else { //取消选中 设置当前的child数组 setcheckedjob( [...checkedjob].filter((d: number) => d !== e.target.value) ); //判断当前父级中是否存在praentrowskey,存在则去除 if (!!~selectedrowkeys.indexof(praentrowskey)) { setselectedrowkeys( [...selectedrowkeys].filter((d: any) => d !== praentrowskey) ); } } };
4、搜索过滤
前3步骤完成后,目前来说,正常的一级二级联动已经完成,现在进行第4步,搜索过滤。
简单的说,搜索的时候,只要改变我们的data,就可以重新渲染table,这样就可以达成搜索过滤的效果。具体代码如下
//search组件搜索时,触发更改data<search placeholder="请输入岗位名称" onsearch={(value) => { setdata(loop(value)); }} /> //搜索岗位时,进行过滤 const loop = (searchvalue: any) => { let loopdata = initialdata.current?.map((item: any) => { //判断一级是否包含该搜索内容 let parentkey = !!~item.title.indexof(searchvalue); let childrendata: any = []; if (item.checkboxdata) { //如果存在二级,则进行二级的循环,过滤出搜索到的value childrendata = item.checkboxdata.filter( (d: any) => !!~d.title.indexof(searchvalue) ); } //如果一级有,二级没有,则展示一级下所有的二级 //如果一级没有,二级有,则只展示存在的二级以及对应的一级 //如果一级有,二级有,则展示存在的二级以及对应的一级 //如果一级没有,二级也没有,则不展示 if(parentkey&&!childrendata.length){ return { title:item.title, key:item.key, checkboxdata:item.checkboxdata } }else if((!parentkey || parentkey)&&childrendata.length){ return{ title:item.title, key:item.key, checkboxdata:childrendata } }else{ } }); //搜索的值不为空时,返回搜索过滤后都数据(因为map出来的数据中有undefined,所以需要再次进行过滤),为空时返回初始化数据 return searchvalue ? loopdata.filter((d: any) => d) : initialdata.current; };
5、搜索后,禁止一级全选和取消全选
动态控制table的选择功能,需要使用rowselection的getcheckboxprops。具体代码如下。
const [selectalldisabled, setselectalldisabled] = usestate<boolean>(false); //声明一个变量,控制是否允许选择,默认为false //在rowselection中添加getcheckboxprops const rowselection = { selectedrowkeys, onselect, getcheckboxprops: (record: any) => ({ disabled: selectalldisabled, //true:禁止,false:允许 }), }; //在搜索的时候设置 const loop = (searchvalue: any) => { ... setselectalldisabled(searchvalue ? true : false); //当搜索内容为空时,因为回到的是初始值,所以需要它允许选择,搜索内容不为空时,禁止选择 ... };
6、设置自动展开
前5步完成后,如果不需要设置自动展开,则该功能就可以到此结束。
设置自动展开,需要用到expandable中的onexpand以及expandedrowkeys
expandedrowkeys:展开的行,控制属性
onexpand:点击展开图标时触发,(expanded,record)=> expanded:true:展开,false:收起。record:操作的当前行的数据
具体代码如下:
const [expandedrowkeys, setexpandedrowkeys] = usestate<any>([]); //声明变量设置展开的行,默认全都收起 //table的 expandable添加 onexpand,expandedrowkeys <table expandable={{ expandedrowrender, onexpand, expandedrowkeys, }} /> //搜索时改变状态 const loop = (searchvalue: any) => { ... //有数据时自动展开所有搜索到的,无数据的时候默认全部收起 setexpandedrowkeys( searchvalue ? initialdata.current.map((d: any) => d.key) : [] ); ... }; //控制表格的展开收起 const onexpand = (expanded: any, record: any) => { if (expanded) { setexpandedrowkeys([...expandedrowkeys, record.key]); //展开时,将需要展开的key添加到数组中 } else { setexpandedrowkeys( [...expandedrowkeys].filter((d: any) => d !== record.key) //收起时,将该key移除数组 ); } };
三、优化
一级选择框有三种状态,全选,二级选中某些个,未选中,三种状态对应不同的样式,如下图所示。
这种优化,就需要设置rowselection的rendercell(注意,rendercell在antd的4.1+版本才能生效),配合checkbox进行更改。具体代码如下。
1、设置rendercell
将我们在第二步和第五步设置的onselect以及getcheckboxprops隐藏,再配置rendercell
const rowselection = { selectedrowkeys, // onselect, // getcheckboxprops: (record: any) => ({ // disabled: selectalldisabled, // }), rendercell: (checked: any, record: any) => { //当前record.key对应大初始化数据的一级所有数据 let parentarr = initialdata?.current?.find( (d: any) => d.key === record.key ); //从所有已经选择过的数据中过滤出在parentarr中的数据 let checkarr = parentarr?.checkboxdata?.filter( (item: any) => checkedjob.indexof(item.key) > -1 ); return ( <checkbox indeterminate={ parentarr?.checkboxdata && !!checkarr?.length && checkarr.length < parentarr.checkboxdata.length ? true : false } //比较 当过滤后选中数据的长度 < 初始化数据的长度时,设置 indeterminate 状态为true,否则为false onclick={(e) => onclick(e, record)} checked={checked} disabled={selectalldisabled} ></checkbox> ); }, };
2、设置onclick事件
onclick事件其实就是原来的onselect,具体代码如下
const onclick = (e: any, record: any) => { //存在搜索时,需要进行处理selectparentdata let initialparent = initialdata.current.find( (d: any) => d.key === record.key ); let selectparentdata = initialparent.checkboxdata ? initialparent.checkboxdata.map((d: any) => d.key) : []; if (e.target.checked) { //向选中数组中添加key值 setselectedrowkeys([...selectedrowkeys, record.key]); //更新child数组,将selectparentdata中的数据全部过滤添加 setcheckedjob(array.from(new set([...checkedjob, ...selectparentdata]))); } else { //从父级数组中移除key值 setselectedrowkeys( [...selectedrowkeys].filter((d: any) => d !== record.key) ); //更新child数组,将selectparentdata中的数据全部过滤掉 let newarr: any = []; [...checkedjob].foreach((v) => { if (selectparentdata.indexof(v) === -1) { newarr.push(v); } }); setcheckedjob(newarr); } };
四、完整代码
table+checkbox模拟tree完整代码
import react, { usestate, useref, useeffect } from "react"; import { table, input, checkbox } from "antd"; const { search } = input; export default () => { const initialdata: any = useref([]); const [data, setdata] = usestate([ { key: 1, title: "普通餐厅(中餐/日料/西餐厅)", checkboxdata: [ { key: 12, title: "普通服务员" }, { key: 13, title: "收银" }, { key: 14, title: "迎宾/接待" }, ], }, { key: 2, title: "零售/快消/服装", checkboxdata: [ { key: 17, title: "基础店员" }, { key: 19, title: "收银员" }, { key: 20, title: "理货员" }, ], }, ]); useeffect(() => { initialdata.current = [...data]; //设置初始化值 }, []); const [checkedjob, setcheckedjob] = usestate([12]); //设置选择的二级 const [selectedrowkeys, setselectedrowkeys] = usestate<any>([]); //设置选择的行 const [expandedrowkeys, setexpandedrowkeys] = usestate<any>([]); //设置展开的行 const [selectalldisabled, setselectalldisabled] = usestate<boolean>(false); //选择的时候,禁止全选 //搜索岗位时,进行过滤 const loop = (searchvalue: any) => { let loopdata = initialdata.current?.map((item: any) => { let parentkey = !!~item.title.indexof(searchvalue); let childrendata: any = []; if (item.checkboxdata) { //如果存在二级,则进行二级的循环,过滤出搜索到的value childrendata = item.checkboxdata.filter( (d: any) => !!~d.title.indexof(searchvalue) ); } //1.如果一级有,二级没有,则展示一级下所有的二级 //2.如果一级没有,二级有,则只展示存在的二级以及对应的一级 //3.如果一级有,二级有,则展示则存在的二级以及对应的一级 //4.如果一级没有,二级也没有,则不展示 if (parentkey && !childrendata.length) { return { title: item.title, key: item.key, checkboxdata: item.checkboxdata, }; } else if ((!parentkey || parentkey) && childrendata.length) { return { title: item.title, key: item.key, checkboxdata: childrendata, }; } else { } }); setselectalldisabled(searchvalue ? true : false); //有数据时自动展开所有搜索到的,无数据的时候默认全部收起 setexpandedrowkeys( searchvalue ? initialdata.current.map((d: any) => d.key) : [] ); return searchvalue ? loopdata.filter((d: any) => d) : initialdata.current; }; const iscontained = (a: any, b: any) => { if (!(a instanceof array) || !(b instanceof array)) return false; if (a.length < b.length) return false; var astr = a.tostring(); for (var i = 0, len = b.length; i < len; i++) { if (astr.indexof(b[i]) == -1) return false; } return true; }; const checkchange = (e: any) => { let praentrowskey: any; //找到点击child到一级key initialdata.current.foreach((v: any) => { if (v.checkboxdata.find((d: any) => d.key === e.target.value)) { praentrowskey = v.key; } }); if (e.target.checked) { //选中时 设置当前的child数组 let newcheckedjob = [...checkedjob, e.target.value]; setcheckedjob(newcheckedjob); //判断当前child的内容是否全部被选中,如果全部选中,则需要设置selectedrowkeys //praentrowskey下的所有子元素 let childarr = initialdata.current .find((d: any) => d.key === praentrowskey) ?.checkboxdata?.map((i: any) => i.key); // 为当前选择之后的新数组 if (iscontained(newcheckedjob, childarr)) { //全部包含,设置父级 setselectedrowkeys([...selectedrowkeys, praentrowskey]); } } else { //取消选中 设置当前的child数组 setcheckedjob( [...checkedjob].filter((d: number) => d !== e.target.value) ); //判断当前父级中是否存在praentrowskey,存在则去除 if (!!~selectedrowkeys.indexof(praentrowskey)) { setselectedrowkeys( [...selectedrowkeys].filter((d: any) => d !== praentrowskey) ); } } }; //父节点变化时,进行的操作 // const onselect = (record: any, selected: any) => { // //存在搜索时,需要进行处理selectparentdata // let initialparent = initialdata.current.find( // (d: any) => d.key === record.key // ); // let selectparentdata = initialparent.checkboxdata // ? initialparent.checkboxdata.map((d: any) => d.key) // : []; // if (selected) { // //向选中数组中添加key值 // setselectedrowkeys([...selectedrowkeys, record.key]); // //更新child数组,将selectparentdata中的数据全部过滤添加 // setcheckedjob(array.from(new set([...checkedjob, ...selectparentdata]))); // } else { // //从父级数组中移除key值 // setselectedrowkeys( // [...selectedrowkeys].filter((d: any) => d !== record.key) // ); // //更新child数组,将selectparentdata中的数据全部过滤掉 // let newarr: any = []; // [...checkedjob].foreach((v) => { // if (selectparentdata.indexof(v) === -1) { // newarr.push(v); // } // }); // setcheckedjob(newarr); // } // }; //控制表格的展开收起 const onexpand = (expanded: any, record: any) => { //expanded: true展开,false:关闭 if (expanded) { setexpandedrowkeys([...expandedrowkeys, record.key]); } else { setexpandedrowkeys( [...expandedrowkeys].filter((d: any) => d !== record.key) ); } }; const onclick = (e: any, record: any) => { //存在搜索时,需要进行处理selectparentdata let initialparent = initialdata.current.find( (d: any) => d.key === record.key ); let selectparentdata = initialparent.checkboxdata ? initialparent.checkboxdata.map((d: any) => d.key) : []; if (e.target.checked) { //向选中数组中添加key值 setselectedrowkeys([...selectedrowkeys, record.key]); //更新child数组,将selectparentdata中的数据全部过滤添加 setcheckedjob(array.from(new set([...checkedjob, ...selectparentdata]))); } else { //从父级数组中移除key值 setselectedrowkeys( [...selectedrowkeys].filter((d: any) => d !== record.key) ); //更新child数组,将selectparentdata中的数据全部过滤掉 let newarr: any = []; [...checkedjob].foreach((v) => { if (selectparentdata.indexof(v) === -1) { newarr.push(v); } }); setcheckedjob(newarr); } }; const expandedrowrender = (record: any) => { return ( <div style={{ paddingleft: 50, boxsizing: "border-box" }}> <p>请选择岗位,或勾选类别全选岗位</p> <div> <checkbox.group value={checkedjob}> {record.checkboxdata.map((item: any) => { return ( <checkbox value={item.key} key={item.key} onchange={checkchange} > {item.title} </checkbox> ); })} </checkbox.group> </div> </div> ); }; const rowselection = { selectedrowkeys, // onselect, // getcheckboxprops: (record: any) => ({ // disabled: selectalldisabled, // }), rendercell: (checked: any, record: any) => { //当前record.key对应大初始化数据的一级所有数据 let parentarr = initialdata?.current?.find( (d: any) => d.key === record.key ); //从所有已经选择过的数据中过滤出在parentarr中的数据 let checkarr = parentarr?.checkboxdata?.filter( (item: any) => checkedjob.indexof(item.key) > -1 ); return ( <checkbox indeterminate={ parentarr?.checkboxdata && !!checkarr?.length && checkarr.length < parentarr.checkboxdata.length ? true : false } //比较 当过滤后选中数据的长度 < 初始化数据的长度时,设置 indeterminate 状态为true,否则为false onclick={(e) => onclick(e, record)} checked={checked} disabled={selectalldisabled} ></checkbox> ); }, }; return ( <div style={{ background: "#fff", padding: 24, boxsizing: "border-box", width: 982, }} > <search placeholder="请输入岗位名称" onsearch={(value) => { console.log(loop(value)); setdata(loop(value)); }} /> <table showheader={false} columns={columns} expandable={{ expandedrowrender, onexpand, expandedrowkeys, }} datasource={data} pagination={false} rowselection={rowselection} /> </div> ); }; const columns = [{ title: "title", dataindex: "title", key: "title" }];
以上这篇ant design vue table 单击行选中 勾选checkbox教程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
上一篇: 华硕笔记本声卡驱动无法安装的解决办法
下一篇: 详解c# SpinWait