Element-UI 的 Table组件很强大,但是我们的需求更强大...

简单粗暴的来一发效果图:

一、数据驱动

传统的拖动效果,都是基于通过 mousedown、mousemove、mouseup 事件来修改删减 dom 节点

但 Vue 是一个数据驱动的前端框架,开发时应尽量避免操作 dom

而且 Element-UI 的 Table 组件封装得很严谨,直接操作 dom 很容易产生不可预计的 bug

所以我的核心思路就是:通过一个数组渲染表头(列),然后修改这个数组的顺序,从而修改列表的列排序

template 部分:

  <div class="w-table" :class="{'w-table_moving': dragState.dragging}">
<el-table :data="data"
:border="option.border"
:height="option.height"
:max-height="option.maxHeight"
:style="{ width: parseInt(option.width)+'px' }"
:cell-class-name="cellClassName"
:header-cell-class-name="headerCellClassName"
>
<slot name="fixed"></slot>
<el-table-column v-for="(col, index) in tableHeader" :key="index"
:prop="col.prop"
:label="col.label"
:width="col.width"
:min-width="col.minWidth"
:type="col.type"
:header-align="col.headerAlign"
:column-key="index.toString()"
:render-header="renderHeader"
>
</el-table-column>
</el-table>
</div>

上面的 data 是列表数据集合,option 是 Table 组件配置项,header 是表头数据集合,由父组件传入

  props: {
data: {
default: function () {
return []
},
type: Array
},
header: {
default: function () {
return []
},
type: Array
},
option: {
default: function () {
return {}
},
type: Object
}
}

配置项可以根据 Element-UI 的api自行删减

但有几个参数在组件内部被征用:

1. header-cell-class-name

绑定了一个函数,动态给表头单元格添加 class,从而实现拖动中的虚线效果。

2. cell-class-name

同上。用一个函数给表头以外的所有单元格添加 class,用于区分选中列。

3. column-key

绑定为 header 数组的 index,用于确定需要修改的 header 元素下标

4. render-header

表头渲染函数,用以添加自定义方法,以监听 mousemove 等相关事件

二、记录拖动状态

拖动过程中需要记录几个关键参数:

  data () {
return {
tableHeader: this.header,
dragState: {
start: -9, // 起始元素的 index
end: -9, // 移动鼠标时所覆盖的元素 index
dragging: false, // 是否正在拖动
direction: undefined // 拖动方向
}
}
}

另外父元素传入了一个表头数据 header,但拖动完成后会修改这个数据

在子组件中直接修改父元素的数据是不推荐的,所以这里初始化了一个 tableHeader 用于托管表头数据 header

但为了让 header 修改时,tableHeader 也能响应修改,就得添加一个监视器 watch

  watch: {
header (val, oldVal) {
this.tableHeader = val
}
}

三、自定义表头

Element-UI 的 Table 组件为了实现【拖拽边框以修改列宽】的功能,没有将 mousemove、mouseup、mousedown 这三个事件暴露出来

所以需要自定义表头,并手动添加鼠标事件的处理函数,这就需要用到 renderHeader() 方法

    renderHeader (createElement, {column}) {
return createElement(
'div', {
'class': ['thead-cell'],
on: {
mousedown: ($event) => { this.handleMouseDown($event, column) },
mousemove: ($event) => { this.handleMouseMove($event, column) }
}
}, [
// 添加 <a> 用于显示表头 label
createElement('a', column.label),
// 添加一个空标签用于显示拖动动画
createElement('span', {
'class': ['virtual']
})
])
},

三个鼠标事件中,第一个参数是事件对象,第二个是表头对象

在对应的处理函数中,可以通过 column.columnKey 获取到对应的表头元素下标 index

空标签 <span class="virtual"> 用来显示拖动时的动画(虚线)

四、事件处理

按下鼠标时,记录下起始列。鼠标抬起时,记录下结束列。根据二者之差计算出拖动的方向。

然后根据起始列和结束列的位置,将表头数据重新排序,从而实现列的拖动

拖动过程的处理函数如下:

