关于vue.js:一个应付需求的拖拽表格

5次阅读

共计 7028 个字符,预计需要花费 18 分钟才能阅读完成。

公司 PM 又搞事了,非要做什么拖拽表格,拖拽表头调整地位不行,还要能调整宽度 …
市面上现成的轮子,要么是拖拽地位,要么是拖拽宽度,简略找了找,还是本人造轮子吧,本人造的想怎么捏怎么捏。

需要刚刚开始的时候,依据开源大佬提醒,写了这样的拖拽款式,开开心心的交给 PM 看,后果被仁慈的驳回,“我要拿得起放得下的表头”:PM 如是说。
好吧,好吧,我写,我写。

当初需要写好了,我来整顿一下思路吧,看着很难,写起来很难真正做完又不感觉难的一个需要,还是被坑了好些下的。
首先要明确需要,表格模式,要能拿起以后点击的表头,而后拖动,当他通过左右两侧任意一个表头一半宽度的时候,在该表头,左 & 右减少一个 2 像素的边,松开鼠标,将拿起的表头放到竖线处,这是表头的拖动。
其次,表头的宽度能够任意拖动

重点来了,两端须要有固定的列

好了,需要明确了,那么咱们当初开始整顿思路造作吧,
饿了么的表格有现成的拖动宽,固定列,那么就用饿了么的表格组件吧,
emmm,组长说,用 div 本人布局吧,开展元素内容过多,用饿了么的组件会有限度。
行行行,我都本人写!

先确定表头的数据格式,此处还是要借鉴饿了么,白嫖一时爽,始终白嫖一爽快~

tableHeader [
  {
    label: '表头名称',
    prop: 'name', // 当前列数据对应字段,很重要
    width: ''     // 以后宽度
  }
]

通过创立一个数组来存储表头(行),通过调整数组程序来调整列表排序,通过数组内对象属性 width 来存储当前列宽

<div class="d-table" ref="dTable">
    <el-scrollbar ref="el_scroll_dom" class="default-scrollbar" wrap-class="default-scrollbar__wrap" view-class="default-scrollbar__view">
      <div class="d-table_header thead flex" :style="[{width:allwidth}]">
        <div
          v-for="(col,index) in tableHeader" 
          :key="col.label" 
          :class="['d-table_th','column_'+ index]" 
          :style="{width: parseInt(col.width)+'px'}"
          @mousedown="handleMouseDown($event, index)"
          @mousemove="handleMouseMove($event, index)"
        >
        {{col.label}}
        </div>
        <div class="draggingDom" ref="draggingDom" v-show="draggingDomVisible">{{draggingText}}</div>
        <div class="resize-proxy" ref="resizeProxy" v-show="resizeProxyVisible"></div>
      </div>
    </el-scrollbar>
</div>      

这样,一个表头咱们就渲染进去了,至于办法咱们上面说。

className column_ 用来辨别每一个表头,毕竟操作表头的宽度肯定会操作 dom,mousedown 办法为以后鼠标点下事件,即用户操作宽度或调整地位的触发点,mousemove 用来记录鼠标挪动,这里的 mousemove 次要用来拖动表头宽度时,鼠标款式的成果。

接下来是拖动表头宽度

之所以先写这个,起因是整个拖拽的思路都是白嫖了饿了么表格宽度调整
首先咱们须要整顿一下,对于拖动都须要哪些元素,这里利用了 getBoundingClientRect 函数

  const tableEl = this.$refs.dTable  // 获取外层父级实例
  const tableLeft = tableEl.getBoundingClientRect().left // 父级间隔屏幕右边间隔
  const columnEl = this.$el.querySelector('.column_'+i) // 获取以后点击元素 dom
  const columnRect = columnEl.getBoundingClientRect() 
  const minLeft = columnRect.left - tableLeft + 30  // 为什么加 30?this.dragState = {
    startMouseLeft: event.clientX, // 鼠标以后 x 坐标,设置为起始
    startLeft: columnRect.right - tableLeft, // 间隔右边初始值
    startColumnLeft: columnRect.left - tableLeft, // 表头右边间隔
    tableLeft
  }

