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工具_Form 封装
Aug 21 Javascript
一个js拖拽的效果类和dom-drag.js浅析
Jul 17 Javascript
javaScript矢量图表库-gRaphael几行代码实现精美的条形图/饼图/点图/曲线图
Jan 09 Javascript
JS获取select-option-text_value的方法
Dec 26 Javascript
jQuery树形下拉菜单特效代码分享
Aug 15 Javascript
全面解析Bootstrap弹窗的实现方法
Dec 01 Javascript
javascript基础知识分享之类与函数化
Feb 13 Javascript
实例讲解JavaScript的Backbone.js框架中的View视图
May 05 Javascript
JS正则匹配中文的方法示例
Jan 06 Javascript
详谈js中数组(array)和对象(object)的区别
Feb 27 Javascript
Javascript中引用类型传递的知识点小结
Mar 06 Javascript
Webpack 之 babel-loader文件预处理器详解
Mar 23 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中extract()函数的妙用分析
2012/07/11 PHP
PHP图像处理之imagecreate、imagedestroy函数介绍
2014/11/19 PHP
php jsonp单引号转义
2014/11/23 PHP
使用ltrace工具跟踪PHP库函数调用的方法
2016/04/25 PHP
PHP 文件锁与进程锁的使用示例
2017/08/07 PHP
超清晰的document对象详解
2007/02/27 Javascript
Javascript this 的一些学习总结
2012/08/31 Javascript
js如何改变文章的字体大小
2016/01/08 Javascript
Bootstrap打造一个左侧折叠菜单的系统模板(二)
2016/05/17 Javascript
JavaScript操作表单实例讲解(上)
2016/06/20 Javascript
Node.js中防止错误导致的进程阻塞的方法
2016/08/11 Javascript
two.js之实现动画效果示例
2017/11/06 Javascript
vue+vuex+axios实现登录、注册页权限拦截
2018/03/09 Javascript
NodeJS 将文件夹按照存放路径变成一个对应的JSON的方法
2018/10/17 NodeJs
jQuery实现模拟搜索引擎的智能提示功能简单示例
2019/01/27 jQuery
怎样使你的 JavaScript 代码简单易读(推荐)
2019/04/16 Javascript
小程序实现录音上传功能
2019/11/22 Javascript
JS实现基本的网页计算器功能示例
2020/01/16 Javascript
[01:03:03]VP vs Mineski 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
[00:57]英雄,你的补给到了!
2020/11/13 DOTA
python3访问sina首页中文的处理方法
2014/02/24 Python
python 的列表遍历删除实现代码
2020/04/12 Python
Python字符串特性及常用字符串方法的简单笔记
2016/01/04 Python
Python中不同进制的语法及转换方法分析
2016/07/27 Python
python引用(import)某个模块提示没找到对应模块的解决方法
2019/01/19 Python
win10子系统python开发环境准备及kenlm和nltk的使用教程
2019/10/14 Python
pycharm中使用request和Pytest进行接口测试的方法
2020/07/31 Python
Sneaker Studio捷克:购买运动鞋
2018/07/08 全球购物
新西兰航空中国官网:Air New Zealand China
2018/07/24 全球购物
当x.equals(y)等于true时,x.hashCode()与y.hashCode()可以不相等,这句话对不对
2015/05/02 面试题
幼儿园元旦家长感言
2014/02/27 职场文书
生日庆典策划方案
2014/06/02 职场文书
装修公司工程部经理岗位职责
2015/04/09 职场文书
SQL 聚合、分组和排序
2021/11/11 MySQL
十大冰系宝可梦排名,颜值最高的阿罗拉九尾,第三使用率第一
2022/03/18 日漫
Golang 切片(Slice)实现增删改查
2022/04/22 Golang