dasboard
This commit is contained in:
@@ -118,6 +118,26 @@ interface KOLOverviewResponse {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// 添加概览卡片API响应接口
|
||||
interface DashboardCardsResponse {
|
||||
success: boolean;
|
||||
data: {
|
||||
comments_count: {
|
||||
current: number;
|
||||
change_percentage: number;
|
||||
};
|
||||
engagement_rate: {
|
||||
current: number;
|
||||
change_percentage: number;
|
||||
};
|
||||
sentiment_score: {
|
||||
current: number;
|
||||
change_percentage: number;
|
||||
};
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const Analytics: React.FC = () => {
|
||||
const [timeRange, setTimeRange] = useState('30'); // 修改默认值为'30'与API匹配
|
||||
const [selectedKOL, setSelectedKOL] = useState('all');
|
||||
@@ -170,6 +190,19 @@ const Analytics: React.FC = () => {
|
||||
const [trackingSuccess, setTrackingSuccess] = useState<string | null>(null);
|
||||
const [trackingError, setTrackingError] = useState<string | null>(null);
|
||||
|
||||
// 添加概览卡片数据状态
|
||||
const [dashboardCards, setDashboardCards] = useState<{
|
||||
commentsCount: { current: number; changePercentage: number };
|
||||
engagementRate: { current: number; changePercentage: number };
|
||||
sentimentScore: { current: number; changePercentage: number };
|
||||
}>({
|
||||
commentsCount: { current: 0, changePercentage: 0 },
|
||||
engagementRate: { current: 0, changePercentage: 0 },
|
||||
sentimentScore: { current: 0, changePercentage: 0 }
|
||||
});
|
||||
const [cardsLoading, setCardsLoading] = useState(true);
|
||||
const [cardsError, setCardsError] = useState<string | null>(null);
|
||||
|
||||
// 获取KOL概览数据
|
||||
const fetchKolOverviewData = async () => {
|
||||
setKolLoading(true);
|
||||
@@ -245,6 +278,9 @@ const Analytics: React.FC = () => {
|
||||
// 获取贴文表现数据
|
||||
fetchPostPerformanceData();
|
||||
|
||||
// 获取概览卡片数据
|
||||
fetchDashboardCards();
|
||||
|
||||
const fetchAnalyticsData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -802,6 +838,98 @@ const Analytics: React.FC = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
// 获取概览卡片数据
|
||||
const fetchDashboardCards = async () => {
|
||||
try {
|
||||
setCardsLoading(true);
|
||||
setCardsError(null);
|
||||
|
||||
// 构建API URL
|
||||
let url = `http://localhost:4000/api/analytics/dashboard-cards`;
|
||||
|
||||
// 添加时间范围参数
|
||||
if (timeRange) {
|
||||
url += `?timeRange=${timeRange}`;
|
||||
}
|
||||
|
||||
// 添加项目过滤参数(如果选择了特定项目)
|
||||
if (selectedProject !== 'all') {
|
||||
url += `${timeRange ? '&' : '?'}projectId=${selectedProject}`;
|
||||
}
|
||||
|
||||
// 添加平台过滤参数(如果选择了特定平台)
|
||||
if (selectedPlatform !== 'all') {
|
||||
url += `${(timeRange || selectedProject !== 'all') ? '&' : '?'}platform=${selectedPlatform}`;
|
||||
}
|
||||
|
||||
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() as DashboardCardsResponse;
|
||||
console.log('成功获取概览卡片数据:', result);
|
||||
|
||||
if (result.success) {
|
||||
// 更新状态
|
||||
setDashboardCards({
|
||||
commentsCount: {
|
||||
current: result.data.comments_count.current,
|
||||
changePercentage: result.data.comments_count.change_percentage
|
||||
},
|
||||
engagementRate: {
|
||||
current: result.data.engagement_rate.current,
|
||||
changePercentage: result.data.engagement_rate.change_percentage
|
||||
},
|
||||
sentimentScore: {
|
||||
current: result.data.sentiment_score.current,
|
||||
changePercentage: result.data.sentiment_score.change_percentage
|
||||
}
|
||||
});
|
||||
|
||||
// 更新情感分析数据 - 基于情感分数计算正面、中性、负面比例
|
||||
const sentimentScore = result.data.sentiment_score.current;
|
||||
// 假设情感分数范围是0-1,0是负面,0.5是中性,1是正面
|
||||
// 根据情感分数生成模拟的正面、中性、负面百分比
|
||||
const positive = Math.round(Math.min(1, Math.max(0, sentimentScore)) * 100);
|
||||
const negative = Math.round(Math.min(1, Math.max(0, 1 - sentimentScore)) * 100);
|
||||
const neutral = Math.max(0, 100 - positive - negative);
|
||||
|
||||
setSentimentData({
|
||||
positive,
|
||||
neutral,
|
||||
negative
|
||||
});
|
||||
|
||||
// 如果API返回了模拟数据标志
|
||||
if ('is_mock_data' in result && result.is_mock_data) {
|
||||
console.info('注意: 使用的是模拟概览卡片数据');
|
||||
}
|
||||
} else {
|
||||
setCardsError(result.error || '获取概览卡片数据失败');
|
||||
console.error('API调用失败:', result.error || '未知错误');
|
||||
}
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
setCardsError(`获取失败 (${response.status}): ${errorText}`);
|
||||
console.error('获取概览卡片数据失败,HTTP状态:', response.status, errorText);
|
||||
}
|
||||
} catch (error) {
|
||||
setCardsError(`获取概览卡片数据时发生错误: ${error instanceof Error ? error.message : String(error)}`);
|
||||
console.error('获取概览卡片数据时发生错误:', error);
|
||||
} finally {
|
||||
setCardsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="p-6">
|
||||
@@ -1250,11 +1378,30 @@ const Analytics: React.FC = () => {
|
||||
<h3 className="text-lg font-medium text-gray-800">留言总数</h3>
|
||||
<MessageSquare className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
<p className="mb-2 text-3xl font-bold text-gray-900">{platformData.reduce((sum, item) => sum + item.value, 0)}</p>
|
||||
<div className="flex items-center text-sm">
|
||||
<TrendingUp className="w-4 h-4 mr-1 text-green-500" />
|
||||
<span className="text-green-500">↑ 12% 较上周</span>
|
||||
</div>
|
||||
{cardsLoading ? (
|
||||
<div className="flex items-center justify-center h-20">
|
||||
<div className="w-8 h-8 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
||||
</div>
|
||||
) : cardsError ? (
|
||||
<div className="text-sm text-red-500">{cardsError}</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-2 text-3xl font-bold text-gray-900">{dashboardCards.commentsCount.current.toLocaleString()}</p>
|
||||
<div className="flex items-center text-sm">
|
||||
{dashboardCards.commentsCount.changePercentage > 0 ? (
|
||||
<>
|
||||
<TrendingUp className="w-4 h-4 mr-1 text-green-500" />
|
||||
<span className="text-green-500">↑ {dashboardCards.commentsCount.changePercentage.toFixed(1)}% 较上周</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TrendingDown className="w-4 h-4 mr-1 text-red-500" />
|
||||
<span className="text-red-500">↓ {Math.abs(dashboardCards.commentsCount.changePercentage).toFixed(1)}% 较上周</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-6 bg-white rounded-lg shadow">
|
||||
@@ -1262,11 +1409,30 @@ const Analytics: React.FC = () => {
|
||||
<h3 className="text-lg font-medium text-gray-800">平均互动率</h3>
|
||||
<Users className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
<p className="mb-2 text-3xl font-bold text-gray-900">4.8%</p>
|
||||
<div className="flex items-center text-sm">
|
||||
<TrendingUp className="w-4 h-4 mr-1 text-green-500" />
|
||||
<span className="text-green-500">↑ 0.5% 较上周</span>
|
||||
</div>
|
||||
{cardsLoading ? (
|
||||
<div className="flex items-center justify-center h-20">
|
||||
<div className="w-8 h-8 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
||||
</div>
|
||||
) : cardsError ? (
|
||||
<div className="text-sm text-red-500">{cardsError}</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-2 text-3xl font-bold text-gray-900">{(dashboardCards.engagementRate.current / 100).toFixed(1)}%</p>
|
||||
<div className="flex items-center text-sm">
|
||||
{dashboardCards.engagementRate.changePercentage > 0 ? (
|
||||
<>
|
||||
<TrendingUp className="w-4 h-4 mr-1 text-green-500" />
|
||||
<span className="text-green-500">↑ {dashboardCards.engagementRate.changePercentage.toFixed(1)}% 较上周</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TrendingDown className="w-4 h-4 mr-1 text-red-500" />
|
||||
<span className="text-red-500">↓ {Math.abs(dashboardCards.engagementRate.changePercentage).toFixed(1)}% 较上周</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-6 bg-white rounded-lg shadow">
|
||||
@@ -1274,11 +1440,30 @@ const Analytics: React.FC = () => {
|
||||
<h3 className="text-lg font-medium text-gray-800">情感分析</h3>
|
||||
<PieChart className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
<p className="mb-2 text-3xl font-bold text-gray-900">{sentimentData.positive}% 正面</p>
|
||||
<div className="flex items-center text-sm">
|
||||
<TrendingUp className="w-4 h-4 mr-1 text-green-500" />
|
||||
<span className="text-green-500">↑ 5% 较上周</span>
|
||||
</div>
|
||||
{cardsLoading ? (
|
||||
<div className="flex items-center justify-center h-20">
|
||||
<div className="w-8 h-8 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
||||
</div>
|
||||
) : cardsError ? (
|
||||
<div className="text-sm text-red-500">{cardsError}</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-2 text-3xl font-bold text-gray-900">{sentimentData.positive}% 正面</p>
|
||||
<div className="flex items-center text-sm">
|
||||
{dashboardCards.sentimentScore.changePercentage > 0 ? (
|
||||
<>
|
||||
<TrendingUp className="w-4 h-4 mr-1 text-green-500" />
|
||||
<span className="text-green-500">↑ {dashboardCards.sentimentScore.changePercentage.toFixed(1)}% 较上周</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TrendingDown className="w-4 h-4 mr-1 text-red-500" />
|
||||
<span className="text-red-500">↓ {Math.abs(dashboardCards.sentimentScore.changePercentage).toFixed(1)}% 较上周</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user