而后咱们设置鼠标通过时款式变动,在鼠标通过的元素左边间隔 8 像素的地位,将鼠标指针更改为 col-resize,并且将以后点拖拽锁设定为拖拽宽度。

// 鼠标挪动事件,减少鼠标款式
handleMouseMove(event, i) {
  let target = event.target;
  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';
      target.style.cursor = 'col-resize';
      this.draggingColumn = true
    } else if (!this.dragging) {
      bodyStyle.cursor = '';
      target.style.cursor = 'move';
      this.draggingColumn = null;
    }
  }
  
},

接下来开始写鼠标拖拽表头宽度,咱们先将必要元素筹备好

this.dragging = true; // 开启拖动
this.resizeProxyVisible = true;  // 显示拖拽线实例
const resizeProxy = this.$refs.resizeProxy; // 获取实例
resizeProxy.style.left = this.dragState.startLeft + 'px'; 批改右边距
document.onselectstart = function() { return false;}; // 禁止选中文字
document.ondragstart = function() { return false;}; // 禁止拖拽
// 增加监听事件,以监听鼠标挪动,抬起事件
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);

而后咱们设置鼠标挪动成果

const handleMouseMove = (event) => {
  const deltaLeft = event.clientX - this.dragState.startMouseLeft; // 获取鼠标理论滚动间隔
  const proxyLeft = this.dragState.startLeft + deltaLeft;          // 以后所处地位
  resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px';    // 设置竖线地位
};

当初咱们再设置鼠标抬起事件

const handleMouseUp = () => {if (this.dragging) {
        const {
          startColumnLeft,
          startLeft
        } = this.dragState;
        const finalLeft = parseInt(resizeProxy.style.left, 10);
        let columnWidth = finalLeft - startColumnLeft;
        if(columnWidth < 68) columnWidth = 68 // 最小拖动宽度,能够按需要本人定义
        this.tableHeader[i].width = columnWidth
        const params = JSON.stringify(this.tableHeader)
        // 这里执行申请事件,将表头以 json 格局发送给后端,保留以后设置的宽度,实现后设置以后元素宽
        columnEl.style.width = columnWidth + 'px'
        document.body.style.cursor = ''; 
        this.dragging = false;
        this.draggingColumn = null;
        this.dragState = {};
        this.resizeProxyVisible = false;
      }
      // 移除监听事件
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
      document.onselectstart = null;
      document.ondragstart = null;
    };

这样,拖动宽度的办法就实现了,次要参照的饿了么表格宽度调整。

拖拽表头

上面咱们开始写拖拽表头元素,思路与拖拽宽度一样,咱们创立一个 dom(draggingDom)来作为 拿起 的元素,而后当咱们移动超过左右元素的一半宽度时,咱们将前(后)元素设置边框。起初写到这里时,陷入思维误区,卡了很久,感激好基友陟上晴明提供的中线思路,不然我还在傻傻的动静计算元素宽度。
咱们将整个表头元素模仿成坐标轴,以以后鼠标点击的节点为 (0 ,0),向右边为负(dragging_left),向右为正(dragging_right)
同样,咱们先将须要的元素筹备好,先计算每个表头构造的中线地位,工夫紧迫,这里写了两个办法来计算,前面有工夫复盘再来批改吧。(如果哪个大佬看完有更好的办法请分割我,请你喝咖啡哦,本人太懒了...)

