front/src/utils/priceWeightInterval.ts
zzh 5a26ae0048 feat: 小红书模块迁移、登录重构与 xhs-agent-desktop 桌面 Agent
- 新增小红书前端模块(启动服务、内容分析、爆款创作、发布记录、技能卡片、拟人设置、AI 助手)

- 新增 xhs-agent-desktop 独立桌面 Agent,对接 OMS 任务调度

- 登录页重构,整合微信登录与 OMS JWT 认证

- 订单、渠道、用户中心等页面与 API 增强

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 16:13:13 +08:00

96 lines
3.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 等重量命中。
*/
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);
}
/** 提取因素值列表中的重量上限并升序排列(用于区间左界)。 */
export function weightFactorValuesToSortedUpperBounds(values: string[]): number[] {
const nums = (values ?? [])
.map((v) => {
const parsed = parseWeightIntervalBounds(v);
if (parsed) return parsed.end;
const n = Number.parseFloat(String(v).trim());
return Number.isFinite(n) ? n : null;
})
.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);
}