vue 开发table 列表时,需要动态调整列字段的顺序和显示隐藏
实现效果如图所示:
vue 组件代码
<template>
<div style="width: 90%; margin: 0 auto;">
<el-table :data="tableData" border="" ref="tableNode" @selection-change="handleSelectionChange"
:height="windowHeight - 200" v-loading="loading" row-key="field">
<el-table-column type="selection" width="70">
</el-table-column>
<el-table-column prop="fieldName" label="列名">
</el-table-column>
<el-table-column prop="sortNum" label="排序">
<template slot-scope="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
</el-table>
<div style="display: flex; justify-content: flex-end; padding-top: 20px;">
<el-button @click="reset" size="mini">取消</el-button>
<el-button type="primary" @click="submit" size="mini">保存</el-button>
</div>
</div>
</template>
<script>
import { setPageFieldSort, getPageFieldSort } from "@/api/public";
import Sortable from "sortablejs";
export default {
name: 'listFieldSet',
data() {
return {
pageName: "",
tableData: [],
multipleSelection: [],
selectedRowsBackup: [],
loading: false,
windowHeight: window.innerHeight,
}
},
updated() {
this.$nextTick(() => {
this.$refs.tableNode.doLayout()
})
},
mounted() {
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
},
methods: {
handleResize() {
this.windowHeight = window.innerHeight;
},
initData(pageName) {
this.pageName = pageName;
if (this.tableData && this.tableData.length === 0) {
this.loading = true;
getPageFieldSort({
'pageName': pageName
}).then(res => {
this.tableData = res.data;
if (this.tableData) {
this.tableData.forEach(item => {
if (item.isShow) {
this.multipleSelection.push(item.field);
this.$nextTick(() => {
this.$refs.tableNode.toggleRowSelection(item, true);
});
}
});
}
}).finally(() => {
this.loading = false;
});
//声明表格拖动排序方法
this.pullSort();
}
},
//
//表格拖动排序方法
pullSort() {
// 通过ref获取Dom节点
const el = this.$refs.tableNode.$el.querySelectorAll(
".el-table__body-wrapper > table > tbody"
)[0];
this.sortable = Sortable.create(el, {
animation: 200, //拖拽动画(毫秒)
setData: function (dataTransfer) {
dataTransfer.setData("Text", "");
},
onStart: () => {
// 拖拽开始前保存当前选中的行
this.selectedRowsBackup = [...this.multipleSelection];
},
// 结束拖拽
onEnd: (evt) => {
const movedItem = this.tableData.splice(evt.oldIndex, 1)[0];
this.tableData.splice(evt.newIndex, 0, movedItem);
this.$nextTick(() => {
this.selectedRowsBackup.forEach(row => {
const targetRow = this.tableData.find(item => item.field === row);
if (targetRow) {
this.$refs.tableNode.toggleRowSelection(targetRow, true); // 重新选中
}
});
})
},
});
},
handleSelectionChange(rows) {
this.multipleSelection = rows.map(row => row.field);
},
reset() {
this.$emit('close');
},
submit() {
this.loading = true;
this.tableData.forEach(item => {
item.isShow = this.multipleSelection.findIndex(row => row === item.field) > -1;
});
setPageFieldSort({
'fields': this.tableData,
'pageName': this.pageName
}).then(res => {
this.$message.success("保存成功");
}).finally(() => {
this.loading = false;
});
},
}
}
</script>
列表字段实例数据
[{
"id": 449,
"userCode": "klfadmin",
"field": "id",
"pageName": "projectLaborManagement",
"sortNum": 1,
"isShow": 1,
"fieldName": "序号"
},
{
"id": 450,
"userCode": "klfadmin",
"field": "title",
"pageName": "projectLaborManagement",
"sortNum": 2,
"isShow": 1,
"fieldName": "劳务标题"
},
{
"id": 451,
"userCode": "klfadmin",
"field": "state",
"pageName": "projectLaborManagement",
"sortNum": 3,
"isShow": 1,
"fieldName": "状态"
}
]
代码说明
template
普通的表格列表展示,主要展示所有的可设置的字段@selection-change
勾选时触发事件,存储一下勾选项,记录的是每行的field
字段:height
是设置容器的高度,此处是自动适应窗口高度。实现逻辑可参考 前面的文章
initData
方法,加载列表字段,见上的实例数据,此处是接口加载。请求的数据是 第4步存储的数据。this.$refs.tableNode.toggleRowSelection(item, true);
接口请求到数据后,遍历数组,触发勾选项默认勾选- 注意点:
- 数据未完全渲染到表格前,尝试操作选中状态,
toggleRowSelection
是失效的。 - 使用
$nextTick
确保 DOM 更新后再操作选中,此处我就踩坑
了,没有用$nextTick
,导致一直失效,好久才反应过来。
- 数据未完全渲染到表格前,尝试操作选中状态,
pullSort
触发表格拖动排序方法setData
解决 Firefox 浏览器拖拽时的兼容性问题,避免出现禁止拖拽的图标。onStart
拖拽开始前,将选中的数据备份一下。踩坑
,因为拖拽过程中重新渲染
了表格数据,导致选中状态丢失
,大家可测试打印一下,看看是否丢失。onEnd
拖拽结束时的回调函数evt.oldIndex 和 evt.newIndex
Sortable.js
提供的拖拽事件参数,表示拖拽前后的位置索引。
数组元素移动逻辑
splice(evt.oldIndex, 1)[0]
:从原位置删除元素并返回该元素。splice(evt.newIndex, 0, movedItem)
:将元素插入新位置。
this.$nextTick
- 确保 DOM 更新完成后再操作选中状态,避免因异步渲染导致的行选中失效。
恢复选中状态的逻辑
selectedRowsBackup
:拖拽前备份的选中行字段(如 [‘field1’, ‘field2’])。this.tableData.find(item => item.field === row):
根据field
找到拖拽后的行对象。toggleRowSelection(targetRow, true)
:手动设置行选中(需配合 表格 ref)。
submit
提交保存,将排序后的数组保存下来,并且同时保存一下选中状态。
Sortable.js 完整流程图
开始拖拽
→ 备份选中行 (onStart)
→ 移动数据 (onEnd)
→ 等待DOM更新 ($nextTick)
→ 遍历备份的选中字段
→ 找到新位置的行对象
→ 重新选中 (toggleRowSelection)
结束
父组件使用方式
Drawer 抽屉组件 加载子组件设置
<el-drawer title="列表设置" :visible.sync="listFieldSet.dialogVisible" size="20%" direction="rtl">
<listFieldSet ref="listFieldSetRef" @close="listFieldSet.dialogVisible = false">
</listFieldSet>
</el-drawer>
父组件使用
<template>
<el-table :data="tableData" v-loading="loading" border style="width: 100%" ref="table">
<template v-for="(item, index) in listFieldData">
<el-table-column v-if="item.isShow" :key="index" :prop="item.field" :label="item.fieldName" width="120">
<template slot-scope="scope">
<span v-if="item.field === 'proName'">
{{ scope.row.project ? scope.row.project.proName : '' }}
</span>
<span v-else>
{{ scope.row[item.field] || '' }}
</span>
</template>
</el-table-column>
</template>
<el-table-column label="操作" fixed="right">
</el-table-column>
</el-table>
</template>
使用的场景和方式非常多,上面我是用遍历+判断,正常渲染排序后的列表字段。大家理解后可以灵活应对类似需求