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 相关文章推荐
JQuery 学习笔记 选择器之五
Jul 23 Javascript
js 控制下拉菜单刷新的方法
Mar 03 Javascript
Javascript中函数名.length属性用法分析(对比arguments.length)
Sep 16 Javascript
jQuery实现上传图片前预览效果功能
Aug 03 jQuery
ES6中数组array新增方法实例总结
Nov 07 Javascript
node实现socket链接与GPRS进行通信的方法
May 20 Javascript
vue elementUI使用tabs与导航栏联动
Jun 21 Javascript
Vue函数式组件的应用实例详解
Aug 30 Javascript
js实现转动骰子模型
Oct 24 Javascript
Vue基于iview table展示图片实现点击放大
Aug 05 Javascript
如何优化vue打包文件过大
Apr 13 Vue.js
Vue2项目中对百度地图的封装使用详解
Jun 16 Vue.js
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人民币金额转大写实例代码
2015/10/02 PHP
PHP排序二叉树基本功能实现方法示例
2018/05/26 PHP
php 使用mpdf实现指定字段配置字体样式的方法
2019/07/29 PHP
DIV外区域Click后关闭DIV的实现代码
2011/12/21 Javascript
JS实现Enter键跳转及控件获得焦点
2013/08/12 Javascript
js调用css属性写法
2013/09/21 Javascript
javascript模拟地球旋转效果代码实例
2013/12/02 Javascript
js中自定义方法实现停留几秒sleep
2014/07/11 Javascript
javascript中sort()的用法实例分析
2015/01/30 Javascript
浅析BootStrap模态框的使用(经典)
2016/04/29 Javascript
js鼠标按键事件和键盘按键事件用法实例汇总
2016/10/03 Javascript
React创建组件的三种方式及其区别
2017/01/12 Javascript
js禁止浏览器的回退事件
2017/04/20 Javascript
BootStrap Table前台和后台分页对JSON格式的要求
2017/06/28 Javascript
React Native仿美团下拉菜单的实例代码
2017/08/08 Javascript
Vue组件之单向数据流的解决方法
2018/11/10 Javascript
ES6知识点整理之数组解构和字符串解构的应用示例
2019/04/17 Javascript
用webpack4开发小程序的实现方法
2019/06/04 Javascript
Python中的复制操作及copy模块中的浅拷贝与深拷贝方法
2016/07/02 Python
R语言 vs Python对比:数据分析哪家强?
2017/11/17 Python
Django中日期处理注意事项与自定义时间格式转换详解
2018/08/06 Python
Django中使用CORS实现跨域请求过程解析
2019/08/05 Python
Tensorflow 实现将图像与标签数据转化为tfRecord文件
2020/02/17 Python
python多维数组分位数的求取方式
2020/03/03 Python
PySide2出现“ImportError: DLL load failed: 找不到指定的模块”的问题及解决方法
2020/06/10 Python
Answear匈牙利:来自全球200多个知名时尚品牌
2017/04/21 全球购物
瑞士设计师家具和家居饰品网上商店:Bruno Wickart
2019/03/18 全球购物
解除劳动合同协议书
2014/04/14 职场文书
工作失误检讨书(经典集锦版)
2014/10/17 职场文书
房屋分割离婚协议书范本
2014/12/01 职场文书
2014年基建工作总结
2014/12/12 职场文书
小学教师节活动总结
2015/03/20 职场文书
业务员岗位职责范本
2015/04/03 职场文书
太空授课观后感
2015/06/17 职场文书
六年级作文之自救
2019/12/19 职场文书
SQL Server数据定义——模式与基本表操作
2021/04/05 SQL Server