// 按下鼠标开始拖动
handleMouseDown (e, column) {
this.dragState.dragging = true
this.dragState.start = parseInt(column.columnKey)
// 给拖动时的虚拟容器添加宽高
let table = document.getElementsByClassName('w-table')[]
let virtual = document.getElementsByClassName('virtual')
for (let item of virtual) {
item.style.height = table.clientHeight - + 'px'
item.style.width = item.parentElement.parentElement.clientWidth + 'px'
}
 document.addEventListener('mouseup', this.handleMouseUp);
}, // 鼠标放开结束拖动
handleMouseUp () {
this.dragColumn(this.dragState)
// 初始化拖动状态
this.dragState = {
start: -,
end: -,
dragging: false,
direction: undefined
}
 document.removeEventListener('mouseup', this.handleMouseUp);
}, // 拖动中
handleMouseMove (e, column) {
if (this.dragState.dragging) {
let index = parseInt(column.columnKey) // 记录起始列
if (index - this.dragState.start !== ) {
this.dragState.direction = index - this.dragState.start < ? 'left' : 'right' // 判断拖动方向
this.dragState.end = parseInt(column.columnKey)
} else {
this.dragState.direction = undefined
}
} else {
return false
}
}, // 拖动易位
dragColumn ({start, end, direction}) {
let tempData = []
let left = direction === 'left'
let min = left ? end : start -
let max = left ? start + : end
for (let i = ; i < this.tableHeader.length; i++) {
if (i === end) {
tempData.push(this.tableHeader[start])
} else if (i > min && i < max) {
tempData.push(this.tableHeader[ left ? i - : i + ])
} else {
tempData.push(this.tableHeader[i])
}
}
this.tableHeader = tempData
},

五、动态样式

在拖动过程中,通过 mousemove 事件,改变当前列的表头状态

然后借助 headerCellClassName, cellClassName 动态修改单元格 class

  headerCellClassName ({column, columnIndex}) {
let active = columnIndex - === this.dragState.end ? `darg_active_${this.dragState.direction}` : ''
let start = columnIndex - === this.dragState.start ? `darg_start` : ''
return `${active} ${start}`
} cellClassName ({column, columnIndex}) {
return (columnIndex - === this.dragState.start ? `darg_start` : '')
}

这里的 darg_active 用来给上面的空标签 <span class="virtual"> 添加虚线,darg_start 用于实现选中效果

贴一下我自己写的完整样式(使用了 sass 作为编译工具):

<style lang="scss">
.w-table {
 .el-table .darg_start {
background-color: #f3f3f3;
}
.el-table th {
padding:;
.virtual{
position: fixed;
display: block;
width:;
height:;
margin-left: -10px;
background: none;
border: none;
}
&.darg_active_left {
.virtual {
border-left: 2px dotted #666;
z-index: 99;
}
}
&.darg_active_right {
.virtual {
border-right: 2px dotted #666;
z-index: 99;
}
}
}
.thead-cell {
padding:;
display: inline-flex;
flex-direction: column;
align-items: left;
cursor: pointer;
overflow: initial;
&:before {
content: "";
position: absolute;
top:;
left:;
bottom:;
right:;
}
}
&.w-table_moving {
.el-table th .thead-cell{
cursor: move !important;
}
.el-table__fixed {
cursor: not-allowed;
}
}
}

六:父组件调用

