根据现有需求考虑到表头很多,写了组件弹窗支持列搜索列、上移、下移、置顶、置底,上代码:
子组件:
<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-inputref="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-buttontype="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); // 提取 keythis.$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);
}
}
}
展示效果