<script setup>
import { defineProps, defineEmits, useAttrs, ref, defineComponent } from 'vue'
import { ElTable, ElTableColumn, ElPagination } from 'element-plus'
const props = defineProps({
tableData: {
type: Array,
required: true,
default: () => []
},
columns: {
type: Array,
required: true,
default: () => []
},
customProps: {
type: Object,
default: () => ({})
},
total: {
type: Number,
default: 0
},
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
// 新增分页相关配置
pagination: {
type: Object,
default: () => ({
enable: false, // 是否启用分页
position: 'bottom', // 分页位置: top/bottom/both
pageSizes: [10, 20, 30, 50], // 每页显示个数选择器的选项
layout: 'total, sizes, prev, pager, next, jumper', // 组件布局
justifyContent: 'flex-end', // 分页对齐方式: left/center/right
alignItems: 'center',
paginationProps: {}, // 用于传递给 el-pagination 的其他属性,
padding: '0'
})
}
})
const emit = defineEmits([
'select',
'select-all',
'selection-change',
'cell-click',
'cell-dblclick',
'cell-contextmenu',
'cell-mouse-enter',
'cell-mouse-leave',
'row-click',
'row-dblclick',
'row-contextmenu',
'header-click',
'header-contextmenu',
'sort-change',
'filter-change',
'current-change',
'header-dragend',
'expand-change',
'update:currentPage',
'update:pageSize',
'pagination-change',
// 新增 el-pagination 的所有事件
'size-change',
'prev-click',
'next-click'
])
const attrs = useAttrs()
const RecursiveColumn = defineComponent({
name: 'RecursiveColumn',
props: {
column: {
type: Object,
required: true
}
},
template: `
<el-table-column
v-if="column.children && column.children.length"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
:show-overflow-tooltip="column.showOverflowTooltip"
v-bind="column.customAttrs"
>
<recursive-column
v-for="child in column.children"
:key="child.prop"
:column="child"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</recursive-column>
</el-table-column>
<el-table-column
v-else
:prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
:sortable="column.sortable"
:filters="column.filters"
:filter-method="column.filterMethod"
:show-overflow-tooltip="column.showOverflowTooltip"
v-bind="column.customAttrs"
>
<template v-if="column.slot" #default="scope">
<slot :name="column.slot" v-bind="scope"></slot>
</template>
</el-table-column>
`
})
const tableRef = ref(null)
const handleCurrentChange = (val) => {
emit('update:currentPage', val)
emit('pagination-change', { page: val, pageSize: props.pageSize })
emit('current-change', val)
}
const handleSizeChange = (val) => {
emit('update:pageSize', val)
emit('pagination-change', { page: props.currentPage, pageSize: val })
emit('size-change', val)
}
const handlePrevClick = (val) => {
emit('prev-click', val)
}
const handleNextClick = (val) => {
emit('next-click', val)
}
defineExpose({
clearSort: () => tableRef.value?.clearSort(),
clearFilter: (columnKeys) => tableRef.value?.clearFilter(columnKeys),
toggleRowExpansion: (row, expanded) => tableRef.value?.toggleRowExpansion(row, expanded),
toggleAllSelection: () => tableRef.value?.toggleAllSelection(),
setCurrentRow: (row) => tableRef.value?.setCurrentRow(row),
clearSelection: () => tableRef.value?.clearSelection(),
sort: (prop, order) => tableRef.value?.sort(prop, order),
getTableRef: () => tableRef.value
})
</script>
<template>
<div class="table-wrapper">
<!-- 上方分页 -->
<div v-if="pagination?.enable && ['top', 'both'].includes(pagination.position)" class="pagination-wrapper" :style="{display: 'flex', justifyContent: pagination.align || 'right'}">
<el-pagination
v-bind="pagination.paginationProps"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="pagination.pageSizes"
:total="total"
:layout="pagination.layout"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-pagination>
</div>
<el-table
ref="tableRef"
:data="tableData"
v-bind="{ ...attrs, ...customProps }"
@select="(selection, row) => emit('select', selection, row)"
@select-all="(selection) => emit('select-all', selection)"
@selection-change="(selection) => emit('selection-change', selection)"
@cell-click="(row, column, cell, event) => emit('cell-click', row, column, cell, event)"
@cell-dblclick="(row, column, cell, event) => emit('cell-dblclick', row, column, cell, event)"
@cell-contextmenu="(row, column, cell, event) => emit('cell-contextmenu', row, column, cell, event)"
@cell-mouse-enter="(row, column, cell, event) => emit('cell-mouse-enter', row, column, cell, event)"
@cell-mouse-leave="(row, column, cell, event) => emit('cell-mouse-leave', row, column, cell, event)"
@row-click="(row, column, event) => emit('row-click', row, column, event)"
@row-dblclick="(row, column, event) => emit('row-dblclick', row, column, event)"
@row-contextmenu="(row, column, event) => emit('row-contextmenu', row, column, event)"
@header-click="(column, event) => emit('header-click', column, event)"
@header-contextmenu="(column, event) => emit('header-contextmenu', column, event)"
@sort-change="(obj) => emit('sort-change', obj)"
@filter-change="(filters) => emit('filter-change', filters)"
@current-change="(currentRow, oldCurrentRow) => emit('current-change', currentRow, oldCurrentRow)"
@header-dragend="(newWidth, oldWidth, column, event) => emit('header-dragend', newWidth, oldWidth, column, event)"
@expand-change="(row, expanded) => emit('expand-change', row, expanded)"
>
<slot name="selection"></slot>
<slot name="index"></slot>
<slot name="expand"></slot>
<template v-for="column in columns" :key="column.prop">
<recursive-column :column="column">
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</recursive-column>
</template>
<template #empty>
<slot name="empty"></slot>
</template>
<template #append>
<slot name="append"></slot>
</template>
</el-table>
<!-- 下方分页 -->
<div v-if="pagination?.enable && ['bottom', 'both'].includes(pagination.position)" class="pagination-wrapper" :style="{display: 'flex', justifyContent: pagination.align || 'right', padding: pagination.padding || '0'}">
<el-pagination
v-bind="pagination.paginationProps"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="pagination.pageSizes"
:total="total"
:layout="pagination.layout"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-pagination>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits, useAttrs, ref, defineComponent } from 'vue'
import { ElTable, ElTableColumn, ElPagination } from 'element-plus'
const props = defineProps({
tableData: {
type: Array,
required: true,
default: () => []
},
columns: {
type: Array,
required: true,
default: () => []
},
customProps: {
type: Object,
default: () => ({})
},
total: {
type: Number,
default: 0
},
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
// 新增分页相关配置
pagination: {
type: Object,
default: () => ({
enable: false, // 是否启用分页
position: 'bottom', // 分页位置: top/bottom/both
pageSizes: [10, 20, 30, 50], // 每页显示个数选择器的选项
layout: 'total, sizes, prev, pager, next, jumper', // 组件布局
justifyContent: 'flex-end', // 分页对齐方式: left/center/right
alignItems: 'center',
paginationProps: {}, // 用于传递给 el-pagination 的其他属性,
padding: '0'
})
}
})
const emit = defineEmits([
'select',
'select-all',
'selection-change',
'cell-click',
'cell-dblclick',
'cell-contextmenu',
'cell-mouse-enter',
'cell-mouse-leave',
'row-click',
'row-dblclick',
'row-contextmenu',
'header-click',
'header-contextmenu',
'sort-change',
'filter-change',
'current-change',
'header-dragend',
'expand-change',
'update:currentPage',
'update:pageSize',
'pagination-change',
// 新增 el-pagination 的所有事件
'size-change',
'prev-click',
'next-click'
])
const attrs = useAttrs()
const RecursiveColumn = defineComponent({
name: 'RecursiveColumn',
props: {
column: {
type: Object,
required: true
}
},
template: `
<el-table-column
v-if="column.children && column.children.length"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
:show-overflow-tooltip="column.showOverflowTooltip"
v-bind="column.customAttrs"
>
<template v-if="column.headerSlot" #header="scope">
<slot :name="column.headerSlot" v-bind="scope"></slot>
</template>
<recursive-column
v-for="child in column.children"
:key="child.prop"
:column="child"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</recursive-column>
</el-table-column>
<el-table-column
v-else
:prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
:sortable="column.sortable"
:filters="column.filters"
:filter-method="column.filterMethod"
:show-overflow-tooltip="column.showOverflowTooltip"
v-bind="column.customAttrs"
>
<template v-if="column.slot" #default="scope">
<slot :name="column.slot" v-bind="scope"></slot>
</template>
<template v-if="column.headerSlot" #header="scope">
<slot :name="column.headerSlot" v-bind="scope"></slot>
</template>
<template v-for="(slotName, slotKey) in column.slots" :key="slotKey" #[slotKey]="scope">
<slot :name="slotName" v-bind="scope"></slot>
</template>
</el-table-column>
`
})
const tableRef = ref(null)
const handleCurrentChange = (val) => {
emit('update:currentPage', val)
emit('pagination-change', { page: val, pageSize: props.pageSize })
emit('current-change', val)
}
const handleSizeChange = (val) => {
emit('update:pageSize', val)
emit('pagination-change', { page: props.currentPage, pageSize: val })
emit('size-change', val)
}
const handlePrevClick = (val) => {
emit('prev-click', val)
}
const handleNextClick = (val) => {
emit('next-click', val)
}
defineExpose({
clearSort: () => tableRef.value?.clearSort(),
clearFilter: (columnKeys) => tableRef.value?.clearFilter(columnKeys),
toggleRowExpansion: (row, expanded) => tableRef.value?.toggleRowExpansion(row, expanded),
toggleAllSelection: () => tableRef.value?.toggleAllSelection(),
setCurrentRow: (row) => tableRef.value?.setCurrentRow(row),
clearSelection: () => tableRef.value?.clearSelection(),
sort: (prop, order) => tableRef.value?.sort(prop, order),
getTableRef: () => tableRef.value
})
</script>
<template>
<div class="table-wrapper">
<!-- 上方分页 -->
<div v-if="pagination?.enable && ['top', 'both'].includes(pagination.position)" class="pagination-wrapper" :style="{display: 'flex', justifyContent: pagination.align || 'right'}">
<el-pagination
v-bind="pagination.paginationProps"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="pagination.pageSizes"
:total="total"
:layout="pagination.layout"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-pagination>
</div>
<el-table
ref="tableRef"
:data="tableData"
v-bind="{ ...attrs, ...customProps }"
@select="(selection, row) => emit('select', selection, row)"
@select-all="(selection) => emit('select-all', selection)"
@selection-change="(selection) => emit('selection-change', selection)"
@cell-click="(row, column, cell, event) => emit('cell-click', row, column, cell, event)"
@cell-dblclick="(row, column, cell, event) => emit('cell-dblclick', row, column, cell, event)"
@cell-contextmenu="(row, column, cell, event) => emit('cell-contextmenu', row, column, cell, event)"
@cell-mouse-enter="(row, column, cell, event) => emit('cell-mouse-enter', row, column, cell, event)"
@cell-mouse-leave="(row, column, cell, event) => emit('cell-mouse-leave', row, column, cell, event)"
@row-click="(row, column, event) => emit('row-click', row, column, event)"
@row-dblclick="(row, column, event) => emit('row-dblclick', row, column, event)"
@row-contextmenu="(row, column, event) => emit('row-contextmenu', row, column, event)"
@header-click="(column, event) => emit('header-click', column, event)"
@header-contextmenu="(column, event) => emit('header-contextmenu', column, event)"
@sort-change="(obj) => emit('sort-change', obj)"
@filter-change="(filters) => emit('filter-change', filters)"
@current-change="(currentRow, oldCurrentRow) => emit('current-change', currentRow, oldCurrentRow)"
@header-dragend="(newWidth, oldWidth, column, event) => emit('header-dragend', newWidth, oldWidth, column, event)"
@expand-change="(row, expanded) => emit('expand-change', row, expanded)"
>
<slot name="selection"></slot>
<slot name="index"></slot>
<slot name="expand"></slot>
<template v-for="column in columns" :key="column.prop">
<recursive-column :column="column">
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</recursive-column>
</template>
<template #empty>
<slot name="empty"></slot>
</template>
<template #append>
<slot name="append"></slot>
</template>
</el-table>
<!-- 下方分页 -->
<div v-if="pagination?.enable && ['bottom', 'both'].includes(pagination.position)" class="pagination-wrapper" :style="{display: 'flex', justifyContent: pagination.align || 'right', padding: pagination.padding || '0'}">
<el-pagination
v-bind="pagination.paginationProps"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="pagination.pageSizes"
:total="total"
:layout="pagination.layout"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-pagination>
</div>
</div>
</template>
useTableHooks
import { ref } from 'vue'
export const useTable = (emit) => {
const tableRef = ref(null)
// Table methods
const clearSort = () => tableRef.value?.clearSort()
const clearFilter = (columnKeys) => tableRef.value?.clearFilter(columnKeys)
const toggleRowExpansion = (row, expanded) => tableRef.value?.toggleRowExpansion(row, expanded)
const toggleAllSelection = () => tableRef.value?.toggleAllSelection()
const setCurrentRow = (row) => tableRef.value?.setCurrentRow(row)
const clearSelection = () => tableRef.value?.clearSelection()
const sort = (prop, order) => tableRef.value?.sort(prop, order)
const getTableRef = () => tableRef.value
// Table event handlers
const handleTableEvents = {
select: (selection, row) => emit('select', selection, row),
'select-all': (selection) => emit('select-all', selection),
'selection-change': (selection) => emit('selection-change', selection),
'cell-click': (row, column, cell, event) => emit('cell-click', row, column, cell, event),
'cell-dblclick': (row, column, cell, event) => emit('cell-dblclick', row, column, cell, event),
'cell-contextmenu': (row, column, cell, event) => emit('cell-contextmenu', row, column, cell, event),
'cell-mouse-enter': (row, column, cell, event) => emit('cell-mouse-enter', row, column, cell, event),
'cell-mouse-leave': (row, column, cell, event) => emit('cell-mouse-leave', row, column, cell, event),
'row-click': (row, column, event) => emit('row-click', row, column, event),
'row-dblclick': (row, column, event) => emit('row-dblclick', row, column, event),
'row-contextmenu': (row, column, event) => emit('row-contextmenu', row, column, event),
'header-click': (column, event) => emit('header-click', column, event),
'header-contextmenu': (column, event) => emit('header-contextmenu', column, event),
'sort-change': (obj) => emit('sort-change', obj),
'filter-change': (filters) => emit('filter-change', filters),
'current-change': (currentRow, oldCurrentRow) => emit('current-change', currentRow, oldCurrentRow),
'header-dragend': (newWidth, oldWidth, column, event) => emit('header-dragend', newWidth, oldWidth, column, event),
'expand-change': (row, expanded) => emit('expand-change', row, expanded)
}
return {
tableRef,
clearSort,
clearFilter,
toggleRowExpansion,
toggleAllSelection,
setCurrentRow,
clearSelection,
sort,
getTableRef,
handleTableEvents
}
}
usePaginationHooks
export const usePagination = (props, emit) => {
const handleCurrentChange = (val) => {
emit('update:currentPage', val)
emit('pagination-change', { page: val, pageSize: props.pageSize })
emit('current-change', val)
}
const handleSizeChange = (val) => {
emit('update:pageSize', val)
emit('pagination-change', { page: props.currentPage, pageSize: val })
emit('size-change', val)
}
const handlePrevClick = (val) => {
emit('prev-click', val)
}
const handleNextClick = (val) => {
emit('next-click', val)
}
return {
handleCurrentChange,
handleSizeChange,
handlePrevClick,
handleNextClick
}
}
TableTest.vue
<script setup>
import { defineEmits, defineComponent, useAttrs } from 'vue'
import { ElTable, ElTableColumn, ElPagination } from 'element-plus'
import { useTable } from '../hooks/useTable'
import { usePagination } from '../hooks/usePagination'
const props = defineProps({
tableData: {
type: Array,
required: true,
default: () => []
},
columns: {
type: Array,
required: true,
default: () => []
},
customProps: {
type: Object,
default: () => ({})
},
total: {
type: Number,
default: 0
},
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
// 新增分页相关配置
pagination: {
type: Object,
default: () => ({
enable: false, // 是否启用分页
position: 'bottom', // 分页位置: top/bottom/both
pageSizes: [10, 20, 30, 50], // 每页显示个数选择器的选项
layout: 'total, sizes, prev, pager, next, jumper', // 组件布局
justifyContent: 'flex-end', // 分页对齐方式: left/center/right
alignItems: 'center',
paginationProps: {}, // 用于传递给 el-pagination 的其他属性,
padding: '0'
})
}
})
const emit = defineEmits([
'select',
'select-all',
'selection-change',
'cell-click',
'cell-dblclick',
'cell-contextmenu',
'cell-mouse-enter',
'cell-mouse-leave',
'row-click',
'row-dblclick',
'row-contextmenu',
'header-click',
'header-contextmenu',
'sort-change',
'filter-change',
'current-change',
'header-dragend',
'expand-change',
'update:currentPage',
'update:pageSize',
'pagination-change',
// 新增 el-pagination 的所有事件
'size-change',
'prev-click',
'next-click'
])
const attrs = useAttrs()
// Use hooks
const {
tableRef,
handleTableEvents,
...tableExposeMethods
} = useTable(emit)
const {
handleCurrentChange,
handleSizeChange,
handlePrevClick,
handleNextClick
} = usePagination(props, emit)
// Define expose methods
defineExpose(tableExposeMethods)
const RecursiveColumn = defineComponent({
name: 'RecursiveColumn',
props: {
column: {
type: Object,
required: true
}
},
template: `
<el-table-column
v-if="column.children && column.children.length"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
:show-overflow-tooltip="column.showOverflowTooltip"
v-bind="column.customAttrs"
>
<template v-if="column.headerSlot" #header="scope">
<slot :name="column.headerSlot" v-bind="scope"></slot>
</template>
<recursive-column
v-for="child in column.children"
:key="child.prop"
:column="child"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</recursive-column>
</el-table-column>
<el-table-column
v-else
:prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
:sortable="column.sortable"
:filters="column.filters"
:filter-method="column.filterMethod"
:show-overflow-tooltip="column.showOverflowTooltip"
v-bind="column.customAttrs"
>
<template v-if="column.slot" #default="scope">
<slot :name="column.slot" v-bind="scope"></slot>
</template>
<template v-if="column.headerSlot" #header="scope">
<slot :name="column.headerSlot" v-bind="scope"></slot>
</template>
<template v-for="(slotName, slotKey) in column.slots" :key="slotKey" #[slotKey]="scope">
<slot :name="slotName" v-bind="scope"></slot>
</template>
</el-table-column>
`
})
</script>
<template>
<div class="table-wrapper">
<!-- 上方分页 -->
<div v-if="pagination?.enable && ['top', 'both'].includes(pagination.position)" class="pagination-wrapper" :style="{display: 'flex', justifyContent: pagination.align || 'right'}">
<el-pagination
v-bind="pagination.paginationProps"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="pagination.pageSizes"
:total="total"
:layout="pagination.layout"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-pagination>
</div>
<el-table
ref="tableRef"
:data="tableData"
v-bind="{ ...attrs, ...customProps, ...handleTableEvents }"
>
<slot name="selection"></slot>
<slot name="index"></slot>
<slot name="expand"></slot>
<template v-for="column in columns" :key="column.prop">
<recursive-column :column="column">
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</recursive-column>
</template>
<template #empty>
<slot name="empty"></slot>
</template>
<template #append>
<slot name="append"></slot>
</template>
</el-table>
<!-- 下方分页 -->
<div v-if="pagination?.enable && ['bottom', 'both'].includes(pagination.position)" class="pagination-wrapper" :style="{display: 'flex', justifyContent: pagination.align || 'right', padding: pagination.padding || '0'}">
<el-pagination
v-bind="pagination.paginationProps"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="pagination.pageSizes"
:total="total"
:layout="pagination.layout"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-pagination>
</div>
</div>
</template>
用法
<template>
<div class="about-container">
<table-test
:table-data="tableData"
:columns="columns"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:pagination="paginationConfig"
:custom-props="tableProps"
@pagination-change="handlePaginationChange"
@filter-change="handleFilterChange"
>
<!-- 状态列自定义显示 -->
<template #status-slot="{ row }">
<el-tag :type="row.status === '在线' ? 'success' : row.status === '离线' ? 'info' : 'warning'">
</el-tag>
</template>
</table-test>
<virtual-list></virtual-list>
<virtual-table></virtual-table>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import TableTest from './TableTest.vue'
import { ElMessage } from 'element-plus'
import VirtualList from '@/components/VirtualList.vue'
import VirtualTable from '@/components/VirtualTable.vue'
// 表格数据状态
const tableData = ref([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10)
const loading = ref(false)
// 过滤条件
const filterConditions = ref({})
// 使用本地过滤还是远程过滤的标志
const useLocalFilter = ref(true)
// 自定义过滤处理函数(如果需要的话)
const customFilterHandler = (filters) => {
console.log('自定义过滤处理:', filters)
// 这里可以实现自己的过滤逻辑
}
// 模拟后端数据库
const mockDatabase = (() => {
const data = []
for (let i = 0; i < 1000; i++) {
const age = Math.floor(Math.random() * 50) + 18
const ageGroup = age <= 25 ? '18-25' :
age <= 35 ? '26-35' :
age <= 45 ? '36-45' : '46+'
data.push({
id: i + 1,
name: `用户${i + 1}`,
age,
ageGroup,
address: `中国北京市朝阳区第${Math.floor(Math.random() * 1000)}号`,
email: `user${i + 1}@example.com`,
status: ['在线', '离线', '忙碌'][Math.floor(Math.random() * 3)]
})
}
return data
})()
// 模拟分页请求
const fetchData = async (params) => {
try {
loading.value = true
await new Promise(resolve => setTimeout(resolve, 300))
const { page, pageSize, filters } = params
console.log('应用过滤条件:', filters)
let filteredData = [...mockDatabase]
if (filters && Object.keys(filters).length > 0) {
filteredData = filteredData.filter(item => {
let match = true
// 状态过滤
if (filters.status?.length) {
match = match && filters.status.includes(item.status)
}
// 年龄段过滤
if (filters.ageGroup?.length) {
match = match && filters.ageGroup.includes(item.ageGroup)
}
// 名称搜索
if (filters.name) {
match = match && item.name.toLowerCase().includes(filters.name.toLowerCase())
}
return match
})
}
console.log('过滤���数据条数:', filteredData.length)
const start = (page - 1) * pageSize
const end = start + pageSize
return {
list: filteredData.slice(start, end),
total: filteredData.length
}
} finally {
loading.value = false
}
}
// 处理过滤变化
const handleFilterChange = (filters) => {
console.log('过滤条件:', filters)
// 这里只需要处理分页等逻辑
currentPage.value = 1
loadTableData()
}
// 加载表格数据
const loadTableData = async () => {
try {
const params = {
page: currentPage.value,
pageSize: pageSize.value,
filters: filterConditions.value
}
const { list, total: totalCount } = await fetchData(params)
tableData.value = list
total.value = totalCount
} catch (error) {
ElMessage.error('加载数据失败')
console.error(error)
}
}
// 分页变化处理
const handlePaginationChange = ({ page, pageSize: size }) => {
currentPage.value = page
pageSize.value = size
loadTableData()
}
// 表格配置
const columns = [
{
prop: 'id',
label: 'ID',
width: 80,
fixed: 'left'
},
{
prop: 'name',
label: '姓名',
minWidth: 120,
filterable: true,
customAttrs: {
filterPlacement: 'bottom-start',
filters: [{ value: '', text: '搜索姓名' }],
'filter-multiple': false
}
},
{
prop: 'ageGroup',
label: '年龄段',
width: 100,
sortable: true,
filterable: true,
customAttrs: {
filterPlacement: 'bottom-start',
filters: [
{ value: '18-25', text: '18-25岁' },
{ value: '26-35', text: '26-35岁' },
{ value: '36-45', text: '36-45岁' },
{ value: '46+', text: '46岁以上' }
],
'filter-multiple': true
}
},
{
prop: 'email',
label: '邮箱',
minWidth: 180,
showOverflowTooltip: true
},
{
prop: 'address',
label: '地址',
minWidth: 220,
showOverflowTooltip: true
},
{
prop: 'status',
label: '状态',
width: 100,
slot: 'status-slot',
filterable: true,
customAttrs: {
filterPlacement: 'bottom-start',
filters: [
{ value: '在线', text: '在线' },
{ value: '离线', text: '离线' },
{ value: '忙碌', text: '忙碌' }
],
'filter-multiple': true
}
}
]
// 分页配置
const paginationConfig = {
enable: true,
position: 'bottom',
pageSizes: [10, 20, 50, 100],
layout: 'total, sizes, prev, pager, next, jumper'
}
// 表格属性配置
const tableProps = {
height: '600px',
stripe: true,
border: true,
highlightCurrentRow: true,
size: 'default',
rowKey: 'id',
loading: loading,
popperOptions: {
modifiers: [
{
name: 'computeStyles',
options: {
adaptive: false,
gpuAcceleration: false
}
}
]
}
}
onMounted(() => {
loadTableData()
})
</script>
<style scoped>
.about-container {
padding: 20px;
}
:deep(.filter-popper) {
margin-top: 0;
}
:deep(.el-table-filter) {
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 4px;
box-shadow: var(--el-box-shadow-light);
}
</style>