前言
在使用 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>
排序原理
排序器函数接收两个参数 a 和 b,返回值为:
- 返回负数:
a排在b前面 - 返回 0:位置不变
- 返回正数:
a排在b后面
通过判断 a.module 和 b.module 是否为"全组",我们可以强制改变它们的相对位置。
基础方案的问题
虽然上述方案可以工作,但存在以下问题:
- 缺少排序方向支持 :没有使用
sortOrder参数,导致升序和降序时"全组"的返回值逻辑不够清晰 - 代码重复:每个可排序列都需要写相同的"全组"判断逻辑,造成大量重复代码
- 维护困难:如果需要修改"全组"的判断条件或排序逻辑,需要修改多处代码
优化方案:使用工厂函数
为了解决代码重复问题,我们可以创建一个排序器工厂函数,将公共逻辑抽离出来。
优化后的代码
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,返回一个新的排序器函数。内部逻辑:
- 升序(ascend):数值小的在前,"全组"在最后
- 降序(descend):数值大的在前,"全组"在最后
- 默认情况:自动按降序处理
关键点
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, ["全组", "总计"]);
总结
通过自定义排序器,我们可以轻松实现将特定数据行固定在表格底部的需求:
- 基础方案 :在每个列的
sorter中判断特殊行 - 优化方案:使用工厂函数抽离公共逻辑,减少代码重复
- 扩展方案:支持多个特殊行固定
这种方法简单、高效,适用于任何需要固定特定行位置的表格场景。