一、需求场景
在Web开发的时候,Table表格是十分常见的组件。有的时候,表格的列项会有很多项,全部加载出来表格就会出现横向滚动条,靠后的就需要拖动滚动条才能看见。从易用性的角度来说,是不太方便的,虽然可以将不太重要的信息放置最后,例如下面这样。


这个时候可以采用配置Table表格的列来实现优化。默认展示重要的信息,其他相对不是那么重要的信息就可以根据不同的用户,由用户自行配置显示。
二、准备工作
本Demo使用的是vue2 + Ant Design Vue组件库开发,所以需要在本地自行提前安装好开发环境(你也可以根据具体开发环境安装vue3)。
还有就是本实例中使用到了拖动排序,

所以需要安装 sortablejs 这个依赖。
"dependencies": {
"ant-design-vue": "^4.2.6",
"core-js": "^3.8.3",
"sortablejs": "1.15.2",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},
实现过程
开发环境准备就绪过后就开始代码实现
首先,将Table表格的列项准备,这里我是使用浏览器缓存 localStorage 实现选择暂存,这里就直接贴图代码了,代码也是有注释的,不明白的可以留言。
// 设置本地缓存
export const storageKeys = {
// 上一次选择的列表
COLUMN: "COLUMN",
// 上一次选择的列
CHECKED_OPTIONS: "CHECKED_OPTIONS",
// 上一次选择的
CHECKED_GROUP: "CHECKED_GROUP"
}
export const setStorage = (key, value) => {
const jsonData = JSON.stringify(value);
localStorage.setItem(key, jsonData);
}
export const getStorage = (key, response) => {
const jsonData = localStorage.getItem(key);
if (jsonData) {
return JSON.parse(jsonData);
}
return response || null;
}
// 全部table column 数据
export const columns = [
{
label: "列项一",
key: "lx1",
disabled: false,
content: {
title: "列项一",
dataIndex: "lx1",
key: "lx1",
resizable: true,
minWidth: 100
}
},
{
label: "列项二",
key: "lx2",
disabled: false,
content: {
title: "列项二",
dataIndex: "lx2",
key: "lx2",
resizable: true,
minWidth: 120
}
},
{
label: "列项三",
key: "lx3",
disabled: false,
content: {
title: "列项三",
dataIndex: "lx3",
key: "lx3",
resizable: true,
minWidth: 120
}
},
{
label: "列项四",
key: "lx4",
disabled: false,
content: {
title: "列项四",
dataIndex: "lx4",
key: "lx4",
resizable: true,
minWidth: 240
}
},
{
label: "列项五",
key: "lx5",
disabled: false,
content: {
title: "列项五",
dataIndex: "lx5",
key: "lx5",
resizable: true,
minWidth: 80
}
},
{
label: "列项六",
key: "lx6",
disabled: false,
content: {
title: "列项六",
dataIndex: "lx6",
key: "lx6",
resizable: true,
minWidth: 120
}
},
{
label: "列项七",
key: "lx7",
disabled: false,
content: {
title: "列项七",
dataIndex: "lx7",
key: "lx7",
resizable: true,
minWidth: 80
}
},
{
label: "列项八",
key: "lx8",
disabled: false,
content: {
title: "列项八",
dataIndex: "lx8",
key: "lx8",
resizable: true,
minWidth: 80
}
},
{
label: "列项九",
key: "lx9",
disabled: false,
content: {
title: "列项九",
dataIndex: "lx9",
key: "lx9",
resizable: true,
minWidth: 100
}
},
{
label: "列项十",
key: "lx10",
disabled: false,
content: {
title: "列项十",
dataIndex: "lx10",
key: "lx10",
resizable: true,
minWidth: 100
}
},
{
label: "列项十一",
key: "lx11",
disabled: false,
content: {
title: "列项十一",
dataIndex: "lx11",
key: "lx11",
resizable: true,
minWidth: 100
}
},
{
label: "列项十二",
key: "lx12",
disabled: false,
content: {
title: "列项十二",
dataIndex: "lx12",
key: "lx12",
resizable: true,
minWidth: 100
}
},
{
label: "列项十三",
key: "lx12",
disabled: false,
content: {
title: "列项十三",
dataIndex: "lx12",
key: "lx12",
resizable: true,
minWidth: 100
}
},
{
label: "列项十四",
key: "lx12",
disabled: false,
content: {
title: "列项十四",
dataIndex: "lx12",
key: "lx12",
resizable: true,
minWidth: 100
}
},
{
label: "列项十五",
key: "lx12",
disabled: false,
content: {
title: "列项十五",
dataIndex: "lx12",
key: "lx12",
resizable: true,
minWidth: 100
}
},
{
label: "列项十六",
key: "lx12",
disabled: false,
content: {
title: "列项十六",
dataIndex: "lx12",
key: "lx12",
resizable: true,
minWidth: 100
}
},
];
// 默认展示列
export const defaultOptions = ["lx1", "lx3", "lx5", "lx7", "lx9", "lx11"];
// 获取全部列
export const getAllColumns = () => {
return columns;
}
// 获取默认选项
export const getDefaultOptions = () => {
return defaultOptions;
}
// 获取初始化的 table 列
export const getInitOptions = () => {
let options = getStorage(storageKeys.CHECKED_OPTIONS, []);
if (!options.length) {
// 缓存没有走默认
options = getDefaultOptions();
setStorage(storageKeys.CHECKED_OPTIONS, options);
}
return options;
}
export const getInitTableColumns = () => {
let tableColumns = [];
let checkedColumns = getInitOptions();
tableColumns = columns.map(item => {
if (checkedColumns.includes(item.key)) {
return item.content;
}
}).filter(item => item);
return tableColumns;
}
关于前端页面,为了复用性,我这里是将列项选择弹窗写成了一个组件。
表格组件(父组件)页面

