el-table封装一个自定义列配置表格组件(vue3开箱即用)

组件核心功能

  • 拖拽排序(使用 vuedraggable

  • 显示/隐藏控制

  • 列宽调整

  • 列固定状态记忆

  • 搜索过滤列

  • 本地存储(localStorage)可改成接口保存

  • 默认配置恢复

  • 通过 searchText 动态过滤列。

安装拖拽依赖

复制代码
npm install vuedraggable
  • 如果需要支持后端存储,可以在 savePreferences 方法中添加 API 调用。

  • 确保 columns 配置中包含 proplabel 字段。

创建组件ColumnVisibilityControl.vue

javascript 复制代码
<template>
  <div class="column-control">
    <el-button @click="showControl = true"
      ><el-icon><Grid /></el-icon
    ></el-button>

    <el-dialog v-model="showControl" title="列配置" width="600px">
      <el-input
        v-model="searchText"
        placeholder="搜索列"
        clearable
        style="margin-bottom: 16px"
      />

      <draggable v-model="currentColumns" item-key="prop" @end="handleDragEnd">
        <template #item="{ element: col }">
          <div
            v-if="col.label.toLowerCase().includes(searchText.toLowerCase())"
            class="column-item"
          >
            <el-checkbox v-model="col.visible">{{ col.label }}</el-checkbox>
            <el-input-number
              v-if="col.visible"
              v-model="col.width"
              :min="50"
              :max="500"
              style="margin-left: 16px"
              placeholder="列宽"
            />
            <el-checkbox
              v-if="col.visible"
              v-model="col.fixed"
              style="margin-left: 16px"
            >
              固定
            </el-checkbox>
          </div>
        </template>
      </draggable>

      <template #footer>
        <el-button @click="resetToDefault">恢复默认</el-button>
        <el-button type="primary" @click="savePreferences">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, watch, onMounted } from "vue";
import { ElMessage } from "element-plus";
import draggable from "vuedraggable";
import {
  Check,
  Delete,
  Edit,
  Message,
  Search,
  Grid,
} from "@element-plus/icons-vue";

const props = defineProps({
  columns: {
    type: Array,
    default: () => [],
    validator: (value) => value.every((col) => "prop" in col && "label" in col),
  },
  storageKey: {
    type: String,
    default: "table-columns-preference",
  },
});

const emit = defineEmits(["columns-change"]);

const showControl = ref(false);
const searchText = ref("");
const currentColumns = ref([]);
const defaultColumns = ref([]);

const initializeColumns = () => {
  defaultColumns.value = JSON.parse(JSON.stringify(props.columns));
  currentColumns.value = JSON.parse(JSON.stringify(props.columns));

  const savedData = localStorage.getItem(props.storageKey);
  if (savedData) {
    try {
      const parsed = JSON.parse(savedData);
      applySavedPreferences(parsed);
    } catch {
      localStorage.removeItem(props.storageKey);
    }
  }
};

const applySavedPreferences = (savedData) => {
  currentColumns.value = props.columns
    .map((col) => {
      const savedCol = savedData.find((c) => c.prop === col.prop);
      return savedCol ? { ...col, ...savedCol } : col;
    })
    .sort((a, b) => a.order - b.order);
};

const updateVisibleColumns = () => {
  const visibleColumns = currentColumns.value
    .filter((col) => col.visible)
    .map((col) => ({
      prop: col.prop,
      label: col.label,
      width: col.width,
      fixed: col.fixed,
    }));
  emit("columns-change", visibleColumns);
};

const handleDragEnd = () => {
  currentColumns.value = currentColumns.value.map((col, index) => ({
    ...col,
    order: index,
  }));
};

const savePreferences = () => {
  const dataToSave = currentColumns.value.map((col) => ({
    prop: col.prop,
    visible: col.visible,
    order: col.order,
    width: col.width,
    fixed: col.fixed,
  }));
  localStorage.setItem(props.storageKey, JSON.stringify(dataToSave));
  showControl.value = false;
  ElMessage.success("配置已保存");
};

