在 Ant Design Vue 的 a-table 中将特定数据行固定在底部

前言

在使用 Ant Design Vue 的 a-table 组件时,经常会遇到这样的需求:某些数据行(如汇总行、总计行等)需要始终显示在表格底部,无论其他数据如何排序,这些特殊行都不应该移动位置。

本文将介绍如何通过自定义排序器实现这一功能,并展示从基础实现到优化的完整过程。


问题背景

假设我们有一个展示各模块缺陷统计的表格,其中包含一行"全组"的汇总数据:

matlab 复制代码
模块     | 缺陷数 | 占比   | 缺陷密度
---------|--------|--------|----------
模块A    | 15     | 12%    | 0.5
模块B    | 20     | 15%    | 0.8
模块C    | 10     | 8%     | 0.3
全组     | 45     | 35%    | 1.6

当用户点击表头进行排序时,"全组"这行数据应该始终保持在底部,而其他行则根据实际数值进行排序。


基础实现方案

Ant Design Vue 的 a-table 组件支持通过 sorter 属性自定义排序逻辑。我们可以利用这个特性,在排序器中判断是否为"全组"行,如果是则强制将其排在最后。

示例代码

vue 复制代码
<script setup>
const columns = [
  { title: "模块", dataIndex: "module", key: "module" },
  {
    title: "缺陷数",
    dataIndex: "bugs",
    key: "bugs",
    sorter: (a, b) => {
      // "全组"始终排在最后
      if (a.module === "全组") return 1;
      if (b.module === "全组") return -1;
      return a.bugs - b.bugs;
    },
  },
  {
    title: "占比",
    dataIndex: "bugRatio",
    key: "bugRatio",
    sorter: (a, b) => {
      // "全组"始终排在最后
      if (a.module === "全组") return 1;
      if (b.module === "全组") return -1;
      const aVal = parseFloat(a.bugRatio) || 0;
      const bVal = parseFloat(b.bugRatio) || 0;
      return aVal - bVal;
    },
  },
];
</script>

排序原理

排序器函数接收两个参数 ab,返回值为:

  • 返回负数:a 排在 b 前面
  • 返回 0:位置不变
  • 返回正数:a 排在 b 后面

通过判断 a.moduleb.module 是否为"全组",我们可以强制改变它们的相对位置。


基础方案的问题

虽然上述方案可以工作,但存在以下问题:

  1. 缺少排序方向支持 :没有使用 sortOrder 参数,导致升序和降序时"全组"的返回值逻辑不够清晰
  2. 代码重复:每个可排序列都需要写相同的"全组"判断逻辑,造成大量重复代码
  3. 维护困难:如果需要修改"全组"的判断条件或排序逻辑,需要修改多处代码

优化方案:使用工厂函数

为了解决代码重复问题,我们可以创建一个排序器工厂函数,将公共逻辑抽离出来。

优化后的代码

vue 复制代码
<script setup>
// 通用排序器工厂函数
const createSorter = compareFn => {
  return (a, b, sortOrder) => {
    if (sortOrder === "ascend") {
      if (a.module === "全组") return 1;
      if (b.module === "全组") return -1;
    } else if (sortOrder === "descend") {
      if (a.module === "全组") return -1;
      if (b.module === "全组") return 1;
    }
    return -compareFn(a, b);
  };
};

const columns = [
  { title: "模块", dataIndex: "module", key: "module" },
  {
    title: "缺陷数",
    dataIndex: "bugs",
    key: "bugs",
    sorter: createSorter((a, b) => a.bugs - b.bugs),
    defaultSortOrder: "ascend",
  },
  {
    title: "占比",
    dataIndex: "bugRatio",
    key: "bugRatio",
    sorter: createSorter((a, b) => {
      const aVal = parseFloat(a.bugRatio) || 0;
      const bVal = parseFloat(b.bugRatio) || 0;
      return aVal - bVal;
    }),
  },
];
</script>

工厂函数说明

createSorter 函数接收一个比较函数 compareFn,返回一个新的排序器函数。内部逻辑:

  1. 升序(ascend):数值小的在前,"全组"在最后
  2. 降序(descend):数值大的在前,"全组"在最后
  3. 默认情况:自动按降序处理

关键点

  • sortOrder 参数由 a-table 组件传入,值为 "ascend""descend"null
  • 在降序时,使用 -compareFn(a, b) 反转比较结果
  • 对于百分比等需要转换类型的字段,在传入的比较函数中处理

完整示例

下面是一个完整的表格组件示例,包含多个可排序列:

