/** * 卖价/买价重量因素:左开右闭区间 (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); }