posts hot
This commit is contained in:
@@ -51,6 +51,10 @@ interface Article {
|
|||||||
views: number;
|
views: number;
|
||||||
engagement: number;
|
engagement: number;
|
||||||
platform: string;
|
platform: string;
|
||||||
|
influencer?: string;
|
||||||
|
date?: string;
|
||||||
|
engagementRate?: number;
|
||||||
|
isHighEngagement?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EngagementData {
|
interface EngagementData {
|
||||||
@@ -189,6 +193,26 @@ interface CommentTrendResponse {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加热门文章API响应接口
|
||||||
|
interface PopularPostsResponse {
|
||||||
|
success: boolean;
|
||||||
|
data: {
|
||||||
|
title: string;
|
||||||
|
platform: string;
|
||||||
|
influencer_name: string;
|
||||||
|
publish_date: string;
|
||||||
|
engagement_count: number;
|
||||||
|
views_count: number;
|
||||||
|
engagement_rate: number;
|
||||||
|
is_high_engagement: boolean;
|
||||||
|
}[];
|
||||||
|
metadata: {
|
||||||
|
total: number;
|
||||||
|
high_engagement_count: number;
|
||||||
|
};
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Analytics: React.FC = () => {
|
const Analytics: React.FC = () => {
|
||||||
const [timeRange, setTimeRange] = useState('30'); // 修改默认值为'30'与API匹配
|
const [timeRange, setTimeRange] = useState('30'); // 修改默认值为'30'与API匹配
|
||||||
const [selectedKOL, setSelectedKOL] = useState('all');
|
const [selectedKOL, setSelectedKOL] = useState('all');
|
||||||
@@ -263,6 +287,9 @@ const Analytics: React.FC = () => {
|
|||||||
const [sentimentError, setSentimentError] = useState<string | null>(null);
|
const [sentimentError, setSentimentError] = useState<string | null>(null);
|
||||||
const [sentimentScore, setSentimentScore] = useState(0);
|
const [sentimentScore, setSentimentScore] = useState(0);
|
||||||
|
|
||||||
|
const [postsLoading, setPostsLoading] = useState(true);
|
||||||
|
const [postsError, setPostsError] = useState<string | null>(null);
|
||||||
|
|
||||||
// 获取KOL概览数据
|
// 获取KOL概览数据
|
||||||
const fetchKolOverviewData = async () => {
|
const fetchKolOverviewData = async () => {
|
||||||
setKolLoading(true);
|
setKolLoading(true);
|
||||||
@@ -350,12 +377,17 @@ const Analytics: React.FC = () => {
|
|||||||
// 获取情感分析数据
|
// 获取情感分析数据
|
||||||
fetchSentimentAnalysis();
|
fetchSentimentAnalysis();
|
||||||
|
|
||||||
|
// 获取热门文章数据
|
||||||
|
fetchPopularPosts();
|
||||||
|
|
||||||
const fetchAnalyticsData = async () => {
|
const fetchAnalyticsData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// 删除平台分布的硬编码数据,使用API数据替代
|
// 删除平台分布的硬编码数据,使用API数据替代
|
||||||
|
|
||||||
|
// 删除情感分析的硬编码数据,使用API数据替代
|
||||||
|
|
||||||
// Set mock status data
|
// Set mock status data
|
||||||
setStatusData([
|
setStatusData([
|
||||||
{ name: 'approved', value: 45, color: '#10B981' },
|
{ name: 'approved', value: 45, color: '#10B981' },
|
||||||
@@ -363,14 +395,14 @@ const Analytics: React.FC = () => {
|
|||||||
{ name: 'rejected', value: 25, color: '#EF4444' }
|
{ name: 'rejected', value: 25, color: '#EF4444' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Set mock popular articles
|
// 删除热门文章的硬编码数据,使用API数据替代
|
||||||
setPopularArticles([
|
// setPopularArticles([
|
||||||
{ id: '1', title: 'How to Increase Engagement', views: 1200, engagement: 85, platform: 'Facebook' },
|
// { id: '1', title: 'How to Increase Engagement', views: 1200, engagement: 85, platform: 'Facebook' },
|
||||||
{ id: '2', title: 'Top 10 Marketing Strategies', views: 980, engagement: 72, platform: 'LinkedIn' },
|
// { id: '2', title: 'Top 10 Marketing Strategies', views: 980, engagement: 72, platform: 'LinkedIn' },
|
||||||
{ id: '3', title: 'Social Media in 2023', views: 850, engagement: 68, platform: 'Twitter' },
|
// { id: '3', title: 'Social Media in 2023', views: 850, engagement: 68, platform: 'Twitter' },
|
||||||
{ id: '4', title: 'Building Your Brand Online', views: 750, engagement: 63, platform: 'Instagram' },
|
// { id: '4', title: 'Building Your Brand Online', views: 750, engagement: 63, platform: 'Instagram' },
|
||||||
{ id: '5', title: 'Content Creation Tips', views: 620, engagement: 57, platform: 'YouTube' }
|
// { id: '5', title: 'Content Creation Tips', views: 620, engagement: 57, platform: 'YouTube' }
|
||||||
]);
|
// ]);
|
||||||
|
|
||||||
// 从API获取漏斗数据
|
// 从API获取漏斗数据
|
||||||
try {
|
try {
|
||||||
@@ -1212,6 +1244,79 @@ const Analytics: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取热门文章数据
|
||||||
|
const fetchPopularPosts = async () => {
|
||||||
|
try {
|
||||||
|
setPostsLoading(true);
|
||||||
|
setPostsError(null);
|
||||||
|
|
||||||
|
// 构建热门文章API URL
|
||||||
|
const url = `http://localhost:4000/api/analytics/popular-posts?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 PopularPostsResponse;
|
||||||
|
console.log('成功获取热门文章数据:', result);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// 将API返回的数据映射到Article结构
|
||||||
|
const mappedData = result.data.map((post, index) => ({
|
||||||
|
id: `post-${index}`,
|
||||||
|
title: post.title,
|
||||||
|
views: post.views_count,
|
||||||
|
engagement: post.engagement_count,
|
||||||
|
platform: post.platform,
|
||||||
|
influencer: post.influencer_name,
|
||||||
|
date: new Date(post.publish_date).toLocaleDateString('zh-CN'),
|
||||||
|
engagementRate: post.engagement_rate,
|
||||||
|
isHighEngagement: post.is_high_engagement
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
setPopularArticles(mappedData);
|
||||||
|
|
||||||
|
// 如果API返回了模拟数据标志
|
||||||
|
if ('is_mock_data' in result && result.is_mock_data) {
|
||||||
|
console.info('注意: 使用的是模拟热门文章数据');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setPostsError(result.error || '获取热门文章数据失败');
|
||||||
|
console.error('API调用失败:', result.error || '未知错误');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text();
|
||||||
|
setPostsError(`获取失败 (${response.status}): ${errorText}`);
|
||||||
|
console.error('获取热门文章数据失败,HTTP状态:', response.status, errorText);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setPostsError(`获取热门文章数据时发生错误: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
console.error('获取热门文章数据时发生错误:', error);
|
||||||
|
} finally {
|
||||||
|
setPostsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 overflow-auto">
|
<div className="flex-1 overflow-auto">
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
@@ -1931,22 +2036,55 @@ const Analytics: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 热门文章 */}
|
{/* 热门文章 */}
|
||||||
<div className="p-6 bg-white rounded-lg shadow">
|
<div className="p-6 mt-8 bg-white rounded-lg shadow">
|
||||||
<h3 className="mb-4 text-lg font-medium text-gray-800">热门文章</h3>
|
<h3 className="mb-4 text-lg font-medium text-gray-800">热门文章</h3>
|
||||||
|
{postsLoading ? (
|
||||||
|
<div className="flex flex-col items-center justify-center h-64">
|
||||||
|
<div className="w-12 h-12 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
||||||
|
<p className="mt-4 text-gray-600">加载热门文章数据中...</p>
|
||||||
|
</div>
|
||||||
|
) : postsError ? (
|
||||||
|
<div className="flex flex-col items-center justify-center h-64 p-4 rounded-lg bg-red-50">
|
||||||
|
<AlertTriangle className="w-10 h-10 mb-2 text-red-500" />
|
||||||
|
<p className="text-center text-red-600">{postsError}</p>
|
||||||
|
</div>
|
||||||
|
) : popularArticles.length === 0 ? (
|
||||||
|
<div className="flex flex-col items-center justify-center h-64 p-4 rounded-lg bg-gray-50">
|
||||||
|
<BookOpen className="w-10 h-10 mb-2 text-gray-400" />
|
||||||
|
<p className="text-center text-gray-600">没有找到热门文章数据</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{popularArticles.map((article: any, index: number) => (
|
{popularArticles.map((article, index) => (
|
||||||
<div key={index} className="pb-3 border-b border-gray-200 last:border-0 last:pb-0">
|
<div key={article.id} className="pb-3 border-b border-gray-200 last:border-0 last:pb-0">
|
||||||
<p className="mb-1 text-sm font-medium text-gray-800">{article.title}</p>
|
<p className="mb-1 text-sm font-medium text-gray-800">{article.title}</p>
|
||||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||||
<span>{article.count} 则留言</span>
|
<div className="flex items-center space-x-2">
|
||||||
|
<span>
|
||||||
|
{article.influencer || '匿名创作者'} | {article.platform || '未知平台'} | {article.date || '未知日期'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Eye className="w-3 h-3 mr-1 text-gray-400" />
|
||||||
|
<span>{article.views.toLocaleString()} 浏览</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<MessageSquare className="w-3 h-3 mr-1 text-gray-400" />
|
||||||
|
<span>{article.engagement.toLocaleString()} 互动</span>
|
||||||
|
</div>
|
||||||
|
{article.isHighEngagement && (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="w-2 h-2 mr-1 bg-green-500 rounded-full"></div>
|
<div className="w-2 h-2 mr-1 bg-green-500 rounded-full"></div>
|
||||||
<span>高互动</span>
|
<span>高互动</span>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user