/** * 财务计算工具函数 */ /** * 计算同比变化率 * @param currentValue 当前值 * @param currentPeriod 当前期间 * @param allData 所有数据 * @param metricPath 指标路径 * @returns 变化率和强度 */ export const calculateYoYChange = ( currentValue: number | null | undefined, currentPeriod: string, allData: Array<{ period: string; [key: string]: unknown }>, metricPath: string ): { change: number; intensity: number } => { if (!currentValue || !currentPeriod) return { change: 0, intensity: 0 }; // 找到去年同期的数据 const currentDate = new Date(currentPeriod); const currentYear = currentDate.getFullYear(); const currentMonth = currentDate.getMonth() + 1; // 查找去年同期 const lastYearSamePeriod = allData.find((item) => { const itemDate = new Date(item.period); const itemYear = itemDate.getFullYear(); const itemMonth = itemDate.getMonth() + 1; return itemYear === currentYear - 1 && itemMonth === currentMonth; }); if (!lastYearSamePeriod) return { change: 0, intensity: 0 }; const previousValue = metricPath .split('.') .reduce((obj: unknown, key: string) => { if (obj && typeof obj === 'object') { return (obj as Record)[key]; } return undefined; }, lastYearSamePeriod) as number | undefined; if (!previousValue || previousValue === 0) return { change: 0, intensity: 0 }; const change = ((currentValue - previousValue) / Math.abs(previousValue)) * 100; const intensity = Math.min(Math.abs(change) / 50, 1); // 50%变化达到最大强度 return { change, intensity }; }; /** * 获取单元格背景色(中国市场颜色) * @param change 变化率 * @param intensity 强度 * @returns 背景色 */ export const getCellBackground = (change: number, intensity: number): string => { if (change > 0) { return `rgba(239, 68, 68, ${intensity * 0.15})`; // 红色背景,涨 } else if (change < 0) { return `rgba(34, 197, 94, ${intensity * 0.15})`; // 绿色背景,跌 } return 'transparent'; }; /** * 从对象中获取嵌套路径的值 * @param obj 对象 * @param path 路径(如 'assets.current_assets.cash') * @returns 值 */ export const getValueByPath = ( obj: unknown, path: string ): T | undefined => { return path.split('.').reduce((current: unknown, key: string) => { if (current && typeof current === 'object') { return (current as Record)[key]; } return undefined; }, obj) as T | undefined; }; /** * 判断是否为成本费用类指标(负向指标) * @param key 指标 key * @returns 是否为负向指标 */ export const isNegativeIndicator = (key: string): boolean => { return ( key.includes('cost') || key.includes('expense') || key === 'income_tax' || key.includes('impairment') || key.includes('days') || key.includes('debt_ratio') ); }; /** * 计算两个值的差异百分比 * @param value1 当前股票值 * @param value2 对比股票值 * @param format 格式类型:percent 直接相减,number 计算变化率 * @returns 差异百分比或 null */ export const calculateDiff = ( value1: number | null | undefined, value2: number | null | undefined, format: 'percent' | 'number' ): number | null => { if (value1 == null || value2 == null) return null; if (format === 'percent') { return value1 - value2; } if (value2 !== 0) { return ((value1 - value2) / Math.abs(value2)) * 100; } return null; };