feat: 添加 KeywordCloud 主容器组件
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { CloudWord } from './CloudWord';
|
||||
import { QuarterNavigator } from './QuarterNavigator';
|
||||
import { ProgressIndicator } from './ProgressIndicator';
|
||||
import { useKeywordCloud } from '@/hooks/useKeywordCloudClient';
|
||||
|
||||
interface KeywordData {
|
||||
id: number;
|
||||
word: string;
|
||||
trendScore: number;
|
||||
description: string;
|
||||
detailPoints: string[];
|
||||
visualConfig: {
|
||||
color: string;
|
||||
size: string;
|
||||
border: string;
|
||||
rotation?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface QuarterData {
|
||||
quarter: string;
|
||||
title: string;
|
||||
titleEn?: string;
|
||||
subtitle?: string;
|
||||
subtitleEn?: string;
|
||||
keywords: KeywordData[];
|
||||
}
|
||||
|
||||
export function KeywordCloud({ initialQuarter }: { initialQuarter: string }) {
|
||||
const [currentQuarter, setCurrentQuarter] = useState(initialQuarter);
|
||||
const [quarters, setQuarters] = useState<string[]>([]);
|
||||
const { data, isLoading, error } = useKeywordCloud(currentQuarter);
|
||||
|
||||
// 加载季度列表
|
||||
useEffect(() => {
|
||||
async function loadQuarters() {
|
||||
try {
|
||||
const response = await fetch('/api/keyword-cloud/quarters');
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
const quarterStrings = json.quarters.map((q: any) => q.quarter);
|
||||
setQuarters(quarterStrings);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load quarters:', error);
|
||||
}
|
||||
}
|
||||
loadQuarters();
|
||||
}, []);
|
||||
|
||||
const handleNavigate = (quarter: string) => {
|
||||
setCurrentQuarter(quarter);
|
||||
// 更新 URL 而不刷新页面
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('quarter', quarter);
|
||||
window.history.pushState({}, '', url.toString());
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center">
|
||||
<div className="inline-block animate-spin rounded-full h-12 w-12 border-4 border-black border-t-primary mb-4" />
|
||||
<p className="font-display font-bold text-lg">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center">
|
||||
<p className="font-display font-bold text-lg text-red-500 mb-4">
|
||||
加载失败
|
||||
</p>
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="bg-primary text-black border-2 border-black px-4 py-2 font-bold hover:bg-yellow-500 transition-colors"
|
||||
>
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white/50 dark:bg-black/20 backdrop-blur-sm border-4 border-black p-8 md:p-12 shadow-hard relative overflow-visible">
|
||||
{/* 进度条 */}
|
||||
<ProgressIndicator
|
||||
currentQuarter={currentQuarter}
|
||||
totalQuarters={quarters.length}
|
||||
quarters={quarters}
|
||||
/>
|
||||
|
||||
{/* 季度导航 */}
|
||||
<QuarterNavigator
|
||||
current={currentQuarter}
|
||||
quarters={quarters}
|
||||
onNavigate={handleNavigate}
|
||||
/>
|
||||
|
||||
{/* 词云区域 */}
|
||||
<div className="relative py-12 px-4">
|
||||
<div className="word-cluster flex flex-wrap gap-3 items-center justify-center max-w-full">
|
||||
{data.keywords.map((keyword: KeywordData) => (
|
||||
<CloudWord key={keyword.id} data={keyword} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 装饰元素 */}
|
||||
{data.keywords.length > 0 && (
|
||||
<div className="absolute -top-16 -right-8 md:right-0 bg-black text-white p-5 rounded-xl text-sm md:text-base font-display w-64 shadow-hard rotate-6 hidden lg:block">
|
||||
"{data.keywords[0].word}" 是本季度最热门词汇!
|
||||
<div className="absolute -bottom-2 left-1/2 -translate-x-1/2 w-4 h-4 bg-black transform rotate-45" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user