vue 复制代码
<template>
  <a-table :columns="columns" :data-source="dataSource" :pagination="false" bordered />
</template>

<script setup>
import { ref } from "vue";

// 示例数据
const dataSource = ref([
  { module: "模块A", bugs: 15, bugRatio: "12%", avgDensity: 0.5 },
  { module: "模块B", bugs: 20, bugRatio: "15%", avgDensity: 0.8 },
  { module: "模块C", bugs: 10, bugRatio: "8%", avgDensity: 0.3 },
  { module: "全组", bugs: 45, bugRatio: "35%", avgDensity: 1.6 },
]);

// 通用排序器工厂函数
const createSorter = compareFn => {
  return (a, b, sortOrder) => {
    if (sortOrder === "ascend") {
      if (a.module === "全组") return 1;
      if (b.module === "全组") return -1;
    } else if (sortOrder === "descend") {
      if (a.module === "全组") return -1;
      if (b.module === "全组") return 1;
    }
    return -compareFn(a, b);
  };
};

// 表格列定义
const columns = [
  {
    title: "模块",
    dataIndex: "module",
    key: "module",
    width: 150,
    align: "center",
  },
  {
    title: "缺陷数",
    dataIndex: "bugs",
    key: "bugs",
    width: 150,
    align: "center",
    sorter: createSorter((a, b) => a.bugs - b.bugs),
    defaultSortOrder: "ascend",
  },
  {
    title: "占比",
    dataIndex: "bugRatio",
    key: "bugRatio",
    width: 150,
    align: "center",
    sorter: createSorter((a, b) => {
      const aVal = parseFloat(a.bugRatio) || 0;
      const bVal = parseFloat(b.bugRatio) || 0;
      return aVal - bVal;
    }),
  },
  {
    title: "缺陷密度",
    dataIndex: "avgDensity",
    key: "avgDensity",
    width: 150,
    align: "center",
    sorter: createSorter((a, b) => a.avgDensity - b.avgDensity),
  },
];
</script>

扩展:支持多个特殊行

如果需要固定多个特殊行(如"全组"、"总计"等),可以修改工厂函数:

javascript 复制代码
const createSorter = (compareFn, pinnedModules = ["全组", "总计"]) => {
  return (a, b, sortOrder) => {
    const aIsPinned = pinnedModules.includes(a.module);
    const bIsPinned = pinnedModules.includes(b.module);

    if (sortOrder === "ascend") {
      if (aIsPinned && !bIsPinned) return 1;
      if (!aIsPinned && bIsPinned) return -1;
      return compareFn(a, b);
    } else if (sortOrder === "descend") {
      if (aIsPinned && !bIsPinned) return -1;
      if (!aIsPinned && bIsPinned) return 1;
      return -compareFn(a, b);
    }

    if (aIsPinned && !bIsPinned) return -1;
    if (!aIsPinned && bIsPinned) return 1;
    return -compareFn(a, b);
  };
};

// 使用
sorter: createSorter((a, b) => a.bugs - b.bugs, ["全组", "总计"]);

总结

通过自定义排序器,我们可以轻松实现将特定数据行固定在表格底部的需求:

  1. 基础方案 :在每个列的 sorter 中判断特殊行
  2. 优化方案:使用工厂函数抽离公共逻辑,减少代码重复
  3. 扩展方案:支持多个特殊行固定

这种方法简单、高效,适用于任何需要固定特定行位置的表格场景。


相关资源

相关推荐
掘金安东尼2 小时前
Vercel:我们为 React2Shell 发起了一项价值 100 万美元的黑客挑战
前端·javascript·github
掘金安东尼2 小时前
浏览器处理Base64数据的速度有多快?
前端·javascript·github
掘金安东尼2 小时前
为不同场景设计多样化的页面过渡动画
前端·javascript·github
elangyipi1232 小时前
2025 搜索优化新革命:GEO 正在悄然取代 SEO?
前端·人工智能
持续升级打怪中2 小时前
深入解析深浅拷贝:原理、实现与最佳实践
开发语言·前端·javascript
我有一棵树2 小时前
空值合并运算符 ?? ,|| 的替代方案
前端·javascript
Apifox2 小时前
Apifox 12 月更新| AI 生成用例同步生成测试数据、接口文档完整性检测、设计 SSE 流式接口、从 Git 仓库导入数据
前端·后端·测试
禾叙_2 小时前
【NIO】ByteBuffer
前端·html·nio
chilavert3182 小时前
技术演进中的开发沉思-278 AJax :Rich Text(上)
前端·javascript·ajax