代码
```html
<template>
<div class="about">
<Space>
<Button type="primary" @click="openColumnModal">
<SettingOutlined />
</Button>
</Space>
<Table :dataSource="dataSource" :scroll="{ x: 'max-content', y: 400 }" style="margin-top: 16px;">
<a-table-column v-for="item in columns" :key="item.key" :title="item.title" :dataIndex="item.dataIndex"
:resizable="item.resizable" :min-width="item.minWidth" :width="item.minWidth">
</a-table-column>
</Table>
<CustomColumn ref="customColumnRef" @apply="apply" />
</div>
</template>
<script>
import CustomColumn from "@/components/CustomColumn.vue"
import { Table, Space, Button } from "ant-design-vue";
import { SettingOutlined } from '@ant-design/icons-vue';
import {
getInitTableColumns,
getStorage,
storageKeys
} from "@/data";
export default {
name: 'AboutView',
components: {
CustomColumn,
Table, Space, Button,
SettingOutlined
},
data() {
return {
dataSource: [{
id: "lx1",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx2",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx3",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx4",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx5",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx6",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx7",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx8",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx9",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
{
id: "lx10",
lx1: "lx1",
lx2: 'lx1',
lx3: "lx3",
lx4: "lx4",
lx5: "lx5",
lx6: "lx6",
lx7: "lx7",
lx8: "lx8",
lx9: "lx9",
lx10: "lx10",
lx11: "lx11",
lx12: "lx12",
},
],
columns: getStorage(storageKeys.COLUMN, null) || getInitTableColumns(),
}
},
mounted() {
},
methods: {
openColumnModal() {
this.$refs.customColumnRef.showModal();
},
apply(cloumn) {
this.columns = cloumn;
}
},
}
</script>
选项弹窗操作组件(子组件)页面

代码
<template>
<Modal v-model:open="visible" :width="650" title="列项自定义" okText="应用" cancelText="取消" @ok="confirm" @cancel="cancel">
<div style="width: 100%;">
<div style="margin-bottom: 16px;">
您可以勾选常用的列项,保存为自定义模板
<Checkbox v-model="allChecked" style="margin-left: 16px;" :checked="allChecked" :indeterminate="indeterminate"
@click="changeIndeterminate">全选</Checkbox>
</div>
<CheckboxGroup v-model:value="checkedOptions" style="width: 100%;">
<div id="columnMove" style="width: 100%;display: flex;flex-wrap: wrap;justify-content: flex-start;">
<transition-group type="transition" name="flip-list">
<Checkbox v-for="item in allOptions" :key="item.key" :value="item.key" :disabled="item.disabled"
:class="{ filter: item.disabled }" style="width: 150px;margin-bottom: 16px;margin-left: 0px;">
<span>{{ item.label }}</span>
</Checkbox>
</transition-group>
</div>
</CheckboxGroup>
</div>
</Modal>
</template>
<script>
import { Modal, Checkbox, CheckboxGroup } from "ant-design-vue";
import {
getInitOptions,
getAllColumns,
getDefaultOptions,
getStorage,
setStorage,
storageKeys
} from "@/data";
import { Sortable } from "sortablejs";
export default {
name: 'CustomColumn',
components: {
Modal,
Checkbox,
CheckboxGroup
},
data() {
return {
visible: false,
allChecked: false,
checkedOptions: getInitOptions(), //已选择的项
allOptions: getStorage(storageKeys.CHECKED_GROUP) || getAllColumns(),//所有的操作项
}
},
computed: {
indeterminate() {
return this.checkedOptions.length !== this.allOptions.length && this.checkedOptions.length !== 0;
}
},
watch: {
checkedOptions: {
handler(newValue) {
if (newValue.length === this.allOptions.length) {
this.allChecked = true;
} else {
this.allChecked = false;
}
},
immediate: true
}
},
mounted() {
},
methods: {
changeIndeterminate(ev) {
if (ev.target.checked) {
this.checkedOptions = this.allOptions.map(item => item.key);
} else {
this.checkedOptions = getDefaultOptions();
}
},
rowDrop() {
const _this = this;
const tBody = document.getElementById("columnMove");
Sortable.create(tBody, {
group: {
name: "moveSetting",
pull: false,
put: false,
},
handle: "#columnMove",
sort: true,
animation: 300,
filter: ".filter",
onEnd: function (event) {
const start = event.oldIndex;
const end = event.newIndex;
const arr = [..._this.allOptions];
if (start > end) {
arr.splice(end, 0, arr[start]);
arr.splice(start + 1, 1);
} else {
arr.splice(end + 1, 0, arr[start]);
arr.splice(start, 1);
}
_this.allOptions = arr;
}
})
},
showModal() {
this.visible = true;
this.$nextTick(() => {
this.rowDrop();
})
},
cancel() {
this.checkedOptions = getStorage(storageKeys.CHECKED_OPTIONS);
this.visible = false;
},
confirm() {
let columnOption = [];
this.allOptions.forEach(item => {
if (this.checkedOptions.includes(item.key)) {
columnOption.push({
...item.content,
});
}
});
setStorage(storageKeys.COLUMN, columnOption);
setStorage(storageKeys.CHECKED_OPTIONS, this.checkedOptions);
setStorage(storageKeys.CHECKED_GROUP, this.allOptions);
this.$emit("apply", columnOption);
this.visible = false;
}
},
}
</script>
<style scoped lang='scss'></style>
说明:
1、在页面通过点击
这个按钮图标
2、唤起
这个操作列项弹窗。可以拖动改变顺序,勾选选择显示。
3、点击 应用 按钮,关闭弹窗,并将处理后的数据渲染在在Table表格。
4、演示



其他玩法:
可以通过配置,让某些选项不可操作,如:列项三、列项四

只需要将代码中的 disabled 改为 true
{
label: "列项三",
key: "lx3",
disabled: true,
content: {
title: "列项三",
dataIndex: "lx3",
key: "lx3",
resizable: true,
minWidth: 120
}
},
{
label: "列项四",
key: "lx4",
disabled: true,
content: {
title: "列项四",
dataIndex: "lx4",
key: "lx4",
resizable: true,
minWidth: 240
}
},
其他的操作,可以根据自己去灵活变更。
580

被折叠的 条评论
为什么被折叠?



