diff --git a/web/src/components/Analytics.tsx b/web/src/components/Analytics.tsx index c9c73c0..78ff47e 100644 --- a/web/src/components/Analytics.tsx +++ b/web/src/components/Analytics.tsx @@ -64,6 +64,8 @@ interface EngagementData { likes: number; comments: number; shares: number; + sentiment?: string; + sentimentScore?: number; } // 更新KOL数据接口,与API接口结构匹配 @@ -136,6 +138,7 @@ const Analytics: React.FC = () => { const [error, setError] = useState(null); const [kolError, setKolError] = useState(null); // 新增KOL数据错误状态 const [filteredEngagementData, setFilteredEngagementData] = useState([]); + const [postDataLoading, setPostDataLoading] = useState(true); // 添加贴文数据加载状态 // 添加项目相关状态 const [projects, setProjects] = useState([ @@ -239,6 +242,9 @@ const Analytics: React.FC = () => { // 获取KOL概览数据 fetchKolOverviewData(); + // 获取贴文表现数据 + fetchPostPerformanceData(); + const fetchAnalyticsData = async () => { try { setLoading(true); @@ -385,14 +391,98 @@ const Analytics: React.FC = () => { // 不需要额外排序,API已经排序 const sortedKOLs = filteredKOLs; + // 定义函数获取贴文表现数据 - 将函数移到组件级别便于多处调用 + const fetchPostPerformanceData = async () => { + try { + setPostDataLoading(true); // 设置加载状态 + setFilteredEngagementData([]); // 重置现有数据 + + // 构建API URL - 不添加任何查询参数,因为默认返回已经按照engagement排序 + let url = `http://localhost:4000/api/analytics/post-performance`; + + console.log('请求贴文表现数据URL:', url); + + // 添加认证头 + const authToken = 'eyJhbGciOiJIUzI1NiIsImtpZCI6Inl3blNGYnRBOGtBUnl4UmUiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h0cWhsdXpvcm5hemxta29udWNyLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI1YjQzMThiZi0yMWE4LTQ3YWMtOGJmYS0yYThmOGVmOWMwZmIiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzQxNjI3ODkyLCJpYXQiOjE3NDE2MjQyOTIsImVtYWlsIjoidml0YWxpdHltYWlsZ0BnbWFpbC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7ImVtYWlsX3ZlcmlmaWVkIjp0cnVlfSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc0MTYyNDI5Mn1dLCJzZXNzaW9uX2lkIjoiODlmYjg0YzktZmEzYy00YmVlLTk0MDQtNjI1MjE0OGIyMzVlIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.VuUX2yhqN-FZseKL8fQG91i1cohfRqW2m1Z8CIWhZuk'; + + const response = await fetch(url, { + headers: { + 'accept': 'application/json', + 'Authorization': `Bearer ${authToken}` + } + }); + + if (response.ok) { + const result = await response.json(); + console.log('成功获取贴文表现数据:', result); + + if (result.success) { + // 检查API响应中是否有数据 + if (result.data && Array.isArray(result.data)) { + // 将API返回的数据映射到EngagementData结构 - 根据实际API响应格式调整 + const mappedData = result.data.map((post: any) => ({ + id: post.post_id || post.id || `post-${Math.random().toString(36).substr(2, 9)}`, + title: post.title || '无标题贴文', + thumbnail: post.thumbnail || post.image_url || `https://via.placeholder.com/150?text=${post.platform}`, + platform: post.platform, + kolId: post.kol_id, + date: post.publish_date?.split(' ')[0] || new Date().toISOString().split('T')[0], + views: post.metrics?.views || 0, + likes: post.metrics?.likes || 0, + comments: post.metrics?.comments || 0, + shares: post.metrics?.shares || 0, + sentiment: post.sentiment || (post.sentiment_score > 0.6 ? 'positive' : post.sentiment_score > 0.4 ? 'neutral' : 'negative'), + sentimentScore: post.sentiment_score ? Math.round(post.sentiment_score * 100) : 50 + })); + + // 根据KOL和平台过滤数据 + let filteredData = mappedData; + + // 如果选择了特定KOL,过滤数据 + if (selectedKOL !== 'all') { + filteredData = filteredData.filter(post => post.kolId === selectedKOL); + } + + // 如果选择了特定平台,过滤数据 + if (selectedPlatform !== 'all') { + filteredData = filteredData.filter(post => + post.platform.toLowerCase() === selectedPlatform.toLowerCase() + ); + } + + setFilteredEngagementData(filteredData); + + // 如果API返回了模拟数据标志 + if (result.is_mock_data) { + console.info('注意: 使用的是模拟贴文表现数据'); + } + } else { + console.error('API返回的数据格式不正确:', result); + setFilteredEngagementData([]); + } + } else { + console.error('API调用失败:', result.error || '未知错误'); + setFilteredEngagementData([]); + } + } else { + console.error('获取贴文表现数据失败,HTTP状态:', response.status); + const errorText = await response.text(); + console.error('错误详情:', errorText); + setFilteredEngagementData([]); + } + } catch (error) { + console.error('获取贴文表现数据时发生错误:', error); + setFilteredEngagementData([]); + } finally { + setPostDataLoading(false); // 无论成功失败都结束加载状态 + } + }; + // Update filtered engagement data when KOL selection changes useEffect(() => { - if (selectedKOL === 'all') { - setFilteredEngagementData([]); - } else { - setFilteredEngagementData([]); - } - }, [selectedKOL]); + // 当KOL、平台或项目选择变化时,重新获取贴文数据 + fetchPostPerformanceData(); + }, [selectedKOL, selectedPlatform, selectedProject, timeRange]); const getPlatformIcon = (platform: string) => { const platformLower = platform.toLowerCase(); @@ -1025,117 +1115,132 @@ const Analytics: React.FC = () => { {/* KOL 贴文表现 */}

