游侠网云服务,免实名免备案服务器 游侠云域名,免实名免备案域名

统一声明:

1.本站联系方式
QQ:709466365
TG:@UXWNET
官方TG频道:@UXW_NET
如果有其他人通过本站链接联系您导致被骗,本站一律不负责!

2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET
3.免实名域名注册购买- 游侠云域名
4.免实名国外服务器购买- 游侠网云服务
Vue3+Element Plus可定制动态表格列配置组件手把手实现教程

本文从实际需求出发,手把手教你搭建组件:先拆解核心功能(列显隐、顺序调整、宽度自定义),再一步步实现配置弹窗、用状态管理工具(Pinia/Vuex)维护列状态、打通与ElTable的联动逻辑,还会分享避坑技巧(如表格渲染性能优化、配置数据持久化)。不管是Vue3新手还是需优化现有表格的开发者,跟着流程走就能快速拥有可复用的动态列配置组件,让表格从“固定死”变“用户说了算”,直接提升项目灵活性与用户体验。

你有没有过这种经历?做后台管理系统时,运营同事追着你改表格列——“把‘用户备注’列藏了吧,占地方”“能不能把‘订单金额’挪到第二列?”“这个列能不能调宽点,字都挤在一起了”。每次改都要翻代码找ElTable的columns配置,改完再部署,来来回回半天,两边都闹心。我去年帮朋友的电商后台解决过这个问题,用Vue3+Element Plus做了个可定制的动态列配置组件,上线后他们运营说“终于不用天天找技术了”,我自己也省了好多重复工作。今天就把这套方法分享给你,没复杂逻辑,跟着做就能成。

先想清楚:动态列配置到底要解决哪些问题?

别着急写代码,先掰着手指头数需求——用户要的不是“能改列”,是“改得爽”。我当初做的时候,先跟运营聊了半小时, 出三个核心需求:

第一是列显隐:没用的列能一键隐藏,比如“创建时间”对运营来说不如“支付时间”有用;

第二是顺序调整:常用的列能往前挪,比如“订单号”“金额”要放在显眼位置;

第三是宽度自定义:有些列内容长(比如“商品名称”),得能拉宽看全;

第四是状态持久化:用户调完设置,下次登录还得保持,总不能每次刷新都重设吧?

这些需求里,前三个是“表面功夫”,第四个才是“用户体验的关键”。我第一次做的时候没考虑持久化,朋友的运营说“每次刷新都要重新调,还不如不改”,后来我用Pinia加localStorage存配置,才算真正解决问题。举个例子:用户把“商品名称”列调宽到200px,隐藏了“创建时间”,这些设置会存在localStorage里,key是“table-columns-config-xxx”(xxx是表格的唯一标识,比如“order-list”),下次登录时Pinia会先读localStorage,再初始化状态,这样用户的设置就不会丢了。

还有个容易忽略的点:列的“唯一标识”。每列必须有个唯一的id(比如“order_id”“product_name”),不能用label当标识——不然如果有两个列叫“名称”,就会乱套。我之前帮另一个项目改bug时,就遇到过这种情况:他们用label当id,结果运营把“商品名称”改成“产品名称”,所有配置全乱了,后来换成id才解决。

手把手搭组件:从0到1实现动态列配置

接下来进入正题,我们分四步做组件:配置弹窗、状态管理、拖拽排序、表格联动。我尽量讲细,连代码里的注释都告诉你。

第一步:做个“好用的”配置弹窗

