1.整体代码实现
<template>
<div
:id="id"
class="table-drag"
:class="{ 'table-drag_moving': dragState.dragging }"
>
<el-table
class="table-drag_table"
:data="tableData"
v-bind="$attrs"
v-on="$listeners"
v-loading="vLoading"
:element-loading-text="elementLoadingText"
:lazy="lazy"
:load="load"
:ref="id"
:header-cell-style="{
color: '#848484',
fontSize: '14px',
backgroundColor: '#qua',
}"
:header-cell-class-name="headerCellClassName"
cell-class-name="table-body-cell-style"
@header-dragend="headerDragend"
@selection-change="selectionChange"
:tree-props="treeProps"
:row-key="rowKey"
:row-class-name="rowClassName"
:row-style="rowStyle"
:indent="indent"
:default-expand-all="getDefaultExpandAll"
>
<el-table-column
v-if="vSelection"
type="selection"
align="center"
:reserve-selection="reserveSelection"
width="55"
/>
<el-table-column
v-for="(col, index) in tableHeader"
:key="index"
:column-key="index + ''"
:align="col.align || 'center'"
v-bind="col"
>
<template
slot="header"
slot-scope="scope"
>
<div
:class="{ 'thead-cell': !col.fixed }"
@mousedown="handleMouseDown($event, scope.column, col.fixed)"
@mouseup="handleMouseUp($event, scope.column)"
@mousemove="handleMouseMove($event, scope.column)"
>
<a>{{ scope.column.label }}</a>
</div>
</template>
<template slot-scope="scope">
<slot
:name="col.prop"
:row="scope.row"
:index="scope.$index"
:column="scope.column"
>
{{ scope.row[col.prop] }}
</slot>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import _ from 'lodash'
export default {
props: {
id: {
type: String,
required: true
},
reserveSelection: {
type: Boolean,
default: false
},
// 懒加载
lazy: {
type: Boolean,
default: false
},
// 懒加载时加载的数据
load: {
type: Function
},
vLoading: {
type: Boolean,
default: false
},
elementLoadingText: {
type: String,
default: '请稍后...'
},
// 表格内容数组
data: {
default: function () {
return []
},
type: Array
},
// 表格数组
header: {
default: function () {
return []
},
type: Array
},
vSelection: {
type: Boolean,
default: false
},
rowKey: {
type: [String, Function]
},
treeProps: {
type: Object,
default: () => {
return { hasChildren: 'hasChildren', children: 'children' }
}
},
rowClassName: {
type: Function
},
rowStyle: {
type: Function
},
// 展示树形数据时,树节点的缩进量
indent: {
type: Number,
default: 16
},
// 是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效
defaultExpandAll: {
type: Boolean,
default: false
}
},
watch: {
data: {
handler(newVal, odlVal) {
// console.log('改变表格数据')
this.tableData = newVal
},
immediate: true
},
header: {
handler(newVal, odlVal) {
this.tableHeader = _.cloneDeep(newVal)
},
immediate: true,
deep: true
}
},
data() {
return {
tableHeader: [],
tableData: [],
dragState: {
// 起始元素的 index
start: -1,
// 结束元素的 index
end: -1,
// 移动鼠标时所覆盖的元素 index
move: -1,
// 是否正在拖动
dragging: false,
// 拖动方向
direction: undefined
},
table: null,
selection: [],
// 当前移动的列Fixed是不是为true
isFixed: false
}
},
mounted() {
this.$nextTick(() => {
this.table = document.querySelector('#' + this.id)
// 鼠标移出拖拽元素且松开了左键范围外,需重新初始化状态
document.onmouseup = this.resetDragIfNecessary
this.handleCaretLocation()
})
},
methods: {
// 调整过列的宽度,将列的新宽度映射回tableHeader
headerDragend(newWidth, oldWidth, column, event) {
this.tableHeader.forEach((it) => {
if (it.prop === column.property) {
it.width = newWidth
}
})
},
handleCaretLocation() {
this.$nextTick(() => {
this.table.querySelectorAll('.is-sortable > .cell').forEach((it) => {
let caret = it.querySelector('.caret-wrapper')
it.querySelector('.thead-cell').appendChild(caret)
})
})
},
// 滚动条回到顶部
initTop() {
this.$refs[this.id].bodyWrapper.scrollTop = 0
},
headerCellClassName({ column, columnIndex }) {
let cls = ''
if (!column.fixed) {
// if (columnIndex - 1 === this.dragState.move) {
if (columnIndex === this.dragState.move) {
cls = `drag_active_${this.dragState.direction}`
} else {
cls = ''
}
}
return cls + ' table-header-cell-style'
},
// 按下鼠标开始拖动,记录起始列
handleMouseDown(e, column, fixed) {
this.isFixed = false
if (fixed) {
this.isFixed = true
return
}
this.dragState.dragging = true
this.dragState.start = parseInt(column.columnKey)
},
// 拖动中
handleMouseMove(e, column) {
// console.log(e, column,'handleMouseMove');
if (this.isFixed) return
if (this.dragState.dragging) {
let index = parseInt(column.columnKey)
if (index - this.dragState.start !== 0) {
// 判断拖动方向
this.dragState.direction = index - this.dragState.start < 0 ? 'left' : 'right'
this.dragState.move = parseInt(column.columnKey)
} else {
this.dragState.direction = undefined
}
}
},
// 鼠标放开结束拖动
handleMouseUp(e, column) {
// console.log(e,column, 'handleMouseUp')
if (this.isFixed) return
// 记录结束列
this.dragState.end = parseInt(column.columnKey)
this.dragColumn(this.dragState)
this.resetDragState()
},
// 拖动易位
dragColumn(item) {
let { start, end, direction } = item
// 判断拖动结束位置的fixed属性是否为真,真就不执行
if (this.tableHeader[end].fixed) return
// console.log(start, end, direction, 'tempData')
if (this.dragState.start !== -1 && this.dragState.start !== this.dragState.end) {
let tempData = []
let left = direction === 'left'
let min = left ? end : start - 1
let max = left ? start + 1 : end
for (let i = 0; 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 - 1 : i + 1])
} else {
tempData.push(this.tableHeader[i])
}
}
this.tableHeader = tempData
// tableHeader变了后内容没有相应发生变化,可能是elementUI的bug,须有这行代码来强制触发内容重渲染
this.tableData.splice(0, 0)
this.handleCaretLocation()
}
},
resetDragIfNecessary(e) {
if (e.buttons === 0) {
this.resetDragState()
}
},
// 初始化鼠标记录的位置
resetDragState() {
if (this.dragState.dragging) {
this.dragState.start = -1
this.dragState.end = -1
this.dragState.move = -1
this.dragState.dragging = false
this.dragState.direction = undefined
}
},
selectionChange(val) {
this.$emit('selectionChange', val)
},
clearSelection() {
this.$refs[this.id].clearSelection()
},
toggleRowSelection(row, flag) {
this.$refs[this.id].toggleRowSelection(row, flag)
},
toggleRowExpansion(row) {
this.$refs[this.id].toggleRowExpansion(row, true)
}
},
computed: {
getDefaultExpandAll() {
console.log(this.defaultExpandAll, 'this.defaultExpandAll')
return this.defaultExpandAll
}
}
}
</script>
<style lang="scss">
// .tag-cls {
// font-size: 22px;
// width: 100%;
// text-align: center;
// margin: 20px 0;
// }
// .table-header-row-style {
// color: rgb(32, 31, 53);
// font-size: 9pt;
// font-weight: normal;
// height: 40px;
// }
.table-drag .el-table th {
padding: 0;
}
.table-drag .el-table th.drag_active_left {
border-left: 3px solid #409eff;
}
.table-drag .el-table th.drag_active_right {
border-right: 3px solid #409eff;
}
.table-drag .table-header-row-style .thead-cell {
cursor: pointer;
overflow: initial;
width: 100%;
}
.table-drag.table-drag_moving .el-table th .thead-cell {
cursor: move !important;
}
.table-drag.table-drag_moving .el-table__fixed {
cursor: not-allowed;
}
</style>
<style lang="scss" scoped>
::v-deep .table-drag_table .el-table__body-wrapper {
overflow-y: auto !important;
}
</style>