news 2026/5/16 19:29:17

ant-design 1.x版本表格头部拖拽、可拖拽列实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ant-design 1.x版本表格头部拖拽、可拖拽列实现

表格列宽拖拽调整 — 问题总结

版本

  • “vue”: “2.6.11”,
  • “vue-draggable-resizable”: “^2.3.0”,
  • "ant-design “:”1.7.0“

问题 1:thDom为 null 导致getBoundingClientRect报错

现象:TypeError: Cannot read properties of null (reading 'getBoundingClientRect')

根因:onDragthis.$set(draggingState, key, 0)触发 Vue 响应式重渲染。旧<th>销毁时 Vue 2 的ref回调用null清理闭包中的thDom,之后dragstop事件拿到的就是null

解决:用非响应式对象this._dragX作为拖拽手柄x的数据源,onDrag期间不触发重渲染,仅在onDragstop时通过$set提交最终值。外加if (!thDom) return防御。

问题 2:vue-draggable-resizabletransform 不对、拖拽错列、拖拽点乱跑

现象:拖拽 handle 的transform位置计算错误,拖拽 A 列实际改变了 B 列的宽度。

根因:vue-draggable-resizable内部用transform: translateX()驱动拖拽,但 CSS 把 handle 固定在right: -5pxleft: auto !important)。组件内部的x定位和 CSS 定位坐标系冲突,onDrag上报的x值不可信。

解决:移除vue-draggable-resizable依赖,改用原生mousedown/mousemove/mouseup实现。Handle 仅靠 CSS(right: -5px)定位,用鼠标位移增量delta计算新宽度,彻底消除坐标系冲突。

问题 3:拖拽一次后位置不确定刷新 / 跳动

现象:拖拽过程中 handle 位置跳动,释放后列宽不确定。

根因:同问题 1,onDrag触发重渲染导致vue-draggable-resizablexprop 被重置。

解决:随问题 2 一并解决,原生事件方案不依赖任何响应式状态驱动拖拽位置。

问题 4:Scoped 样式不生效 — handlewidth: 0导致无法展示拖拽按钮

现象:.table-draggable-handlewidth: 10px不生效,handle 宽度为 0,无法拖拽。其他样式(如position: absolute)正常。

根因:Vue 2 scoped 样式通过data-v-xxx属性匹配,但h()在 render 函数中创建的元素不在 template 里,不会自动添加 scope 属性。scoped 块(line 814)的width对 handle 不生效,实际走的是非 scoped 块(line 1040),而该块之前缺少width

解决:在非 scoped 样式块中补充width: 10px; top: 0; z-index: 1,去掉vue-draggable-resizable时代的残留样式(height: 100% !important; left: auto !important)。

问题 5:替换 header cell 后排序失效

现象:拖拽列无法点击排序。

根因:ant-design-vue 传给components.header.cellprops是完整 VNode data 对象(含classonstyle等顶层属性),但代码把所有属性塞进了attrs{ attrs: { ...restProps } }),导致on.click(排序回调)、class(排序样式)丢失。

解决:改为正确分层:

// 之前(错误){attrs:{...restProps,width:col.width},class:'resize-table-th'}// 之后(正确){...restProps,attrs:{...restProps.attrs,width:col.width},class:['resize-table-th',restProps.class]}

问题 6:拖拽结束后触发了排序

现象:拖拽过程中sorter图标变化,释放鼠标后触发了排序请求。

根因:mousedown之后会触发click事件,click冒泡到<th>被 ant-design-vue 的排序逻辑捕获。

解决:在拖拽 handle 上添加click: e => { e.stopPropagation() },阻止click事件冒泡到<th>

问题 7:depColumns树形结构只遍历了顶层

现象:depColumns是树形结构({ title: "基本情况", children: [...] }),叶子节点才有dataIndexwidth。但created初始化和resizeableTitle查找都只处理了顶层数组。

解决:两处都改为递归遍历:

  • createdwalkColumns递归展开所有层级,只收集叶子节点(无childrenchildren为空的列)
  • resizeableTitlefindColumn递归搜索children找到匹配的列

问题 8:需要按列控制可拖拽 + 最大/最小宽度

需求:

  • 只有标记了isDraggable: true的列才出现拖拽手柄
  • 支持minWidth/maxWidth限制拖拽范围

解决:

  • 判断条件从!col.width改为!col.isDraggable
  • onMouseMove中用Math.min(Math.max(newWidth, col.minWidth || 50), col.maxWidth || Infinity)限制范围,minWidth 默认 50px

最终列配置示例

