Antdesignvuetable单击⾏选中勾选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的onSel
ect,可以设置选择/取消选择某⾏的回调。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){      retur  });
  //搜索的值不为空时,返回搜索过滤后都数据(因为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
antdesignvuetable动态表头.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);