数据表格高级功能:Naive Ui Admin中的筛选与排序实现
痛点与解决方案
在企业级中后台系统开发中,数据表格(Data Table)是核心组件之一。开发者常常面临以下痛点:如何高效实现复杂的筛选逻辑?如何处理服务端与客户端排序的差异?如何保证大数据量下的性能优化?本文将以Naive Ui Admin为基础,深入解析其表格组件的筛选与排序实现原理,并提供完整的实战指南。
读完本文你将获得:
- 理解Naive Ui Admin表格组件的架构设计
- 掌握3种筛选模式的实现方法(基础筛选、高级筛选、自定义筛选)
- 学会服务端与客户端排序的最佳实践
- 获取性能优化的5个实用技巧
- 拥有可直接复用的代码模板
表格组件架构设计
Naive Ui Admin的表格组件采用分层设计,核心功能通过hooks实现解耦。筛选与排序功能主要依赖以下模块:
核心数据流如下:
筛选功能实现详解
1. 基础筛选实现
基础筛选通常直接集成在表头,通过下拉选择或文本输入实现快速筛选。Naive Ui Admin通过在columns配置中定义筛选属性实现:
// src/views/comp/table/basicColumns.ts
export const columns: BasicColumn<ListData>[] = [
{
title: '状态',
key: 'status',
render(record) {
return h(
NTag,
{
type: record.status === 'close' ? 'default' :
record.status === 'refuse' ? 'error' : 'success',
},
{ default: () => statusMap[record.status] }
);
},
// 筛选配置
filter: {
type: 'select',
options: [
{ label: '全部', value: '' },
{ label: '已通过', value: 'pass' },
{ label: '已拒绝', value: 'refuse' },
{ label: '已取消', value: 'close' },
],
},
},
// 其他列...
];
筛选逻辑在useDataSource hook中处理:
// src/components/Table/src/hooks/useDataSource.ts
async function fetch(opt?) {
try {
setLoading(true);
const { request, pagination, beforeRequest } = unref(propsRef);
if (!request) return;
// 组装分页与筛选参数
let params = {
...getPaginationParams(),
...getFilterParams(),
...opt,
};
// 前置处理
if (beforeRequest && isFunction(beforeRequest)) {
params = (await beforeRequest(params)) || params;
}
// 发送请求
const res = await request(params);
dataSourceRef.value = res[APISETTING.listField] || [];
// 更新分页信息
setPagination({
pageCount: res[APISETTING.totalField],
itemCount: res[APISETTING.countField],
});
} finally {
setLoading(false);
}
}
2. 高级筛选实现
对于多条件组合筛选,Naive Ui Admin提供了抽屉式高级筛选面板。实现步骤如下:
- 定义筛选表单:
<!-- 高级筛选抽屉组件 -->
<template>
<n-drawer
v-model:show="showAdvancedFilter"
:width="500"
title="高级筛选"
>
<n-form ref="filterFormRef" :model="filterForm">
<n-form-item label="创建日期" path="dateRange">
<n-date-picker
v-model:value="filterForm.dateRange"
type="datetimerange"
/>
</n-form-item>
<n-form-item label="状态" path="status">
<n-select
v-model:value="filterForm.status"
:options="statusOptions"
/>
</n-form-item>
<n-form-item label="城市" path="city">
<n-select
v-model:value="filterForm.city"
:options="cityOptions"
multiple
/>
</n-form-item>
<div class="filter-actions">
<n-button @click="resetFilter">重置</n-button>
<n-button type="primary" @click="confirmFilter">确认</n-button>
</div>
</n-form>
</n-drawer>
</template>
- 集成到表格组件:
<!-- 表格页面 -->
<template>
<div class="table-container">
<div class="table-toolbar">
<n-button
type="primary"
@click="showAdvancedFilter = true"
>
高级筛选
</n-button>
</div>
<BasicTable
ref="tableRef"
:columns="columns"
:request="loadDataTable"
/>
<AdvancedFilterDrawer
v-model:show="showAdvancedFilter"
@confirm="handleFilterConfirm"
/>
</div>
</template>
<script setup>
const tableRef = ref();
const showAdvancedFilter = ref(false);
// 处理筛选确认
const handleFilterConfirm = (values) => {
// 调用表格的reload方法,传入筛选参数
tableRef.value.reload({
...formatFilterParams(values),
// 重置到第一页
page: 1
});
showAdvancedFilter.value = false;
};
</script>
3. 自定义筛选实现
对于业务复杂的筛选场景(如标签筛选、树形筛选),可通过自定义组件实现。以标签筛选为例:
// 自定义标签筛选列
{
title: '标签',
key: 'tags',
render(record) {
return (
<div class="tags-container">
{record.tags.map(tag => (
<n-tag
key={tag}
variant="outline"
class="tag-item"
>
{tag}
</n-tag>
))}
</div>
);
},
// 自定义筛选组件
filter: {
render: () => h(TagFilter, {
onConfirm: (selectedTags) => {
tableRef.value.reload({
tags: selectedTags.join(','),
page: 1
});
}
})
}
}
排序功能实现详解
1. 客户端排序
客户端排序适用于数据量较小(<1000条)的场景,实现简单且响应迅速:
// useDataSource.ts 客户端排序实现
function sortDataSource(data, sortInfo) {
if (!sortInfo || !sortInfo.columnKey) return data;
const { columnKey, order } = sortInfo;
const sortedData = [...data].sort((a, b) => {
if (a[columnKey] < b[columnKey]) {
return order === 'ascend' ? -1 : 1;
}
if (a[columnKey] > b[columnKey]) {
return order === 'ascend' ? 1 : -1;
}
return 0;
});
return sortedData;
}
// 监听排序变化
watch(
() => tableInstance.sortState,
(sortInfo) => {
if (isClientSort) {
const sortedData = sortDataSource(unref(dataSourceRef), sortInfo);
dataSourceRef.value = sortedData;
} else {
// 服务端排序
reload({
sortField: sortInfo.columnKey,
sortOrder: sortInfo.order,
});
}
}
);
2. 服务端排序
大数据量场景下,应使用服务端排序,通过接口参数控制:
// 表格配置
const columns = [
{
title: '创建时间',
key: 'createDate',
sorter: true, // 启用排序
sortOrder: null, // 排序状态
// 自定义排序函数
sortDirections: ['ascend', 'descend', null],
},
// 其他列...
];
// 加载数据函数
const loadDataTable = async (params) => {
// params中自动包含排序参数:
// { sortField: 'createDate', sortOrder: 'ascend', ... }
return await getTableList(params);
};
服务端排序参数处理:
// API请求函数
export function getTableList(params) {
// 格式化排序参数
const { sortField, sortOrder, ...otherParams } = params;
const sortParams = sortField
? {
sort: sortField,
order: sortOrder === 'ascend' ? 'asc' : 'desc'
}
: {};
return http.get('/api/table/list', {
params: { ...otherParams, ...sortParams }
});
}
性能优化实践
1. 虚拟滚动
对于超大数据量(>1000行)表格,启用虚拟滚动:
<BasicTable
:columns="columns"
:request="loadDataTable"
:scroll-y="600"
:virtual-scroll="true"
:virtual-scroll-item-size="50"
/>
2. 筛选缓存
实现筛选条件缓存,避免重复请求:
// 缓存筛选参数
const cacheFilterParams = ref({});
// 筛选确认时更新缓存
const handleFilterConfirm = (values) => {
const params = formatFilterParams(values);
cacheFilterParams.value = params;
tableRef.value.reload({ ...params, page: 1 });
};
// 组件挂载时恢复缓存
onMounted(() => {
if (Object.keys(cacheFilterParams.value).length > 0) {
tableRef.value.reload(cacheFilterParams.value);
}
});
3. 防抖动搜索
输入框筛选添加防抖动:
// 使用防抖hook
import { useDebounceFn } from '@/hooks/core/useDebounceFn';
// 防抖搜索函数
const debouncedSearch = useDebounceFn((value) => {
tableRef.value.reload({ searchKey: value, page: 1 });
}, 500);
// 输入框
<n-input
placeholder="搜索名称"
@input="debouncedSearch.run"
/>
完整代码示例
基础表格页面
<template>
<n-card :bordered="false" class="proCard">
<div class="table-toolbar">
<n-input
v-model:value="searchValue"
placeholder="搜索名称"
style="width: 200px"
@input="handleSearch"
/>
<n-button
type="primary"
@click="showAdvancedFilter = true"
style="margin-left: 10px"
>
高级筛选
</n-button>
</div>
<BasicTable
title="用户列表"
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="tableRef"
:actionColumn="actionColumn"
:scroll-x="1360"
/>
<AdvancedFilterDrawer
v-model:show="showAdvancedFilter"
@confirm="handleFilterConfirm"
/>
</n-card>
</template>
<script lang="ts" setup>
import { ref, h } from 'vue';
import { BasicTable, TableAction } from '@/components/Table';
import { getTableList } from '@/api/table/list';
import { columns } from './basicColumns';
import AdvancedFilterDrawer from './components/AdvancedFilterDrawer.vue';
import { useDebounceFn } from '@/hooks/core/useDebounceFn';
const tableRef = ref();
const showAdvancedFilter = ref(false);
const searchValue = ref('');
// 防抖搜索
const debouncedSearch = useDebounceFn((value) => {
tableRef.value.reload({ name: value, page: 1 });
}, 300);
// 处理搜索
const handleSearch = (value) => {
debouncedSearch.run(value);
};
// 处理高级筛选
const handleFilterConfirm = (values) => {
tableRef.value.reload({ ...values, page: 1 });
showAdvancedFilter.value = false;
};
// 加载数据
const loadDataTable = async (params) => {
return await getTableList(params);
};
// 操作列
const actionColumn = {
width: 180,
title: '操作',
key: 'action',
fixed: 'right',
render(record) {
return h(TableAction, {
actions: [
{
label: '编辑',
onClick: () => handleEdit(record),
},
{
label: '删除',
onClick: () => handleDelete(record),
},
],
});
},
};
const handleEdit = (record) => {
// 编辑逻辑
};
const handleDelete = (record) => {
// 删除逻辑
};
</script>
筛选列定义
// basicColumns.ts
export const columns = [
{
title: '名称',
key: 'name',
sorter: true,
},
{
title: '性别',
key: 'sex',
render(record) {
return h(
NTag,
{ type: record.sex === 'male' ? 'info' : 'error' },
{ default: () => sexMap[record.sex] }
);
},
filter: {
type: 'select',
options: [
{ label: '全部', value: '' },
{ label: '男', value: 'male' },
{ label: '女', value: 'female' },
],
},
},
{
title: '城市',
key: 'city',
filter: {
type: 'select',
multiple: true,
options: cityOptions,
},
},
{
title: '状态',
key: 'status',
render(record) {
return h(
NTag,
{
type: record.status === 'pass' ? 'success' :
record.status === 'refuse' ? 'error' : 'default',
},
{ default: () => statusMap[record.status] }
);
},
filter: {
type: 'select',
options: [
{ label: '全部', value: '' },
{ label: '已通过', value: 'pass' },
{ label: '已拒绝', value: 'refuse' },
{ label: '已取消', value: 'close' },
],
},
sorter: true,
},
{
title: '创建时间',
key: 'createDate',
sorter: true,
},
];
总结与展望
Naive Ui Admin的表格组件通过hooks设计模式,将筛选与排序功能进行了优雅实现。核心要点包括:
- 分层设计:通过useColumns、useDataSource等hooks实现功能解耦
- 参数标准化:统一的筛选与排序参数格式,降低接口对接成本
- 灵活性:支持基础筛选、高级筛选、自定义筛选等多种场景
- 性能优化:提供虚拟滚动、防抖搜索等机制处理大数据量
未来,表格组件可以进一步优化:
- 集成AI辅助筛选功能,通过自然语言生成筛选条件
- 实现筛选条件的保存与加载,支持用户自定义筛选模板
- 增强离线排序与筛选能力,提升弱网环境下的用户体验
掌握这些技能后,你可以轻松应对90%以上的中后台表格需求。建议结合实际项目,进一步扩展和定制这些功能,打造更符合业务需求的表格组件。
如果觉得本文对你有帮助,请点赞、收藏并关注,下期将带来《表格组件的权限控制与数据导出实战》。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



