'use client'; import { useEffect, useMemo, useRef, useState } from 'react'; import { flexRender, getCoreRowModel, useReactTable, } from '@tanstack/react-table'; import { fetchFundHistory } from '../api/fund'; import { cachedRequest } from '../lib/cacheRequest'; import { CloseIcon } from './Icons'; import { Dialog, DialogContent, DialogTitle, } from '@/components/ui/dialog'; import { Drawer, DrawerClose, DrawerContent, DrawerHeader, DrawerTitle, } from '@/components/ui/drawer'; function buildRows(history) { if (!Array.isArray(history) || history.length === 0) return []; const reversed = [...history].reverse(); return reversed.map((item, i) => { const prev = reversed[i + 1]; let dailyChange = null; if (prev && Number.isFinite(item.value) && Number.isFinite(prev.value) && prev.value !== 0) { dailyChange = ((item.value - prev.value) / prev.value) * 100; } return { date: item.date, netValue: item.value, dailyChange, }; }); } const columns = [ { accessorKey: 'date', header: '日期', cell: (info) => info.getValue(), meta: { align: 'left' }, }, { accessorKey: 'netValue', header: '净值', cell: (info) => { const v = info.getValue(); return v != null && Number.isFinite(v) ? Number(v).toFixed(4) : '—'; }, meta: { align: 'center' }, }, { accessorKey: 'dailyChange', header: '日涨幅', cell: (info) => { const v = info.getValue(); if (v == null || !Number.isFinite(v)) return '—'; const sign = v > 0 ? '+' : ''; const cls = v > 0 ? 'up' : v < 0 ? 'down' : ''; return {sign}{v.toFixed(2)}%; }, meta: { align: 'right' }, }, ]; export default function FundHistoryNetValueModal({ open, onOpenChange, code, theme }) { const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [visibleCount, setVisibleCount] = useState(30); const [isMobile, setIsMobile] = useState(false); const scrollRef = useRef(null); useEffect(() => { if (typeof window === 'undefined') return; const mq = window.matchMedia('(max-width: 768px)'); const update = () => setIsMobile(mq.matches); update(); mq.addEventListener('change', update); return () => mq.removeEventListener('change', update); }, []); useEffect(() => { if (!open || !code) return; let active = true; setLoading(true); setError(null); setVisibleCount(30); const cacheKey = `fund_history_${code}_all_modal`; cachedRequest(() => fetchFundHistory(code, 'all'), cacheKey, { cacheTime: 10 * 60 * 1000 }) .then((res) => { if (!active) return; setData(buildRows(res || [])); setLoading(false); }) .catch((err) => { if (!active) return; setError(err); setData([]); setLoading(false); }); return () => { active = false; }; }, [open, code]); const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), }); const rows = table.getRowModel().rows.slice(0, visibleCount); const hasMore = table.getRowModel().rows.length > visibleCount; const handleOpenChange = (next) => { if (!next) { onOpenChange?.(false); } }; const handleScroll = (e) => { const target = e.currentTarget; if (!target || !hasMore) return; const distance = target.scrollHeight - target.scrollTop - target.clientHeight; if (distance < 40) { setVisibleCount((prev) => { const next = prev + 30; const total = table.getRowModel().rows.length; return next > total ? total : next; }); } }; const header = (
| {flexRender(h.column.columnDef.header, h.getContext())} | ))}
|---|
| {flexRender(cell.column.columnDef.cell, cell.getContext())} | ))}