From ee684021dbf8b0051685c2f2a7d5be4c9d76417f Mon Sep 17 00:00:00 2001 From: xuncha <1658671838@qq.com> Date: Tue, 17 Mar 2026 23:11:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=89=87=E8=A7=A3=E5=AF=86=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/services/imageDecryptService.ts | 31 +++++++++++++++++- src/pages/ChatPage.tsx | 41 +++++++++++------------- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/electron/services/imageDecryptService.ts b/electron/services/imageDecryptService.ts index 9ad2c25..64c0e0a 100644 --- a/electron/services/imageDecryptService.ts +++ b/electron/services/imageDecryptService.ts @@ -154,11 +154,40 @@ export class ImageDecryptService { async decryptImage(payload: { sessionId?: string; imageMd5?: string; imageDatName?: string; force?: boolean }): Promise { await this.ensureCacheIndexed() - const cacheKey = payload.imageMd5 || payload.imageDatName + const cacheKeys = this.getCacheKeys(payload) + const cacheKey = cacheKeys[0] if (!cacheKey) { return { success: false, error: '缺少图片标识' } } + if (payload.force) { + for (const key of cacheKeys) { + const cached = this.resolvedCache.get(key) + if (cached && existsSync(cached) && this.isImageFile(cached) && !this.isThumbnailPath(cached)) { + this.cacheResolvedPaths(cacheKey, payload.imageMd5, payload.imageDatName, cached) + this.clearUpdateFlags(cacheKey, payload.imageMd5, payload.imageDatName) + const dataUrl = this.fileToDataUrl(cached) + const localPath = dataUrl || this.filePathToUrl(cached) + this.emitCacheResolved(payload, cacheKey, localPath) + return { success: true, localPath } + } + if (cached && !this.isImageFile(cached)) { + this.resolvedCache.delete(key) + } + } + + for (const key of cacheKeys) { + const existingHd = this.findCachedOutput(key, true, payload.sessionId) + if (!existingHd || this.isThumbnailPath(existingHd)) continue + this.cacheResolvedPaths(cacheKey, payload.imageMd5, payload.imageDatName, existingHd) + this.clearUpdateFlags(cacheKey, payload.imageMd5, payload.imageDatName) + const dataUrl = this.fileToDataUrl(existingHd) + const localPath = dataUrl || this.filePathToUrl(existingHd) + this.emitCacheResolved(payload, cacheKey, localPath) + return { success: true, localPath } + } + } + if (!payload.force) { const cached = this.resolvedCache.get(cacheKey) if (cached && existsSync(cached) && this.isImageFile(cached)) { diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx index 3ff0417..1454e37 100644 --- a/src/pages/ChatPage.tsx +++ b/src/pages/ChatPage.tsx @@ -6489,6 +6489,10 @@ function MessageBubble({ return 'image/jpeg' }, []) + const getImageObserverRoot = useCallback((): Element | null => { + return imageContainerRef.current?.closest('.message-list') ?? null + }, []) + // 获取头像首字母 const getAvatarLetter = (name: string): string => { if (!name) return '?' @@ -6793,34 +6797,25 @@ function MessageBubble({ const observer = new IntersectionObserver( (entries) => { const entry = entries[0] - // rootMargin 设置为 200px,提前触发解密 - if (entry.isIntersecting && !imageAutoDecryptTriggered.current) { - imageAutoDecryptTriggered.current = true - void requestImageDecrypt() - } - }, - { rootMargin: '200px', threshold: 0 } - ) - - observer.observe(container) - return () => observer.disconnect() - }, [isImage, imageLocalPath, message.imageMd5, message.imageDatName, requestImageDecrypt]) - - // 进入视野时自动尝试切换高清图 - useEffect(() => { - if (!isImage) return - const container = imageContainerRef.current - if (!container) return - const observer = new IntersectionObserver( - (entries) => { - const entry = entries[0] + // rootMargin 设置为 200px,提前感知即将进入视野的图片 setImageInView(entry.isIntersecting) }, - { rootMargin: '120px', threshold: 0 } + { root: getImageObserverRoot(), rootMargin: '200px', threshold: 0 } ) + observer.observe(container) return () => observer.disconnect() - }, [isImage]) + }, [getImageObserverRoot, isImage]) + + // 进入视野后自动触发一次普通解密 + useEffect(() => { + if (!isImage || !imageInView) return + if (imageLocalPath || imageLoading) return + if (!message.imageMd5 && !message.imageDatName) return + if (imageAutoDecryptTriggered.current) return + imageAutoDecryptTriggered.current = true + void requestImageDecrypt() + }, [isImage, imageInView, imageLocalPath, imageLoading, message.imageMd5, message.imageDatName, requestImageDecrypt]) useEffect(() => { if (!isImage || !imageHasUpdate || !imageInView) return