hotkeyword

This commit is contained in:
2025-03-14 22:02:10 +08:00
parent 942fb592b5
commit bd1a5ce384
5 changed files with 496 additions and 0 deletions

View File

@@ -232,6 +232,21 @@ interface PopularPostsResponse {
error?: string;
}
// 添加热门关键字API响应接口
interface HotKeywordsResponse {
success: boolean;
data: {
keyword: string;
count: number;
percentage: number;
sentiment_score: number;
}[];
metadata: {
total: number;
};
error?: string;
}
const Analytics: React.FC = () => {
const [timeRange, setTimeRange] = useState('30'); // 修改默认值为'30'与API匹配
const [selectedKOL, setSelectedKOL] = useState('all');
@@ -261,6 +276,16 @@ const Analytics: React.FC = () => {
const [moderationLoading, setModerationLoading] = useState(true);
const [moderationError, setModerationError] = useState<string | null>(null);
// 添加热门关键字相关状态
const [hotKeywords, setHotKeywords] = useState<{
keyword: string;
count: number;
percentage: number;
sentiment_score: number;
}[]>([]);
const [keywordsLoading, setKeywordsLoading] = useState(true);
const [keywordsError, setKeywordsError] = useState<string | null>(null);
// 添加项目相关状态
const [projects, setProjects] = useState<Project[]>([
{ id: '1', name: '项目 1', description: '示例项目 1' },
@@ -502,6 +527,9 @@ const Analytics: React.FC = () => {
// 获取审核状态分布数据
fetchModerationStatus();
// 获取热门关键字数据
fetchHotKeywords();
const fetchAnalyticsData = async () => {
try {
setLoading(true);
@@ -1449,6 +1477,7 @@ const Analytics: React.FC = () => {
fetchSentimentAnalysis();
fetchPopularPosts();
fetchModerationStatus(); // 添加刷新审核状态数据
fetchHotKeywords(); // 添加刷新热门关键字数据
};
// 项目选择变化处理函数
@@ -1461,6 +1490,7 @@ const Analytics: React.FC = () => {
fetchSentimentAnalysis();
fetchPopularPosts();
fetchModerationStatus(); // 添加刷新审核状态数据
fetchHotKeywords(); // 添加刷新热门关键字数据
};
// 平台选择变化处理函数
@@ -1473,6 +1503,95 @@ const Analytics: React.FC = () => {
fetchSentimentAnalysis();
fetchPopularPosts();
fetchModerationStatus(); // 添加刷新审核状态数据
fetchHotKeywords(); // 添加刷新热门关键字数据
};
// 获取热门关键字数据
const fetchHotKeywords = async () => {
try {
setKeywordsLoading(true);
setKeywordsError(null);
// 构建API URL
const url = `http://localhost:4000/api/analytics/hot-keywords?timeRange=${timeRange}`;
// 添加项目过滤参数(如果选择了特定项目)
const urlWithFilters = selectedProject !== 'all'
? `${url}&projectId=${selectedProject}`
: url;
// 添加平台过滤参数(如果选择了特定平台)
const finalUrl = selectedPlatform !== 'all'
? `${urlWithFilters}&platform=${selectedPlatform}`
: urlWithFilters;
console.log('请求热门关键字数据URL:', finalUrl);
// 添加认证头
const authToken = 'eyJhbGciOiJIUzI1NiIsImtpZCI6Inl3blNGYnRBOGtBUnl4UmUiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h0cWhsdXpvcm5hemxta29udWNyLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI1YjQzMThiZi0yMWE4LTQ3YWMtOGJmYS0yYThmOGVmOWMwZmIiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzQxNjI3ODkyLCJpYXQiOjE3NDE2MjQyOTIsImVtYWlsIjoidml0YWxpdHltYWlsZ0BnbWFpbC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7ImVtYWlsX3ZlcmlmaWVkIjp0cnVlfSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc0MTYyNDI5Mn1dLCJzZXNzaW9uX2lkIjoiODlmYjg0YzktZmEzYy00YmVlLTk0MDQtNjI1MjE0OGIyMzVlIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.VuUX2yhqN-FZseKL8fQG91i1cohfRqW2m1Z8CIWhZuk';
const response = await fetch(finalUrl, {
headers: {
'accept': 'application/json',
'Authorization': `Bearer ${authToken}`
}
});
if (response.ok) {
const result = await response.json() as HotKeywordsResponse;
console.log('成功获取热门关键字数据:', result);
if (result.success) {
// 将API返回的数据设置到状态
setHotKeywords(result.data);
} else {
setKeywordsError(result.error || '获取热门关键字数据失败');
console.error('API调用失败:', result.error || '未知错误');
// 设置默认数据
setHotKeywords([
{ keyword: '价格', count: 45, percentage: 22.5, sentiment_score: 0.2 },
{ keyword: '质量', count: 38, percentage: 19.0, sentiment_score: 0.7 },
{ keyword: '服务', count: 32, percentage: 16.0, sentiment_score: -0.3 },
{ keyword: '快递', count: 28, percentage: 14.0, sentiment_score: 0.1 },
{ keyword: '推荐', count: 24, percentage: 12.0, sentiment_score: 0.8 },
{ keyword: '问题', count: 18, percentage: 9.0, sentiment_score: -0.6 },
{ keyword: '体验', count: 15, percentage: 7.5, sentiment_score: 0.4 }
]);
}
} else {
const errorText = await response.text();
setKeywordsError(`获取失败 (${response.status}): ${errorText}`);
console.error('获取热门关键字数据失败HTTP状态:', response.status, errorText);
// 设置默认数据
setHotKeywords([
{ keyword: '价格', count: 45, percentage: 22.5, sentiment_score: 0.2 },
{ keyword: '质量', count: 38, percentage: 19.0, sentiment_score: 0.7 },
{ keyword: '服务', count: 32, percentage: 16.0, sentiment_score: -0.3 },
{ keyword: '快递', count: 28, percentage: 14.0, sentiment_score: 0.1 },
{ keyword: '推荐', count: 24, percentage: 12.0, sentiment_score: 0.8 },
{ keyword: '问题', count: 18, percentage: 9.0, sentiment_score: -0.6 },
{ keyword: '体验', count: 15, percentage: 7.5, sentiment_score: 0.4 }
]);
}
} catch (error) {
setKeywordsError(`获取热门关键字数据时发生错误: ${error instanceof Error ? error.message : String(error)}`);
console.error('获取热门关键字数据时发生错误:', error);
// 设置默认数据
setHotKeywords([
{ keyword: '价格', count: 45, percentage: 22.5, sentiment_score: 0.2 },
{ keyword: '质量', count: 38, percentage: 19.0, sentiment_score: 0.7 },
{ keyword: '服务', count: 32, percentage: 16.0, sentiment_score: -0.3 },
{ keyword: '快递', count: 28, percentage: 14.0, sentiment_score: 0.1 },
{ keyword: '推荐', count: 24, percentage: 12.0, sentiment_score: 0.8 },
{ keyword: '问题', count: 18, percentage: 9.0, sentiment_score: -0.6 },
{ keyword: '体验', count: 15, percentage: 7.5, sentiment_score: 0.4 }
]);
} finally {
setKeywordsLoading(false);
}
};
return (
@@ -2282,6 +2401,57 @@ const Analytics: React.FC = () => {
</div>
)}
</div>
{/* 热门关键字 */}
<div className="p-6 mb-8 mt-5 bg-white rounded-lg shadow">
<h3 className="mb-4 text-lg font-medium text-gray-800"></h3>
{keywordsLoading ? (
<div className="flex items-center justify-center h-48">
<div className="w-10 h-10 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
</div>
) : keywordsError ? (
<div className="flex flex-col items-center justify-center h-48 p-4 rounded-lg bg-red-50">
<AlertTriangle className="w-8 h-8 mb-2 text-red-500" />
<p className="text-center text-red-600">{keywordsError}</p>
</div>
) : hotKeywords.length === 0 ? (
<div className="flex flex-col items-center justify-center h-48 p-4 rounded-lg bg-gray-50">
<MessageSquare className="w-8 h-8 mb-2 text-gray-400" />
<p className="text-center text-gray-600"></p>
</div>
) : (
<div className="space-y-4">
{hotKeywords.slice(0, 10).map((keyword, index) => (
<div key={index} className="flex items-center">
<div className="w-1/2">
<div className="flex items-center">
<span className="w-8 h-8 mr-2 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 text-sm font-medium">
{index + 1}
</span>
<span className="font-medium text-gray-700">{keyword.keyword}</span>
</div>
</div>
<div className="w-1/2">
<div className="flex items-center justify-between mb-1">
<div className="text-sm font-medium text-gray-500">
{keyword.count} ({keyword.percentage.toFixed(1)}%)
</div>
<div className={`text-sm font-medium ${keyword.sentiment_score > 0 ? 'text-green-500' : keyword.sentiment_score < 0 ? 'text-red-500' : 'text-gray-500'}`}>
: {keyword.sentiment_score.toFixed(1)}
</div>
</div>
<div className="w-full h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className={`h-full ${keyword.sentiment_score > 0.3 ? 'bg-green-500' : keyword.sentiment_score < -0.3 ? 'bg-red-500' : 'bg-yellow-500'}`}
style={{ width: `${keyword.percentage}%` }}
></div>
</div>
</div>
</div>
))}
</div>
)}
</div>
</>
)}
</div>