likes
comments
collection
share

vue3+element+table表格列拖拽实现本地缓存拖拽位置

作者站长头像
站长
· 阅读数 94

前言

vue3+element+table实现同步本地缓存表格列拖拽功能,可跟着文章从下包开始一步步实现一个本地的小demo,由于直接缓存表宽度会出现表格拖拽精度缺失(当每列的宽的总和小于表格宽度,拖拽精度就会丢失),所以下面会把拖拽当前列的偏差值算到右列,解决拖拽精度丢失问题。

初始化一个vue3demo

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模块

vue3+element+table表格列拖拽实现本地缓存拖拽位置

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
评论
请登录