欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Ant design vue table 单击行选中 勾选checkbox教程

程序员文章站 2022-06-22 09:27:50
最近了解ant design 设计table 单击行选中checkedbox功能,相比于element的 @row-click 再触发togglerowselection,ant design的api...

最近了解ant design 设计table 单击行选中checkedbox功能,相比于element的 @row-click 再触发togglerowselection,ant design的api就没那么清晰了,言归正传

期望:ant design table 单击行选中 勾选checkedbox

实现:

Ant design vue table 单击行选中 勾选checkbox教程

单选:

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

一、小功能大需求

先看下设计图:

Ant design vue table 单击行选中 勾选checkbox教程

需求如下:

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移除数组
  );
 }
 };

三、优化

一级选择框有三种状态,全选,二级选中某些个,未选中,三种状态对应不同的样式,如下图所示。

Ant design vue table 单击行选中 勾选checkbox教程

这种优化,就需要设置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教程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。