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);
}
}
}
展示效果