diff --git a/web/src/components/Analytics.tsx b/web/src/components/Analytics.tsx index 835f870..c9c73c0 100644 --- a/web/src/components/Analytics.tsx +++ b/web/src/components/Analytics.tsx @@ -282,15 +282,27 @@ const Analytics: React.FC = () => { { id: '5', title: 'Content Creation Tips', views: 620, engagement: 57, platform: 'YouTube' } ]); - // 尝试从API获取漏斗数据 + // 从API获取漏斗数据 try { - // 使用选中的项目ID - const projectId = selectedProject; + // 构建漏斗数据API URL + let url = `http://localhost:4000/api/analytics/kol-funnel?timeRange=${timeRange}`; + + // 添加项目过滤参数(如果选择了特定项目) + if (selectedProject !== 'all') { + url += `&projectId=${selectedProject}`; + } + + // 添加平台过滤参数(如果选择了特定平台) + if (selectedPlatform !== 'all') { + url += `&platform=${selectedPlatform}`; + } // 添加认证头 const authToken = 'eyJhbGciOiJIUzI1NiIsImtpZCI6Inl3blNGYnRBOGtBUnl4UmUiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h0cWhsdXpvcm5hemxta29udWNyLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI1YjQzMThiZi0yMWE4LTQ3YWMtOGJmYS0yYThmOGVmOWMwZmIiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzQxNjI3ODkyLCJpYXQiOjE3NDE2MjQyOTIsImVtYWlsIjoidml0YWxpdHltYWlsZ0BnbWFpbC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7ImVtYWlsX3ZlcmlmaWVkIjp0cnVlfSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc0MTYyNDI5Mn1dLCJzZXNzaW9uX2lkIjoiODlmYjg0YzktZmEzYy00YmVlLTk0MDQtNjI1MjE0OGIyMzVlIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.VuUX2yhqN-FZseKL8fQG91i1cohfRqW2m1Z8CIWhZuk'; - const response = await fetch(`http://localhost:4000/api/analytics/project/${projectId}/conversion-funnel?timeframe=${timeRange}`, { + console.log('请求漏斗数据URL:', url); + + const response = await fetch(url, { headers: { 'accept': 'application/json', 'Authorization': `Bearer ${authToken}` @@ -298,51 +310,44 @@ const Analytics: React.FC = () => { }); if (response.ok) { - const data = await response.json(); - console.log('成功获取漏斗数据:', data); + const result = await response.json(); + console.log('成功获取漏斗数据:', result); - if (data.funnel_data) { - setFunnelData(data.funnel_data); - - // 如果是模拟数据,在控制台显示提示 - if (data.is_mock_data) { - console.info('注意: 使用的是模拟数据,因为无法连接到数据库或找不到项目'); + if (result.success) { + // 检查API响应中是否有数据 + if (result.data && Array.isArray(result.data)) { + // 将API返回的数据映射到FunnelData结构 + setFunnelData(result.data.map((item: any) => ({ + stage: item.stage, + count: item.count, + rate: item.rate + }))); + + // 如果API返回了模拟数据标志 + if (result.is_mock_data) { + console.info('注意: 使用的是模拟漏斗数据'); + } + } else { + console.error('API返回的数据格式不正确:', result); + // 使用模拟数据作为后备 + setFallbackFunnelData(); } } else { - console.error('API返回的数据中没有funnel_data字段'); + console.error('API调用失败:', result.error || '未知错误'); // 使用模拟数据作为后备 - setFunnelData([ - { stage: 'Awareness', count: 10000, rate: 100 }, - { stage: 'Interest', count: 7500, rate: 75 }, - { stage: 'Consideration', count: 5000, rate: 50 }, - { stage: 'Intent', count: 3000, rate: 30 }, - { stage: 'Evaluation', count: 2000, rate: 20 }, - { stage: 'Purchase', count: 1000, rate: 10 } - ]); + setFallbackFunnelData(); } } else { - console.error('Failed to fetch funnel data from API, using mock data instead'); + console.error('获取漏斗数据失败,HTTP状态:', response.status); + const errorText = await response.text(); + console.error('错误详情:', errorText); // 使用模拟数据作为后备 - setFunnelData([ - { stage: 'Awareness', count: 10000, rate: 100 }, - { stage: 'Interest', count: 7500, rate: 75 }, - { stage: 'Consideration', count: 5000, rate: 50 }, - { stage: 'Intent', count: 3000, rate: 30 }, - { stage: 'Evaluation', count: 2000, rate: 20 }, - { stage: 'Purchase', count: 1000, rate: 10 } - ]); + setFallbackFunnelData(); } } catch (error) { - console.error('Error fetching funnel data:', error); + console.error('获取漏斗数据时发生错误:', error); // 使用模拟数据作为后备 - setFunnelData([ - { stage: 'Awareness', count: 10000, rate: 100 }, - { stage: 'Interest', count: 7500, rate: 75 }, - { stage: 'Consideration', count: 5000, rate: 50 }, - { stage: 'Intent', count: 3000, rate: 30 }, - { stage: 'Evaluation', count: 2000, rate: 20 }, - { stage: 'Purchase', count: 1000, rate: 10 } - ]); + setFallbackFunnelData(); } setLoading(false); @@ -352,8 +357,20 @@ const Analytics: React.FC = () => { } }; + // 添加辅助函数,设置后备漏斗数据 + const setFallbackFunnelData = () => { + setFunnelData([ + { stage: 'Awareness', count: 10000, rate: 100 }, + { stage: 'Interest', count: 7500, rate: 75 }, + { stage: 'Consideration', count: 5000, rate: 50 }, + { stage: 'Intent', count: 3000, rate: 30 }, + { stage: 'Evaluation', count: 2000, rate: 20 }, + { stage: 'Purchase', count: 1000, rate: 10 } + ]); + }; + fetchAnalyticsData(); - }, [timeRange, selectedProject, kolSortBy, kolSortOrder, kolPage, kolPageSize]); // 更新依赖项 + }, [timeRange, selectedProject, kolSortBy, kolSortOrder, kolPage, kolPageSize, selectedPlatform]); // 更新依赖项,添加selectedPlatform // 当排序或筛选条件变化时,重置页码 useEffect(() => {