fix: 修复模特详情页照片管理图片显示问题
- 创建imagePathUtils工具函数,智能处理本地路径和云端URL - 修复ModelImageGallery组件中图片路径处理逻辑 - 修复ModelImagePreviewModal组件中图片路径处理逻辑 - 云端URL(https://)直接使用,本地路径通过convertFileSrc转换 - 添加完整的单元测试覆盖路径处理逻辑 解决问题:模特详情页照片管理部分图片无法显示 原因:对所有路径都使用convertFileSrc,但该函数只适用于本地路径
This commit is contained in:
parent
616ff39812
commit
abe9cfac94
|
|
@ -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<ModelImageGalleryProps> = ({
|
|||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className={`space-y-6 ${className}`}>
|
||||
{/* 头部工具栏 */}
|
||||
|
|
@ -277,7 +279,7 @@ export const ModelImageGallery: React.FC<ModelImageGalleryProps> = ({
|
|||
{/* 图片 */}
|
||||
<div className="aspect-square overflow-hidden">
|
||||
<img
|
||||
src={convertFileSrc(photo.file_path)}
|
||||
src={getImageSrc(photo.file_path)}
|
||||
alt={photo.file_name}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-200"
|
||||
/>
|
||||
|
|
@ -346,7 +348,7 @@ export const ModelImageGallery: React.FC<ModelImageGalleryProps> = ({
|
|||
{/* 缩略图 */}
|
||||
<div className="w-16 h-16 rounded-lg overflow-hidden flex-shrink-0">
|
||||
<img
|
||||
src={convertFileSrc(photo.file_path)}
|
||||
src={getImageSrc(photo.file_path)}
|
||||
alt={photo.file_name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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<ModelImagePreviewModalProps> = ({
|
|||
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 (
|
||||
<div className={`fixed inset-0 z-50 flex items-center justify-center bg-black ${
|
||||
|
|
@ -441,7 +443,7 @@ export const ModelImagePreviewModal: React.FC<ModelImagePreviewModalProps> = ({
|
|||
}`}
|
||||
>
|
||||
<img
|
||||
src={convertFileSrc(photo.file_path)}
|
||||
src={getImageSrc(photo.file_path)}
|
||||
alt={photo.file_name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
};
|
||||
Loading…
Reference in New Issue