import type { FinanceCostFilterState } from '../../types'; /** * 文件职责:成本管理页面纯函数与格式化工具。 */ import type { FinanceCostCategoryCode, FinanceCostEntryCategoryDto, SaveFinanceCostCategoryPayload, } from '#/api/finance/cost'; /** 解析为有限数字。 */ export function toFiniteNumber(value: unknown, fallback = 0) { const normalized = Number(value); return Number.isFinite(normalized) ? normalized : fallback; } /** 金额保留两位小数。 */ export function roundAmount(value: unknown) { const normalized = toFiniteNumber(value, 0); return Math.round(normalized * 100) / 100; } /** 货币格式化。 */ export function formatCurrency(value: unknown) { return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(roundAmount(value)); } /** 百分比格式化。 */ export function formatPercent(value: unknown) { return `${roundAmount(value).toFixed(2)}%`; } /** 获取当前月份(yyyy-MM)。 */ export function getCurrentMonthString() { const now = new Date(); const month = `${now.getMonth() + 1}`.padStart(2, '0'); return `${now.getFullYear()}-${month}`; } /** 月份字符串转标题文本。 */ export function formatMonthTitle(month: string) { const normalized = month.trim(); const [year, monthValue] = normalized.split('-'); if (!year || !monthValue) { return normalized || '--'; } return `${year}年${Number(monthValue)}月`; } /** 月份位移。 */ export function shiftMonth(month: string, offset: number) { const parsed = parseMonth(month); if (!parsed) { return getCurrentMonthString(); } parsed.setMonth(parsed.getMonth() + offset); return formatMonth(parsed); } /** 构建查询作用域参数。 */ export function buildScopeQueryPayload(filters: FinanceCostFilterState) { return { dimension: filters.dimension, month: filters.month || undefined, storeId: filters.dimension === 'store' ? filters.storeId || undefined : undefined, }; } /** 将 API 分类数据映射为页面视图模型。 */ export function mapCategoriesToViewModel( categories: FinanceCostEntryCategoryDto[], expandedMap: Map, ) { return (categories ?? []).map((item) => ({ ...item, items: [...(item.items ?? [])], expanded: expandedMap.get(item.category) ?? false, })); } /** 计算分类总金额。 */ export function sumCategoryItems( category: Pick, ) { let total = 0; for (const item of category.items ?? []) { if (category.category === 'labor') { const quantity = toFiniteNumber(item.quantity, 0); const unitPrice = toFiniteNumber(item.unitPrice, 0); total += roundAmount(quantity * unitPrice); continue; } total += roundAmount(item.amount); } return roundAmount(total); } /** 计算全部分类总成本。 */ export function sumAllCategoryTotal(categories: FinanceCostEntryCategoryDto[]) { let total = 0; for (const category of categories ?? []) { total += roundAmount(category.totalAmount); } return roundAmount(total); } /** 构建保存请求分类数组。 */ export function buildSaveCategoryPayload( categories: FinanceCostEntryCategoryDto[], ): SaveFinanceCostCategoryPayload[] { return (categories ?? []).map((category) => ({ category: category.category, totalAmount: roundAmount(category.totalAmount), items: (category.items ?? []).map((item, index) => ({ itemId: item.itemId, itemName: item.itemName.trim(), amount: roundAmount(item.amount), quantity: item.quantity === undefined ? undefined : roundAmount(item.quantity), unitPrice: item.unitPrice === undefined ? undefined : roundAmount(item.unitPrice), sortOrder: item.sortOrder > 0 ? item.sortOrder : index + 1, })), })); } function parseMonth(value: string) { const normalized = value.trim(); const [year, month] = normalized.split('-'); const yearValue = Number(year); const monthValue = Number(month); if ( !Number.isInteger(yearValue) || !Number.isInteger(monthValue) || monthValue < 1 || monthValue > 12 ) { return null; } return new Date(yearValue, monthValue - 1, 1); } function formatMonth(date: Date) { const year = date.getFullYear(); const month = `${date.getMonth() + 1}`.padStart(2, '0'); return `${year}-${month}`; }