修复群聊分析白屏

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; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; gap: 20px;
justify-content: center; padding: 24px;
padding: 32px; overflow-y: auto;
.selected-group-info { .selected-group-info {
text-align: center; display: flex;
margin-bottom: 40px; 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 { .group-avatar.large {
width: 80px; width: 80px;
height: 80px; height: 80px;
border-radius: 10px; border-radius: 10px;
overflow: hidden; overflow: hidden;
margin: 0 auto 16px; margin: 0;
flex-shrink: 0;
img { img {
width: 100%; 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 { h2 {
font-size: 20px; font-size: 22px;
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: var(--text-primary);
margin-bottom: 4px; margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
p { p {
color: var(--text-secondary); color: var(--text-secondary);
font-size: 14px; font-size: 14px;
margin: 0;
} }
} }
.function-grid { .function-grid {
display: flex; width: 100%;
flex-wrap: wrap; display: grid;
gap: 20px; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
justify-content: center; gap: 16px;
} }
.function-card { .function-card {
width: 140px; min-height: 148px;
padding: 24px 16px; padding: 20px 18px;
background: rgba(255, 255, 255, 0.15); background: color-mix(in srgb, var(--card-bg) 92%, var(--bg-secondary));
border-radius: 16px; border-radius: 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: flex-start;
gap: 12px; justify-content: flex-start;
gap: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); box-shadow: var(--shadow-sm);
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.15); border: 1px solid var(--border-color);
text-align: left;
&:hover { &:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); box-shadow: var(--shadow-md);
background: rgba(255, 255, 255, 0.25); background: color-mix(in srgb, var(--card-bg) 100%, var(--bg-hover));
} }
svg { svg {
@@ -575,10 +601,16 @@
} }
span { span {
font-size: 13px; font-size: 15px;
font-weight: 500; font-weight: 600;
color: var(--text-primary); 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 [groups, setGroups] = useState<GroupChatInfo[]>([])
const [filteredGroups, setFilteredGroups] = useState<GroupChatInfo[]>([]) const [filteredGroups, setFilteredGroups] = useState<GroupChatInfo[]>([])
const [isLoading, setIsLoading] = useState(true) 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 [selectedFunction, setSelectedFunction] = useState<AnalysisFunction | null>(null)
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const selectedGroup = useMemo(
() => (selectedGroupId ? groups.find(group => group.username === selectedGroupId) || null : null),
[groups, selectedGroupId]
)
// 功能数据 // 功能数据
const [members, setMembers] = useState<GroupMember[]>([]) const [members, setMembers] = useState<GroupMember[]>([])
@@ -274,7 +278,7 @@ function GroupAnalyticsPage() {
preselectAppliedRef.current = true preselectAppliedRef.current = true
if (matchedGroup) { if (matchedGroup) {
setSelectedGroup(matchedGroup) setSelectedGroupId(matchedGroup.username)
setSelectedFunction(null) setSelectedFunction(null)
setSearchQuery('') setSearchQuery('')
} }
@@ -311,7 +315,7 @@ function GroupAnalyticsPage() {
const handleChange = () => { const handleChange = () => {
setGroups([]) setGroups([])
setFilteredGroups([]) setFilteredGroups([])
setSelectedGroup(null) setSelectedGroupId(null)
setSelectedFunction(null) setSelectedFunction(null)
setMembers([]) setMembers([])
setRankings([]) setRankings([])
@@ -325,15 +329,13 @@ function GroupAnalyticsPage() {
}, [loadExportPath, loadGroups]) }, [loadExportPath, loadGroups])
const handleGroupSelect = (group: GroupChatInfo) => { const handleGroupSelect = (group: GroupChatInfo) => {
if (selectedGroup?.username !== group.username) { setSelectedGroupId(group.username)
setSelectedGroup(group) setSelectedFunction(null)
setSelectedFunction(null) setSelectedExportMemberUsername('')
setSelectedExportMemberUsername('') setMemberSearchKeyword('')
setMemberSearchKeyword('') setShowMemberSelect(false)
setShowMemberSelect(false) setShowFormatSelect(false)
setShowFormatSelect(false) setShowDisplayNameSelect(false)
setShowDisplayNameSelect(false)
}
} }
@@ -343,12 +345,12 @@ function GroupAnalyticsPage() {
await loadFunctionData(func) await loadFunctionData(func)
} }
const loadFunctionData = async (func: AnalysisFunction) => { const loadFunctionData = async (func: AnalysisFunction, targetGroup: GroupChatInfo | null = selectedGroup) => {
if (!selectedGroup) return if (!targetGroup) return
const taskId = registerBackgroundTask({ const taskId = registerBackgroundTask({
sourcePage: 'groupAnalytics', sourcePage: 'groupAnalytics',
title: `群分析:${func}`, title: `群分析:${func}`,
detail: `正在读取 ${selectedGroup.displayName || selectedGroup.username} 的分析数据`, detail: `正在读取 ${targetGroup.displayName || targetGroup.username} 的分析数据`,
progressText: func, progressText: func,
cancelable: true cancelable: true
}) })
@@ -365,7 +367,7 @@ function GroupAnalyticsPage() {
detail: '正在读取群成员列表', detail: '正在读取群成员列表',
progressText: '成员列表' progressText: '成员列表'
}) })
const result = await window.electronAPI.groupAnalytics.getGroupMembers(selectedGroup.username) const result = await window.electronAPI.groupAnalytics.getGroupMembers(targetGroup.username)
if (isBackgroundTaskCancelRequested(taskId)) { if (isBackgroundTaskCancelRequested(taskId)) {
finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群成员列表未继续写入' }) finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群成员列表未继续写入' })
return return
@@ -382,7 +384,7 @@ function GroupAnalyticsPage() {
detail: '正在读取导出成员列表', detail: '正在读取导出成员列表',
progressText: '成员导出' progressText: '成员导出'
}) })
const result = await window.electronAPI.groupAnalytics.getGroupMembers(selectedGroup.username) const result = await window.electronAPI.groupAnalytics.getGroupMembers(targetGroup.username)
if (isBackgroundTaskCancelRequested(taskId)) { if (isBackgroundTaskCancelRequested(taskId)) {
finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,成员导出列表未继续写入' }) finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,成员导出列表未继续写入' })
return return
@@ -399,7 +401,7 @@ function GroupAnalyticsPage() {
detail: '正在计算群消息排行', detail: '正在计算群消息排行',
progressText: '消息排行' 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)) { if (isBackgroundTaskCancelRequested(taskId)) {
finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群消息排行未继续写入' }) finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群消息排行未继续写入' })
return return
@@ -416,7 +418,7 @@ function GroupAnalyticsPage() {
detail: '正在计算群活跃时段', detail: '正在计算群活跃时段',
progressText: '活跃时段' 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)) { if (isBackgroundTaskCancelRequested(taskId)) {
finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群活跃时段未继续写入' }) finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群活跃时段未继续写入' })
return return
@@ -433,7 +435,7 @@ function GroupAnalyticsPage() {
detail: '正在统计群消息类型', detail: '正在统计群消息类型',
progressText: '消息类型' 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)) { if (isBackgroundTaskCancelRequested(taskId)) {
finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群消息类型统计未继续写入' }) finishBackgroundTask(taskId, 'canceled', { detail: '已停止后续加载,群消息类型统计未继续写入' })
return return
@@ -770,7 +772,7 @@ function GroupAnalyticsPage() {
filteredGroups.map(group => ( filteredGroups.map(group => (
<div <div
key={group.username} key={group.username}
className={`group-item ${selectedGroup?.username === group.username ? 'active' : ''}`} className={`group-item ${selectedGroupId === group.username ? 'active' : ''}`}
onClick={() => handleGroupSelect(group)} onClick={() => handleGroupSelect(group)}
> >
<div className="group-avatar"> <div className="group-avatar">
@@ -794,29 +796,37 @@ function GroupAnalyticsPage() {
<div className="group-avatar large"> <div className="group-avatar large">
<Avatar src={selectedGroup?.avatarUrl} name={selectedGroup?.displayName} size={80} /> <Avatar src={selectedGroup?.avatarUrl} name={selectedGroup?.displayName} size={80} />
</div> </div>
<h2>{selectedGroup?.displayName}</h2> <div className="selected-group-meta">
<p>{selectedGroup?.memberCount} </p> <span className="group-summary-label"></span>
<h2>{selectedGroup?.displayName}</h2>
<p>{selectedGroup?.memberCount} </p>
</div>
</div> </div>
<div className="function-grid"> <div className="function-grid">
<div className="function-card" onClick={() => handleFunctionSelect('members')}> <div className="function-card" onClick={() => handleFunctionSelect('members')}>
<Users size={32} /> <Users size={32} />
<span></span> <span></span>
<small></small>
</div> </div>
<div className="function-card" onClick={() => handleFunctionSelect('memberExport')}> <div className="function-card" onClick={() => handleFunctionSelect('memberExport')}>
<Download size={32} /> <Download size={32} />
<span></span> <span></span>
<small></small>
</div> </div>
<div className="function-card" onClick={() => handleFunctionSelect('ranking')}> <div className="function-card" onClick={() => handleFunctionSelect('ranking')}>
<BarChart3 size={32} /> <BarChart3 size={32} />
<span></span> <span></span>
<small></small>
</div> </div>
<div className="function-card" onClick={() => handleFunctionSelect('activeHours')}> <div className="function-card" onClick={() => handleFunctionSelect('activeHours')}>
<Clock size={32} /> <Clock size={32} />
<span></span> <span></span>
<small></small>
</div> </div>
<div className="function-card" onClick={() => handleFunctionSelect('mediaStats')}> <div className="function-card" onClick={() => handleFunctionSelect('mediaStats')}>
<Image size={32} /> <Image size={32} />
<span></span> <span></span>
<small></small>
</div> </div>
</div> </div>
</div> </div>