vue3+element+table表格列拖拽实现本地缓存拖拽位置
前言
用vue3
+element
+table
实现同步本地缓存表格列拖拽功能,可跟着文章从下包开始一步步实现一个本地的小demo
,由于直接缓存表宽度会出现表格拖拽精度缺失(当每列的宽的总和小于表格宽度,拖拽精度就会丢失),所以下面会把拖拽当前列的偏差值算到右列,解决拖拽精度丢失问题。
初始化一个vue3
的demo
npm create vite
下载element
包
npm install element-plus --save
在main.ts
文件引入样式并中挂载element
包
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
解决项目引入vue
报找不到vue
模块
在tsconfig.json
文件中修改moduleResolution
配置
"moduleResolution": "bundler"
改为node
即可
"moduleResolution": "node"
引入官方示例table
模拟数据
把border
设置为true可设置为表格拖拽模式
<template>
<el-table :border="true" :data="tableData" style="width: 100%" >
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
<script lang="ts" setup>
import {reactive} from 'vue'
const tableData = reactive([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
</script>
实现表格拖拽缓存思路
实现方式:将每个拖拽的列宽记录下来到本地,但有个缺陷就是,当拖拽的每一列宽度加起来小于表格的宽度,拖拽效果就会变得无法精确。
解决思路:当拖拽当前列时,获取新值宽度减去旧值宽度,把计算后的差异补全给右边字段,这样就能解决拖拽后列宽变少导致的拖拽无法精确问题
拖拽准备工作
更改表结构
先把表字段column
弄成数组,遍历表el-table-column
字段,用插槽的方式插进表格数据,用prop直接读也行,用插槽可以方便后续需要加判断逻辑自定义数据的场景
<template>
<div class="box">
<el-table :border="true" :data="tableData" style="width: 100%">
<el-table-column v-for="item in cloumn" :key="item.prop" :label="item.label" :width="item.width">
<template #default="scope">
{{ scope.row.date }}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts" setup>
import {reactive} from 'vue'
const tableData = reactive([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
const cloumn = reactive([
{
prop: 'date',
label: 'Date',
width: '180',
},
{
prop: 'name',
label: 'Name',
width: '180',
},
{
prop: 'address',
label: 'Address',
},
])
</script>
<style scoped>
.box{
width: 800px;
margin: auto;
}
</style>
开始写拖拽逻辑
行拖拽时触发header-dragend
方法,设置当前字段和宽度,获取下个字段的数据,如何存在下个字段则计算上个字段拖拽的偏差值,首先要拿到下个元素的宽度进行计算,获取下个字段偏差值计算公式是:( 下个字段宽 -(当前字段宽新 - 当前字段宽旧)),获取下个字段元素精确度问题:由于拖拽时通过event
获取元素时会出现层级问题,导致获取拖拽的元素无法精确,所以获取右边元素的宽度的时候需要做两层赋值,两个字段的宽度计算完毕,缓存到本地缓存中
<el-table @header-dragend="headerDragend"></el-table>
//拖拽表头本地缓存记录宽度
const headerDragend = (newWidth: any, oldWidth: any, columns: any, event: any) => {
const tableHeader = JSON.parse(localStorage.getItem('TableHeader') || '{}');
const nextColumn = column.value[columns.getColumnIndex() + 1]; //获取表格下个字段
//更新当前拖拽字段宽
if (tableHeader[key]) {
tableHeader[key][columns.rawColumnKey] = newWidth;
} else {
tableHeader[key] = { [`${columns.rawColumnKey}`]: newWidth };
}
//存在下个字段更新下个字段的宽
if (nextColumn) {
let nextWidth = event?.target?.parentNode?.nextElementSibling?.offsetWidth; //拖拽右边字段的宽度(DIV)
if (event.target.tagName === 'TH') {
nextWidth = event?.target?.nextElementSibling?.offsetWidth; //拖拽右边字段的宽度
}
const offWidth = newWidth - oldWidth; //新旧偏差值
const nextCountWidth = nextWidth - offWidth; //下个字段总宽度
if (tableHeader[key]) {
tableHeader[key][nextColumn.prop] = nextCountWidth;
} else {
tableHeader[key] = { [`${nextColumn.prop}`]: nextCountWidth };
}
}
localStorage.setItem('TableHeader', JSON.stringify(tableHeader));
};
监听tableHeader
值的变化
通过watch
监听tableHeader
发生了改变,把拖拽的宽度重新赋值到表字段column
中,就能实现实时更新数据
watch(
() => tableHeader,
(tableHeader) => {
column.value = column.value.map((item: any) => {
if (tableHeader[key] && tableHeader[key][item.prop]) {
item.width = tableHeader[key][item.prop];
}
return { ...item };
});
},
{ immediate: true, deep: true },
);
整体代码
这里给表格加了个key
用来区分多表格存储的唯一标识
<template>
<div class="box">
<el-table :border="true" :data="tableData" style="width: 100%" @header-dragend="headerDragend">
<el-table-column v-for="item in column" :key="item.prop" :label="item.label" :width="item.width">
<template #default="scope">
{{ scope.row.date }}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts" setup>
import {reactive,watch,ref} from 'vue'
const key = 'indexTable';//表格的唯一标识key(区分多个表格)
const tableData = reactive([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
const column = ref([
{
prop: 'date',
label: 'Date',
width: '180',
},
{
prop: 'name',
label: 'Name',
width: '180',
},
{
prop: 'address',
label: 'Address',
},
])
const tableHeader = reactive(JSON.parse(localStorage.getItem('TableHeader') || '{}'));
//拖拽表头本地缓存记录宽度
const headerDragend = (newWidth: any, oldWidth: any, columns: any, event: any) => {
const nextColumn = column.value[columns.getColumnIndex() + 1]; //获取表格下个字段
//更新当前拖拽字段宽
if (tableHeader[key]) {
tableHeader[key][columns.rawColumnKey] = newWidth;
} else {
tableHeader[key] = { [`${columns.rawColumnKey}`]: newWidth };
}
//存在下个字段更新下个字段的宽
if (nextColumn) {
let nextWidth = event?.target?.parentNode?.nextElementSibling?.offsetWidth; //拖拽右边字段的宽度(DIV)
if (event.target.tagName === 'TH') {
nextWidth = event?.target?.nextElementSibling?.offsetWidth; //拖拽右边字段的宽度
}
const offWidth = newWidth - oldWidth; //新旧偏差值
const nextCountWidth = nextWidth - offWidth; //下个字段总宽度
if (tableHeader[key]) {
tableHeader[key][nextColumn.prop] = nextCountWidth;
} else {
tableHeader[key] = { [`${nextColumn.prop}`]: nextCountWidth };
}
}
localStorage.setItem('TableHeader', JSON.stringify(tableHeader));
};
watch(
() => tableHeader,
(tableHeader) => {
column.value = column.value.map((item: any) => {
if (tableHeader[key] && tableHeader[key][item.prop]) {
item.width = tableHeader[key][item.prop];
}
return { ...item };
});
},
{ immediate: true, deep: true },
);
</script>
<style scoped>
.box{
width: 800px;
margin: auto;
}
</style>
结语
写完这篇文章才发现,element
包已经解决了拖拽精度丢失问题,底层会计算表格拖拽的总宽度,所以现在这篇文章只能给其他没有解决拖拽精度丢失的组件库提供一下解决思路~
如果你觉得此文对你有一丁点帮助,麻烦点个赞哈,谢谢大家。
转载自:https://juejin.cn/post/7243437099361189944