<template>
<div>
<wTable :data="tableData" :header="tableHeader" :option="tableOption">
<el-table-column slot="fixed"
fixed
prop="date"
label="日期"
width="150">
</el-table-column>
</wTable>
</div>
</template> <script>
import wTable from '@/components/w-table.vue'
export default {
name: 'Table',
data () {
return {
tableOption: {
border: true,
maxHeight: 500
},
tableHeader: [{
prop: 'name',
label: '姓名',
sortable: true,
sortMethod: this.handleNameSort
}, {
prop: 'province',
label: '省份',
minWidth: '120'
}, {
prop: 'city',
label: '市区',
minWidth: '120'
}, {
prop: 'address',
label: '地区',
minWidth: '150'
}, {
prop: 'zip',
label: '邮编',
minWidth: '120'
}], tableData: [{
date: '2016-05-03',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-02',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-04',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-06',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}]
}
},
methods: {
handleNameSort () {
console.log('handleNameSort')
}
},
components: {
wTable
}
}
</script>

在 Element-UI 的 Table 组件上添加列拖拽效果的更多相关文章

  1. vue问题三:element ui的upload组件上传图片成功和移除事件

    element ui的upload组件上传图片成功和移除事件: 登录后获取到后台传的token存到中: sessionStorage.setItem("token",data.ob ...

  2. 如何在JDBC Connection Configuration配置组件上添加控件

    如何在JDBC Connection Configuration配置组件上添加控件 最近项目刚上线,闲来无事又把Jmeter的源码拿出来研究研究,最初的目的是想扒一扒Jmeter里数据库处理的逻辑是怎 ...

  3. element-UI ,Table组件实现拖拽效果

    拖拽效果,先放效果图,步骤放在后面~~ 一.引入三方插件 1.引入sortable.js的包: npm install sortable.js --save 2.或者npm i -S vuedragg ...

  4. element-ui dialog组件添加可拖拽位置 可拖拽宽高

    edge浏览器下作的gifhttp://www.lanourteam.com/%E6... 有几个点需要注意一下 每个弹窗都要有唯一dom可操作 指令可以做到 拖拽时要添加可拖拽区块 header 由 ...

  5. Qt之股票组件-自选股--列表可以拖拽、右键常用菜单

    目录 一.开头嘴一嘴 二.效果展示 三.自选股列表 1.列表初始化 2.添加Item 3.右键菜单 4.拖拽Item 5.刷新数据 四.相关文章 原文链接:Qt之股票组件-自选股--列表可以拖拽.右键 ...

  6. js拖拽上传 文件上传之拖拽上传

    由于项目需要上传文件到服务器,于是便在文件上传的基础上增加了拖拽上传.拖拽上传当然属于文件上传的一部分,只不过在文件上传的基础上增加了拖拽的界面,主要在于前台的交互, 从拖拽的文件中获取文件列表然后调 ...

  7. 图片上传插件ImgUploadJS:用HTML5 File API 实现截图粘贴上传、拖拽上传

    一 . 背景及效果 当前互联网上传文件最多的就是图片文件了,但是传统web图片的截图上传需要:截图保存->选择路径->保存后再点击上传->选择路径->上传->插入. 图片 ...

  8. .net core版 文件上传/ 支持批量上传,拖拽以及预览,bootstrap fileinput上传文件

    asp.net mvc请移步 mvc文件上传支持批量上传,拖拽以及预览,文件内容校验 本篇内容主要解决.net core中文件上传的问题  开发环境:ubuntu+vscode 1.导入所需要的包:n ...

  9. vue+element UI + axios封装文件上传及进度条组件

    1.前言 之前在做项目的时候,需要实现一个文件上传组件并且需要有文件上传进度条,现将之前的实现过程简单记录一下,希望可以帮助到有需要的人. 项目用的是Vue框架,UI库使用的是element UI,前 ...

随机推荐

  1. hdu2174 kiki's game 博弈

    Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes th ...

  2. js验证后台传递的map数据是否为空

    if(JSON.stringify(data)=='{}'){ $("#year").append("<option>--请选择--</option&g ...

  3. 【mysql】创建索引

    一.联合唯一索引 项目中需要用到联合唯一索引: 例如:有以下需求:每个人每一天只有可能产生一条记录:处了程序约定之外,数据库本身也可以设定: 例如:t_aa 表中有aa,bb两个字段,如果不希望有2条 ...

  4. 你真的了解word-wrap和word-break的区别吗? (转载)

    这两个东西是什么,我相信至今还有很多人搞不清,只会死记硬背的写一个word-wrap:break-word;word-break:break-all;这样的东西来强制断句,又或者是因为这两个东西实在是 ...

  5. Centos7使用yum安装程序软件时提示未找到安装包解决方法

    查找程序软件源,以sshd为例,假设sshd安装包无法找到,那么可以通过以下方法解决 yum provides sshd 然后我们就可以安装*.X86_64的程序软件源 例如yum install o ...

  6. skipper http router 工具

    skipper 是一个http router && 反向代理服务组件,同时支持类似kubernetes 模型的ingress,由zalando 公司的团队开发 并开源,从功能上来看,可 ...

  7. 你不知道的JavaScript(中卷) (Kyle Simpson 著)

    第一部分 类型和语法 第1章 类型 1.1 类型 1.2 内置类型 1.3 值和类型 1.3.1 undefined和undeclared 1.3.2 typeof Undeclared 1.4 小结 ...

  8. ESB雏形 -- 项目企业服务总线初始

    今天要厚着脸皮给大家推荐一个自己做的通信中间件——ServiceAnt,目前已经在我们团队的两个产品线上投入了使用. ServiceAnt是什么 它最初的定位是ESB(企业服务总线),但目前还没有达到 ...

  9. Primitives vs Objects

    这里首先我们要了解什么是primitives 和 objects 其实理解起来很简单. 如果我们懂.NET开发就会知道C#中的值类型和引用类型. primitives variables contai ...

  10. Java Bitset

    Bitset创建一种特殊的数组来保存非负整数的值 取值为true和false,初始都是false.Bitset初始化是一个long,65位,增加位数的话只能是64的整数倍. 如果用一个Bitset存储 ...