使用Antd表格组件实现调度
时间:2023-03-14 17:31:42
科技观察
本文转载自微信公众号《神奇的程序员K》,作者神奇的程序员K,转载请联系神奇的程序员K公众号。前言20多天前,遇到一个业务需求,一个明细表,可以动态添加列,合并单元格,结合公司jsp项目已有的功能,完成单元格的增删改查。在进行需求分析和梳理后,经过一番查找,发现antdReact版的表格组件非常强大,可定制性强,可以帮助我完成这个业务需求的开发。因为需要和jsp交互,所以在实现过程中,遇到了一些困难,踩了很多坑。本文将与大家分享我从0到1实现这个需求的过程和思路,欢迎有兴趣的开发者阅读本文。环境搭建因为公司的项目是基于jsp的,antd本来想用vue版本的,但是和jsp的一些语法冲突,跑不起来,于是尝试了antd的react版本,跑起来没有兼容性问题,一切正常。给React竖起大拇指??。由于我需要和项目中已有的功能进行交互,所以不能使用脚手架。我只能以cdn的形式导入react。如下图,依次导入react、axios、lodah、antd所需的文件。上面使用的资源文件地址:react-antd-schedule/lib我们需要写react相关的代码在text/babel标签中,如下图,我们打印antd和react看是否有值。打开浏览器控制台,如果出现如下信息,说明我们的环境已经搭建成功。image-20201119155715157接下来我们写一个HelloWord来测试一下效果。
执行上面的代码,打开浏览器看到如下效果,证明我们的环境已经搭建好了。image-20201119161505912需要注意的是CDN在引入React和antd的时候,都是全局暴露一个对象。使用其内部方法时,需要React.xx和antd.xx才能访问它。需求分析收到需求的简要描述后,整理了一下:表格栏要显示的内容:日期,日程内容(接口动态返回),日程内容栏可以通过手动添加用户。表格行显示的内容是每一天的数据,每一天的数据分为上午、下午、晚上三个时间段。日程表的内容分为每日日程表和某时间段日程表两种状态。如果是每日计划,则需要合并单元格。日程内容栏中的每个单元格都有5种状态,需要通过一定的方式区分,让用户一眼就能看出当前日程处于什么状态。如果计划内容单元格的内容为空,则需要合并单元格以显示添加图标。点击添加图标后,系统会弹出一个窗口进行添加操作。操作完成后,内容会渲染到刚刚点击的单元格中。如果内容单元格有内容,根据不同的状态,打开不同的弹窗进行修改和删除操作,操作完成后将结果更新到对应的单元格中。需求确定后,老板给我分配了一个后台。跟后台沟通后,开发周期预估1周。我的页面估计2天,剩下的3天用于和后台数据对接。两天后,我完成了页面并定义了表格所需的数据格式。把数据格式发给后台后,他说可以,没问题。因为设计图没有UI,所以我凭直觉做了第一个版本,做出来的东西比较难看。下图是我根据需要实现的页面。image-20201119172808318然而,事情并没有想象中那么顺利。我做完页面后,在开发周期的最后一天下午,后台给了我接口,但是返回的数据不是我期望的格式,所以我做了两次处理,页面渲染完成后,快下班了。预估的开发时间还没有完成也是可以理解的,毕竟后端要处理的数据比较复杂。我原本预估一个星期的开发时间,以及后面需求的不断增加、变化、UI设计效果图,我的页面代码也从开始的100多行积累到现在的1000多行。这套折腾下来,直到需求开发,交由测试,用了20多天。需求实现下面给大家分享一下实现这个需求时遇到的困难,踩过的一些坑,以及我的解决方案。最终效果如下,请移步执行代码:react-antd-schedule/index.htmlimage-20201119175256753动态添加列到这个schedule。用户可以单击添加图标来添加一列时间表。这时候我们需要到表头的开头添加一列数据。一开始我以为在antd的column和dataSource中添加一条数据就可以了,如下图:constApp=()=>{const[columns,setColumns]=React.useState([]);const[optRecords,setOptRecords]=React.useState([]);//添加按钮函数constbtnClick=(e)=>{index++;letcolumnsObj={dataIndex:'rcnr'+(index),title:'schedulecontent'+index,align:'center',onCell:tdSet,render:rctd_render,}//给表添加一列columns.push(columnsObj)setColumns(columns);//处理表数据for(leti=0;i
{const[optRecords,setOptRecords]=React.useState([]);const[columns,setColumns]=React.useState([]);//添加按钮函数constbtnClick=(e)=>{if(tableLoadingStatus){alert("表格数据尚未加载");returnfalse;}columnsIndex++;letcolumnsObj={dataIndex:"rcnr"+(columnsIndex),title:"schedulecontent"+columnsIndex,align:"left",className:"rcnrfontSet",width:189.5,onCell:tdSet,render:rctd_render};//给表格column添加一列setColumns((arr=>[...arr,columnsObj]));//处理表数据setOptRecords((arr)=>arr.map((item)=>{return{...item,["rcnr"+columnsIndex]:{wz:columnsIndex-1}};}));};}tablecolumns填充在后端返回的数据中,如果有不存在的schedule,则不直接返回字段,导致wufa渲染时出现列和表数据不匹配导致的问题antd是渲染的,所以我只能把所有的数据遍历一次,找到最大列长,然后补全数据列数更少。由于接口在添加数据的时候需要传递当前点击的是哪一列,刚才完成的数据不包含wz字段,所以我们需要再次遍历数据,添加wz字段,代码如下://tabledatarenderingfunctionconsttableDataRendering=function(res){//获取最大子节点的key个数letmaxChildLength=Object.keys(defaultData[0].children[0]).length;for(leti=0;imaxChildLength){maxChildLength=currentObjLength;}}}//填充缺失节点for(leti=0;i{//tablecolumnformatdefinitionconstdefaultColumns=[{dataIndex:"rq",title:"date",align:"center",fixed:"left",colSpan:2,width:140.5,className:"rqfontSet",onCell:dateHandle,render:(value,item,index)=>{}},{dataIndex:"sjd",title:"时间段",width:70,colSpan:0,fixed:"left",align:"center",className:"sjdfontSet",render:(value,item,index)=>{letv1=value.charAt(0);letv2=value.charAt(1);return{v1}
{v2}
;}}];//表格数据渲染函数consttableDataRendering=function(res){//根据schedule列字段数据赋值表列的schedule字段,rcList中包含sjd,所以需要从1开始for(leti=1;i{if(data==null){data=defaultData;}letnewArr=[];data.map(item=>{if(item.children){item.children.forEach((subItem,i)=>{letobj={...item};Object.assign(obj,subItem);deleteobj.children;obj.rowLength=item。children.length;newArr.push(obj);});}});//console.log("处理后的表数据");//console.log(newArr);//将处理后的数据放入optRecords,使用cloneDeep进行深拷贝,触发useState更新setOptRecords(_.cloneDeep(newArr));};}另一种方案是使用JSON.parse进行深拷贝,但是这种深拷贝有一个问题:但是json数据时有函数在里面,里面的函数就会失效,无法执行。由于需要自定义antd表,json数据中包含函数,所以无法使用该方法进行top/bottom加载数据。由于业务需要,不能使用antd的分页功能,需要从上往前加载30条数据,从下往后加载30条数据。总共只能加载3个月的数据。实现代码如下:这里需要比较的是,如果是触到top/bottom,拖动水平滚动也会触发滚动监听,所以我们需要排除水平滚动事件。这里需要比较的地方是如果触到top/bottom,拖动水平滚动也会触发Scroll监听,所以我们需要排除水平滚动事件