动态列配置的入口一般是表格右上角的“列设置”按钮,点了弹出Dialog。弹窗里要放什么?

  • 列显隐选择:用Element Plus的,每个选项对应一列,label是列的标题,value是列的id;
  • 顺序调整:把选中的列做成可拖拽的列表,用Sortable.js实现(Element Plus没有拖拽组件,Sortable.js轻量又好用);
  • 宽度调整:给每个列加个,让用户输宽度(比如100px或100);
  • 确定/重置按钮:确定按钮保存配置,重置按钮恢复默认设置。
  • 我做的时候,弹窗的结构大概是这样的(简化版):

    
    

    <!-

  • 列显隐 >
  • {{ col.label }}

    <!-

  • 拖拽排序 >
  • {{ col.label }}

    <!-

  • 按钮 >
  • 重置

    确定

    这里要注意:checkedColumns是选中的列id数组,sortedColumns是拖拽后的列列表(按order排序)。Sortable.js的初始化代码要放在onMounted里,比如:

    import Sortable from 'sortablejs'
    

    onMounted(() => {

    const el = document.querySelector('.sortable-list')

    Sortable.create(el, {

    onEnd: (evt) => {

    // 拖拽结束后更新列的order

    const [movedItem] = sortedColumns.splice(evt.oldIndex, 1)

    sortedColumns.splice(evt.newIndex, 0, movedItem)

    // 更新每个列的order序号

    sortedColumns.forEach((col, index) => {

    col.order = index

    })

    }

    })

    })

    我当初用Sortable.js时踩了个坑:拖拽后的数组要手动更新order,不然下次排序会乱。比如你把第三列拖到第一列,它的order还是2,后面再拖就会重复,所以一定要遍历数组重新设order。

    第二步:用Pinia管状态,让配置“动起来”

    动态列的核心是状态同步——配置弹窗改了,表格得跟着变;表格改了(比如用户拖动列宽),配置也得同步。这时候就得用状态管理工具,我选的是Pinia(Vue3官方推荐,比Vuex轻量)。

    定义一个Pinia store,比如columnsStore.js

    import { defineStore } from 'pinia'
    

    export const useColumnsStore = defineStore('columns', {

    state: () => ({

    // 列配置列表,默认值从组件props获取

    columns: [],

    // 表格唯一标识,用来存localStorage

    tableKey: ''

    }),

    actions: {

    // 初始化配置:从localStorage读,没有就用默认值

    initConfig(defaultColumns, key) {

    this.tableKey = key

    const saved = localStorage.getItem(table-columns-${key})

    if (saved) {

    this.columns = JSON.parse(saved)

    } else {

    this.columns = defaultColumns.map(col => ({

    ...col,

    order: col.order || 0,

    visible: col.visible !== false,

    width: col.width || ''

    }))

    }

    },

    // 保存配置到localStorage

    saveConfig() {

    localStorage.setItem(table-columns-${this.tableKey}, JSON.stringify(this.columns))

    },

    // 重置配置到默认值

    resetConfig(defaultColumns) {

    this.columns = defaultColumns.map(col => ({

    ...col,

    order: col.order || 0,

    visible: col.visible !== false,

    width: col.width || ''

    }))

    this.saveConfig()

    }

    },

    getters: {

    // 过滤出显示的列,并按order排序

    visibleColumns: (state) => {

    return state.columns

    .filter(col => col.visible)

    .sort((a, b) => a.order

  • b.order)
  • }

    }

    })

    这里的关键是visibleColumns这个getter——它返回“显示的列+按顺序排序”的数组,直接绑定到ElTable的columns prop上:

    这样一来,只要columnsStore.columns变了,visibleColumns就会变,ElTable也会自动更新——这就是“联动”的核心逻辑。

    我当初做的时候,一开始没写visibleColumns,直接把columns绑到ElTable,结果显示的列没过滤,隐藏的列还在,后来加了这个getter才对。所以说,getter的作用就是“把状态转换成组件需要的格式”,别嫌麻烦,这步很重要。

    第三步:和ElTable联动,让配置“生效”

    最后一步是把配置弹窗和Pinia store连起来。比如,组件的props要接收defaultColumns(默认列配置)和tableKey(表格唯一标识),然后在onMounted里初始化store:

    
    

    import { onMounted } from 'vue'

    import { useColumnsStore } from './columnsStore'

    const props = defineProps({

    defaultColumns: {

    type: Array,

    required: true

    },

    tableKey: {

    type: String,

    required: true

    }

    })

    const columnsStore = useColumnsStore()

    onMounted(() => {

    columnsStore.initConfig(props.defaultColumns, props.tableKey)

    })

    然后,配置弹窗里的checkedColumns要绑定到store的columns里的visible字段:

    
    

    {{ col.label }}

    // 用computed同步checkedIds和columns的visible

    const checkedIds = computed({

    get() {

    return columnsStore.columns.filter(col => col.visible).map(col => col.id)

    },

    set(val) {

    columnsStore.columns.forEach(col => {

    col.visible = val.includes(col.id)

    })

    }

    })

    拖拽排序和宽度调整也是一样的道理——直接修改columnsStore.columns里的orderwidth字段,Pinia会自动更新状态,ElTable也会跟着变。

    这里要讲个避坑技巧:别直接修改数组元素的属性,比如col.visible = true,虽然Pinia能检测到变化,但有时候会延迟。最好用columnsStore.$patch方法,比如:

    columnsStore.$patch((state) => {
    

    state.columns.forEach(col => {

    col.visible = val.includes(col.id)

    })

    })

    这样更新更高效,表格渲染也更实时。我之前直接改属性时,遇到过弹窗点确定后,表格要等1秒才更新的情况,用$patch就解决了。

    最后再补几个“能提升体验的小细节”

    做组件不能只满足“能用”,得“好用”。我再分享几个当初加的小细节,让你的组件更贴心:

  • 列宽度同步:用户拖动ElTable的列宽时,同步更新store里的width字段——用ElTable的header-dragend事件,获取拖动后的宽度,再更新store:
  • vue

    const handleHeaderDragend = (newWidth, oldWidth, column) => {

    columnsStore.$patch((state) => {

    const col = state.columns.find(c => c.id === column.id)

    if (col) col.width = newWidth

    })

    columnsStore.saveConfig()

    }

  • 默认配置提示:重置按钮旁边加个提示“重置后会恢复到系统默认设置”,避免用户误点;
  • 空状态处理:如果所有列都隐藏,ElTable会显示“No Data”,可以加个自定义空状态:“请在列设置中选择要显示的列”,更友好;
  • 拖拽时的提示:Sortable.js拖拽时,加个半透明的占位符,让用户知道拖到哪了——用ghostClass配置:
  • javascript

    Sortable.create(el, {

    ghostClass: ‘sortable-ghost’, // 自己写css样式,比如opacity: 0.5

    onEnd: (evt) => { // }

    })

    我当初做完这个组件,朋友的运营说“比之前用的第三方组件好用10倍”——不是因为功能多,是因为贴合实际需求:运营要的是“快”“方便”“不用找技术”,我做的组件刚好满足这些。现在这个组件已经在他们后台用了一年,没出过大问题,偶尔有小bug也是我远程改改,比之前天天改列配置轻松多了。

    如果你按这个步骤做,遇到问题可以随时找我——毕竟我踩过的坑,不想让你再踩一遍。等你做完,记得跟我说说运营的反馈,我也想沾沾光~

    最后给你个列配置字段的说明表,帮你理清楚每个字段的作用:

    字段名 类型 说明 默认值
    id String 列的唯一标识(必填,不能重复)
    label String 列显示的标题(必填)
    visible Boolean 是否显示该列 true
    order Number 列的排序序号(越小越靠前) 0
    width String/Number 列的宽度(支持px或数字,如’200px’或200)

    按这个表定义defaultColumns,保准不会错。比如


    我自己做动态列配置那会,一开始想着跟着老项目用Vuex吧,结果写起来别提多绕了——想改个列的显示状态,得先定义个mutation,再写个action dispatch过去,明明一行代码能搞定的事,偏要拆成三四步,写得我都嫌麻烦。后来看到Vue3官方说Pinia是“下一代状态管理工具”,抱着试试的心态换了,才发现这工具是真的懂开发者:不用mutation,直接就能改state里的列配置,比如想把“商品名称”列的width调成200px,直接写col.width = 200就行,语法和组件里的data一模一样,根本不用学新东西。而且它天生支持TypeScript,定义列的id、label这些字段时,编辑器会自动提示类型,再也不用怕把id写成number导致配置乱套了,对我们这种要精准处理列属性的场景来说,简直是救星。

    再说动态列配置本身就是个“轻量级”的活儿——无非是管管列的显隐、顺序、宽度,犯不着用Vuex那种分modules、搞命名空间的复杂结构。Pinia的store写起来就像个普通的JS对象,getter里过滤显示的列、按order排序,action里存localStorage,逻辑顺得像流水账,写完自己看着都舒服。要是你们项目已经在用Vuex,不想动老代码,那把列配置的状态塞到一个单独的module里也能跑,但要是新项目或者想重构,我肯定优先选Pinia——之前用Vuex写的那些冗余代码,现在全省了,维护的时候再也不用翻着mutation找半天“哪个方法改了列的visible”,直接看store里的state就行,省下来的时间能多喝杯奶茶呢。


    实现状态管理用Pinia还是Vuex更好?

    推荐用Pinia。Pinia是Vue3官方推荐的状态管理工具,比Vuex更轻量,语法更简洁(无需mutation,直接修改state),且天生支持TypeScript。对于动态列配置这种“轻量级状态管理”场景,Pinia的学习成本更低,代码更易维护。如果项目已在用Vuex,也可以适配,但Pinia会更顺手。

    状态持久化除了localStorage还能怎么存?

    除了localStorage,还可以通过后端接口存储(将配置存到用户表,适合多端同步需求),或用sessionStorage(仅当前会话有效,适合临时配置)。localStorage是最常用的方案,优点是无需后端参与,缺点是换设备会丢失配置;后端存储适合企业级项目,能实现多设备同步,但需要接口支持。

    用Sortable.js做拖拽排序会有兼容问题吗?

    Sortable.js是成熟的拖拽库,兼容主流浏览器(包括IE11),与Element Plus的组件适配性良好。只要确保拖拽的节点是普通DOM元素(比如用

    包裹列项),而非Element Plus的自定义组件,就能避免兼容问题。文章中的示例已处理好节点绑定,直接复用即可。

    动态列配置会影响表格渲染性能吗?

    只要做好优化,不会有明显影响。 1)用ElTable的virtual-scroll(虚拟滚动)优化大数据量渲染;2)控制显示列的数量(避免同时显示50+列);3)用Pinia的getter缓存visibleColumns(避免重复计算)。如果列数在20以内,即使不做虚拟滚动,性能也足够流畅。

    怎么给多个不同表格配置独立的列设置?

    关键是给每个表格分配唯一的tableKey(比如“order-list”“product-list”)。这个key会作为localStorage的键名(如“table-columns-config-order-list”),确保不同表格的配置互不干扰。在组件初始化时,将tableKey传入Pinia的initConfig方法,就能实现独立配置。