在 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. 扩展方案:支持多个特殊行固定

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


相关资源

相关推荐
恋猫de小郭3 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端