mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 19:47:08 +08:00
perf: 优化聊天渲染与后台任务性能
This commit is contained in:
@@ -330,7 +330,7 @@ function App(): React.ReactElement {
|
||||
usernames.length ? 55 : 90
|
||||
)
|
||||
let loadedCount = 0
|
||||
const chunkSize = 8
|
||||
const chunkSize = 32
|
||||
for (let index = 0; index < usernames.length; index += chunkSize) {
|
||||
if (runId !== contactAvatarHydrationRunRef.current) return
|
||||
const chunk = usernames.slice(index, index + chunkSize)
|
||||
@@ -361,7 +361,7 @@ function App(): React.ReactElement {
|
||||
} catch (error) {
|
||||
console.warn('[Contacts] avatar hydrate failed:', error)
|
||||
}
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 150))
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 50))
|
||||
}
|
||||
if (usernames.length) onProgress?.('头像加载完成', 90)
|
||||
}
|
||||
|
||||
@@ -1143,6 +1143,20 @@ body {
|
||||
background: #f0f2ef;
|
||||
}
|
||||
|
||||
.virtual-message-list {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.virtual-message-group {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flow-root;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wechat-message-row {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
@@ -7061,7 +7075,9 @@ body {
|
||||
border: 1.5px solid var(--wxex-border);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
background 0.2s;
|
||||
}
|
||||
|
||||
.report-template-item.active {
|
||||
@@ -7114,7 +7130,10 @@ body {
|
||||
background: #fff;
|
||||
color: var(--wxex-text-primary, #1f2933);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
||||
transition:
|
||||
background 0.2s,
|
||||
color 0.2s,
|
||||
border-color 0.2s;
|
||||
}
|
||||
|
||||
.report-template-preview-btn:hover {
|
||||
|
||||
@@ -20,7 +20,6 @@ interface ChatWindowProps {
|
||||
isAiLoading?: boolean
|
||||
}
|
||||
|
||||
const MAX_RENDERED_MESSAGES = 600
|
||||
const DATE_RANGE_LABELS: Record<string, string> = {
|
||||
today: '今天',
|
||||
yesterday: '昨日',
|
||||
@@ -242,12 +241,6 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
return typeMatch && contentMatch
|
||||
})
|
||||
}, [messages, contentFilter])
|
||||
const hiddenMessageCount = Math.max(0, filteredMessages.length - MAX_RENDERED_MESSAGES)
|
||||
const renderedMessages = React.useMemo(
|
||||
() => filteredMessages.slice(-MAX_RENDERED_MESSAGES),
|
||||
[filteredMessages]
|
||||
)
|
||||
|
||||
if (!contact) return <EmptyConversationState />
|
||||
|
||||
const dateRangeLabel = getChatHeaderRangeLabel(dateRange)
|
||||
@@ -272,8 +265,8 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
<DataTrustBar messageCount={messages.length} />
|
||||
<MessageList
|
||||
contact={contact}
|
||||
messages={renderedMessages}
|
||||
hiddenMessageCount={hiddenMessageCount}
|
||||
messages={filteredMessages}
|
||||
hiddenMessageCount={0}
|
||||
isLoadingMessages={isLoadingMessages}
|
||||
isGroupChat={isGroupChat}
|
||||
showAvatar={showAvatar}
|
||||
@@ -283,7 +276,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
onImageClick={openImagePreview}
|
||||
/>
|
||||
<ChatStatusBar
|
||||
count={renderedMessages.length}
|
||||
count={filteredMessages.length}
|
||||
showAvatar={showAvatar}
|
||||
isAtLatest={isAtLatest}
|
||||
onShowAvatarChange={setShowAvatar}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useVirtualizer } from '@tanstack/react-virtual'
|
||||
import { Contact, Message } from '../../../../shared/types'
|
||||
import { MessageGroup } from './MessageGroup'
|
||||
import { buildMessageGroups } from './messageGrouping'
|
||||
@@ -29,6 +30,16 @@ export function MessageList({
|
||||
onImageClick
|
||||
}: MessageListProps): React.ReactElement {
|
||||
const groups = React.useMemo(() => buildMessageGroups(messages), [messages])
|
||||
// TanStack Virtual intentionally exposes mutable measurement methods.
|
||||
// eslint-disable-next-line react-hooks/incompatible-library
|
||||
const virtualizer = useVirtualizer({
|
||||
count: groups.length,
|
||||
getScrollElement: () => listRef.current,
|
||||
estimateSize: () => 96,
|
||||
getItemKey: (index) => groups[index]?.id || index,
|
||||
overscan: 8
|
||||
})
|
||||
const virtualItems = virtualizer.getVirtualItems()
|
||||
|
||||
return (
|
||||
<div className="message-list wechat-message-list" ref={listRef} onScroll={onScroll}>
|
||||
@@ -40,16 +51,29 @@ export function MessageList({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{groups.map((group) => (
|
||||
<MessageGroup
|
||||
key={group.id}
|
||||
group={group}
|
||||
contact={contact}
|
||||
isGroupChat={isGroupChat}
|
||||
showAvatar={showAvatar}
|
||||
onImageClick={onImageClick}
|
||||
/>
|
||||
))}
|
||||
<div className="virtual-message-list" style={{ height: `${virtualizer.getTotalSize()}px` }}>
|
||||
{virtualItems.map((virtualItem) => {
|
||||
const group = groups[virtualItem.index]
|
||||
if (!group) return null
|
||||
return (
|
||||
<div
|
||||
key={virtualItem.key}
|
||||
ref={virtualizer.measureElement}
|
||||
data-index={virtualItem.index}
|
||||
className="virtual-message-group"
|
||||
style={{ transform: `translateY(${virtualItem.start}px)` }}
|
||||
>
|
||||
<MessageGroup
|
||||
group={group}
|
||||
contact={contact}
|
||||
isGroupChat={isGroupChat}
|
||||
showAvatar={showAvatar}
|
||||
onImageClick={onImageClick}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -195,8 +195,9 @@ export function AgentHubWorkspace(): React.ReactElement {
|
||||
<h2>微信数据助手</h2>
|
||||
<p>机器人通过本机 Agent Hub 调用 WechatExplorer,不向公网暴露数据库。</p>
|
||||
<div className="agent-hub-example">
|
||||
<span>可以这样问</span>
|
||||
<span>支持自然语言,可以这样问</span>
|
||||
<strong>“最近 5 条消息是谁?”</strong>
|
||||
<strong>“帮我看看最近跟xx聊了些什么”</strong>
|
||||
<strong>“生成产品交流群今天的群聊总结图片”</strong>
|
||||
</div>
|
||||
<ul>
|
||||
@@ -212,6 +213,10 @@ export function AgentHubWorkspace(): React.ReactElement {
|
||||
<i />
|
||||
消息重复保护
|
||||
</li>
|
||||
<li>
|
||||
<i />
|
||||
使用已配置 AI 理解自然语言
|
||||
</li>
|
||||
<li>
|
||||
<i className={status.dataApi === 'online' ? '' : 'offline'} />
|
||||
本地数据 API:{status.dataApi === 'online' ? '已连接' : '未连接'}
|
||||
|
||||
Reference in New Issue
Block a user