PayrollManagementSystem/frontend/src/utils/rating.ts
liuwenxin 111ac5425a feat(customer): 客户列表展示等级徽标并统一等级不落库口径
评级计算器并入客户管理后,列表只有分数看不出档位,本批补上等级徽标:
- 抽出 utils/rating.ts 的 resolveGrade(按 sort_order 升序取首个
  min_score ≤ 总分的等级,单边判定避免区间边界歧义),列表页与弹窗共用
- 等级映射按需拉取并缓存在内存,等级配置改动后回调父页重拉,避免
  「同一分数在弹窗是 B、在列表是 C」的两处口径分歧
- 徽标色调按等级名首字母映射,自定义等级名落回中性灰(等级名可配置,
  不能靠写死枚举判语义色)
- 悬浮提示给出等级区间与评级时间

同时订正三处与实现不符的注释:rate_customer 实际只写 rating_score 与
rating_time,等级由分数按当前映射实时推导,三处却写成「后端判定并固化等级
名快照」,v13 迁移 docstring 还引用了并不存在的 v14(d5b8f2a46c71)。
纯注释改动,无逻辑变更。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 16:18:47 +08:00

49 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { RatingGradeItem } from '@/api/ratings'
/**
* 评级等级判定(纯函数,客户列表与评级弹窗共用同一口径)。
*
* 规则:按 sort_order 升序取首个「min_score ≤ 总分」的等级,max_score 仅作
* 展示 —— 单边判定避免区间边界(如 75 分)在「≥75 且 ≤89」与「>74 且 <75」
* 两套写法间的重叠/遗漏歧义。
*
* 后端 min_score 是字符串直出(分值红线:分值全程不进 float),比较前必须
* parseFloat 转数值,禁止字符串直接比大小("90.0" 的字典序比较会得出错误
* 结果)。无命中(总分低于全部下限)返回 null,由调用方展示未命中提示。
*/
export function resolveGrade(
grades: RatingGradeItem[],
totalScore: number,
): RatingGradeItem | null {
const ordered = [...grades].sort((a, b) => a.sort_order - b.sort_order)
for (const grade of ordered) {
if (totalScore >= parseFloat(grade.min_score)) {
return grade
}
}
return null
}
/**
* 等级名称 → 色调后缀(客户列表的等级徽标用)。
*
* 按名称首字母映射到体系语义色:A 优秀(绿)、B 良好(靛蓝)、C 合格
* (琥珀)、D 待改进(砖红);自定义等级名不在此列,落回中性灰 —— 等级
* 名称是业务可配置的,不能靠写死的枚举判定语义,未知即中性最安全。
*/
export function gradeTone(gradeName: string): '' | 'ok' | 'lock' | 'wait' | 'reject' {
const initial = gradeName.trim().charAt(0).toUpperCase()
switch (initial) {
case 'A':
return 'ok'
case 'B':
return 'lock'
case 'C':
return 'wait'
case 'D':
return 'reject'
default:
return ''
}
}