feat(report): stream available years loading

This commit is contained in:
tisonhuang
2026-03-04 16:33:20 +08:00
parent d476fbbdae
commit 28d68d8a8e
5 changed files with 242 additions and 40 deletions

View File

@@ -11,33 +11,79 @@ function AnnualReportPage() {
const [selectedYear, setSelectedYear] = useState<YearOption | null>(null)
const [selectedPairYear, setSelectedPairYear] = useState<YearOption | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [isLoadingMoreYears, setIsLoadingMoreYears] = useState(false)
const [isGenerating, setIsGenerating] = useState(false)
const [loadError, setLoadError] = useState<string | null>(null)
useEffect(() => {
loadAvailableYears()
}, [])
let disposed = false
let taskId = ''
const loadAvailableYears = async () => {
setIsLoading(true)
setLoadError(null)
try {
const result = await window.electronAPI.annualReport.getAvailableYears()
const years = result.data
if (result.success && Array.isArray(years) && years.length > 0) {
const stopListen = window.electronAPI.annualReport.onAvailableYearsProgress((payload) => {
if (disposed) return
if (taskId && payload.taskId !== taskId) return
if (!taskId) taskId = payload.taskId
const years = Array.isArray(payload.years) ? payload.years : []
if (years.length > 0) {
setAvailableYears(years)
setSelectedYear((prev) => prev ?? years[0])
setSelectedPairYear((prev) => prev ?? years[0])
} else if (!result.success) {
setLoadError(result.error || '加载年度数据失败')
setSelectedYear((prev) => {
if (prev === 'all') return prev
if (typeof prev === 'number' && years.includes(prev)) return prev
return years[0]
})
setSelectedPairYear((prev) => {
if (prev === 'all') return prev
if (typeof prev === 'number' && years.includes(prev)) return prev
return years[0]
})
// 只要有首批年份可选,就不再阻塞整个页面。
setIsLoading(false)
}
if (payload.error && !payload.canceled) {
setLoadError(payload.error || '加载年度数据失败')
}
if (payload.done) {
setIsLoading(false)
setIsLoadingMoreYears(false)
} else {
setIsLoadingMoreYears(true)
}
})
const startLoad = async () => {
setIsLoading(true)
setIsLoadingMoreYears(true)
setLoadError(null)
try {
const startResult = await window.electronAPI.annualReport.startAvailableYearsLoad()
if (!startResult.success || !startResult.taskId) {
setLoadError(startResult.error || '加载年度数据失败')
setIsLoading(false)
setIsLoadingMoreYears(false)
return
}
taskId = startResult.taskId
} catch (e) {
console.error(e)
setLoadError(String(e))
setIsLoading(false)
setIsLoadingMoreYears(false)
}
} catch (e) {
console.error(e)
setLoadError(String(e))
} finally {
setIsLoading(false)
}
}
void startLoad()
return () => {
disposed = true
stopListen()
if (taskId) {
void window.electronAPI.annualReport.cancelAvailableYearsLoad(taskId)
}
}
}, [])
const handleGenerateReport = async () => {
if (selectedYear === null) return
@@ -58,16 +104,16 @@ function AnnualReportPage() {
navigate(`/dual-report?year=${yearParam}`)
}
if (isLoading) {
if (isLoading && availableYears.length === 0) {
return (
<div className="annual-report-page">
<Loader2 size={32} className="spin" style={{ color: 'var(--text-tertiary)' }} />
<p style={{ color: 'var(--text-tertiary)', marginTop: 16 }}>...</p>
<p style={{ color: 'var(--text-tertiary)', marginTop: 16 }}>...</p>
</div>
)
}
if (availableYears.length === 0) {
if (availableYears.length === 0 && !isLoadingMoreYears) {
return (
<div className="annual-report-page">
<Calendar size={64} style={{ color: 'var(--text-tertiary)', opacity: 0.5 }} />
@@ -93,6 +139,9 @@ function AnnualReportPage() {
<Sparkles size={32} className="header-icon" />
<h1 className="page-title"></h1>
<p className="page-desc"></p>
{isLoadingMoreYears && (
<p className="page-desc">...</p>
)}
<div className="report-sections">
<section className="report-section">