update point tier rule

This commit is contained in:
Timi
2026-08-14 18:26:11 +08:00
parent 3b5ff75772
commit a43694daa3
16 changed files with 940 additions and 32 deletions
+76
View File
@@ -0,0 +1,76 @@
import type { EventItem } from "@/types/EventItem";
/**
* 返回事件数值单位的前端文案。
* 后端仍使用 CENT 表示金额分,前端统一按元展示和录入。
*/
export function getEventValueUnitText(valueUnit?: EventItem["valueUnit"]): string {
if (valueUnit === "CENT") {
return "元";
}
if (valueUnit === "COUNT") {
return "次";
}
return "未配置单位";
}
/**
* 获取事件数值输入提示。
*/
export function getEventNumberValuePlaceholder(event: EventItem): string {
if (event.valueUnit === "CENT") {
return event.requireValue ? "请输入金额(元),最多两位小数" : "请输入金额(元),可留空,最多两位小数";
}
return event.requireValue ? "请输入次数,必须是大于等于 0 的整数" : "请输入次数,可留空,必须是大于等于 0 的整数";
}
/**
* 将前端输入的事件数值转换为后端要求的整数。
* 金额使用字符串拆分后转换为分,避免直接使用浮点数放大导致精度误差。
*/
export function resolveEventNumberValue(event: EventItem, valueText: string): number | undefined {
const normalizedValue = valueText.trim();
if (!normalizedValue) {
return undefined;
}
if (event.valueUnit === "CENT") {
return resolveCentValue(normalizedValue);
}
if (!/^\d+$/.test(normalizedValue)) {
return undefined;
}
const value = Number(normalizedValue);
return Number.isSafeInteger(value) ? value : undefined;
}
/**
* 将后端返回的整数分转换为前端金额文案。
*/
export function formatEventNumberValue(event: EventItem | undefined, value?: number | null): string {
if (value === undefined || value === null) {
return "-";
}
if (event?.valueUnit !== "CENT") {
return `${value}${event?.valueUnit === "COUNT" ? " 次" : ""}`;
}
const centText = String(Math.trunc(value));
const yuanText = centText.length < 3 ? "0" : centText.slice(0, -2);
const decimalText = centText.slice(-2).padStart(2, "0").replace(/0+$/, "");
return `${yuanText}${decimalText ? `.${decimalText}` : ""} 元`;
}
function resolveCentValue(valueText: string): number | undefined {
const match = /^(\d+)(?:\.(\d{0,2}))?$/.exec(valueText);
if (!match) {
return undefined;
}
const integerText = match[1];
const decimalText = (match[2] || "").padEnd(2, "0");
const centValue = BigInt(integerText) * 100n + BigInt(decimalText || "0");
if (centValue > BigInt(Number.MAX_SAFE_INTEGER)) {
return undefined;
}
return Number(centValue);
}
+29
View File
@@ -0,0 +1,29 @@
import type { PointRuleTierForm } from "@/types/Point";
export function resolveNonNegativeInteger(value: string): number | undefined {
if (!value.trim()) {
return undefined;
}
const numberValue = Number(value);
return Number.isSafeInteger(numberValue) && !(numberValue < 0) ? numberValue : undefined;
}
export function getTierMinValue(tierList: PointRuleTierForm[], index: number): number | undefined {
if (index === 0) {
return 0;
}
const previousMax = resolveNonNegativeInteger(tierList[index - 1]?.maxText || "");
if (previousMax === undefined || !Number.isSafeInteger(previousMax + 1)) {
return undefined;
}
return previousMax + 1;
}
export function getTierMinText(tierList: PointRuleTierForm[], index: number): string {
const min = getTierMinValue(tierList, index);
return min === undefined ? "待填写" : String(min);
}
export function isLastTier(tierList: PointRuleTierForm[], index: number): boolean {
return index === tierList.length - 1;
}