React Ant Design树形表格的复杂增删改操作


Posted in Javascript onNovember 02, 2020

最近因为业务接触了antd,使用antd完成一个复杂的树形表格的显示以及修改。在这其中遇见了不少坑,很多功能antd只写了初步的功能,更为细化的功能只能自己完善。踩过的坑都写在了这里。

树形表格的显示

在antd中对于表格的key值有着严格的控制,每一个row都必须有一个独一无二的key值,可以是数字也可以是字符串。这一点和我曾经使用过得iview有着很大的区别。react使用key来代表每一行是为了避免重新渲染的问题,这个优化也在实际的开发中带来了不少的问题。比如新建行时需要自定义新key。

下面直接上一下代码及代码效果,这是一个三级的树形表格,且其中包含二级标题。

最终效果 ​

React Ant Design树形表格的复杂增删改操作

colums标题: 简易版标题,随着功能的增加,我们将增加colums的复杂度。

let columns = [
 {
  title: '题目',
  dataIndex: 'text'
 },
 {
  title: '类型',
  children: [
   {
    title: '一级',
    dataIndex: 'text1'
   },
   {
    title: '二级',
    dataIndex: 'text2',
   }]
 },
 {
  title: '内容',
  dataIndex: 'content'
 },
 {
  title: '答案',
  dataIndex: 'answer',
 },
 {
  title: '类型',
  dataIndex: 'mark_type',
  className: 'line'
 },
 {
  title: '版本',
  dataIndex: 'version',
  className: 'line'
 },
 {
  title: '一级内容点',
  dataIndex: 'value1',
  className: 'line'
 },
 {
  title: '二级内容点',
  dataIndex: 'value2',
  className: 'line'
 },
 {
  title: '操作',
  key: 'action',
  width: 205
 }
];

data数据:

let data = [{
   "key": 1,
   "text": "题目一",
   "children": [{
    "key": 11,
    "text1": "数学一",
    "children":[]
   }, {
    "key": 12,
    "text1": "语文一",
    "value1": "语文",
    "children": [{
     "key": 121,
     "value2": "选择",
     "text2": "选择题",
     "content": "题目内容",
     "answer": "A",
     "mark_type": "1",
     "version": "1"
    },{
     "key": 122,
     "value2":"填空",
     "text2": "填空题",
     "content": "题目内容",
     "answer": "梅花",
     "mark_type": "1",
     "version": "1"
    },{
     "key": 123,
     "value2": "阅读",
     "text2": "阅读题",
     "content": "题目内容",
     "answer": "野蛮生长",
     "mark_type": "1",
     "version": "1"
    },{
     "key": 124,
     "value2": "文言文",
     "text2": "文言文",
     "content": "题目内容",
     "answer": "滕王阁序",
     "mark_type": "1",
     "version": "1"
    }],
   }],
  }, {
   "key": 2,
   "text": "题目二",
   "children": [ {
    "key": 21,
    "text1": "英语一",
    "value1": "英语",
    "children": [{
     "key": 211,
     "value2": "完型",
     "text2": "完形填空",
     "content": "题目内容",
     "answer": "ABC",
     "mark_type": "2",
     "version": "1"
    },{
     "key": 212,
     "value2": "一级代码",
     "text2": "选择",
     "content": "题目内容",
     "answer": "D",
     "mark_type": "2",
     "version": "1"
    }],
   }],
  }];

增加子项数据

增加子项数据使用操作中的增加按钮进行增加,增加按钮设置为图形,更为形象具体清晰化。

//button样式使用antd自带icon样式
<Button type="primary" shape="circle" icon="plus" size={'small'}
   onClick={this.handleAdd.bind(this, record)}
 />

注意事项:

1、对于最子项来说没有增加子项选择,需要对不同数据行进行不同处理

2、不同数据行新增数据的内容不同,字段也不同

3、新增之后需要点击确认或者取消

4、加入数据点击确认之后需要添加于当前key下的children中

本想使用antd自带的editable属性,但是这个属性不支持二级标题的编辑,所以只有自己写render

这个问题,在7102年就提出来了,但是已经8102年了快9102年都没有更新。

新增一级类型题目样式

