From d730ae5bef0f63013758cf682ff333e38b3e9dd1 Mon Sep 17 00:00:00 2001 From: xuncha <1658671838@qq.com> Date: Mon, 16 Mar 2026 17:12:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BE=A4=E8=81=8A=E5=88=86?= =?UTF-8?q?=E6=9E=90=E7=99=BD=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/GroupAnalyticsPage.scss | 78 ++++++++++++++++++++++--------- src/pages/GroupAnalyticsPage.tsx | 56 +++++++++++++--------- 2 files changed, 88 insertions(+), 46 deletions(-) diff --git a/src/pages/GroupAnalyticsPage.scss b/src/pages/GroupAnalyticsPage.scss index d7f5184..d6a2645 100644 --- a/src/pages/GroupAnalyticsPage.scss +++ b/src/pages/GroupAnalyticsPage.scss @@ -497,20 +497,27 @@ flex: 1; display: flex; flex-direction: column; - align-items: center; - justify-content: center; - padding: 32px; + gap: 20px; + padding: 24px; + overflow-y: auto; .selected-group-info { - text-align: center; - margin-bottom: 40px; + display: flex; + align-items: center; + gap: 18px; + padding: 20px 24px; + background: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 20px; + box-shadow: var(--shadow-sm); .group-avatar.large { width: 80px; height: 80px; border-radius: 10px; overflow: hidden; - margin: 0 auto 16px; + margin: 0; + flex-shrink: 0; img { width: 100%; @@ -529,45 +536,64 @@ } } + .selected-group-meta { + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; + } + + .group-summary-label { + font-size: 12px; + color: var(--text-tertiary); + letter-spacing: 0.04em; + } + h2 { - font-size: 20px; + font-size: 22px; font-weight: 600; color: var(--text-primary); - margin-bottom: 4px; + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } p { color: var(--text-secondary); font-size: 14px; + margin: 0; } } .function-grid { - display: flex; - flex-wrap: wrap; - gap: 20px; - justify-content: center; + width: 100%; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 16px; } .function-card { - width: 140px; - padding: 24px 16px; - background: rgba(255, 255, 255, 0.15); + min-height: 148px; + padding: 20px 18px; + background: color-mix(in srgb, var(--card-bg) 92%, var(--bg-secondary)); border-radius: 16px; display: flex; flex-direction: column; - align-items: center; - gap: 12px; + align-items: flex-start; + justify-content: flex-start; + gap: 10px; cursor: pointer; transition: all 0.2s; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + box-shadow: var(--shadow-sm); backdrop-filter: blur(8px); - border: 1px solid rgba(255, 255, 255, 0.15); + border: 1px solid var(--border-color); + text-align: left; &:hover { transform: translateY(-2px); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); - background: rgba(255, 255, 255, 0.25); + box-shadow: var(--shadow-md); + background: color-mix(in srgb, var(--card-bg) 100%, var(--bg-hover)); } svg { @@ -575,10 +601,16 @@ } span { - font-size: 13px; - font-weight: 500; + font-size: 15px; + font-weight: 600; color: var(--text-primary); } + + small { + font-size: 12px; + line-height: 1.5; + color: var(--text-secondary); + } } } diff --git a/src/pages/GroupAnalyticsPage.tsx b/src/pages/GroupAnalyticsPage.tsx index ae372ad..9103f2f 100644 --- a/src/pages/GroupAnalyticsPage.tsx +++ b/src/pages/GroupAnalyticsPage.tsx @@ -62,9 +62,13 @@ function GroupAnalyticsPage() { const [groups, setGroups] = useState([]) const [filteredGroups, setFilteredGroups] = useState([]) const [isLoading, setIsLoading] = useState(true) - const [selectedGroup, setSelectedGroup] = useState(null) + const [selectedGroupId, setSelectedGroupId] = useState(null) const [selectedFunction, setSelectedFunction] = useState(null) const [searchQuery, setSearchQuery] = useState('') + const selectedGroup = useMemo( + () => (selectedGroupId ? groups.find(group => group.username === selectedGroupId) || null : null), + [groups, selectedGroupId] + ) // 功能数据 const [members, setMembers] = useState([]) @@ -274,7 +278,7 @@ function GroupAnalyticsPage() { preselectAppliedRef.current = true if (matchedGroup) { - setSelectedGroup(matchedGroup) + setSelectedGroupId(matchedGroup.username) setSelectedFunction(null) setSearchQuery('') } @@ -311,7 +315,7 @@ function GroupAnalyticsPage() { const handleChange = () => { setGroups([]) setFilteredGroups([]) - setSelectedGroup(null) + setSelectedGroupId(null) setSelectedFunction(null) setMembers([]) setRankings([]) @@ -325,15 +329,13 @@ function GroupAnalyticsPage() { }, [loadExportPath, loadGroups]) const handleGroupSelect = (group: GroupChatInfo) => { - if (selectedGroup?.username !== group.username) { - setSelectedGroup(group) - setSelectedFunction(null) - setSelectedExportMemberUsername('') - setMemberSearchKeyword('') - setShowMemberSelect(false) - setShowFormatSelect(false) - setShowDisplayNameSelect(false) - } + setSelectedGroupId(group.username) + setSelectedFunction(null) + setSelectedExportMemberUsername('') + setMemberSearchKeyword('') + setShowMemberSelect(false) + setShowFormatSelect(false) + setShowDisplayNameSelect(false) } @@ -343,12 +345,12 @@ function GroupAnalyticsPage() { await loadFunctionData(func) } - const loadFunctionData = async (func: AnalysisFunction) => { - if (!selectedGroup) return + const loadFunctionData = async (func: AnalysisFunction, targetGroup: GroupChatInfo | null = selectedGroup) => { + if (!targetGroup) return const taskId = registerBackgroundTask({ sourcePage: 'groupAnalytics', title: `群分析:${func}`, - detail: `正在读取 ${selectedGroup.displayName || selectedGroup.username} 的分析数据`, + detail: `正在读取 ${targetGroup.displayName || targetGroup.username} 的分析数据`, progressText: func, cancelable: true }) @@ -365,7 +367,7 @@ function GroupAnalyticsPage() { detail: '正在读取群成员列表', progressText: '成员列表' }) - const result = await window.electronAPI.groupAnalytics.getGroupMembers(selectedGroup.username) + const result = await window.electronAPI.groupAnalytics.getGroupMembers(targetGroup.username) if (isBackgroundTaskCancelRequested(taskId)) { finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群成员列表未继续写入' }) return @@ -382,7 +384,7 @@ function GroupAnalyticsPage() { detail: '正在读取导出成员列表', progressText: '成员导出' }) - const result = await window.electronAPI.groupAnalytics.getGroupMembers(selectedGroup.username) + const result = await window.electronAPI.groupAnalytics.getGroupMembers(targetGroup.username) if (isBackgroundTaskCancelRequested(taskId)) { finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,成员导出列表未继续写入' }) return @@ -399,7 +401,7 @@ function GroupAnalyticsPage() { detail: '正在计算群消息排行', progressText: '消息排行' }) - const result = await window.electronAPI.groupAnalytics.getGroupMessageRanking(selectedGroup.username, 20, startTime, endTime) + const result = await window.electronAPI.groupAnalytics.getGroupMessageRanking(targetGroup.username, 20, startTime, endTime) if (isBackgroundTaskCancelRequested(taskId)) { finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群消息排行未继续写入' }) return @@ -416,7 +418,7 @@ function GroupAnalyticsPage() { detail: '正在计算群活跃时段', progressText: '活跃时段' }) - const result = await window.electronAPI.groupAnalytics.getGroupActiveHours(selectedGroup.username, startTime, endTime) + const result = await window.electronAPI.groupAnalytics.getGroupActiveHours(targetGroup.username, startTime, endTime) if (isBackgroundTaskCancelRequested(taskId)) { finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群活跃时段未继续写入' }) return @@ -433,7 +435,7 @@ function GroupAnalyticsPage() { detail: '正在统计群消息类型', progressText: '消息类型' }) - const result = await window.electronAPI.groupAnalytics.getGroupMediaStats(selectedGroup.username, startTime, endTime) + const result = await window.electronAPI.groupAnalytics.getGroupMediaStats(targetGroup.username, startTime, endTime) if (isBackgroundTaskCancelRequested(taskId)) { finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群消息类型统计未继续写入' }) return @@ -770,7 +772,7 @@ function GroupAnalyticsPage() { filteredGroups.map(group => (
handleGroupSelect(group)} >
@@ -794,29 +796,37 @@ function GroupAnalyticsPage() {
-

{selectedGroup?.displayName}

-

{selectedGroup?.memberCount} 位成员

+
+ 已选择群聊 +

{selectedGroup?.displayName}

+

{selectedGroup?.memberCount} 位成员

+
handleFunctionSelect('members')}> 群成员查看 + 查看群成员列表和基础资料
handleFunctionSelect('memberExport')}> 成员消息导出 + 按成员筛选并导出群聊记录
handleFunctionSelect('ranking')}> 群聊发言排行 + 统计成员发言数量排行
handleFunctionSelect('activeHours')}> 群聊活跃时段 + 查看全天活跃时间分布
handleFunctionSelect('mediaStats')}> 媒体内容统计 + 统计文本、图片、语音等类型