feat: 优化MaterialSegmentView文件显示和添加打开目录功能
文件显示优化: - 提取文件名显示:从完整路径中提取文件名,避免显示过长的路径 - 处理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. 享受跨平台一致的用户体验
This commit is contained in:
parent
30a4cfc23f
commit
91eb22aaa9
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -185,3 +185,63 @@ pub fn select_file(app: AppHandle, filters: Option<Vec<(String, Vec<String>)>>)
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<MaterialSegmentViewProps> = ({ projec
|
|||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-medium text-gray-900 truncate">
|
||||
{segment.material_name || '未知素材'}
|
||||
</h4>
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="text-sm font-medium text-gray-900 truncate">
|
||||
{extractFileName(segment.segment.file_path)}
|
||||
</h4>
|
||||
{segment.segment.file_path && (
|
||||
<button
|
||||
onClick={() => openFileDirectory(segment.segment.file_path)}
|
||||
className="p-1 text-gray-400 hover:text-blue-600 transition-colors"
|
||||
title="打开文件所在目录"
|
||||
>
|
||||
<FolderOpen size={14} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{formatDuration(segment.segment.start_time)} - {formatDuration(segment.segment.end_time)}
|
||||
<span className="ml-2">时长: {formatDuration(segment.segment.duration)}</span>
|
||||
|
|
|
|||
Loading…
Reference in New Issue