React Ant Design树形表格的复杂增删改操作

新增二级类型题目样式

React Ant Design树形表格的复杂增删改操作

这里就部分render代码:

//分级标题
{
 title: '类型', 
 children: 
 [{
  title: '一级',
  dataIndex: 'text1',
  render: (text, record) => {
   if (this.state.isEditing && this.state.editingOneKey === record.key)
    return <Input defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'text1') }} />
   return text
  }
 },
 {
  title: '二级',
  dataIndex: 'text2',
  render: (text, record) => {
   if (this.state.isEditing && this.state.editingTwoKey === record.key)
    return <Input defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'text2') }} />
   return text
  }
 }]
}

//select选择
{
 title: '内容',
 dataIndex:'content',
 render:(text, record) => {
  if (text === 0)
   text = '内容一';
  if (text === 1)
   text = '内容二';
  if (this.state.isEditing && this.state.editingTwoKey === record.key) {
   return <Select defaultValue={text} onChange={(e) => {
    this.changeEdit(e, record.key, 'flag')
   }} getPopupContainer={triggerNode => triggerNode.parentNode}>
    <Option value='0'>有效</Option>
    <Option value='1'>无效</Option>
   </Select>
  }
  return text
 }
}

注意展开

如果对一行未展开的行下新增数据,那么无法看到打开的编辑状态是一个非常糟糕的问题。所以我们需要在新增子项的时候自动展开父项。

//steate初始化 
expandedRows: [] //展开行
//render 初始化
<Table
  bordered
  expandedRowKeys={this.state.expandedRows}
  onExpandedRowsChange={this.changeExpandedRows.bind(this)}
/>

//自动展开变化,获取当前展开行
changeExpandedRows = (expandedRows) => {
  this.setState({
   expandedRows
  })
 };

//增加函数中
let rows = this.state.expandedRows;
rows.push(record.key);
this.setState({
 expandedRows: rows
});

删除行数据

删除在antd中相较比较简单,因为antd的table每行都有key属性,key是唯一且必须的属性,所以只要过滤掉包含目的key的数据即可视为删除。因为这是树形数据,普通的遍历无法进行操作,所以使用深度优先遍历来进行数据处理,也可以使用广度优先,根据数据来变更。

handleDelete = (key) => {
  let data = this.state.data;
  data = dsFilter(data, key);

  this.setState({
   data
  });

  function dsFilter(dealData, dealKey) {
   for (let i = 0; i < dealData.length; i++) {
    if (dealData[i].children && dealData[i].children.length > 0) {
     dealData[i].children = dsFilter(dealData[i].children, dealKey);
    }
   }
   return dealData.filter(item => item.key !== dealKey);
  }
 };

修改某些字段

修改和新增的colums相同,不过修改需要保存之前的值。需要在input或select中设置defaultValue={text}来保证初始值相同

保存

无论是新增修改还是删除,都需要进行保存。这里使用✓和✗ 来代表确定和取消。

同时需要注意,如果在一行的编辑中点击其他行的操作,需要将之前行的操作视为取消。

React Ant Design树形表格的复杂增删改操作

<div>
 <Button shape="circle" icon="check" size={'small'} style={{ backgroundColor: '#65BF34', color: '#FFF', border: 'none' }}
   onClick={this.saveEdit.bind(this, record.key)}
 />
 <Button shape="circle" icon="close" size={'small'} style={{ backgroundColor: '#FF3333', color: '#FFF', border: 'none' }}
   onClick={this.cancelEdit.bind(this, record.key)}
 />
</div>

修改顺序

修改顺序就是修改数据在数组中的顺序,使用简单的temp进行交换即可。

