[ISSUE-0045] 质量分真正能排序 — 撤回 v0.10.64 阉割做法¶
v0.10.64 修 ISSUE-0044 Bug 5 时把
quality加进unsortable,让表头不可点 — 功能阉割。用户截图(v0.10.52)仍然能看到 ↑ 箭头(旧版可点击但没效果), 升级后又变成"完全点不动" — 体验更差。本次(v0.10.65)改成真排序:服务端检测 computed-sort,走 hasEmailOnly 同款"全表 + JS sort + slice" 路径。
病灶(v0.10.64 之前)¶
| 链路 | 问题 |
|---|---|
| 用户点 quality 表头 | DataGrid 显示 ↑ 箭头 ✓ |
formatSortParams |
{ quality: -1 } ✓ |
apiLocalDataList 传 sort 给 jsstore |
jsstore DB 无 quality 列 → 静默忽略 |
| 返回行 | 仍是原 DB 顺序(默认 create_time desc) |
| 用户视觉 | "排序坏了" |
病灶(v0.10.64 之后 — 反而更差)¶
sortable: false → 表头完全不可点,连箭头都没有。看似"诚实"但用户的核心痛点
(按高质量找客户)反而无解。
修(v0.10.65)¶
1. 撤回 unsortable 添加 'quality'¶
const unsortable = ['profile', 'contact', 'location', 'logo'];
// social 也撤了 — 它的 valueGetter 是 count,可以 JS sort
quality / email / phone / social 全部 sortable: true。
2. apiLocalDataList 加 needsClientSort 检测¶
const COMPUTED_SORT_FIELDS = new Set(['quality', 'email', 'phone', 'social']);
const needsClientSort = hasComputedSort(sort);
// 拆 sort:dbSort 给 jsstore(仅真实列),完整 sort 给 JS post-sort
const dbSort = sort.filter((s) => !COMPUTED_SORT_FIELDS.has(s?.by));
3. 合并到 hasEmailOnly 路径¶
if (hasEmailOnly || needsClientSort) {
const big = await getListByQuery(baseQuery, 1, HAS_EMAIL_SCAN_LIMIT, dbSort);
let scanned = big?.list || [];
const truncated = scanned.length >= HAS_EMAIL_SCAN_LIMIT;
if (hasEmailOnly) {
scanned = scanned.filter((r) => Array.isArray(r.emails) && r.emails.length > 0);
}
if (needsClientSort) {
scanned = applyClientSort(scanned, sort);
}
const start = (current - 1) * pageSize;
return { success: true, data: { list: scanned.slice(start, start + pageSize), total: scanned.length, ..., truncated, scanLimit: HAS_EMAIL_SCAN_LIMIT } };
}
4. computeSortValue + applyClientSort 实现¶
function computeSortValue(row, field) {
switch (field) {
case 'quality': return computeQualityScore(row);
case 'email': return (row.emails || []).length;
case 'phone': return row.phone ? 1 : 0;
case 'social': return ['website', 'facebook', 'instagram', ...].filter((k) => row[k]).length;
default: return row[field] ?? 0;
}
}
function applyClientSort(list, sort) {
return [...list].sort((a, b) => {
for (const s of sort.filter((x) => x?.by)) {
const va = computeSortValue(a, s.by);
const vb = computeSortValue(b, s.by);
const diff = typeof va === 'number' && typeof vb === 'number'
? va - vb
: String(va).localeCompare(String(vb));
if (diff !== 0) return s.type === 'desc' ? -diff : diff;
}
return 0;
});
}
支持多列 sort:先 by:quality 再 by:rating 等。
性能¶
- HAS_EMAIL_SCAN_LIMIT = 50,000 行
- 测算:50k 行 quality sort ≈ 50-100ms(同 hasEmailOnly path 性能)
-
50k 时
truncated: true→ UI banner 提示用户「仅显示前 5w,请缩小筛选」
audit_grep 闸¶
audit_grep:
- pattern: "unsortable\\s*=\\s*\\[(?:[^\\]])*['\"]quality['\"]"
description: "v0.10.65 起不应再有 quality in unsortable"
元洞察¶
"做不到"和"做得到但需要客户端"是不同等级的方案。
v0.10.64 我跳过了"做得到但需要客户端",直接选了"做不到"。这是工程偷懒, 反映在 UX 上就是阉割功能。
用户给的 v0.10.52 截图本身就证明:当年的代码"假装支持排序"也比"明确不支持" 强 — 至少给了用户希望(虽然没生效)。正确方案永远是真实现, 不是删功能。
相关¶
- [[0044-screenshot-6-ux-feedback-batch|0044-用户截图6项UX反馈-缩放-按钮色-排序-分页-顶部CTA]] — 上一轮(v0.10.64 阉割做法)
- [[0018-hasemailonly-50k-silent-truncate|0018-hasEmailOnly-50k上限静默截断]] — 同 path 同 LIMIT 同模板