From abe9cfac94f2b3249a2c93cd9c639fa536ecb6fe Mon Sep 17 00:00:00 2001 From: imeepos Date: Wed, 30 Jul 2025 14:53:28 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A8=A1=E7=89=B9?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5=E7=85=A7=E7=89=87=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建imagePathUtils工具函数,智能处理本地路径和云端URL - 修复ModelImageGallery组件中图片路径处理逻辑 - 修复ModelImagePreviewModal组件中图片路径处理逻辑 - 云端URL(https://)直接使用,本地路径通过convertFileSrc转换 - 添加完整的单元测试覆盖路径处理逻辑 解决问题:模特详情页照片管理部分图片无法显示 原因:对所有路径都使用convertFileSrc,但该函数只适用于本地路径 --- .../src/components/ModelImageGallery.tsx | 8 ++- .../src/components/ModelImagePreviewModal.tsx | 8 ++- .../utils/__tests__/imagePathUtils.test.ts | 68 +++++++++++++++++++ apps/desktop/src/utils/imagePathUtils.ts | 33 +++++++++ 4 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 apps/desktop/src/utils/__tests__/imagePathUtils.test.ts create mode 100644 apps/desktop/src/utils/imagePathUtils.ts diff --git a/apps/desktop/src/components/ModelImageGallery.tsx b/apps/desktop/src/components/ModelImageGallery.tsx index 3acaefa..6fdd4d6 100644 --- a/apps/desktop/src/components/ModelImageGallery.tsx +++ b/apps/desktop/src/components/ModelImageGallery.tsx @@ -9,10 +9,10 @@ import { Search } from 'lucide-react'; import { ModelPhoto, PhotoType } from '../types/model'; -import { convertFileSrc } from '@tauri-apps/api/core'; import { ModelImageUploader } from './ModelImageUploader'; import { ModelImagePreviewModal } from './ModelImagePreviewModal'; import { DeleteConfirmDialog } from './DeleteConfirmDialog'; +import { getImageSrc } from '../utils/imagePathUtils'; interface ModelImageGalleryProps { photos: ModelPhoto[]; @@ -175,6 +175,8 @@ export const ModelImageGallery: React.FC = ({ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; + + return (
{/* 头部工具栏 */} @@ -277,7 +279,7 @@ export const ModelImageGallery: React.FC = ({ {/* 图片 */}
{photo.file_name} @@ -346,7 +348,7 @@ export const ModelImageGallery: React.FC = ({ {/* 缩略图 */}
{photo.file_name} diff --git a/apps/desktop/src/components/ModelImagePreviewModal.tsx b/apps/desktop/src/components/ModelImagePreviewModal.tsx index 21d0236..8f59e37 100644 --- a/apps/desktop/src/components/ModelImagePreviewModal.tsx +++ b/apps/desktop/src/components/ModelImagePreviewModal.tsx @@ -17,7 +17,7 @@ import { Tag } from 'lucide-react'; import { ModelPhoto, PhotoType } from '../types/model'; -import { convertFileSrc } from '@tauri-apps/api/core'; +import { getImageSrc } from '../utils/imagePathUtils'; interface ModelImagePreviewModalProps { photos: ModelPhoto[]; @@ -181,9 +181,11 @@ export const ModelImagePreviewModal: React.FC = ({ return new Date(dateString).toLocaleString('zh-CN'); }; + + if (!isOpen || !currentPhoto) return null; - const imageUrl = convertFileSrc(currentPhoto.file_path); + const imageUrl = getImageSrc(currentPhoto.file_path); return (
= ({ }`} > {photo.file_name} diff --git a/apps/desktop/src/utils/__tests__/imagePathUtils.test.ts b/apps/desktop/src/utils/__tests__/imagePathUtils.test.ts new file mode 100644 index 0000000..018cda0 --- /dev/null +++ b/apps/desktop/src/utils/__tests__/imagePathUtils.test.ts @@ -0,0 +1,68 @@ +import { describe, it, expect, vi } from 'vitest'; +import { getImageSrc, isCloudUrl, isLocalPath } from '../imagePathUtils'; + +// Mock Tauri API +vi.mock('@tauri-apps/api/core', () => ({ + convertFileSrc: vi.fn((path: string) => `tauri://localhost${path}`) +})); + +describe('imagePathUtils', () => { + describe('isCloudUrl', () => { + it('应该正确识别HTTPS URL', () => { + expect(isCloudUrl('https://cdn.roasmax.cn/models/photos/model-1/image.jpg')).toBe(true); + }); + + it('应该正确识别HTTP URL', () => { + expect(isCloudUrl('http://example.com/image.jpg')).toBe(true); + }); + + it('应该正确识别本地路径', () => { + expect(isCloudUrl('/local/path/to/image.jpg')).toBe(false); + expect(isCloudUrl('C:\\Windows\\path\\to\\image.jpg')).toBe(false); + expect(isCloudUrl('./relative/path/image.jpg')).toBe(false); + }); + }); + + describe('isLocalPath', () => { + it('应该正确识别本地路径', () => { + expect(isLocalPath('/local/path/to/image.jpg')).toBe(true); + expect(isLocalPath('C:\\Windows\\path\\to\\image.jpg')).toBe(true); + expect(isLocalPath('./relative/path/image.jpg')).toBe(true); + }); + + it('应该正确识别云端URL', () => { + expect(isLocalPath('https://cdn.roasmax.cn/models/photos/model-1/image.jpg')).toBe(false); + expect(isLocalPath('http://example.com/image.jpg')).toBe(false); + }); + }); + + describe('getImageSrc', () => { + it('应该直接返回HTTPS URL', () => { + const cloudUrl = 'https://cdn.roasmax.cn/models/photos/model-1/image.jpg'; + expect(getImageSrc(cloudUrl)).toBe(cloudUrl); + }); + + it('应该直接返回HTTP URL', () => { + const cloudUrl = 'http://example.com/image.jpg'; + expect(getImageSrc(cloudUrl)).toBe(cloudUrl); + }); + + it('应该转换本地路径', () => { + const localPath = '/local/path/to/image.jpg'; + const result = getImageSrc(localPath); + expect(result).toBe('tauri://localhost/local/path/to/image.jpg'); + }); + + it('应该转换Windows本地路径', () => { + const localPath = 'C:\\Windows\\path\\to\\image.jpg'; + const result = getImageSrc(localPath); + expect(result).toBe('tauri://localhostC:\\Windows\\path\\to\\image.jpg'); + }); + + it('应该转换相对路径', () => { + const localPath = './relative/path/image.jpg'; + const result = getImageSrc(localPath); + expect(result).toBe('tauri://localhost./relative/path/image.jpg'); + }); + }); +}); diff --git a/apps/desktop/src/utils/imagePathUtils.ts b/apps/desktop/src/utils/imagePathUtils.ts new file mode 100644 index 0000000..2834494 --- /dev/null +++ b/apps/desktop/src/utils/imagePathUtils.ts @@ -0,0 +1,33 @@ +import { convertFileSrc } from '@tauri-apps/api/core'; + +/** + * 处理图片路径,区分本地路径和云端URL + * @param filePath 文件路径或URL + * @returns 处理后的图片源地址 + */ +export const getImageSrc = (filePath: string): string => { + // 如果是云端URL(以http://或https://开头),直接返回 + if (filePath.startsWith('http://') || filePath.startsWith('https://')) { + return filePath; + } + // 如果是本地路径,使用convertFileSrc转换 + return convertFileSrc(filePath); +}; + +/** + * 检查路径是否为云端URL + * @param filePath 文件路径 + * @returns 是否为云端URL + */ +export const isCloudUrl = (filePath: string): boolean => { + return filePath.startsWith('http://') || filePath.startsWith('https://'); +}; + +/** + * 检查路径是否为本地文件路径 + * @param filePath 文件路径 + * @returns 是否为本地路径 + */ +export const isLocalPath = (filePath: string): boolean => { + return !isCloudUrl(filePath); +};