diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e33f4e2..22d8988 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,4 +28,5 @@ tauri-plugin-fs = "2" tauri-plugin-shell = "2" tokio = { version = "1.0", features = ["full"] } uuid = { version = "1.0", features = ["v4"] } +base64 = "0.21" anyhow = "1.0" diff --git a/src-tauri/src/commands/file_utils.rs b/src-tauri/src/commands/file_utils.rs new file mode 100644 index 0000000..64ad135 --- /dev/null +++ b/src-tauri/src/commands/file_utils.rs @@ -0,0 +1,42 @@ +use std::fs; +use std::path::Path; +use base64::{Engine as _, engine::general_purpose}; +use tauri::command; + +/// 读取图片文件并返回base64编码的数据URL +#[command] +pub async fn read_image_as_data_url(file_path: String) -> Result { + let path = Path::new(&file_path); + + // 检查文件是否存在 + if !path.exists() { + return Err("File does not exist".to_string()); + } + + // 检查是否是图片文件 + let extension = path.extension() + .and_then(|ext| ext.to_str()) + .map(|ext| ext.to_lowercase()) + .unwrap_or_default(); + + let mime_type = match extension.as_str() { + "jpg" | "jpeg" => "image/jpeg", + "png" => "image/png", + "gif" => "image/gif", + "bmp" => "image/bmp", + "webp" => "image/webp", + "svg" => "image/svg+xml", + _ => return Err("Unsupported image format".to_string()), + }; + + // 读取文件内容 + match fs::read(&file_path) { + Ok(file_data) => { + // 编码为base64 + let base64_data = general_purpose::STANDARD.encode(&file_data); + // 返回data URL格式 + Ok(format!("data:{};base64,{}", mime_type, base64_data)) + } + Err(e) => Err(format!("Failed to read file: {}", e)), + } +} diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index be7d297..852f235 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -5,6 +5,7 @@ pub mod ai_video; pub mod file_system; pub mod project; pub mod template; +pub mod file_utils; pub mod resource_category; // Re-export all commands for easy access diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 800a245..db3a0cb 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -55,7 +55,8 @@ pub fn run() { commands::update_project, commands::delete_project, commands::search_projects, - commands::open_project_directory + commands::open_project_directory, + commands::file_utils::read_image_as_data_url ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 67fc6b3..9dd5fbc 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -30,8 +30,6 @@ const Sidebar: React.FC = () => { { path: '/settings', icon: Settings, label: '设置' }, ] - const isAIVideoPage = location.pathname === '/ai-video' - const getStatusIcon = (status: string) => { switch (status) { case 'processing': return @@ -61,24 +59,6 @@ const Sidebar: React.FC = () => { > 导航 - {isAIVideoPage && ( - - )} diff --git a/src/pages/ProjectManagePage.tsx b/src/pages/ProjectManagePage.tsx index 4a72b22..dad2642 100644 --- a/src/pages/ProjectManagePage.tsx +++ b/src/pages/ProjectManagePage.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import { Plus, Edit, Trash2, Search, Save, X, FolderOpen, Package } from 'lucide-react' import { open } from '@tauri-apps/plugin-dialog' -import { convertFileSrc } from '@tauri-apps/api/core' +import { invoke } from '@tauri-apps/api/core' import { ProjectService, Project } from '../services/projectService' const ProjectManagePage: React.FC = () => { @@ -17,16 +17,67 @@ const ProjectManagePage: React.FC = () => { product_image: '' }) - // 辅助函数:转换图片路径为Tauri可访问的URL - const getImageSrc = (imagePath: string): string => { - if (!imagePath) return '' - if (imagePath.startsWith('http')) return imagePath - try { - return convertFileSrc(imagePath) - } catch (error) { - console.error('Failed to convert file src:', error) - return '' + // 图片组件:处理异步图片加载 + const ImageComponent: React.FC<{ + imagePath: string + alt: string + className: string + onError?: () => void + }> = ({ imagePath, alt, className, onError }) => { + const [imageSrc, setImageSrc] = useState('') + const [loading, setLoading] = useState(true) + + useEffect(() => { + const loadImage = async () => { + if (!imagePath) { + setLoading(false) + return + } + + if (imagePath.startsWith('http') || imagePath.startsWith('data:')) { + setImageSrc(imagePath) + setLoading(false) + return + } + + try { + const dataUrl = await invoke('read_image_as_data_url', { filePath: imagePath }) + setImageSrc(dataUrl) + } catch (error) { + console.error('Failed to read image:', error) + onError?.() + } finally { + setLoading(false) + } + } + + loadImage() + }, [imagePath]) + + if (loading) { + return ( +
+
加载中...
+
+ ) } + + if (!imageSrc) { + return ( +
+ +
+ ) + } + + return ( + {alt} + ) } useEffect(() => { @@ -219,30 +270,11 @@ const ProjectManagePage: React.FC = () => {
{/* 商品图片 */}
- {project.product_image ? ( - <> - {project.product_name { - const target = e.target as HTMLImageElement - target.style.display = 'none' - const fallback = target.parentElement?.querySelector('.image-fallback') as HTMLElement - if (fallback) { - fallback.style.display = 'flex' - } - }} - /> -
- -
- - ) : ( -
- -
- )} +
@@ -411,23 +443,12 @@ const ProjectManagePage: React.FC = () => { {formData.product_image && (

预览:

-
- + { - const target = e.target as HTMLImageElement - target.style.display = 'none' - const fallback = target.parentElement?.querySelector('.fallback-text') as HTMLElement - if (fallback) { - fallback.style.display = 'block' - } - }} /> -
- 无法预览 -
)}