diff --git a/app/api/fund.js b/app/api/fund.js index e58053b..9640ffa 100644 --- a/app/api/fund.js +++ b/app/api/fund.js @@ -779,20 +779,29 @@ export const fetchFundHistory = async (code, range = '1m') => { if (Array.isArray(trend) && trend.length) { const startMs = start.startOf('day').valueOf(); - // end 可能是当日任意时刻,这里用 end-of-day 包含最后一天 const endMs = end.endOf('day').valueOf(); - const out = trend - .filter((d) => d && typeof d.x === 'number' && d.x >= startMs && d.x <= endMs) + // 若起始日没有净值,则往前推到最近一日有净值的数据作为有效起始 + const validTrend = trend + .filter((d) => d && typeof d.x === 'number' && Number.isFinite(Number(d.y)) && d.x <= endMs) + .sort((a, b) => a.x - b.x); + const startDayEndMs = startMs + 24 * 60 * 60 * 1000 - 1; + const hasPointOnStartDay = validTrend.some((d) => d.x >= startMs && d.x <= startDayEndMs); + let effectiveStartMs = startMs; + if (!hasPointOnStartDay) { + const lastBeforeStart = validTrend.filter((d) => d.x < startMs).pop(); + if (lastBeforeStart) effectiveStartMs = lastBeforeStart.x; + } + + const out = validTrend + .filter((d) => d.x >= effectiveStartMs && d.x <= endMs) .map((d) => { const value = Number(d.y); - if (!Number.isFinite(value)) return null; const date = dayjs(d.x).tz(TZ).format('YYYY-MM-DD'); return { date, value }; - }) - .filter(Boolean); + }); - // 解析 Data_grandTotal 为多条对比曲线,保存在数组属性 out.grandTotalSeries 上 + // 解析 Data_grandTotal 为多条对比曲线,使用同一有效起始日 if (Array.isArray(grandTotal) && grandTotal.length) { const grandTotalSeries = grandTotal .map((series) => { @@ -801,7 +810,7 @@ export const fetchFundHistory = async (code, range = '1m') => { const points = series.data .filter((item) => Array.isArray(item) && typeof item[0] === 'number') .map(([ts, val]) => { - if (ts < startMs || ts > endMs) return null; + if (ts < effectiveStartMs || ts > endMs) return null; const numVal = Number(val); if (!Number.isFinite(numVal)) return null; const date = dayjs(ts).tz(TZ).format('YYYY-MM-DD'); @@ -814,7 +823,6 @@ export const fetchFundHistory = async (code, range = '1m') => { .filter(Boolean); if (grandTotalSeries.length) { - // 给数组挂一个属性,供前端图表组件读取 out.grandTotalSeries = grandTotalSeries; } } diff --git a/app/components/FundTrendChart.jsx b/app/components/FundTrendChart.jsx index d3b0ee3..9941353 100644 --- a/app/components/FundTrendChart.jsx +++ b/app/components/FundTrendChart.jsx @@ -62,9 +62,14 @@ export default function FundTrendChart({ code, isExpanded, onToggleExpand, trans const [error, setError] = useState(null); const chartRef = useRef(null); const hoverTimeoutRef = useRef(null); + const clearActiveIndexRef = useRef(null); const [hiddenGrandSeries, setHiddenGrandSeries] = useState(() => new Set()); const [activeIndex, setActiveIndex] = useState(null); + useEffect(() => { + clearActiveIndexRef.current = () => setActiveIndex(null); + }); + const chartColors = useMemo(() => getChartThemeColors(theme), [theme]); useEffect(() => { @@ -161,8 +166,11 @@ export default function FundTrendChart({ code, isExpanded, onToggleExpand, trans grandAccent3, chartColors.text, ]; - const grandDatasets = grandTotalSeries.map((series, idx) => { - const color = grandColors[idx % grandColors.length]; + // 隐藏第一条对比线(数据与图示);第二条用原第一条颜色,第三条用原第二条,顺延 + const visibleGrandSeries = grandTotalSeries.filter((_, idx) => idx > 0); + const grandDatasets = visibleGrandSeries.map((series, displayIdx) => { + const color = grandColors[displayIdx % grandColors.length]; + const idx = displayIdx + 1; // 原始索引,用于 hiddenGrandSeries 的 key const key = `${series.name || 'series'}_${idx}`; const isHidden = hiddenGrandSeries.has(key); const pointsByDate = new Map(series.points.map(p => [p.date, p.value])); @@ -374,6 +382,7 @@ export default function FundTrendChart({ code, isExpanded, onToggleExpand, trans chart.tooltip.setActiveElements([], { x: 0, y: 0 }); } chart.update(); + clearActiveIndexRef.current?.(); }, 2000); } }, @@ -606,17 +615,19 @@ export default function FundTrendChart({ code, isExpanded, onToggleExpand, trans )} {Array.isArray(data.grandTotalSeries) && - data.grandTotalSeries.map((series, idx) => { - // 与折线数据使用同一套对比色,且排除红/绿 - const legendAccent3 = theme === 'light' ? '#f97316' : '#fb923c'; - const legendColors = [ - primaryColor, - chartColors.muted, - legendAccent3, - chartColors.text, - ]; - const color = legendColors[idx % legendColors.length]; - const key = `${series.name || 'series'}_${idx}`; + data.grandTotalSeries + .filter((_, idx) => idx > 0) + .map((series, displayIdx) => { + const idx = displayIdx + 1; + const legendAccent3 = theme === 'light' ? '#f97316' : '#fb923c'; + const legendColors = [ + primaryColor, + chartColors.muted, + legendAccent3, + chartColors.text, + ]; + const color = legendColors[displayIdx % legendColors.length]; + const key = `${series.name || 'series'}_${idx}`; const isHidden = hiddenGrandSeries.has(key); let valueText = '--'; if (!isHidden && currentIndex != null && data[currentIndex]) {