/** * KR 名称规整(历史名称污染防护)— 与后端 backend/app/utils/kr_name.py 同规则。 * * 背景:2026-09-11 前的逐键写入点会把「方向符号 + 已输入的首位数字」拼进 KR 名称 * (输入 780 → 名称变 `营业收入≥7`)。写入口已删,但存量模板名仍会经「模板 → KR」预填传染。 * * 判据(勿一刀切): * - 名称尾部「≥/≤ + 数字」且该数字 ≠ 目标值 → 截断值(污染)→ 去掉尾部片段; * - 数字 == 目标值 → 有意命名(`渠道留存率≥90` / `新客户数≥10`)→ 保留; * - 目标值取不到数字(`为正`/`TOP3`/空)→ 无法判定 → 保留。 */ const NUM = /[-+]?\d*\.?\d+/; const TAIL = /^(.*?)\s*[≥≤]\s*(\d+(?:\.\d+)?)\s*$/; export function targetNumber(targetValue: any): number | null { if (targetValue === null || targetValue === undefined || targetValue === "") return null; if (typeof targetValue === "number") return Number.isFinite(targetValue) ? targetValue : null; const m = String(targetValue).match(NUM); return m ? parseFloat(m[0]) : null; } export function sanitizeKrName(name: any, targetValue: any): { name: string; changed: boolean } { const raw = name === null || name === undefined ? "" : String(name); const m = raw.match(TAIL); if (!m) return { name: raw, changed: false }; const tn = targetNumber(targetValue); if (tn === null) return { name: raw, changed: false }; if (Math.abs(parseFloat(m[2]) - tn) < 1e-9) return { name: raw, changed: false }; const base = m[1].trim(); if (!base) return { name: raw, changed: false }; return { name: base, changed: true }; }