add path cont

This commit is contained in:
2025-04-10 11:44:03 +08:00
parent 8b407975e5
commit a8576121e9
4 changed files with 298 additions and 576 deletions

View File

@@ -0,0 +1,111 @@
import React, { useState, useEffect } from 'react';
interface PathAnalyticsProps {
startTime: string;
endTime: string;
linkId?: string;
}
interface PathData {
path: string;
count: number;
percentage: number;
}
const PathAnalytics: React.FC<PathAnalyticsProps> = ({ startTime, endTime, linkId }) => {
const [loading, setLoading] = useState(true);
const [pathData, setPathData] = useState<PathData[]>([]);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!linkId) {
setLoading(false);
return;
}
const fetchPathData = async () => {
try {
const params = new URLSearchParams({
startTime,
endTime,
linkId
});
const response = await fetch(`/api/events/path-analytics?${params.toString()}`);
if (!response.ok) {
throw new Error('获取路径分析数据失败');
}
const result = await response.json();
if (result.success && result.data) {
setPathData(result.data);
} else {
setError(result.error || '加载路径分析失败');
}
} catch (err) {
setError(err instanceof Error ? err.message : '发生错误');
} finally {
setLoading(false);
}
};
fetchPathData();
}, [startTime, endTime, linkId]);
if (loading) {
return <div className="py-8 flex justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500" />
</div>;
}
if (error) {
return <div className="py-4 text-red-500">{error}</div>;
}
if (!linkId) {
return <div className="py-4 text-gray-500"></div>;
}
if (pathData.length === 0) {
return <div className="py-4 text-gray-500"></div>;
}
return (
<div>
<div className="text-sm text-gray-500 mb-4">
URL参数组合会被视为不同的路径 /abc?p=1 /abc?p=2
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
<th className="px-6 py-3 bg-gray-50 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
<th className="px-6 py-3 bg-gray-50 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{pathData.map((item, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{item.path}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right">{item.count}</td>
<td className="px-6 py-4 whitespace-nowrap text-right">
<div className="flex items-center justify-end">
<span className="text-sm text-gray-500 mr-2">{(item.percentage * 100).toFixed(1)}%</span>
<div className="w-32 bg-gray-200 rounded-full h-2.5">
<div className="bg-blue-600 h-2.5 rounded-full" style={{ width: `${item.percentage * 100}%` }}></div>
</div>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default PathAnalytics;