shiftUp = (key) => {
  let data = this.state.data;
  data = dsShift(data, key);
  this.setState({
   data
  });

  function dsShift(dealData, dealKey) {
   for (let i = 0; i < dealData.length; i++) {
    if (dealData[i].children && dealData[i].children.length > 0) {
     dealData[i].children = dsShift(dealData[i].children, dealKey);
    }
    if (dealData[i].key === dealKey) {
     if (i === 0) {
      message.warning('该行已置顶!');
      break;
     }
     let temp = dealData[i - 1];
     dealData[i - 1] = dealData[i];
     dealData[i] = temp;
     break;
    }
   }
   return dealData;
  }
 };

 shiftDown = (key) => {
  let data = this.state.data;
  data = dsShift(data, key);
  this.setState({
   data
  });

  function dsShift(dealData, dealKey) {
   for (let i = 0; i < dealData.length; i++) {
    if (dealData[i].children && dealData[i].children.length > 0) {
     dealData[i].children = dsShift(dealData[i].children, dealKey);
    }
    if (dealData[i].key === dealKey) {
     if (i === dealData.length - 1) {
      message.warning('该行已置尾!');
      break;
     }
     let temp = dealData[i + 1];
     dealData[i + 1] = dealData[i];
     dealData[i] = temp;
     break;
    }
   }
   return dealData;
  }
 };

总结

在进行表格数据操作中,需要进行变量的保存,这里就没有多写了。总的来说antd是个好组件,大部分功能都很齐全,但是很多细节还是需要自己进行完善。

补充知识:Antd(Ant-design),嵌套子表格(expandedRowRender)的异步获取数据

使用阿里的ant-design开源框架,要在表格里面嵌套子表格,需要在用户点击父表格的一行数据后,获取该行的key,然后去异步请求后台的数据用来填充子表格的内容。

如果这样写(省略无关代码):

expandedRowRender = (record) => {
 
  dispatch({
    type: 'flow/getPlanList',
    payload: {
      contractId: record.contract_id, // 该参数是从父表格带过来的key
    },
    callback: () => {
      const {
       flow: { data },
      } = this.props; 
 
      this.setState({
       secData: data.list,
      });
 
      console.log("返回数据(PlanList):" + JSON.stringify(this.state.secData));
    }
  });
  
  return (
    <Table
      columns={secColumns}
      dataSource={this.state.secData}
      pagination={false}
    />
  );
};
 
render() {
  return(
    <Card>
     {this.renderForm()}
     <div>
       <Table
        expandedRowRender={this.expandedRowRender}
        loading={loading}
        rowSelection={rowSelection}
        dataSource={list}
        columns={columns}
        pagination={paginationProps}
        scroll={{ x: 2500}}
        size = 'middle' 
        expandRowByClick={true}
        onSelect={this.seFn}
       />
     </div>
    </Card>
  ) 
}

则会出现不断的发起请求的现象:

React Ant Design树形表格的复杂增删改操作

这是因为,expandedRowRender 实际上是在 Table 组件的 render 方法中调用的,React render 中用 dispatch 会造成重复调用的问题,dispatch -> setState -> render -> dispatch -> setState -> render,循环往复。所以应该把 dispatch 放在 onExpand 中。

onExpand = (expanded, record) => {
  const { dispatch } = this.props;
 
  dispatch({
    type: 'flow/getPlanList',
    payload: {
     contractId: record.contract_id,
    },
    callback: () => {
      const {
        flow: { data },
      } = this.props; 
 
      this.setState({
        secData: data.list ,
      });
 
     console.log("返回数据(PlanList):" + JSON.stringify(this.state.secData));
    }
  });
}

但是单纯的这样做,又会带来新的问题,就是子表格的所有数据都变成了相同的。

本人的解决办法是使用键值对。

onExpand = (expanded, record) => {
  const { dispatch } = this.props;
 
  if (expanded === false) {
   // 因为如果不断的添加键值对,会造成数据过于庞大,浪费资源,
   // 因此在每次合并的时候讲相应键值下的数据清空
   console.log("合并!");
   this.setState({
    subTabData: {
     ...this.state.subTabData,
     [record.contract_id]: [] ,
    }
   });
  } else {
   console.log("展开!");
   dispatch({
    type: 'flow/getPlanList',
    payload: {
     contractId: record.contract_id,
    },
    callback: () => {
     const {
      flow: { data },
     } = this.props; 
 
     this.setState({
      subTabData: {
       ...this.state.subTabData,
       [record.contract_id]: data.list ,
      }
     });
 
     console.log("返回数据(PlanList):" + JSON.stringify(this.state.subTabData));
    }
   });
  }
 }

