vue实现antd-vue table表格 右上角设置列功能(支持列搜索列、上移、下移、置顶、置底)
根据现有需求考虑到表头很多,写了组件弹窗支持列搜索列、上移、下移、置顶、置底,上代码:
子组件:
<a-modal v-model:visible="visible" title="列设置" width="600px" @ok="handleOk" @cancel="handleCancel">
<!-- 选项卡:全部列、显示列、隐藏列 -->
<a-tabs v-model:activeKey="activeTab" @change="changeTabCallback">
<a-tab-pane key="all" tab="全部列">
<a-table
:data-source="filteredData"
:columns="columnsList"
:pagination="false"
:row-selection="{ selectedRowKeys: selectedColumns, onChange: colChange }"
>
<template #headerCell="{ column }">
<template v-if="column.key === 'title'">
<span style="color: #1890ff">名称 {{ selectedColumns.length }}/{{ filteredData.length }}</span>
</template>
</template>
<template #customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
<div style="padding: 8px">
<a-input
ref="searchInput"
:placeholder="`搜索${column.title}`"
:value="selectedKeys[0]"
style="width: 188px; margin-bottom: 8px; display: block"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
/>
<a-button
type="primary"
size="small"
style="width: 90px; margin-right: 8px"
@click="handleSearch(selectedKeys, confirm, column.dataIndex)"
>
<template #icon><SearchOutlined /></template>
Search
</a-button>
<a-button size="small" style="width: 90px" @click="handleReset(clearFilters)"> Reset </a-button>
</div>
</template>
<template #customFilterIcon="{ filtered }">
<search-outlined :style="{ color: filtered ? '#1890ff' : '#1890ff' }" />
</template>
<template #bodyCell="{ record, column, index }">
<span v-if="searchText && searchedColumn === column.dataIndex">
<template v-for="(fragment, i) in record.title.toString().split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i'))">
<mark v-if="fragment.toLowerCase() === searchText.toLowerCase()" :key="i" class="highlight">
{{ fragment }}
</mark>
<template v-else>{{ fragment }}</template>
</template>
</span>
<template v-if="column.dataIndex === 'action'">
<a-space>
<a @click="moveItem(record, index, 'up')">上移</a>
<a-divider type="vertical" />
</a-space>
<a-space>
<a @click="moveItem(record, index, 'down')">下移</a>
<a-divider type="vertical" />
</a-space>
<a-space>
<a @click="moveItem(record, index, 'top')">置顶</a>
<a-divider type="vertical" />
</a-space>
<a-space>
<a @click="moveItem(record, index, 'bottom')">置底</a>
<a-divider type="vertical" />
</a-space>
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="visible" tab="显示列">
<a-list>
<a-list-item v-for="col in visibleColumns" :key="col.key">
{{ col.title }}
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane key="hidden" tab="隐藏列">
<a-list>
<a-list-item v-for="col in hiddenColumns" :key="col.key">
{{ col.title }}
</a-list-item>
</a-list>
</a-tab-pane>
</a-tabs>
</a-modal>
<script> export default { props: { visible: Boolean, columns: Array // 所有列配置 }, emits: ['update:visible', 'change'], data() { return { searchedColumn: '', searchText: '', // 搜索文本 activeTab: 'all', // 当前选项卡(all/visible/hidden) selectedColumns: [], // 当前选中的列(存储 col.key) selectedRowKeys: [], filteredData: [], columnsList: [ { title: '名称', dataIndex: 'title', key: 'title', width: 260, minWidth: 260, customFilterDropdown: true, onFilter: (value, record) => record.title.toString().toLowerCase().includes(value.toLowerCase()), onFilterDropdownOpenChange: visible => { if (visible) { setTimeout(() => { searchInput.value.focus(); }, 100); } } }, { title: '操作', dataIndex: 'action' } ] }; }, computed: { // 显示的列(当前选中的列) visibleColumns() { return this.columns.filter(col => this.selectedColumns.includes(col.key)); }, // 隐藏的列 hiddenColumns() { return this.columns.filter(col => !this.selectedColumns.includes(col.key)); } }, watch: { // 监听初始化 visible() { if (this.visible) { this.searchText = ''; this.filteredData = [...this.columns]; this.selectedColumns = this.columns .filter(col => col.checked) // 初始选中的列(如果 col 有 checked 属性) .map(col => col.key); } } }, methods: { changeTabCallback(key) { if (key == 'visible') { } }, colChange(val, key) { this.selectedColumns = val; this.filteredData = this.updateCheckedStatus(this.filteredData, val); }, updateCheckedStatus(data, targetKeys) { return data.map(column => ({ ...column, // 保留原有属性 checked: targetKeys.includes(column.key) // 如果 key 在 targetKeys 中,则 checked: true,否则 false })); }, handleOk() { this.selectedColumns = this.filteredData .filter(item => item.checked) // 筛选 checked: true 的项 .map(item => item.key); // 提取 key this.$emit('change', this.selectedColumns, this.filteredData); this.$emit('update:visible', false); }, handleCancel() { this.$emit('update:visible', false); }, moveItem(record, index, type) { const arr = this.filteredData; if (type === 'up' && index > 0) { [arr[index - 1], arr[index]] = [arr[index], arr[index - 1]]; } if (type === 'down' && index < arr.length - 1) { [arr[index + 1], arr[index]] = [arr[index], arr[index + 1]]; } if (type === 'top' && index > 0) { const item = arr.splice(index, 1)[0]; arr.unshift(item); } if (type === 'bottom' && index < arr.length - 1) { const item = arr.splice(index, 1)[0]; arr.push(item); } // 带有fixed: right属性始终在最下面 arr.sort((a, b) => { const aHasFixed = a.fixed === 'right'; const bHasFixed = b.fixed === 'right'; if (aHasFixed && !bHasFixed) return 1; // a 带 fixed,b 不带,a 排后面 if (!aHasFixed && bHasFixed) return -1; // a 不带 fixed,b 带,a 排前面 return 0; // 保持原有顺序 }); }, handleSearch(selectedKeys, confirm, dataIndex) { confirm(); this.searchText = selectedKeys[0]; this.searchedColumn = dataIndex; }, handleReset(clearFilters) { clearFilters({ confirm: true }); this.searchText = ''; } } }; </script>
父组件:
<a-tooltip placement="top">
<template #title>
<span>列设置</span>
</template>
<SettingOutlined @click="showColumnSettings" />
</a-tooltip>
<columnSettings v-model:visible="columnSettingsVisible" :columns="columns" @change="handleColumnsChange" />
export default {
data() {
return: {
columnSettingsVisible: false,
// 表格列配置
columns: [
{
key: 'index',
width: 48,
checked: true,
hideInSetting: true,
customRender: ({index}) => this.$refs.table.tableIndex + index
},
{
title: '订单号',
dataIndex: 'orderMovementXid',
key: 'orderMovementXid',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '创建日期',
dataIndex: 'orderCreationDate',
key: 'orderCreationDate',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '金额',
dataIndex: 'contractAmountInc',
key: 'contractAmountInc',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '名称',
dataIndex: 'templateName',
key: 'templateName',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '明细',
dataIndex: 'templateDetailCode',
key: 'templateDetailCode',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '状态',
dataIndex: 'chargingStatus',
key: 'chargingStatus',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '原因',
dataIndex: 'chargingFailReason',
key: 'chargingFailReason',
checked: true,
resizable: true,
width: 160,
minWidth: 100
},
{
title: '操作',
key: 'action',
checked: true,
width: 120,
align: 'center',
fixed: 'right'
}
],
}
},
methods: {
showColumnSettings() {
this.columnSettingsVisible = true;
},
handleColumnsChange(selectedKeys, filteredData) {
// 更新列的显示状态
this.columns = filteredData;
},
},
computed: {
displayedColumns() {
return this.columns.filter(col => col.checked);
}
}
}
展示效果


浙公网安备 33010602011771号