const resetToDefault = () => {
  currentColumns.value = JSON.parse(JSON.stringify(defaultColumns.value));
  localStorage.removeItem(props.storageKey);
  ElMessage.success("已恢复默认配置");
};

watch(
  currentColumns,
  () => {
    updateVisibleColumns();
  },
  { deep: true }
);

onMounted(() => {
  initializeColumns();
});
</script>

<style scoped>
.column-item {
  padding: 8px 12px;
  margin: 4px 0;
  background: #f5f7fa;
  border-radius: 4px;
  cursor: move;
  display: flex;
  align-items: center;
}
</style>

组件的使用

javascript 复制代码
<template>
  <div class="about">
    <div>
      <column-control
        :columns="tableColumns"
        storage-key="user-table-preferences"
        @columns-change="handleColumnsChange"
      />

      <el-table :data="tableData" :key="tableKey">
        <el-table-column
          v-for="col in visibleColumns"
          :key="col.prop"
          :prop="col.prop"
          :label="col.label"
          :width="col.width"
          :fixed="col.fixed"
        />
      </el-table>
    </div>
  </div>
</template>
<script setup>
import { ref } from "vue";
import ColumnControl from "./ColumnVisibilityControl.vue";

// 表格数据
const tableData = ref([
  {
    name: "张三",
    age: 25,
    address: "北京",
    sex: "男",
    type: "A",
    phone: "1234567890",
  },
  {
    name: "李四",
    age: 30,
    address: "上海",
    sex: "女",
    type: "B",
    phone: "1234567890",
  },
  {
    name: "王五",
    age: 28,
    address: "重庆",
    sex: "女",
    type: "C",
    phone: "1234567890",
  },
  {
    name: "王刘",
    age: 33,
    address: "广州",
    sex: "女",
    type: "C",
    phone: "1234567890",
  },
  {
    name: "王气",
    age: 88,
    address: "深圳",
    sex: "男",
    type: "D",
    phone: "1234567890",
  },
]);

// 列配置
const tableColumns = [
  { prop: "name", label: "姓名", visible: true, width: 120, fixed: false },
  { prop: "age", label: "年龄", visible: true, width: 100, fixed: false },
  { prop: "address", label: "地址", visible: true, width: 120, fixed: false },
  { prop: "sex", label: "性别", visible: true, width: 120, fixed: false },
  { prop: "type", label: "类别", visible: true, width: 120, fixed: false },
  { prop: "phone", label: "电话", visible: true, width: 120, fixed: false },
];

// 可见列
const visibleColumns = ref([]);
const tableKey = ref(0);

// 列配置变化回调
const handleColumnsChange = (visibleCols) => {
  visibleColumns.value = visibleCols;
  tableKey.value += 1; // 强制刷新表格
};
</script>
<style>
@media (min-width: 1024px) {
  .about {
    min-height: 100vh;
    display: flex;
    align-items: center;
  }
}
</style>

效果图展示

相关推荐
IT、木易12 分钟前
HTML5 新增的标签有哪些?
前端·html·html5
柳鲲鹏12 分钟前
Ubuntu编译jetlinks-ui-vue
vue.js·ui
Et2nity27 分钟前
tiptap md 编辑器实用场景开发
前端·javascript·编辑器·markdown
轩昂7K40 分钟前
sqoop的sql语言导入方式
前端·sql·sqoop
小王不会写code1 小时前
Vue.prototype 详解及使用
前端·javascript·vue.js
V+zmm101341 小时前
在线办公小程序(springboot论文源码调试讲解)
vue.js·spring boot·微信小程序·小程序·毕业设计
前端菜鸟日常1 小时前
为啥vue3设计不直接用toRefs,而是reactive+toRefs
vue.js
一路向前的月光2 小时前
react(9)-redux
前端·javascript·react.js
大数据追光猿2 小时前
Python中的Flask深入认知&搭建前端页面?
前端·css·python·前端框架·flask·html5
莫忘初心丶2 小时前
python flask 使用教程 快速搭建一个 Web 应用
前端·python·flask