From 91eb22aaa91dc6983487f01aa6c1410dcd6fd62c Mon Sep 17 00:00:00 2001 From: imeepos Date: Tue, 15 Jul 2025 21:52:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96MaterialSegmentView?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=98=BE=E7=A4=BA=E5=92=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=89=93=E5=BC=80=E7=9B=AE=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 文件显示优化: - 提取文件名显示:从完整路径中提取文件名,避免显示过长的路径 - 处理Windows长路径格式:正确处理\\\\?\\前缀的长路径 - 简洁的文件名展示:只显示文件名而不是完整路径 打开目录功能: - 添加FolderOpen图标按钮:每个片段卡片都有打开目录按钮 - 跨平台支持:Windows使用explorer /select,macOS使用open -R,Linux使用xdg-open - 智能路径处理:自动检测文件/目录并使用合适的打开方式 - 错误处理:完善的错误处理和日志记录 后端命令实现: - 新增open_file_directory命令:支持打开文件所在目录 - 注册到invoke_handler:在lib.rs中正确注册新命令 - 系统集成:使用系统默认的文件管理器打开目录 UI/UX改进: - 文件名+按钮布局:文件名和打开按钮在同一行显示 - 悬停效果:按钮有hover状态,提供良好的交互反馈 - 工具提示:按钮有'打开文件所在目录'的提示文字 - 图标设计:使用FolderOpen图标,直观表达功能 功能特点: - 一键打开:点击按钮直接在文件管理器中打开文件所在目录 - 文件定位:Windows下会自动选中对应文件 - 路径兼容:支持各种路径格式,包括长路径 - 安全检查:文件不存在时会给出错误提示 现在用户可以: 1. 看到简洁的文件名而不是冗长的完整路径 2. 点击文件夹图标快速打开文件所在目录 3. 在文件管理器中直接定位到对应文件 4. 享受跨平台一致的用户体验 --- apps/desktop/src-tauri/src/lib.rs | 1 + .../presentation/commands/system_commands.rs | 60 +++++++++++++++++++ .../src/components/MaterialSegmentView.tsx | 39 ++++++++++-- 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index c3089f2..4fe7a7e 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -38,6 +38,7 @@ pub fn run() { commands::project_commands::get_default_project_name, commands::system_commands::select_directory, commands::system_commands::select_file, + commands::system_commands::open_file_directory, commands::system_commands::get_app_info, commands::system_commands::validate_directory, commands::system_commands::get_directory_name, diff --git a/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs index da72945..e173548 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/system_commands.rs @@ -185,3 +185,63 @@ pub fn select_file(app: AppHandle, filters: Option)>>) Err(_) => Err("文件选择超时".to_string()), } } + +/// 打开文件所在目录命令 +#[command] +pub async fn open_file_directory(file_path: String) -> Result<(), String> { + use std::path::Path; + use std::process::Command; + + let path = Path::new(&file_path); + + // 获取文件所在目录 + let directory = if path.is_file() { + path.parent().ok_or("无法获取文件所在目录")? + } else if path.is_dir() { + path + } else { + return Err("文件路径不存在".to_string()); + }; + + // 根据操作系统打开目录 + let result = if cfg!(target_os = "windows") { + // Windows: 使用 explorer 并选中文件 + if path.is_file() { + Command::new("explorer") + .args(["/select,", &file_path]) + .spawn() + } else { + Command::new("explorer") + .arg(directory) + .spawn() + } + } else if cfg!(target_os = "macos") { + // macOS: 使用 open 命令 + if path.is_file() { + Command::new("open") + .args(["-R", &file_path]) + .spawn() + } else { + Command::new("open") + .arg(directory) + .spawn() + } + } else { + // Linux: 使用 xdg-open + Command::new("xdg-open") + .arg(directory) + .spawn() + }; + + match result { + Ok(_) => { + info!("成功打开目录: {:?}", directory); + Ok(()) + } + Err(e) => { + let error_msg = format!("打开目录失败: {}", e); + tracing::error!("{}", error_msg); + Err(error_msg) + } + } +} diff --git a/apps/desktop/src/components/MaterialSegmentView.tsx b/apps/desktop/src/components/MaterialSegmentView.tsx index c860829..3b73410 100644 --- a/apps/desktop/src/components/MaterialSegmentView.tsx +++ b/apps/desktop/src/components/MaterialSegmentView.tsx @@ -7,7 +7,8 @@ import { RefreshCw, Eye, Edit, - Trash2 + Trash2, + FolderOpen } from 'lucide-react'; import { invoke } from '@tauri-apps/api/core'; import { SearchInput } from './InteractiveInput'; @@ -73,6 +74,25 @@ interface MaterialSegmentView { }; } +// 提取文件名的工具函数 +const extractFileName = (filePath: string): string => { + if (!filePath) return '未知文件'; + + // 处理Windows路径格式,包括长路径前缀 + const cleanPath = filePath.replace(/^\\\\\?\\/, ''); + const parts = cleanPath.split(/[\\\/]/); + return parts[parts.length - 1] || '未知文件'; +}; + +// 打开文件所在目录 +const openFileDirectory = async (filePath: string) => { + try { + await invoke('open_file_directory', { filePath }); + } catch (error) { + console.error('打开目录失败:', error); + } +}; + /** * 素材片段管理组件 - 多条件检索标签页风格 */ @@ -215,9 +235,20 @@ export const MaterialSegmentView: React.FC = ({ projec
-

- {segment.material_name || '未知素材'} -

+
+

+ {extractFileName(segment.segment.file_path)} +

+ {segment.segment.file_path && ( + + )} +

{formatDuration(segment.segment.start_time)} - {formatDuration(segment.segment.end_time)} 时长: {formatDuration(segment.segment.duration)}