// 右边中线数组
getPreLine(i) {let arr = []
  let _this = this
  const setPreWith = (n)=> {if(!n) return false
    let len = this.getDomsWidth(n,i) + _this.tableHeader[n-1].width /2
    arr.push(-len)
    setPreWith(n - 1)
  }
  setPreWith(i)
  const newArr = arr.reverse()
  return newArr
},
// 获取邻近宽度汇合
getDomsWidth(k,j) {
  let _this = this
  let len = 0
  for(let i = k; i < j; i++) {len = len + this.tableHeader[i].width
  }
  return len
},
const lineArr = this.getPreLine(i).concat([0]).concat(this.getNextLine(i)) // 中线数组汇合

同样的,咱们应用一个 dom 来承载拖动的内容,点击时定义其宽高以及初始地位

  // 拖动表头地位
  this.draggingcell = true
  this.draggingDomVisible = true
  this.$refs.draggingDom.style.width = columnRect.width + 'px'
  this.$refs.draggingDom.style.left = columnRect.left - 20 + 'px'
  this.draggingText = this.tableHeader[i].label
  // 获取中线
  let end = i;
  this.activedIndex = i
  // 阻止默认事件
  document.onselectstart = function() { return false;};
  document.ondragstart = function() { return false;};

上面是拖动表头的鼠标挪动事件

const handleMouseMove = (event) => {
// 进行挪动
const deltaLeft = event.clientX - this.dragState.startMouseLeft; // 理论位移间隔
const proxyLeft = deltaLeft + columnRect.left - 20 // 位移后间隔右边间隔
this.$refs.draggingDom.style.left = proxyLeft + 'px'; // 生成初始地位
  // 点击处为原点,向左为负,向右为正,咱们只须要比照前后两个 dom 的中线即可
  if(deltaLeft < 0) {
      this.dragState.direction = 'left'
      // 如果挪动间隔小于前一个 dom 中线时
      if(deltaLeft < lineArr[end-1]) {removeClass(this.$el.querySelector('.column_'+ end),'dragging_left')
        removeClass(this.$el.querySelector('.column_'+ end),'dragging_right')
        --end
        addClass(this.$el.querySelector('.column_'+ end),'dragging_'+this.dragState.direction)
      } else if (deltaLeft >= lineArr[end]) {removeClass(this.$el.querySelector('.column_'+ end),'dragging_left')
        removeClass(this.$el.querySelector('.column_'+ end),'dragging_right')
        ++end
        addClass(this.$el.querySelector('.column_'+ end),'dragging_'+this.dragState.direction)
    }
  } else if((deltaLeft > 0)){
    this.dragState.direction = 'right'
    if(deltaLeft > lineArr[end+1]) {removeClass(this.$el.querySelector('.column_'+ end),'dragging_left')
      removeClass(this.$el.querySelector('.column_'+ end),'dragging_right')
      ++end
      addClass(this.$el.querySelector('.column_'+ end),'dragging_'+this.dragState.direction)
    } else if(deltaLeft <= lineArr[end]) {removeClass(this.$el.querySelector('.column_'+ end),'dragging_left')
      removeClass(this.$el.querySelector('.column_'+ end),'dragging_right')
      --end
      addClass(this.$el.querySelector('.column_'+ end),'dragging_'+this.dragState.direction)
    }
  }
};

最初咱们来看一下鼠标抬起时事件

const handleMouseUp = (event) => {
  // 鼠标抬起时,咱们先移除以后的边线
  removeClass(this.$el.querySelector('.column_'+ end),'dragging_left')
  removeClass(this.$el.querySelector('.column_'+ end),'dragging_right')
  end !== this.activedIndex && this.setThDom(end)
  // 此处能够将批改值传给后端
  this.draggingDomVisible = false
  this.draggingcell = false
  this.dragState = {};
  document.removeEventListener('mousemove', handleMouseMove);
  document.removeEventListener('mouseup', handleMouseUp);
  document.onselectstart = null;
  document.ondragstart = null;
}

就像拖拽宽度一样,咱们同样须要监听鼠标事件

document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);

好了,这样拖拽的表格以及拖拽宽度就实现了。

本文参加了 SegmentFault 思否写作挑战赛,欢送正在浏览的你也退出。

正文完
 0