{title:"业务线",dataIndex:"serviceLine",width:70,isDraggable:true,// 显式开启拖拽minWidth:100,// 最小宽度(默认 50)maxWidth:400,// 最大宽度(默认无上限)}

最终列代码示例

<template><a-table bordered:columns="columns":components="tableComponents":data-source="data"></a-table></template><script>importVuefrom'vue';constcolumns=[{title:'Date',dataIndex:'date',width:200,},{title:'Amount',dataIndex:'amount',width:100,},{title:'Type',dataIndex:'type',width:100,},{title:'Note',dataIndex:'note',width:100,},{title:'Action',key:'action',isDraggable:true,// 显式开启拖拽minWidth:100,// 最小宽度(默认 50)maxWidth:400,// 最大宽度(默认无上限)},];constdata=[{key:0,date:'2018-02-11',amount:120,type:'income',note:'transfer',},{key:1,date:'2018-03-11',amount:243,type:'income',note:'transfer',},{key:2,date:'2018-04-11',amount:98,type:'income',note:'transfer',},];exportdefault{name:'App',data(){return{depColumns:data,};},computed:{tableComponents(){return{header:{cell:this.resizeableTitle,},}},},created(){constdraggingMap={}constwalkColumns=(cols)=>{cols.forEach(col=>{if(col.children&&col.children.length){walkColumns(col.children)}else{constk=col.dataIndex||col.keyif(col.width){draggingMap[k]=col.width}}})}walkColumns(this.depColumns)this.draggingState=draggingMap},methods:{resizeableTitle(h,props,children){letthDom=nullconst{key,...restProps}=propsconstfindColumn=(cols,key)=>{for(constcolofcols){if(col.children&&col.children.length){constfound=findColumn(col.children,key)if(found)returnfound}else{constk=col.dataIndex||col.keyif(k===key)returncol}}returnnull}constcol=findColumn(this.depColumns,key)if(!col||!col.isDraggable){returnh('th',{...restProps},children)}letstartX=0letstartWidth=0constonMouseDown=e=>{e.preventDefault()e.stopPropagation()startX=e.pageX startWidth=thDom?thDom.getBoundingClientRect().width:col.widthconstonMouseMove=e=>{constdelta=e.pageX-startXconstnewWidth=startWidth+deltaconstmin=col.minWidth||50constmax=col.maxWidth||Infinitycol.width=Math.min(Math.max(newWidth,min),max)}constonMouseUp=()=>{document.removeEventListener('mousemove',onMouseMove)document.removeEventListener('mouseup',onMouseUp)document.body.style.cursor=''document.body.style.userSelect=''constfinalWidth=thDom?thDom.getBoundingClientRect().width:col.widththis.$set(this.draggingState,key,finalWidth)col.width=finalWidth}document.addEventListener('mousemove',onMouseMove)document.addEventListener('mouseup',onMouseUp)document.body.style.cursor='col-resize'document.body.style.userSelect='none'}consthandleEl=h('div',{class:'table-draggable-handle',on:{mousedown:onMouseDown,click:e=>{e.stopPropagation()},},})returnh('th',{...restProps,attrs:{...restProps.attrs,width:col.width},class:['resize-table-th',restProps.class],ref:r=>{thDom=r},},[...(Array.isArray(children)?children:[children]),handleEl,])},}};</script><style lang="less">.resize-table-th{position:relative;.table-draggable-handle{position:absolute;top:0;right:-5px;bottom:0;width:10px;z-index:1;cursor:col-resize;touch-action:none;}}</style>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 19:27:07

如何用DS4Windows让PS4手柄在PC上完美运行?3步解锁专业游戏体验

如何用DS4Windows让PS4手柄在PC上完美运行&#xff1f;3步解锁专业游戏体验 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows 你是否曾经想过在PC上使用心爱的PS4手柄玩游戏&#xff0c;却发…

作者头像 李华
网站建设 2026/5/16 19:23:36

从零构建IMX6ULL嵌入式系统:内核、设备树与驱动的协同编译实战

1. 为什么内核、设备树与驱动需要协同编译&#xff1f; 当你拿到一块全新的IMX6ULL开发板时&#xff0c;第一件事就是要搭建完整的嵌入式Linux系统。这个过程中最关键的环节就是内核、设备树和驱动的协同编译。很多新手会问&#xff1a;为什么不能单独编译某个部分&#xff1f;…

作者头像 李华
网站建设 2026/5/16 19:23:34

Claude code cli安装实战

1、先安装cli的运行环境---node.js 访问官网&#xff1a;https://nodejs.org/,下载LTS版本&#xff0c;此版本较稳定 按照提示一步一步点击&#xff0c;其中提示安装位置的地方&#xff0c;可以更换到自己指定的位置&#xff0c;默认位置为C盘&#xff0c;后续可能会占位置较大…

作者头像 李华
网站建设 2026/5/16 19:23:05

昇思 Web 与 API 推理服务器部署

OrangePi Alpro 作为鲲鹏 ARM64 架构轻量化 AI 开发板&#xff0c;内置硬件加速单元&#xff0c;可高效运行昇思&#xff08;MindSpore Lite&#xff09;量化模型。为实现远程调用、多设备接入、可视化推理等工业级边缘 AI 需求&#xff0c;在开发板上部署昇思 Web API 推理服…

作者头像 李华
网站建设 2026/5/16 19:21:37

PathOfBuilding终极指南:5步打造完美流放之路角色构建

PathOfBuilding终极指南&#xff1a;5步打造完美流放之路角色构建 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding 想要在《流放之路》中打造真正强大的角色吗&#xff1f…

作者头像 李华