front/src/utils/priceWeightInterval.ts
zzh 8c47c2772a
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m49s
feat: 优化数据权限模板与营销抽屉等运营配置体验
理顺模板配置流程与文案,并统一左侧菜单对比度、会员/券/用户编辑抽屉与因子标签展示。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 18:25:31 +08:00

121 lines
4.2 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.

/**
* 卖价/买价重量因素:左开右闭区间 (start, end]。
* 界面与快照统一存为 (0-0.5] 形式,便于 0.312 等重量命中。
*/
/** 重量因素值下拉:0.5 步进至 30,其后固定 999(卖价/买价共用) */
export function buildWeightFactorSelectOptions(): Array<{ label: string; value: string }> {
return [
...Array.from({ length: 60 }, (_, index) => {
const v = ((index + 1) * 0.5).toFixed(1).replace(/\.0$/, '');
return { label: v, value: v };
}),
{ label: '999', value: '999' },
];
}
export function isWeightPricingFactor(factorKey: string): boolean {
const k = String(factorKey ?? '').trim();
if (!k) return false;
if (k === '重量' || k === 'weight') return true;
return false;
}
/** 解析重量单元格或因素值为区间上下界;单值 0.5 视为 (0, 0.5]。 */
export function parseWeightIntervalBounds(text: string): { start: number; end: number } | null {
const trimmed = String(text ?? '').trim();
if (!trimmed) return null;
const stripped = trimmed.replace(/^\(/, '').replace(/\]$/, '').replace(/^\[/, '');
const parts = stripped
.split(/[,,\-~~]/)
.map((s) => s.trim())
.filter(Boolean);
if (parts.length >= 2) {
const start = Number.parseFloat(parts[0]);
const end = Number.parseFloat(parts[1]);
if (Number.isFinite(start) && Number.isFinite(end) && end > start) {
return { start, end };
}
}
const single = Number.parseFloat(parts[0] ?? stripped);
if (Number.isFinite(single) && single > 0) {
return { start: 0, end: single };
}
return null;
}
function formatWeightNumber(n: number): string {
const s = n.toFixed(4).replace(/\.?0+$/, '');
return s === '' ? '0' : s;
}
/** 格式化为 (start-end] 展示与存储。 */
export function formatWeightIntervalLeftOpenRightClosed(start: number, end: number): string {
return `(${formatWeightNumber(start)}-${formatWeightNumber(end)}]`;
}
/** 将单值或旧格式转为区间字符串 (start-end]。 */
export function weightValueToIntervalCell(
rawValue: string,
allWeightValuesInFactor: string[]
): string {
const trimmed = String(rawValue ?? '').trim();
if (!trimmed) return '';
const parsed = parseWeightIntervalBounds(trimmed);
if (
parsed &&
(trimmed.includes('-') || trimmed.includes(',') || trimmed.includes(',') || trimmed.includes(']'))
) {
return formatWeightIntervalLeftOpenRightClosed(parsed.start, parsed.end);
}
const bounds = weightFactorValuesToSortedUpperBounds(allWeightValuesInFactor);
const end = Number.parseFloat(trimmed);
if (!Number.isFinite(end)) return trimmed;
const idx = bounds.findIndex((n) => Math.abs(n - end) < 1e-9);
const start = idx > 0 ? bounds[idx - 1]! : 0;
const upper = idx >= 0 ? bounds[idx]! : end;
return formatWeightIntervalLeftOpenRightClosed(start, upper);
}
function factorValueSortNumber(v: string): number | null {
const parsed = parseWeightIntervalBounds(v);
if (parsed) return parsed.end;
const n = Number.parseFloat(String(v).trim());
return Number.isFinite(n) ? n : null;
}
/** 按数值大小升序排列因素值(如 2, 0.5, 1 → 0.5, 1, 2)。 */
export function sortFactorValuesBySize(values: string[] | undefined): string[] {
return [...(values ?? [])].sort((a, b) => {
const na = factorValueSortNumber(String(a));
const nb = factorValueSortNumber(String(b));
if (na != null && nb != null && na !== nb) return na - nb;
if (na != null && nb == null) return -1;
if (na == null && nb != null) return 1;
return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' });
});
}
/** 提取因素值列表中的重量上限并升序排列(用于区间左界)。 */
export function weightFactorValuesToSortedUpperBounds(values: string[]): number[] {
const nums = (values ?? [])
.map((v) => factorValueSortNumber(v))
.filter((n): n is number => n != null);
return [...new Set(nums)].sort((a, b) => a - b);
}
/** 提交快照/API 时规范化重量单元格。 */
export function canonicalWeightFactorCellForApi(
rawValue: string,
allWeightValuesInFactor: string[]
): string {
return weightValueToIntervalCell(rawValue, allWeightValuesInFactor);
}