2023年6月20日发(作者:)
antd-table⾃定义实现排序滚动条与列拉伸antd-table ⾃定义实现排序滚动条与列拉伸由于项⽬需求,我使⽤了ant Degisn 的Table组件,随着需求的变化,在原有的表格上增加了列排序、滚动条、列拉伸以及固定列的功能(⼀万头XX在⼼中奔腾。。。)。刚开始听到需求时,我是不以为然的,毕竟ant都提供了对应的属性和⽅法嘛,不慌!。。。结果。。。脸都肿了。
只实现单⼀功能时,ant做的很完美,但是将这些功能集合到⼀起时,就出现了表格错位等界⾯问题。(其实,我觉得很⼤可能是我没有正确使⽤)
⾏吧,那我⾃⼰写。
ps:框架是
react列展⽰需求是表格的列的展⽰可以由⽤户操作,实现上使⽤了,antd的filterDropdown属性:实现逻辑很简单,通过filterDropdown属性写⼀个列多选框,根据⽤户选中的值来重新定义columns,主要涉及四个值defaultTitle:列可选项,defaultColunms:所有⽤户可操作列,thTitle:展⽰列表头数据,columns:table需要的columns数据:// colunms处理// thTitle
表头renderColumns=(thTitle)=>{ const columns = [ { title: '#', dataIndex: 'rock', render: text =>
序号列 ]; h(item => { //
由于项⽬后台需求,传递了⼀些⽤户⽆法看到但是⼜需要返回给后台的数据,所以增加了⼀个参数判断该列是否展⽰,表格所有的信息都是从后台获取 if () { ({ title: , dataIndex: , sorter: ing, width:140, render: text => (
value={ > 0 ? e : defaultTitle} onChange={ge} />
操作列,不同表格拥有不同操作,也是后台传递 }); const defaultColumns = columns; te({ columns, defaultColumns, }); } }//
⽤户操作后触发onChange⽅法: onChange = checkedValues => checkedValues => { const showTitle = []; const datas = [ { title: '#', dataIndex: 'rock', render: (text, record) => {text}, align: 'center', },//
在重写columns时,有些⼀些不可操作列需要重新⼿动存储,⽐如序号列,操作列等 ]; // defaultColunms是获取到的所有列 // columns是需要展⽰的列 // checkedValues为⽤户选中列 // showTitle为新的展⽰表头 h(r => { h(rs => { if ( === rs) { (r); (); } }); }); ({ title: '操作', dataIndex: 'action', width: `${100 / ( + 1)}%`, align: 'center', render: (text, record) => { return Action(text, record); }, }); te({ columns: datas, thTitle: showTitle }); };由于后期需求调整,这个功能就被去掉了,没有效果图,但是效果⼀百分!列排序⾸先利⽤filter在表头设置排序图标: const cols = ((col, index) => ({ ...col, filterDropdown: ed, //排序图标占位,根据传⼊的值判断该列是否需要排序功能 filterIcon: filtered =>
⾃定义筛选图标,就是两个箭头图标,绑定排序⽅法 }));排序⽅法: sorter = (sor,col) => {//sor排序规则,col列属性 //排序图标样式变化 const icon=electorAll("i"); if(>0){ h(item=>="#848484") };//
先将所有图标改为为排序样式 const docu=mentById(dex+sor); ="#1890ff" ;//再改变被选中的图标样式 // //这⾥进⾏数据请求,将值传给Table的onChange⽅法等操作 // };滚动条antd的滚动条是我觉得最坑的⼀个功能,由于项⽬中需要展⽰⼤量表格,且每个表格列数不⼀,在复⽤Table组件时动不动就会错位,以及在同时使⽤fixed属性固定列时,居然会有空⽩,两个功能居然不能完美兼容。所以我对表格滚动进⾏了重写,不需要使⽤antd的scroll属性或者其他属性,实现表格滚动条的⾃适应。这⾥利⽤了Table的components属性,对ant的默认样式进⾏覆盖。//
分别对table、tbody、tr的样式进⾏重新设置。const BaseHeader=(props)=>{ const {...restProps}=props; const tableStyle={ display:'table', tableLayout:'fixed', width:'100%', }; return( )};const BaseTable=(props)=>{ const{...restProps}=props; const tableStyle={ height:tableHeight, overflow:'auto', display:'block', width:'100%' }; return(
)};const BaseRow=(props)=>{ const {...restProps}=props; const tableStyle={ display:'table', // tableLayout:'fixed', width:'100%', height:lineHeight }; return(附带滚动条样式优化::-webkit-scrollbar { width: 6px; height: 6px;}::-webkit-scrollbar-track-piece { background-color: #e8e8e8; -webkit-border-radius: 6px;}::-webkit-scrollbar-thumb:vertical { height: 5px; background-color: rgba(0, 0, 0, 0.5); -webkit-border-radius: 6px;}::-webkit-scrollbar-thumb:horizontal { width: 5px; background-color: rgba(0, 0, 0, 0.5); -webkit-border-radius: 6px;}到这,表格就可以抛弃scroll属性,实现⾃适应滚动条了。但是,如果你同时要求固定表格⾥的某些列,使⽤了fixed属性,你会发现,antd为你⽣成了两个table(固定列与⾮固定列),滚动时不能两个表格同时滚动,这明显不符合我们的需求://
为了解决这个问题,需求在表格渲染后(componentDidUpdate)再对表格进⾏⼀些处理let timer=null;componentDidUpdate(){//
由于可能在同⼀个页⾯中存在多个表格,所以需要先获取表格组件最外层div,以避免表格互相影响 const standardTable=electorAll("div[class='antd-pro-components-task-manager-log-info-standard-table-index-standardTable']"); for(let tableNum=0;tableNum<;tableNum+=1){//
遍历表格组件 const tbody=standardTable[tableNum].querySelectorAll("tbody");//
获取tbody if(>1){//判断是否需要存在两个表格,不存在则不需要处理滚动 //定时器,不设置定时器的话,两个滚动⽅法互相影响,导致⿏标滚轮滚动很慢,所以设定⼀个定时器,在滚动其中⼀个表格时,取消另⼀个表格的滚动⽅法,在⽤户停⽌滚动⼀段时间后再为其加上滚动⽅法 timer=null; //为两个表格分别绑定滚动⽅法 tbody[0].onscroll=()=>Scroll(tbody[1],tbody[0],timer); tbody[1].onscroll=()=>Scroll(tbody[0],tbody[1],timer) } }} // table滚动⽅法 handleScroll=(tbody,tbody2,timer)=>{ ll=null;//取消没有触发滚动操作的表格的滚动⽅法 clearTimeout(timer);//清空定时器,避免上⼀次滚动的定时器影响 const self=this; Top=Top;//表格滚动同步 timer = setTimeout(function() { ll=()=>Scroll(tbody2,tbody,timer); }, 300 );//300毫秒后重新为另⼀个表格绑定滚动⽅法 };ps:最后别忘了在组件卸载前清空定时器。列拉伸敲重点重写之前所有功能都是为了实现这⼀个功能万万没想到这⼀个功能把我坑的那么惨!如果你不需要在滚动固定等功能的基础上实现列拉伸,⽽只是想单独使⽤其中⼀些功能,那么这antd的属性表现得很好,我这些都是废话。列拉伸的实现逻辑就是根据⿏标按下到放开这⼀过程中移动的距离先改变表头的宽度,再改变单元格的宽度,所以在拉伸过程中两者会出现错位,但是在松开⿏标后,表格就会对齐。由于之前尝试使⽤antd推荐的列拉伸⽅式,所以我安装了react-resizable插件,结果最后我居然只使⽤了它提供的样式。。。所以关于拉伸的样式,你们⾃⼰写,可以参考以下下⾯的样式:先在表格表头设置⼀个拉伸功能触发点,我设定的是图中红框位置,相对于表头的绝对定位 position: absolute; width: 10px; height: 100%; bottom: 0; right: -5px; cursor: col-resize;样式设置好了就可以考虑实现功能,实际上就是对渲染后的表格样式进⾏处理,理清了实现逻辑,其实不看代码,⾃⼰就能够实现://由于其中涉及了表格样式,所以看个⼤概意思就好,照搬没有意义,实际实现时需要根据情况进⾏调整componentDidUpdate(){ const standardTable=electorAll("div[class='antd-pro-components-task-manager-log-info-standard-table-index-standardTable']"); for(let tableNum=0;tableNum<;tableNum+=1){ const table=standardTable[tableNum].querySelectorAll("table"); let actionWidth=0;//该参数是为了设置固定列表格的列宽 for (let m=0;m<;m+=1) { const th = table[m].querySelectorAll("th");//表头 const docu = table[m].querySelectorAll("tr[class='ant-table-row ant-table-row-level-0']");//表格内容⾏ if ( > 0 && !th[0].onmousedown) {//绑定⿏标⽅法 Mouse(th, docu); } } //由于表格其中⼀列宽度改变,所以需要对其余列列宽进⾏调整,保证表头与表格内容对齐 for (let n = 0; n < ; n += 1) { const thWidth = th[n].offsetWidth; if(m===0){ actionWidth=thWidth; } const text = th[n].e(/[rn]/g, "");//表头⽂本 //避免多选框,序号列等表头宽度过宽,先设定⼀个定值 if ((text===''&&n===0) || text === "#") { th[n]. = `${numWidth}px`; } for (let i = 0; i < ; i += 1) { const td = docu[i].querySelectorAll("td"); const div = td[n] ? td[n].querySelectorAll("div") : []; if ( > 0) { div[0]. = thWidth < lineWidth ? `${lineWidth - 4}px` : `${thWidth - 4}px`; td[n]. = thWidth < lineWidth ? `${lineWidth}px` : `${thWidth}px`; } //由于padding等属性会影响到th宽度的获取,为了保证表格对齐,所以对表头的宽度再⼀次设置 th[n]. = thWidth < lineWidth ? `${lineWidth}px` : `${thWidth}px`; //根据需求,重新设置多选框,序号列等宽度 if ((text===''&&n===0) || text === "#") { td[n]. = `${numWidth}px`; th[n]. = `${numWidth}px`; if ( > 0) { div[0]. = `${numWidth - 4}px` } } //固定列列宽,防⽌antd⾃适应,⽣成过宽固定列 if(m===1){ if ( > 0) { div[0]. = `${actionWidth - 4}px`; td[n]. = `${actionWidth}px`; } th[n]. = `${actionWidth}px`; } } } } } } //拉伸⽅法 handleMouse=(th,docu)=>{ let moveWidth = 0; for(let n=0;n<;n+=1){ let oldWidth = th[n].clientWidth; const span=th[n].querySelectorAll("span[class='react-resizable-handle']"); if(>0) { const self=this; span[0].onmousedown = function (ev) { //event的兼容性 ev = ev || ; opagation(); //获取⿏标按下的坐标 const x1 = X; //给可视区域添加⿏标的移动事件 emove = function (ev) { //event的兼容性 ev = ev || ; //获取⿏标移动时的坐标 const x2 = X; //计算出⿏标的移动距离 moveWidth = x2 - x1; //更改被操作列列宽度 for(let m=0;m<;m+=1){ for(let m=0;m<;m+=1){ const thWidth=th[m].clientWidth; for (let i = 0; i < ; i += 1) { const td = docu[i].querySelectorAll("td"); const allDiv=td[m]?td[m].querySelectorAll("div"):[]; const div = td[n].querySelectorAll("div"); const text=th[m].e(/[rn]/g,""); if((text===""&&m===0)||text==="#"){ td[m].=`${numWidth}px`; th[m]. =`${numWidth}px`; if( > 0){ allDiv[0]. = `${numWidth-4}px` } } //设定⼀个列宽最⼩值,如果操作后的列宽⼩于140,则不调整 if (moveWidth + oldWidth > 140) { if ( > 0) { div[0]. = `${oldWidth + moveWidth - 4}px` } td[n]. = `${oldWidth + moveWidth}px`; th[n]. = `${oldWidth + moveWidth}px`; } else { if ( > 0) { div[0]. = `136px` } td[n]. = `140px`; th[n]. = `140px`; } } } }; //清除 eup = function (ev) { emove = null; }; }; } } };最后,在实现这些功能中,查看了很多信息,也参考了很多意见,做了很多尝试,在此统⼀感谢各位⼤佬。Bug肯定是会有的,毕竟我是⼀个沉迷于写Bug的⼈呐!代码仅作参考~
2023年6月20日发(作者:)
antd-table⾃定义实现排序滚动条与列拉伸antd-table ⾃定义实现排序滚动条与列拉伸由于项⽬需求,我使⽤了ant Degisn 的Table组件,随着需求的变化,在原有的表格上增加了列排序、滚动条、列拉伸以及固定列的功能(⼀万头XX在⼼中奔腾。。。)。刚开始听到需求时,我是不以为然的,毕竟ant都提供了对应的属性和⽅法嘛,不慌!。。。结果。。。脸都肿了。
只实现单⼀功能时,ant做的很完美,但是将这些功能集合到⼀起时,就出现了表格错位等界⾯问题。(其实,我觉得很⼤可能是我没有正确使⽤)
⾏吧,那我⾃⼰写。
ps:框架是
react列展⽰需求是表格的列的展⽰可以由⽤户操作,实现上使⽤了,antd的filterDropdown属性:实现逻辑很简单,通过filterDropdown属性写⼀个列多选框,根据⽤户选中的值来重新定义columns,主要涉及四个值defaultTitle:列可选项,defaultColunms:所有⽤户可操作列,thTitle:展⽰列表头数据,columns:table需要的columns数据:// colunms处理// thTitle
表头renderColumns=(thTitle)=>{ const columns = [ { title: '#', dataIndex: 'rock', render: text =>
序号列 ]; h(item => { //
由于项⽬后台需求,传递了⼀些⽤户⽆法看到但是⼜需要返回给后台的数据,所以增加了⼀个参数判断该列是否展⽰,表格所有的信息都是从后台获取 if () { ({ title: , dataIndex: , sorter: ing, width:140, render: text => (
value={ > 0 ? e : defaultTitle} onChange={ge} />
操作列,不同表格拥有不同操作,也是后台传递 }); const defaultColumns = columns; te({ columns, defaultColumns, }); } }//
⽤户操作后触发onChange⽅法: onChange = checkedValues => checkedValues => { const showTitle = []; const datas = [ { title: '#', dataIndex: 'rock', render: (text, record) => {text}, align: 'center', },//
在重写columns时,有些⼀些不可操作列需要重新⼿动存储,⽐如序号列,操作列等 ]; // defaultColunms是获取到的所有列 // columns是需要展⽰的列 // checkedValues为⽤户选中列 // showTitle为新的展⽰表头 h(r => { h(rs => { if ( === rs) { (r); (); } }); }); ({ title: '操作', dataIndex: 'action', width: `${100 / ( + 1)}%`, align: 'center', render: (text, record) => { return Action(text, record); }, }); te({ columns: datas, thTitle: showTitle }); };由于后期需求调整,这个功能就被去掉了,没有效果图,但是效果⼀百分!列排序⾸先利⽤filter在表头设置排序图标: const cols = ((col, index) => ({ ...col, filterDropdown: ed, //排序图标占位,根据传⼊的值判断该列是否需要排序功能 filterIcon: filtered =>
⾃定义筛选图标,就是两个箭头图标,绑定排序⽅法 }));排序⽅法: sorter = (sor,col) => {//sor排序规则,col列属性 //排序图标样式变化 const icon=electorAll("i"); if(>0){ h(item=>="#848484") };//
先将所有图标改为为排序样式 const docu=mentById(dex+sor); ="#1890ff" ;//再改变被选中的图标样式 // //这⾥进⾏数据请求,将值传给Table的onChange⽅法等操作 // };滚动条antd的滚动条是我觉得最坑的⼀个功能,由于项⽬中需要展⽰⼤量表格,且每个表格列数不⼀,在复⽤Table组件时动不动就会错位,以及在同时使⽤fixed属性固定列时,居然会有空⽩,两个功能居然不能完美兼容。所以我对表格滚动进⾏了重写,不需要使⽤antd的scroll属性或者其他属性,实现表格滚动条的⾃适应。这⾥利⽤了Table的components属性,对ant的默认样式进⾏覆盖。//
分别对table、tbody、tr的样式进⾏重新设置。const BaseHeader=(props)=>{ const {...restProps}=props; const tableStyle={ display:'table', tableLayout:'fixed', width:'100%', }; return( )};const BaseTable=(props)=>{ const{...restProps}=props; const tableStyle={ height:tableHeight, overflow:'auto', display:'block', width:'100%' }; return(
附带滚动条样式优化::-webkit-scrollbar { width: 6px; height: 6px;}::-webkit-scrollbar-track-piece { background-color: #e8e8e8; -webkit-border-radius: 6px;}::-webkit-scrollbar-thumb:vertical { height: 5px; background-color: rgba(0, 0, 0, 0.5); -webkit-border-radius: 6px;}::-webkit-scrollbar-thumb:horizontal { width: 5px; background-color: rgba(0, 0, 0, 0.5); -webkit-border-radius: 6px;}到这,表格就可以抛弃scroll属性,实现⾃适应滚动条了。但是,如果你同时要求固定表格⾥的某些列,使⽤了fixed属性,你会发现,antd为你⽣成了两个table(固定列与⾮固定列),滚动时不能两个表格同时滚动,这明显不符合我们的需求://
为了解决这个问题,需求在表格渲染后(componentDidUpdate)再对表格进⾏⼀些处理let timer=null;componentDidUpdate(){//
由于可能在同⼀个页⾯中存在多个表格,所以需要先获取表格组件最外层div,以避免表格互相影响 const standardTable=electorAll("div[class='antd-pro-components-task-manager-log-info-standard-table-index-standardTable']"); for(let tableNum=0;tableNum<;tableNum+=1){//
遍历表格组件 const tbody=standardTable[tableNum].querySelectorAll("tbody");//
获取tbody if(>1){//判断是否需要存在两个表格,不存在则不需要处理滚动 //定时器,不设置定时器的话,两个滚动⽅法互相影响,导致⿏标滚轮滚动很慢,所以设定⼀个定时器,在滚动其中⼀个表格时,取消另⼀个表格的滚动⽅法,在⽤户停⽌滚动⼀段时间后再为其加上滚动⽅法 timer=null; //为两个表格分别绑定滚动⽅法 tbody[0].onscroll=()=>Scroll(tbody[1],tbody[0],timer); tbody[1].onscroll=()=>Scroll(tbody[0],tbody[1],timer) } }} // table滚动⽅法 handleScroll=(tbody,tbody2,timer)=>{ ll=null;//取消没有触发滚动操作的表格的滚动⽅法 clearTimeout(timer);//清空定时器,避免上⼀次滚动的定时器影响 const self=this; Top=Top;//表格滚动同步 timer = setTimeout(function() { ll=()=>Scroll(tbody2,tbody,timer); }, 300 );//300毫秒后重新为另⼀个表格绑定滚动⽅法 };ps:最后别忘了在组件卸载前清空定时器。列拉伸敲重点重写之前所有功能都是为了实现这⼀个功能万万没想到这⼀个功能把我坑的那么惨!如果你不需要在滚动固定等功能的基础上实现列拉伸,⽽只是想单独使⽤其中⼀些功能,那么这antd的属性表现得很好,我这些都是废话。列拉伸的实现逻辑就是根据⿏标按下到放开这⼀过程中移动的距离先改变表头的宽度,再改变单元格的宽度,所以在拉伸过程中两者会出现错位,但是在松开⿏标后,表格就会对齐。由于之前尝试使⽤antd推荐的列拉伸⽅式,所以我安装了react-resizable插件,结果最后我居然只使⽤了它提供的样式。。。所以关于拉伸的样式,你们⾃⼰写,可以参考以下下⾯的样式:先在表格表头设置⼀个拉伸功能触发点,我设定的是图中红框位置,相对于表头的绝对定位 position: absolute; width: 10px; height: 100%; bottom: 0; right: -5px; cursor: col-resize;样式设置好了就可以考虑实现功能,实际上就是对渲染后的表格样式进⾏处理,理清了实现逻辑,其实不看代码,⾃⼰就能够实现://由于其中涉及了表格样式,所以看个⼤概意思就好,照搬没有意义,实际实现时需要根据情况进⾏调整componentDidUpdate(){ const standardTable=electorAll("div[class='antd-pro-components-task-manager-log-info-standard-table-index-standardTable']"); for(let tableNum=0;tableNum<;tableNum+=1){ const table=standardTable[tableNum].querySelectorAll("table"); let actionWidth=0;//该参数是为了设置固定列表格的列宽 for (let m=0;m<;m+=1) { const th = table[m].querySelectorAll("th");//表头 const docu = table[m].querySelectorAll("tr[class='ant-table-row ant-table-row-level-0']");//表格内容⾏ if ( > 0 && !th[0].onmousedown) {//绑定⿏标⽅法 Mouse(th, docu); } } //由于表格其中⼀列宽度改变,所以需要对其余列列宽进⾏调整,保证表头与表格内容对齐 for (let n = 0; n < ; n += 1) { const thWidth = th[n].offsetWidth; if(m===0){ actionWidth=thWidth; } const text = th[n].e(/[rn]/g, "");//表头⽂本 //避免多选框,序号列等表头宽度过宽,先设定⼀个定值 if ((text===''&&n===0) || text === "#") { th[n]. = `${numWidth}px`; } for (let i = 0; i < ; i += 1) { const td = docu[i].querySelectorAll("td"); const div = td[n] ? td[n].querySelectorAll("div") : []; if ( > 0) { div[0]. = thWidth < lineWidth ? `${lineWidth - 4}px` : `${thWidth - 4}px`; td[n]. = thWidth < lineWidth ? `${lineWidth}px` : `${thWidth}px`; } //由于padding等属性会影响到th宽度的获取,为了保证表格对齐,所以对表头的宽度再⼀次设置 th[n]. = thWidth < lineWidth ? `${lineWidth}px` : `${thWidth}px`; //根据需求,重新设置多选框,序号列等宽度 if ((text===''&&n===0) || text === "#") { td[n]. = `${numWidth}px`; th[n]. = `${numWidth}px`; if ( > 0) { div[0]. = `${numWidth - 4}px` } } //固定列列宽,防⽌antd⾃适应,⽣成过宽固定列 if(m===1){ if ( > 0) { div[0]. = `${actionWidth - 4}px`; td[n]. = `${actionWidth}px`; } th[n]. = `${actionWidth}px`; } } } } } } //拉伸⽅法 handleMouse=(th,docu)=>{ let moveWidth = 0; for(let n=0;n<;n+=1){ let oldWidth = th[n].clientWidth; const span=th[n].querySelectorAll("span[class='react-resizable-handle']"); if(>0) { const self=this; span[0].onmousedown = function (ev) { //event的兼容性 ev = ev || ; opagation(); //获取⿏标按下的坐标 const x1 = X; //给可视区域添加⿏标的移动事件 emove = function (ev) { //event的兼容性 ev = ev || ; //获取⿏标移动时的坐标 const x2 = X; //计算出⿏标的移动距离 moveWidth = x2 - x1; //更改被操作列列宽度 for(let m=0;m<;m+=1){ for(let m=0;m<;m+=1){ const thWidth=th[m].clientWidth; for (let i = 0; i < ; i += 1) { const td = docu[i].querySelectorAll("td"); const allDiv=td[m]?td[m].querySelectorAll("div"):[]; const div = td[n].querySelectorAll("div"); const text=th[m].e(/[rn]/g,""); if((text===""&&m===0)||text==="#"){ td[m].=`${numWidth}px`; th[m]. =`${numWidth}px`; if( > 0){ allDiv[0]. = `${numWidth-4}px` } } //设定⼀个列宽最⼩值,如果操作后的列宽⼩于140,则不调整 if (moveWidth + oldWidth > 140) { if ( > 0) { div[0]. = `${oldWidth + moveWidth - 4}px` } td[n]. = `${oldWidth + moveWidth}px`; th[n]. = `${oldWidth + moveWidth}px`; } else { if ( > 0) { div[0]. = `136px` } td[n]. = `140px`; th[n]. = `140px`; } } } }; //清除 eup = function (ev) { emove = null; }; }; } } };最后,在实现这些功能中,查看了很多信息,也参考了很多意见,做了很多尝试,在此统⼀感谢各位⼤佬。Bug肯定是会有的,毕竟我是⼀个沉迷于写Bug的⼈呐!代码仅作参考~
发布评论