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 相关文章推荐
15个款优秀的 jQuery 图片特效插件推荐
Nov 21 Javascript
JQuery UI的拖拽功能实现方法小结
Mar 14 Javascript
HTML上传控件取消选择
Mar 06 Javascript
jquery实现邮箱自动补全功能示例分享
Feb 17 Javascript
JS实现仿Windows7风格的网页右键菜单效果代码
Sep 11 Javascript
JavaScript 函数模式详解及示例
Sep 07 Javascript
JavaScript自动点击链接 防止绕过浏览器访问的方法
Jan 19 Javascript
超全面的javascript中变量命名规则
Feb 09 Javascript
解决URL地址中的中文乱码问题的办法
Feb 10 Javascript
JS中showModalDialog关闭子窗口刷新主窗口用法详解
Mar 25 Javascript
JS鼠标3次点击事件实现代码及扩展思路
Sep 12 Javascript
ES6基础之展开语法(Spread syntax)
Feb 21 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语言中global和$GLOBALS[]的分析 之二
2012/02/02 PHP
PHP生成自定义长度随机字符串的函数分享
2014/05/04 PHP
Yii2实现ActiveForm ajax提交
2017/05/26 PHP
YUI Compressor压缩JavaScript原理及微优化
2013/01/07 Javascript
使用jquery自定义鼠标样式满足个性需求
2013/11/05 Javascript
js子页面获取父页面数据示例
2014/05/15 Javascript
javascript实现在网页中运行本地程序的方法
2016/02/03 Javascript
js仿支付宝填写支付密码效果实现多方框输入密码
2016/03/09 Javascript
让DIV的滚动条自动滚动到最底部的3种方法(推荐)
2016/09/24 Javascript
BootStrap 动态添加验证项和取消验证项的实现方法
2016/09/28 Javascript
bootstrap 设置checkbox部分选中效果
2017/04/20 Javascript
bootstrap datetimepicker控件位置异常的解决方法
2017/11/23 Javascript
微信小程序map组件结合高德地图API实现wx.chooseLocation功能示例
2019/01/23 Javascript
服务端预渲染之Nuxt(使用篇)
2019/04/08 Javascript
JavaScript迭代器的含义及用法
2019/06/21 Javascript
微信小程序实现星级评价
2019/11/20 Javascript
js实现鼠标拖拽div左右滑动
2020/01/15 Javascript
浅谈Python中用datetime包进行对时间的一些操作
2016/06/23 Python
10个Python小技巧你值得拥有
2018/09/29 Python
Python产生Gnuplot绘图数据的方法
2018/11/09 Python
基于python使用tibco ems代码实例
2019/12/20 Python
python实现飞行棋游戏
2020/02/05 Python
tensorflow实现将ckpt转pb文件的方法
2020/04/22 Python
使用 css3 实现圆形进度条的示例
2017/07/05 HTML / CSS
Lovedrobe官网:英国领先的大码服装品牌
2019/09/19 全球购物
美国在线艺术商店:HandmadePiece
2020/11/06 全球购物
娇韵诗香港官网:Clarins香港
2020/08/13 全球购物
教师岗位聘任书范文
2014/03/29 职场文书
个人买房协议书范本
2014/10/06 职场文书
学校运动会广播稿
2014/10/11 职场文书
2015年转正工作总结范文
2015/04/02 职场文书
运动会通讯稿600字
2015/07/20 职场文书
参加招聘会后的感想
2015/08/10 职场文书
《去年的树》教学反思
2016/02/18 职场文书
发言稿之优秀教师篇
2019/09/26 职场文书
Golang使用Panic与Recover进行错误捕获
2022/03/22 Golang