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>

效果图展示

相关推荐
程序员拂雨31 分钟前
Angular 知识框架
前端·javascript·angular.js
GoodStudyAndDayDayUp1 小时前
gitlab+portainer 实现Ruoyi Vue前端CI/CD
前端·vue.js·gitlab
程序员阿明1 小时前
vite运行只能访问localhost解决办法
前端·vue
前端 贾公子1 小时前
uniapp -- 验证码倒计时按钮组件
前端·vue.js·uni-app
zhengddzz1 小时前
从卡顿到丝滑:JavaScript性能优化实战秘籍
开发语言·javascript·性能优化
淡笑沐白1 小时前
AJAX技术全解析:从基础到最佳实践
前端·ajax
Go_going_1 小时前
ajax,Promise 和 fetch
javascript·ajax·okhttp
龙正哲2 小时前
如何在Firefox火狐浏览器里-安装梦精灵AI提示词管理工具
前端·firefox
徐徐同学2 小时前
轻量级Web画板Paint Board如何本地部署与随时随地在线绘画分享
前端
LuckyLay2 小时前
Vue百日学习计划Day4-8——Gemini版
前端·vue.js·学习