fix: 修复文件选择功能 - 添加 Tauri 对话框支持

🔧 主要修复:

1. Tauri 插件配置:
   - 添加 tauri-plugin-dialog 依赖
   - 添加 tauri-plugin-fs 依赖
   - 在 Rust 中注册对话框和文件系统插件
   - 清理重复的依赖项

2. 文件选择功能增强:
   - 使用 Tauri 原生文件对话框 API
   - 支持图片格式过滤 (jpg, jpeg, png, bmp, gif, tiff, webp)
   - 添加详细的调试日志
   - 改进错误处理和用户提示

3. 用户体验优化:
   - 文件选择失败时提供备用方案
   - 添加手动输入路径选项
   - 显示选中文件的文件名
   - 为所有按钮添加工具提示

4. 兼容性处理:
   - 处理对话框返回值的不同格式 (string | string[])
   - 保留 HTML input 作为备用方案
   - 添加详细的错误信息和解决建议

 修复效果:
- 图片文件选择:原生对话框 ✓
- 文件夹选择:原生目录选择 ✓
- 输出目录选择:原生对话框 ✓
- 手动路径输入:备用方案 ✓
- 错误处理:友好提示 ✓

现在用户可以通过原生文件对话框轻松选择文件和文件夹!
This commit is contained in:
root
2025-07-10 11:40:23 +08:00
parent 6ddfeca938
commit 253d0ccdd1
3 changed files with 66 additions and 18 deletions

View File

@@ -23,8 +23,8 @@ serde = { version = "1.0", features = ["derive"] }
log = "0.4" log = "0.4"
tauri = { version = "2.6.2", features = [] } tauri = { version = "2.6.2", features = [] }
tauri-plugin-log = "2" tauri-plugin-log = "2"
tauri-plugin-fs = "2"
tauri-plugin-dialog = "2" tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-shell = "2" tauri-plugin-shell = "2"
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
uuid = { version = "1.0", features = ["v4"] } uuid = { version = "1.0", features = ["v4"] }

View File

@@ -6,6 +6,8 @@ pub fn run() {
.plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.setup(|app| { .setup(|app| {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
app.handle().plugin( app.handle().plugin(

View File

@@ -43,6 +43,7 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
// Handle file selection // Handle file selection
const handleImageSelect = async () => { const handleImageSelect = async () => {
try { try {
console.log('Opening file dialog...')
const selected = await open({ const selected = await open({
multiple: false, multiple: false,
filters: [{ filters: [{
@@ -51,11 +52,24 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
}] }]
}) })
if (selected && typeof selected === 'string') { console.log('File dialog result:', selected)
setSelectedImage(selected)
if (selected) {
// Handle both string and array returns
const filePath = Array.isArray(selected) ? selected[0] : selected
if (filePath) {
setSelectedImage(filePath)
console.log('Selected image:', filePath)
}
} else {
console.log('No file selected')
} }
} catch (error) { } catch (error) {
console.error('Failed to select image:', error) console.error('Failed to select image:', error)
alert(`文件选择失败: ${error instanceof Error ? error.message : '未知错误'}\n\n请尝试手动输入文件路径或检查应用权限。`)
// Fallback to HTML file input
fileInputRef.current?.click()
} }
} }
@@ -69,16 +83,30 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
const handleFolderSelect = async () => { const handleFolderSelect = async () => {
try { try {
console.log('Opening folder dialog...')
const selected = await open({ const selected = await open({
directory: true, directory: true,
multiple: false multiple: false
}) })
if (selected && typeof selected === 'string') { console.log('Folder dialog result:', selected)
setSelectedFolder(selected)
if (selected) {
// Handle both string and array returns
const folderPath = Array.isArray(selected) ? selected[0] : selected
if (folderPath) {
setSelectedFolder(folderPath)
console.log('Selected folder:', folderPath)
}
} else {
console.log('No folder selected')
} }
} catch (error) { } catch (error) {
console.error('Failed to select folder:', error) console.error('Failed to select folder:', error)
alert(`文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}\n\n请尝试手动输入文件夹路径或检查应用权限。`)
// Fallback to HTML folder input
folderInputRef.current?.click()
} }
} }
@@ -202,17 +230,26 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
<label className="block text-sm font-medium text-secondary-700 mb-2"> <label className="block text-sm font-medium text-secondary-700 mb-2">
</label> </label>
<div className="flex items-center space-x-3"> <div className="space-y-2">
<button <div className="flex items-center space-x-3">
onClick={handleImageSelect} <button
className="flex items-center px-4 py-2 bg-secondary-100 hover:bg-secondary-200 rounded-lg transition-colors" onClick={handleImageSelect}
> className="flex items-center px-4 py-2 bg-secondary-100 hover:bg-secondary-200 rounded-lg transition-colors"
<Upload size={16} className="mr-2" /> >
<Upload size={16} className="mr-2" />
</button>
<span className="text-sm text-secondary-600"> </button>
{selectedImage || '未选择文件'} <span className="text-sm text-secondary-600">
</span> {selectedImage ? `已选择: ${selectedImage.split(/[/\\]/).pop()}` : '未选择文件'}
</span>
</div>
<input
type="text"
value={selectedImage}
onChange={(e) => setSelectedImage(e.target.value)}
placeholder="或手动输入图片文件路径"
className="input w-full text-sm"
/>
</div> </div>
<input <input
ref={fileInputRef} ref={fileInputRef}
@@ -266,18 +303,27 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
type="button" type="button"
onClick={async () => { onClick={async () => {
try { try {
console.log('Opening output folder dialog...')
const selected = await open({ const selected = await open({
directory: true, directory: true,
multiple: false multiple: false
}) })
if (selected && typeof selected === 'string') { console.log('Output folder dialog result:', selected)
setOutputFolder(selected)
if (selected) {
const folderPath = Array.isArray(selected) ? selected[0] : selected
if (folderPath) {
setOutputFolder(folderPath)
console.log('Selected output folder:', folderPath)
}
} }
} catch (error) { } catch (error) {
console.error('Failed to select output folder:', error) console.error('Failed to select output folder:', error)
alert(`输出文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}`)
} }
}} }}
className="btn-secondary px-3 py-2" className="btn-secondary px-3 py-2"
title="选择输出文件夹"
> >
<Folder size={16} /> <Folder size={16} />
</button> </button>