数据表格高级功能:Naive Ui Admin中的筛选与排序实现

数据表格高级功能:Naive Ui Admin中的筛选与排序实现

【免费下载链接】naive-ui-admin Naive Ui Admin 是一个基于 vue3,vite2,TypeScript 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件、动态菜单、权限校验、粒子化权限控制等功能,它可以帮助你快速搭建企业级中后台项目,相信不管是从新技术使用还是其他方面,都能帮助到你,持续更新中。 【免费下载链接】naive-ui-admin 项目地址: https://gitcode.com/gh_mirrors/na/naive-ui-admin

痛点与解决方案

在企业级中后台系统开发中,数据表格(Data Table)是核心组件之一。开发者常常面临以下痛点:如何高效实现复杂的筛选逻辑?如何处理服务端与客户端排序的差异?如何保证大数据量下的性能优化?本文将以Naive Ui Admin为基础,深入解析其表格组件的筛选与排序实现原理,并提供完整的实战指南。

读完本文你将获得:

  • 理解Naive Ui Admin表格组件的架构设计
  • 掌握3种筛选模式的实现方法(基础筛选、高级筛选、自定义筛选)
  • 学会服务端与客户端排序的最佳实践
  • 获取性能优化的5个实用技巧
  • 拥有可直接复用的代码模板

表格组件架构设计

Naive Ui Admin的表格组件采用分层设计,核心功能通过hooks实现解耦。筛选与排序功能主要依赖以下模块:

mermaid

核心数据流如下:

mermaid

筛选功能实现详解

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提供了抽屉式高级筛选面板。实现步骤如下:

  1. 定义筛选表单
<!-- 高级筛选抽屉组件 -->
<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>
  1. 集成到表格组件
<!-- 表格页面 -->
<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设计模式,将筛选与排序功能进行了优雅实现。核心要点包括:

  1. 分层设计:通过useColumns、useDataSource等hooks实现功能解耦
  2. 参数标准化:统一的筛选与排序参数格式,降低接口对接成本
  3. 灵活性:支持基础筛选、高级筛选、自定义筛选等多种场景
  4. 性能优化:提供虚拟滚动、防抖搜索等机制处理大数据量

未来,表格组件可以进一步优化:

  • 集成AI辅助筛选功能,通过自然语言生成筛选条件
  • 实现筛选条件的保存与加载,支持用户自定义筛选模板
  • 增强离线排序与筛选能力,提升弱网环境下的用户体验

掌握这些技能后,你可以轻松应对90%以上的中后台表格需求。建议结合实际项目,进一步扩展和定制这些功能,打造更符合业务需求的表格组件。

如果觉得本文对你有帮助,请点赞、收藏并关注,下期将带来《表格组件的权限控制与数据导出实战》。

【免费下载链接】naive-ui-admin Naive Ui Admin 是一个基于 vue3,vite2,TypeScript 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件、动态菜单、权限校验、粒子化权限控制等功能,它可以帮助你快速搭建企业级中后台项目,相信不管是从新技术使用还是其他方面,都能帮助到你,持续更新中。 【免费下载链接】naive-ui-admin 项目地址: https://gitcode.com/gh_mirrors/na/naive-ui-admin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值