修复群聊分析白屏

This commit is contained in:
xuncha
2026-03-16 17:12:12 +08:00
parent bf48e865ac
commit d730ae5bef
2 changed files with 88 additions and 46 deletions

View File

@@ -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);
}
}
}

View File

@@ -62,9 +62,13 @@ function GroupAnalyticsPage() {
const [groups, setGroups] = useState<GroupChatInfo[]>([])
const [filteredGroups, setFilteredGroups] = useState<GroupChatInfo[]>([])
const [isLoading, setIsLoading] = useState(true)
const [selectedGroup, setSelectedGroup] = useState<GroupChatInfo | null>(null)
const [selectedGroupId, setSelectedGroupId] = useState<string | null>(null)
const [selectedFunction, setSelectedFunction] = useState<AnalysisFunction | null>(null)
const [searchQuery, setSearchQuery] = useState('')
const selectedGroup = useMemo(
() => (selectedGroupId ? groups.find(group => group.username === selectedGroupId) || null : null),
[groups, selectedGroupId]
)
// 功能数据
const [members, setMembers] = useState<GroupMember[]>([])
@@ -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 => (
<div
key={group.username}
className={`group-item ${selectedGroup?.username === group.username ? 'active' : ''}`}
className={`group-item ${selectedGroupId === group.username ? 'active' : ''}`}
onClick={() => handleGroupSelect(group)}
>
<div className="group-avatar">
@@ -794,29 +796,37 @@ function GroupAnalyticsPage() {
<div className="group-avatar large">
<Avatar src={selectedGroup?.avatarUrl} name={selectedGroup?.displayName} size={80} />
</div>
<h2>{selectedGroup?.displayName}</h2>
<p>{selectedGroup?.memberCount} </p>
<div className="selected-group-meta">
<span className="group-summary-label"></span>
<h2>{selectedGroup?.displayName}</h2>
<p>{selectedGroup?.memberCount} </p>
</div>
</div>
<div className="function-grid">
<div className="function-card" onClick={() => handleFunctionSelect('members')}>
<Users size={32} />
<span></span>
<small></small>
</div>
<div className="function-card" onClick={() => handleFunctionSelect('memberExport')}>
<Download size={32} />
<span></span>
<small></small>
</div>
<div className="function-card" onClick={() => handleFunctionSelect('ranking')}>
<BarChart3 size={32} />
<span></span>
<small></small>
</div>
<div className="function-card" onClick={() => handleFunctionSelect('activeHours')}>
<Clock size={32} />
<span></span>
<small></small>
</div>
<div className="function-card" onClick={() => handleFunctionSelect('mediaStats')}>
<Image size={32} />
<span></span>
<small></small>
</div>
</div>
</div>