diff --git a/src/pages/ExportPage.scss b/src/pages/ExportPage.scss index b5410ec..06ae777 100644 --- a/src/pages/ExportPage.scss +++ b/src/pages/ExportPage.scss @@ -405,9 +405,9 @@ .session-mutual-friends-row { display: grid; - grid-template-columns: 42px minmax(0, 1fr) 96px 72px 110px; + grid-template-columns: 42px minmax(0, 1fr) 72px 110px; gap: 10px; - align-items: center; + align-items: start; padding: 11px 12px; border-bottom: 1px solid color-mix(in srgb, var(--border-color) 68%, transparent); font-size: 13px; @@ -426,6 +426,14 @@ .session-mutual-friends-rank { color: var(--text-tertiary); + padding-top: 3px; +} + +.session-mutual-friends-main { + min-width: 0; + display: flex; + flex-direction: column; + gap: 6px; } .session-mutual-friends-name { @@ -437,6 +445,13 @@ font-weight: 600; } +.session-mutual-friends-tags { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + .session-mutual-friends-source { justify-self: start; border-radius: 999px; @@ -447,22 +462,56 @@ background: var(--bg-secondary); color: var(--text-secondary); - &.both { - color: var(--primary); - border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color)); - background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary)); + &.incoming { + color: #065f46; + border-color: color-mix(in srgb, #10b981 38%, var(--border-color)); + background: color-mix(in srgb, #10b981 10%, var(--bg-secondary)); } - &.reverse { + &.outgoing { color: #92400e; border-color: color-mix(in srgb, #d97706 38%, var(--border-color)); background: color-mix(in srgb, #f59e0b 12%, var(--bg-secondary)); } + + &.bidirectional { + color: var(--primary); + border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color)); + background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary)); + } +} + +.session-mutual-friends-behavior { + &.likes { + color: #1d4ed8; + border-color: color-mix(in srgb, #3b82f6 34%, var(--border-color)); + background: color-mix(in srgb, #3b82f6 9%, var(--bg-secondary)); + } + + &.comments { + color: #7c3aed; + border-color: color-mix(in srgb, #8b5cf6 34%, var(--border-color)); + background: color-mix(in srgb, #8b5cf6 9%, var(--bg-secondary)); + } + + &.both { + color: #be185d; + border-color: color-mix(in srgb, #ec4899 34%, var(--border-color)); + background: color-mix(in srgb, #ec4899 9%, var(--bg-secondary)); + } +} + +.session-mutual-friends-desc { + min-width: 0; + color: var(--text-tertiary); + font-size: 12px; + line-height: 1.45; } .session-mutual-friends-count, .session-mutual-friends-latest { text-align: right; + padding-top: 3px; } .session-mutual-friends-empty { @@ -3828,7 +3877,7 @@ } .session-mutual-friends-row { - grid-template-columns: 34px minmax(0, 1fr) 82px 56px 88px; + grid-template-columns: 34px minmax(0, 1fr) 56px 88px; gap: 8px; font-size: 12px; } diff --git a/src/pages/ExportPage.tsx b/src/pages/ExportPage.tsx index 61e2784..1b9d21b 100644 --- a/src/pages/ExportPage.tsx +++ b/src/pages/ExportPage.tsx @@ -574,15 +574,19 @@ interface SessionSnsRankItem { latestTime: number } -type SessionMutualFriendSource = 'likes' | 'comments' | 'both' | 'reverse' +type SessionMutualFriendDirection = 'incoming' | 'outgoing' | 'bidirectional' +type SessionMutualFriendBehavior = 'likes' | 'comments' | 'both' interface SessionMutualFriendItem { name: string - likeCount: number - commentCount: number + incomingLikeCount: number + incomingCommentCount: number + outgoingLikeCount: number + outgoingCommentCount: number totalCount: number latestTime: number - source: SessionMutualFriendSource + direction: SessionMutualFriendDirection + behavior: SessionMutualFriendBehavior } interface SessionMutualFriendsMetric { @@ -659,19 +663,22 @@ const buildSessionMutualFriendsMetric = ( const name = String(likeNameRaw || '').trim() || '未知用户' const existing = friendMap.get(name) if (existing) { - existing.likeCount += 1 + existing.incomingLikeCount += 1 existing.totalCount += 1 - existing.source = existing.commentCount > 0 ? 'both' : 'likes' + existing.behavior = existing.incomingCommentCount > 0 ? 'both' : 'likes' if (createTime > existing.latestTime) existing.latestTime = createTime continue } friendMap.set(name, { name, - likeCount: 1, - commentCount: 0, + incomingLikeCount: 1, + incomingCommentCount: 0, + outgoingLikeCount: 0, + outgoingCommentCount: 0, totalCount: 1, latestTime: createTime, - source: 'likes' + direction: 'incoming', + behavior: 'likes' }) } @@ -679,19 +686,22 @@ const buildSessionMutualFriendsMetric = ( const name = String(comment?.nickname || '').trim() || '未知用户' const existing = friendMap.get(name) if (existing) { - existing.commentCount += 1 + existing.incomingCommentCount += 1 existing.totalCount += 1 - existing.source = existing.likeCount > 0 ? 'both' : 'comments' + existing.behavior = existing.incomingLikeCount > 0 ? 'both' : 'comments' if (createTime > existing.latestTime) existing.latestTime = createTime continue } friendMap.set(name, { name, - likeCount: 0, - commentCount: 1, + incomingLikeCount: 0, + incomingCommentCount: 1, + outgoingLikeCount: 0, + outgoingCommentCount: 0, totalCount: 1, latestTime: createTime, - source: 'comments' + direction: 'incoming', + behavior: 'comments' }) } } @@ -711,11 +721,41 @@ const buildSessionMutualFriendsMetric = ( } } -const getSessionMutualFriendSourceLabel = (source: SessionMutualFriendSource): string => { - if (source === 'both') return '点赞/评论' - if (source === 'reverse') return '反向关联' - if (source === 'likes') return '仅点赞' - return '仅评论' +const getSessionMutualFriendDirectionLabel = (direction: SessionMutualFriendDirection): string => { + if (direction === 'incoming') return '对方赞/评TA' + if (direction === 'outgoing') return 'TA赞/评对方' + return '双方有互动' +} + +const getSessionMutualFriendBehaviorLabel = (behavior: SessionMutualFriendBehavior): string => { + if (behavior === 'likes') return '赞' + if (behavior === 'comments') return '评' + return '赞/评' +} + +const summarizeMutualFriendBehavior = (likeCount: number, commentCount: number): SessionMutualFriendBehavior => { + if (likeCount > 0 && commentCount > 0) return 'both' + if (likeCount > 0) return 'likes' + return 'comments' +} + +const describeSessionMutualFriendRelation = ( + item: SessionMutualFriendItem, + targetDisplayName: string +): string => { + if (item.direction === 'incoming') { + if (item.behavior === 'likes') return `${item.name} 给 ${targetDisplayName} 点过赞` + if (item.behavior === 'comments') return `${item.name} 给 ${targetDisplayName} 评论过` + return `${item.name} 给 ${targetDisplayName} 点过赞、评论过` + } + if (item.direction === 'outgoing') { + if (item.behavior === 'likes') return `${targetDisplayName} 给 ${item.name} 点过赞` + if (item.behavior === 'comments') return `${targetDisplayName} 给 ${item.name} 评论过` + return `${targetDisplayName} 给 ${item.name} 点过赞、评论过` + } + if (item.behavior === 'likes') return `${targetDisplayName} 和 ${item.name} 双方都有点赞互动` + if (item.behavior === 'comments') return `${targetDisplayName} 和 ${item.name} 双方都有评论互动` + return `${targetDisplayName} 和 ${item.name} 双方都有点赞或评论互动` } interface SessionExportMetric { @@ -2746,15 +2786,35 @@ function ExportPage() { if (reverseMatches.length === 0) continue const reverseCount = reverseMatches.reduce((sum, item) => sum + item.totalCount, 0) + const reverseLikeCount = reverseMatches.reduce((sum, item) => sum + item.incomingLikeCount, 0) + const reverseCommentCount = reverseMatches.reduce((sum, item) => sum + item.incomingCommentCount, 0) const reverseLatestTime = reverseMatches.reduce((latest, item) => Math.max(latest, item.latestTime), 0) - mergedMap.set(sourceProfile.displayName, { - name: sourceProfile.displayName, - likeCount: 0, - commentCount: 0, - totalCount: reverseCount, - latestTime: reverseLatestTime, - source: 'reverse' - }) + const existing = mergedMap.get(sourceProfile.displayName) + if (existing) { + existing.outgoingLikeCount += reverseLikeCount + existing.outgoingCommentCount += reverseCommentCount + existing.totalCount += reverseCount + existing.latestTime = Math.max(existing.latestTime, reverseLatestTime) + existing.direction = (existing.incomingLikeCount + existing.incomingCommentCount) > 0 + ? 'bidirectional' + : 'outgoing' + existing.behavior = summarizeMutualFriendBehavior( + existing.incomingLikeCount + existing.outgoingLikeCount, + existing.incomingCommentCount + existing.outgoingCommentCount + ) + } else { + mergedMap.set(sourceProfile.displayName, { + name: sourceProfile.displayName, + incomingLikeCount: 0, + incomingCommentCount: 0, + outgoingLikeCount: reverseLikeCount, + outgoingCommentCount: reverseCommentCount, + totalCount: reverseCount, + latestTime: reverseLatestTime, + direction: 'outgoing', + behavior: summarizeMutualFriendBehavior(reverseLikeCount, reverseCommentCount) + }) + } } const items = [...mergedMap.values()].sort((a, b) => { @@ -6439,10 +6499,20 @@ function ExportPage() { {filteredSessionMutualFriendsDialogItems.map((item, index) => (