vue实现表格拖动列宽
实现表格列宽拖动的核心思路
通过监听鼠标事件(mousedown、mousemove、mouseup)实现拖拽逻辑,动态修改列宽。需要处理表头与表体的联动,确保拖动时视觉一致。
基础HTML结构
<template>
<div class="draggable-table">
<table>
<thead>
<tr>
<th v-for="(col, index) in columns" :key="index"
@mousedown="startResize(index, $event)">
{{ col.title }}
<div class="resize-handle"></div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in data" :key="rowIndex">
<td v-for="(col, colIndex) in columns" :key="colIndex">
{{ row[col.key] }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
CSS样式关键部分
.resize-handle {
position: absolute;
right: 0;
top: 0;
width: 5px;
height: 100%;
background: transparent;
cursor: col-resize;
}
th {
position: relative;
user-select: none;
}
table {
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #ddd;
padding: 8px;
}
JavaScript核心逻辑
export default {
data() {
return {
columns: [
{ title: '姓名', key: 'name', width: 150 },
{ title: '年龄', key: 'age', width: 100 },
{ title: '地址', key: 'address', width: 300 }
],
data: [
{ name: '张三', age: 25, address: '北京' },
{ name: '李四', age: 30, address: '上海' }
],
isResizing: false,
startX: 0,
startWidth: 0,
resizeIndex: null
}
},
methods: {
startResize(index, e) {
this.isResizing = true
this.resizeIndex = index
this.startX = e.clientX
this.startWidth = this.columns[index].width
document.addEventListener('mousemove', this.handleResize)
document.addEventListener('mouseup', this.stopResize)
},
handleResize(e) {
if (!this.isResizing) return
const newWidth = this.startWidth + (e.clientX - this.startX)
this.columns[this.resizeIndex].width = Math.max(30, newWidth)
},
stopResize() {
this.isResizing = false
document.removeEventListener('mousemove', this.handleResize)
document.removeEventListener('mouseup', this.stopResize)
}
}
}
动态绑定列宽
在HTML中绑定style属性实现宽度同步:
<th v-for="(col, index) in columns" :key="index"
:style="{ width: col.width + 'px' }"
@mousedown="startResize(index, $event)">
{{ col.title }}
<div class="resize-handle"></div>
</th>
<td v-for="(col, colIndex) in columns" :key="colIndex"
:style="{ width: columns[colIndex].width + 'px' }">
{{ row[col.key] }}
</td>
性能优化建议
使用CSS的will-change: width属性减少重排开销:
th, td {
will-change: width;
}
添加拖动时的视觉反馈:
.is-resizing {
background-color: #f0f0f0;
}
在拖动时动态添加class:
handleResize(e) {
if (!this.isResizing) return
document.body.classList.add('is-resizing')
// ...原有逻辑
},
stopResize() {
document.body.classList.remove('is-resizing')
// ...原有逻辑
}
完整组件示例
<template>
<div class="draggable-table">
<table>
<thead>
<tr>
<th v-for="(col, index) in columns" :key="index"
:style="{ width: col.width + 'px' }"
@mousedown="startResize(index, $event)">
{{ col.title }}
<div class="resize-handle"></div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in data" :key="rowIndex">
<td v-for="(col, colIndex) in columns" :key="colIndex"
:style="{ width: columns[colIndex].width + 'px' }">
{{ row[col.key] }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
columns: [
{ title: '姓名', key: 'name', width: 150 },
{ title: '年龄', key: 'age', width: 100 },
{ title: '地址', key: 'address', width: 300 }
],
data: [
{ name: '张三', age: 25, address: '北京' },
{ name: '李四', age: 30, address: '上海' }
],
isResizing: false,
startX: 0,
startWidth: 0,
resizeIndex: null
}
},
methods: {
startResize(index, e) {
if (e.target.classList.contains('resize-handle') || e.offsetX > this.columns[index].width - 10) {
this.isResizing = true
this.resizeIndex = index
this.startX = e.clientX
this.startWidth = this.columns[index].width
document.addEventListener('mousemove', this.handleResize)
document.addEventListener('mouseup', this.stopResize)
}
},
handleResize(e) {
if (!this.isResizing) return
document.body.classList.add('is-resizing')
const newWidth = this.startWidth + (e.clientX - this.startX)
this.$set(this.columns, this.resizeIndex, {
...this.columns[this.resizeIndex],
width: Math.max(30, newWidth)
})
},
stopResize() {
document.body.classList.remove('is-resizing')
this.isResizing = false
document.removeEventListener('mousemove', this.handleResize)
document.removeEventListener('mouseup', this.stopResize)
}
}
}
</script>






