二次封装Element-UI 的 Table 组件,添加列拖拽功能,根据https://www.cnblogs.com/wisewrong/p/8820508.html改编,
1.继承Element-UI 的 Table 组件,所有属性方法可以继承使用
2.修正当出现水平滚动条或有固定列并滚动后,拖动列时出现的虚线位置不正确
3.改善当需要调整列大小时,鼠标经过表对列边缘难以出现调整标志(因为修改后的表头大小自动填满了,都变成是拖动列标志)
demo:
https://output.jsbin.com/xotidip/1
效果:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<title>header Dragend 拖动列改变位置或调整大小</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.5.4/theme-chalk/index.css">
<link rel="stylesheet" href="table_drag.css">
</head>
<body>
<div id="app" class="text-center" style="margin-top: 0px">
<template>
<div class="app-container">
<w-table v-loading="listLoading" :data="tableData" :header="tableHeader" element-loading-text="Loading" border fit
highlight-current-row>
<el-table-column slot="fixed" :key="'fix'" fixed prop="date" label="日期" width="150">
</el-table-column>
<el-table-column :key="'fix2'" slot="fixed" fixed="right" label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" size="small">编辑</el-button>
</template>
</el-table-column>
</w-table>
</div>
</template>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.5.18/vue.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.5.4/index.js"></script>
<script type="text/javascript">
Vue.component("wTable", {
props: {
data: {
default: function() {
return []
},
type: Array
},
header: {
default: function() {
return []
},
type: Array
},
},
data() {
return {
tableHeader: this.header,
dragState: {
start: -9, // 起始元素的 index
end: -9, // 移动鼠标时所覆盖的元素 index
dragging: false, // 是否正在拖动
direction: undefined // 拖动方向
},
// 记录左边的固定列的列数
fixedCount: -1,
formThead: this.header
}
},
mounted() {
const that = this;
that.$nextTick(() => {
});
},
watch: {
header(val, oldVal) {
this.formThead = val
},
},
methods: {
// 当更列位置变化后,有的列没有指定宽度或最小宽度,交换后,发现列表头的siz会自动为span的宽度
// 所以事后恢复为列位置变化前的那个宽度
fixMinWidth() {
const that = this;
let item = null,
colList = null,
fixCount = that.fixedCount;
if (fixCount < 0) {
fixCount = this.getFixedCount()
}
for (let i = 0; i < that.formThead.length; i++) {
item = that.formThead[i];
if (!("minWidth" in item)) {
if (!colList) {
let colgroup = this.$el.querySelector(".el-table__header-wrapper").querySelector("colgroup");
colList = colgroup.children;
}
currentCol = colList[i + fixCount];
item.minWidth = +currentCol.width;
}
}
},
// 计算左边的固定列,有多少列
getFixedCount() {
//el-table__fixed
const that = this;
let fixedTable = that.$el.querySelector(".el-table__fixed");
if (fixedTable) {
// 查找所有不包含类.is-hidden的th
let thList = fixedTable.querySelector(".el-table__fixed-header-wrapper").querySelectorAll("th:not(.is-hidden)");
that.fixedCount = thList.length;
} else {
that.fixedCount = 0;
}
return that.fixedCount;
},
//table.$emit('header-dragend', column.width, startLeft - startColumnLeft, column, event);
//headerDragend(tNewWidth, tOldWidth, tColumn, tEvent) {},
// 按下鼠标开始拖动
handleMouseDown(e, column, tIdx) {
const that = this;
tIdx += that.getFixedCount();
that.dragState.dragging = true
that.dragState.start = tIdx
that.fixMinWidth();
// 给拖动时的虚拟容器添加宽高,因为virtual是v-if ="dragState.dragging"的,所以没有那么展现
that.$nextTick(() => {
const table = that.$el,
virtual = table.getElementsByClassName('virtual'),
scrollLeft = table.querySelector(".el-table__body-wrapper").scrollLeft;
for (const item of virtual) {
item.style.height = table.clientHeight - 1 + 'px'
item.style.width = item.parentElement.parentElement.clientWidth + 'px'
if (scrollLeft > 0) {
//这里减10,是猜的,因为css 里面.el-table th .virtual的marginLeft也是-10
item.style.marginLeft = -scrollLeft - 10 + "px"
}
}
});
document.addEventListener('mouseup', that.handleMouseUp)
},
// 鼠标放开结束拖动
handleMouseUp() {
if (this.dragState.direction != undefined) {
this.dragColumn(this.dragState)
}
// 初始化拖动状态
this.dragState = {
start: -9,
end: -9,
dragging: false,
direction: undefined
}
document.removeEventListener('mouseup', this.handleMouseUp)
},
// 拖动中
handleMouseMove(e, column, tIdx) {
if (this.dragState.dragging) {
tIdx += this.getFixedCount();
// 记录起始列
const index = tIdx
if (index - this.dragState.start !== 0) {
this.dragState.direction = index - this.dragState.start < 0 ? 'left' : 'right' // 判断拖动方向
this.dragState.end = index
} else {
this.dragState.direction = undefined
}
} else {
return false
}
},
// 拖动易位
dragColumn({
start,
end,
direction
}) {
// fixCount,左边的固定列个数
let fixCount = this.fixedCount,
item = null;
if (fixCount < 0) {
fixCount = this.getFixedCount()
}
start = start - fixCount;
end = end - fixCount;
const tempData = []
//const left = direction === 'left'
const left = (direction === 'left') ? -1 : 1;
const min = (left <= 0) ? end : start - 1
const max = (left <= 0) ? start + 1 : end
let colList = null,
currentIndex = null,
currentCol = null;
for (let i = 0; i < this.formThead.length; i++) {
if (i === end) {
currentIndex = start;
item = this.formThead[start];
} else if (i > min && i < max) {
currentIndex = i + left;
item = this.formThead[currentIndex];
} else {
currentIndex = i;
item = this.formThead[i];
}
/*
if (!("minWidth" in item)) {
if (!colList) {
let colgroup = this.$el.querySelector(".el-table__header-wrapper").querySelector("colgroup");
colList = colgroup.children;
}
currentCol = colList[currentIndex + fixCount];
item.minWidth = currentCol.width;
}
*/
tempData.push(item)
}
this.formThead = tempData
},
headerCellClassName({
column,
columnIndex
}) {
const active = columnIndex === this.dragState.end ? `darg_active_${this.dragState.direction}` : ''
const start = columnIndex === this.dragState.start ? `darg_start` : ''
return `${active} ${start}`
},
cellClassName({
column,
columnIndex
}) {
return (columnIndex === this.dragState.start ? `darg_start` : '')
}
},
template: `
<el-table
v-bind:class="{'w-table_moving':dragState.dragging}"
v-bind="$attrs"
v-on="$listeners"
:data="data"
:cell-class-name="cellClassName"
:header-cell-class-name="headerCellClassName">
<slot name="fixed"></slot>
<el-table-column
v-for="(col, index) in formThead"
:key="index"
:prop="col.prop"
:label="col.label"
:width="col.minWidth"
:min-width="col.minWidth"
:type="col.type"
:header-align="col.headerAlign"
:column-key="(index).toString()"
show-overflow-tooltip>
<template slot="header" slot-scope="scope">
<div class="thead-cell"
@mousedown="handleMouseDown($event, col,index)"
@mousemove="handleMouseMove($event, col,index)"
>
<a>{{col.label}}</a>
<span class="virtual" v-if="dragState.dragging"></span>
</div>
</template>
<template slot-scope="scope">
{{ scope.row[col.prop] }}
</template>
</el-table-column>
<slot name="handled"/>
</el-table>
`,
})
new Vue({
el: "#app",
data() {
return {
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
}],
listLoading: false,
tableHeader: [{
prop: 'name',
label: '姓名',
sortable: true,
sortMethod: this.handleNameSort
}, {
prop: 'province',
label: '省份',
minWidth: '120'
}, {
prop: 'city',
label: '市区',
}, {
prop: 'address',
label: '地区',
minWidth: '150'
}, {
prop: 'zip',
label: '邮编',
minWidth: '120'
}],
}
}
});
</script>
</html>
css:
.el-table .darg_start {
background-color: #f3f3f3;
}
.el-table th {
padding: 0;
}
.el-table th .virtual {
position: fixed;
display: block;
width: 0;
height: 0;
margin-left: -10px;
background: none;
border: none;
}
/*竖线虚线*/
.el-table .el-table__header-wrapper th.darg_active_left .virtual {
border-left: 2px dotted #666;
border-color: #2196F3;
z-index: 99;
}
.el-table th.darg_active_right .virtual {
border-right: 2px dotted #666;
z-index: 99;
}
.el-table .thead-cell {
padding: 0;
display: inline-flex;
flex-direction: column;
align-items: left;
cursor: pointer;
overflow: initial;
}
/*
.el-table .thead-cell:before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
*/
.w-table_moving.el-table th .thead-cell {
cursor: move !important;
}
.w-table_moving .el-table__fixed, .w-table_moving .el-table__fixed-right{
cursor: not-allowed;
}