KOL 贴文表现

-
- - - - - - - - - - - - - - - - {filteredEngagementData.map((post, index) => ( - - - - - - - - - - + {postDataLoading ? ( +
+
+

加载贴文数据中...

+
+ ) : filteredEngagementData.length === 0 ? ( +
+
+ +
+

没有找到贴文数据

+

请尝试更改筛选条件或检查所选项目是否有贴文数据。

+
+ ) : ( +
+
- 贴文 - - KOL - - 平台 - - 发布日期 - - 观看数 - - 赞数 - - 留言数 - - 分享数 - - 情绪指标 -
-
-
- {post.title} -
-
{post.title}
-
-
-
-
- k.influencer_id === post.kolId)?.avatar || ''} - alt={kolData.find(k => k.influencer_id === post.kolId)?.name || ''} - className="object-cover w-full h-full" - /> -
-
- {kolData.find(k => k.influencer_id === post.kolId)?.name || ''} -
-
-
-
- {getPlatformIcon(post.platform)} - - {post.platform === 'xiaohongshu' ? '小红书' : post.platform} - -
-
- {post.date} - -
- - {post.views.toLocaleString()} -
-
-
- - {post.likes.toLocaleString()} -
-
-
- - {post.comments.toLocaleString()} -
-
-
- - {post.shares.toLocaleString()} -
-
-
-
-
-
- {post.sentimentScore}% -
-
+ + + + + + + + + + + - ))} - -
+ 贴文 + + KOL + + 平台 + + 发布日期 + + 观看数 + + 赞数 + + 留言数 + + 分享数 + + 情绪指标 +
-
+ + + {filteredEngagementData.map((post, index) => ( + + +
+
+ {post.title} +
+
{post.title}
+
+ + +
+
+ k.influencer_id === post.kolId)?.avatar || ''} + alt={kolData.find(k => k.influencer_id === post.kolId)?.name || ''} + className="object-cover w-full h-full" + /> +
+
+ {kolData.find(k => k.influencer_id === post.kolId)?.name || ''} +
+
+ + +
+ {getPlatformIcon(post.platform)} + + {post.platform === 'xiaohongshu' ? '小红书' : post.platform} + +
+ + + {post.date} + + +
+ + {post.views.toLocaleString()} +
+ + +
+ + {post.likes.toLocaleString()} +
+ + +
+ + {post.comments.toLocaleString()} +
+ + +
+ + {post.shares.toLocaleString()} +
+ + +
+
+
+
+ {post.sentimentScore}% +
+ + + ))} + + +
+ )} {/* 概览卡片 */}