美文网首页
vue + 原生实现表格单元列列宽可重置

vue + 原生实现表格单元列列宽可重置

作者: jeneen1129 | 来源:发表于2022-10-18 10:44 被阅读0次

以下仅用于学习记录!侵权请联系删除!

需求说明

原生效果就想要实现和 elementui 中 el-table 的头部可以 resizable 功能。

调研

找到一个插件,使用的时命令来定制列宽,https://github.com/onmotion/vue-columns-resizable
可以通过 npm / yarn 下载,

但是本人下载之后,发现此插件的不适用于当前需求,因为命令的inserted 执行时期:被绑定元素插入父节点时调用(仅保证父节点存在,不一定已被插入文档中),即此插件运用原理时加一些模拟的div来设置,但是如果 当前节点还没有具体宽高时 div 会没有宽高。

所以放弃该方法,然后基于需求,也搜到很多用原生写的,主要就是通过 onmousemove、onmouseout、onmousedown 三个事件来编写相关的逻辑。
直接掏出 element-ui 的 .\node_modules\element-ui\packages\table\src\table-header.js 文件学习这边是怎么实现的。

代码实现

本次具体实现的代码如下:

data() {
   return {
        dragState: {}, // 记录子表的列宽移动的一些数值
        dragging: false, // 子表是否在重置列宽
    }
},
methods: {
handleMouseMove(event) {
  let target = event.target
  while (target && target.tagName !== 'TH') {
    target = target.parentNode
  }
  if (!this.dragging) {
    let rect = target.getBoundingClientRect()
    const bodyStyle = document.body.style
    if (rect.width > 12 && rect.right - event.pageX < 8) {
      // 拖拽的鼠标样式
      bodyStyle.cursor = 'col-resize'
    } else if (!this.dragging) {
      bodyStyle.cursor = ''
    }
  }
},
handleMouseOut() {
  document.body.style.cursor = ''
},
handleMouseDown(event) {
  // 开始拖拽
  this.dragging = true
  // 当前拖拽的列所在的表格
  let tableEl = event.target
  // 当前所在列(单元格)
  let thEL = event.target
  while (tableEl && tableEl.tagName !== 'TABLE') {
    tableEl = tableEl.parentNode
  }
  // 获取列宽拖拽的显示线(拖拽线)
  let resizeProxy = tableEl.querySelector(
    '.el-table__column-resize-proxy'
  )
  while (thEL && thEL.tagName !== 'TH') {
    thEL = thEL.parentNode
  }
  const columnRect = thEL.getBoundingClientRect()
  thEL.classList.add('noclick')
  this.dragState = {
    startMouseLeft: event.clientX, // 鼠标开始的地方
    columnWidth: columnRect.width, // th开始拖拽的宽度
  }
  resizeProxy.style.top = tableEl.getBoundingClientRect().top + 'px'
  resizeProxy.style.height =
    tableEl.getBoundingClientRect().height + 'px'
  resizeProxy.style.left = this.dragState.startMouseLeft + 'px'
  resizeProxy.classList.remove('dn')
  document.onselectstart = function () {
    return false
  }
  document.ondragstart = function () {
    return false
  }

  const handleMouseMove = event => {
    // 拖拽中,拖拽线与鼠标的位置同步
    resizeProxy.style.left = event.clientX + 'px'
  }

  const handleMouseUp = () => {
    if (this.dragging) {
      // 拖拽完毕
      const { startMouseLeft, columnWidth } = this.dragState
      const finalLeft = parseInt(resizeProxy.style.left, 10)
      const columnWidthDiff = finalLeft - startMouseLeft
      const finalColumnWidth = columnWidthDiff + columnWidth
      const columnMinWidth = parseInt(thEL.style.minWidth, 10)
      thEL.style.width = finalColumnWidth + 'px'
      // 当单元格宽度改变时 表格宽度也进行改变: 1)有最小宽度时宽度改变了 2)无最小宽度时
      if (
        (columnMinWidth && finalColumnWidth >= columnMinWidth) ||
        !columnMinWidth
      ) {
        tableEl.style.width =
          columnWidthDiff + tableEl.clientWidth + 'px'
      }
      document.body.style.cursor = ''
      this.dragging = false
      this.dragState = {}
      resizeProxy.classList.add('dn')
    }

    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
    document.onselectstart = null
    document.ondragstart = null
    setTimeout(function () {
      thEL.classList.remove('noclick')
    }, 0)
  }

  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
},
}
<table>
    <thead>
        <tr><th @mousemove="handleMouseMove" @mouseout="handleMouseOut" @mousedown="handleMouseDown"></th></tr>
    </thead>
</table>

思路梳理

在 th 的三个事件中主要是在鼠标按下事件中,开始拖拽,给 document 来增加响应鼠标移动和松开的事件,记录 当前鼠标的 X 方向的位置,document 的移动事件记录 鼠标的移动之后 X 方向的位置,document 松开事件去进行 设置拖拽之后单元格的宽度和表格的宽度,拖拽完毕。

相关文章

网友评论

      本文标题:vue + 原生实现表格单元列列宽可重置

      本文链接:https://www.haomeiwen.com/subject/gklwartx.html