Compare commits

2 Commits

Author SHA1 Message Date
1a9e28bd7e show label 2025-04-03 21:57:55 +08:00
d1d21948b6 tag fix 2025-04-03 17:56:16 +08:00
4 changed files with 54 additions and 21 deletions

View File

@@ -29,6 +29,7 @@ export async function GET(request: NextRequest) {
const teamIds = searchParams.getAll('teamId'); const teamIds = searchParams.getAll('teamId');
const projectIds = searchParams.getAll('projectId'); const projectIds = searchParams.getAll('projectId');
const tagIds = searchParams.getAll('tagId'); const tagIds = searchParams.getAll('tagId');
const tagNames = searchParams.getAll('tagName');
// 获取UTM类型参数 // 获取UTM类型参数
const utmType = searchParams.get('utmType') || 'source'; const utmType = searchParams.get('utmType') || 'source';
@@ -72,9 +73,12 @@ export async function GET(request: NextRequest) {
} }
// 添加标签筛选 // 添加标签筛选
if (tagIds && tagIds.length > 0) { if ((tagIds && tagIds.length > 0) || (tagNames && tagNames.length > 0)) {
// 优先使用tagNames如果有的话
const tagsToUse = tagNames.length > 0 ? tagNames : tagIds;
// 使用与buildFilter函数相同的处理方式 // 使用与buildFilter函数相同的处理方式
const tagConditions = tagIds.map(tag => const tagConditions = tagsToUse.map(tag =>
`link_tags LIKE '%${tag}%'` `link_tags LIKE '%${tag}%'`
); );
conditions.push(`(${tagConditions.join(' OR ')})`); conditions.push(`(${tagConditions.join(' OR ')})`);

View File

@@ -50,9 +50,9 @@ export default function UtmAnalytics({ startTime, endTime, linkId, teamIds, proj
projectIds.forEach(id => params.append('projectId', id)); projectIds.forEach(id => params.append('projectId', id));
} }
// 添加标签ID参数 // 添加标签名称参数
if (tagIds && tagIds.length > 0) { if (tagIds && tagIds.length > 0) {
tagIds.forEach(id => params.append('tagId', id)); tagIds.forEach(tagName => params.append('tagName', tagName));
} }
// 发送请求 // 发送请求

View File

@@ -97,6 +97,7 @@ const extractEventInfo = (event: Event) => {
eventTime: event.created_at || event.event_time, eventTime: event.created_at || event.event_time,
linkName: event.link_label || linkAttrs?.name || eventAttrs?.link_name || event.link_slug || '-', linkName: event.link_label || linkAttrs?.name || eventAttrs?.link_name || event.link_slug || '-',
originalUrl: event.link_original_url || eventAttrs?.origin_url || '-', originalUrl: event.link_original_url || eventAttrs?.origin_url || '-',
fullUrl: eventAttrs?.full_url || '-',
eventType: event.event_type || '-', eventType: event.event_type || '-',
visitorId: event.visitor_id?.substring(0, 8) || '-', visitorId: event.visitor_id?.substring(0, 8) || '-',
referrer: eventAttrs?.referrer || '-', referrer: eventAttrs?.referrer || '-',
@@ -124,9 +125,8 @@ export default function HomePage() {
const [selectedTeamIds, setSelectedTeamIds] = useState<string[]>([]); const [selectedTeamIds, setSelectedTeamIds] = useState<string[]>([]);
// 添加项目选择状态 - 使用数组支持多选 // 添加项目选择状态 - 使用数组支持多选
const [selectedProjectIds, setSelectedProjectIds] = useState<string[]>([]); const [selectedProjectIds, setSelectedProjectIds] = useState<string[]>([]);
// 添加标签选择状态 - 使用数组支持多选 // 添加标签名称状态 - 用于在UI中显示和API请求
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]); const [selectedTagNames, setSelectedTagNames] = useState<string[]>([]);
// 添加分页状态 // 添加分页状态
const [currentPage, setCurrentPage] = useState<number>(1); const [currentPage, setCurrentPage] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(10); const [pageSize, setPageSize] = useState<number>(10);
@@ -172,10 +172,10 @@ export default function HomePage() {
}); });
} }
// 添加标签ID参数 - 支持多个标签 // 添加标签名称参数 - 支持多个标签
if (selectedTagIds.length > 0) { if (selectedTagNames.length > 0) {
selectedTagIds.forEach(tagId => { selectedTagNames.forEach(tagName => {
params.append('tagId', tagId); params.append('tagName', tagName);
}); });
} }
@@ -224,7 +224,7 @@ export default function HomePage() {
}; };
fetchData(); fetchData();
}, [dateRange, selectedTeamIds, selectedProjectIds, selectedTagIds, currentPage, pageSize]); }, [dateRange, selectedTeamIds, selectedProjectIds, selectedTagNames, currentPage, pageSize]);
if (loading) { if (loading) {
return ( return (
@@ -272,8 +272,17 @@ export default function HomePage() {
teamIds={selectedTeamIds.length > 0 ? selectedTeamIds : undefined} teamIds={selectedTeamIds.length > 0 ? selectedTeamIds : undefined}
/> />
<TagSelector <TagSelector
value={selectedTagIds} value={selectedTagNames}
onChange={(value) => setSelectedTagIds(Array.isArray(value) ? value : [value])} onChange={(value) => {
// TagSelector返回的是标签名称
if (Array.isArray(value)) {
setSelectedTagNames(value);
} else {
setSelectedTagNames(value ? [value] : []);
}
// 我们需要将标签名称映射回ID但由于TagSelector内部已经做了处理
// 这里不需要额外的映射代码selectedTagNames存储名称即可
}}
className="w-[250px]" className="w-[250px]"
multiple={true} multiple={true}
teamIds={selectedTeamIds.length > 0 ? selectedTeamIds : undefined} teamIds={selectedTeamIds.length > 0 ? selectedTeamIds : undefined}
@@ -346,26 +355,29 @@ export default function HomePage() {
)} )}
{/* 显示标签选择信息 */} {/* 显示标签选择信息 */}
{selectedTagIds.length > 0 && ( {selectedTagNames.length > 0 && (
<div className="bg-blue-50 rounded-lg p-3 mb-6 flex items-center"> <div className="bg-blue-50 rounded-lg p-3 mb-6 flex items-center">
<span className="text-blue-700 font-medium mr-2"> <span className="text-blue-700 font-medium mr-2">
{selectedTagIds.length === 1 ? 'Tag filter:' : 'Tags filter:'} {selectedTagNames.length === 1 ? 'Tag filter:' : 'Tags filter:'}
</span> </span>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{selectedTagIds.map(tagName => ( {selectedTagNames.map(tagName => (
<span key={tagName} className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full"> <span key={tagName} className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
{tagName} {tagName}
<button <button
onClick={() => setSelectedTagIds(selectedTagIds.filter(name => name !== tagName))} onClick={() => {
// 移除对应的标签名称
setSelectedTagNames(selectedTagNames.filter(name => name !== tagName));
}}
className="ml-1 text-blue-600 hover:text-blue-800" className="ml-1 text-blue-600 hover:text-blue-800"
> >
× ×
</button> </button>
</span> </span>
))} ))}
{selectedTagIds.length > 0 && ( {selectedTagNames.length > 0 && (
<button <button
onClick={() => setSelectedTagIds([])} onClick={() => setSelectedTagNames([])}
className="text-xs text-gray-500 hover:text-gray-700 underline" className="text-xs text-gray-500 hover:text-gray-700 underline"
> >
Clear all Clear all
@@ -424,6 +436,9 @@ export default function HomePage() {
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Original URL Original URL
</th> </th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Full URL
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Event Type Event Type
</th> </th>
@@ -463,6 +478,11 @@ export default function HomePage() {
{info.originalUrl} {info.originalUrl}
</a> </a>
</td> </td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-blue-600">
<a href={info.fullUrl} className="hover:underline truncate max-w-xs block" target="_blank" rel="noopener noreferrer">
{info.fullUrl}
</a>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm"> <td className="px-6 py-4 whitespace-nowrap text-sm">
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${ <span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
info.eventType === 'click' info.eventType === 'click'
@@ -772,7 +792,7 @@ export default function HomePage() {
endTime={format(dateRange.to, "yyyy-MM-dd'T'HH:mm:ss'Z'")} endTime={format(dateRange.to, "yyyy-MM-dd'T'HH:mm:ss'Z'")}
teamIds={selectedTeamIds} teamIds={selectedTeamIds}
projectIds={selectedProjectIds} projectIds={selectedProjectIds}
tagIds={selectedTagIds} tagIds={selectedTagNames}
/> />
</> </>
</div> </div>

View File

@@ -0,0 +1,9 @@
-- add_req_full_path.sql
-- Add req_full_path column to the shorturl_analytics.events table
ALTER TABLE
shorturl_analytics.events
ADD
COLUMN IF NOT EXISTS req_full_path String COMMENT 'Full request path including query parameters';
-- Display the updated table structure
DESCRIBE TABLE shorturl_analytics.events;