以上这篇React Ant Design树形表格的复杂增删改操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js页面滚动时层智能浮动定位实现(jQuery/MooTools)
Aug 23 Javascript
用jQuery实现一些导航条切换,显示隐藏的实例代码
Jun 08 Javascript
js传参数受特殊字符影响错误的解决方法
Oct 21 Javascript
关于js中for in的缺陷浅析
Dec 02 Javascript
鼠标拖拽移动子窗体的JS实现
Feb 25 Javascript
jquery实现点击查看更多内容控制段落文字展开折叠效果
Aug 06 Javascript
JavaScript简单获取页面图片原始尺寸的方法
Jun 21 Javascript
关于JavaScript限制字数的输入框的那些事
Aug 14 Javascript
jQuery扩展实现text提示还能输入多少字节的方法
Nov 28 Javascript
Require.js的基本用法详解
Jul 03 Javascript
AngularJS中ng-options实现下拉列表的数据绑定方法
Aug 13 Javascript
vue实现权限控制路由(vue-router 动态添加路由)
Nov 04 Javascript
vue缓存之keep-alive的理解和应用详解
Nov 02 #Javascript
详解vue-router的导航钩子(导航守卫)
Nov 02 #Javascript
vue+elementUI中表格高亮或字体颜色改变操作
Nov 02 #Javascript
vue element-ui中table合计指定列求和实例
Nov 02 #Javascript
vue 动态添加class,三个以上的条件做判断方式
Nov 02 #Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
Nov 02 #Javascript
在vant 中使用cell组件 定义图标该图片和位置操作
Nov 02 #Javascript
You might like
PHP 和 MySQL 基础教程(四)
2006/10/09 PHP
php 数组二分法查找函数代码
2010/02/16 PHP
coreseek 搜索英文的问题详解
2013/06/08 PHP
PHP实现利用MySQL保存session的方法
2014/08/23 PHP
PHP实现函数内修改外部变量值的方法示例
2018/12/28 PHP
php获取微信openid方法总结
2019/10/10 PHP
JavaScript经典效果集锦
2010/07/06 Javascript
JQuery与JSon实现的无刷新分页代码
2011/09/13 Javascript
jQuery 遍历- 关于closest() 的方法介绍以及与parents()的方法区别分析
2013/04/26 Javascript
js里取容器大小、定位、距离等属性搜集整理
2013/08/19 Javascript
Area 区域实现post提交数据的js写法
2014/04/22 Javascript
jQuery实现的经典滑动门效果
2015/09/22 Javascript
jQuery ajax提交Form表单实例(附demo源码)
2016/04/06 Javascript
如何利用Promises编写更优雅的JavaScript代码
2016/05/17 Javascript
JavaScript实现点击按钮复制指定区域文本(推荐)
2016/11/25 Javascript
js正则表达式验证表单【完整版】
2017/03/06 Javascript
swiper自定义分页器使用方法详解
2020/09/14 Javascript
JS严格模式知识点总结
2018/02/27 Javascript
React Form组件的实现封装杂谈
2018/05/07 Javascript
JavaScript 声明私有变量的两种方式
2021/02/05 Javascript
从0开始的Python学习016异常
2019/04/08 Python
Pyqt QImage 与 np array 转换方法
2019/06/27 Python
在notepad++中实现直接运行python代码
2019/12/18 Python
Pytorch GPU显存充足却显示out of memory的解决方式
2020/01/13 Python
css3实现椭圆轨迹旋转的示例代码
2018/10/29 HTML / CSS
Gap工厂店:Gap Factory
2017/11/02 全球购物
美国户外烹饪产品购物网站:Outdoor Cooking
2020/01/10 全球购物
什么是重载?CTS、CLS和CLR分别做何解释
2012/05/06 面试题
市政施工员自我鉴定
2014/01/15 职场文书
项目经理聘任书
2014/03/29 职场文书
中学教师师德师风演讲稿
2014/08/22 职场文书
销售代理协议书
2014/09/30 职场文书
六年级语文下册教学计划
2015/01/22 职场文书
小学庆六一主持词
2015/06/30 职场文书
会议新闻稿
2015/07/17 职场文书
教师研修随笔感言
2015/11/18 职场文书