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"
tauri = { version = "2.6.2", features = [] }
tauri-plugin-log = "2"
tauri-plugin-fs = "2"
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-shell = "2"
tokio = { version = "1.0", features = ["full"] }
uuid = { version = "1.0", features = ["v4"] }

View File

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

View File

@@ -43,6 +43,7 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
// Handle file selection
const handleImageSelect = async () => {
try {
console.log('Opening file dialog...')
const selected = await open({
multiple: false,
filters: [{
@@ -51,11 +52,24 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
}]
})
if (selected && typeof selected === 'string') {
setSelectedImage(selected)
console.log('File dialog result:', 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) {
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 () => {
try {
console.log('Opening folder dialog...')
const selected = await open({
directory: true,
multiple: false
})
if (selected && typeof selected === 'string') {
setSelectedFolder(selected)
console.log('Folder dialog result:', 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) {
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>
<div className="flex items-center space-x-3">
<button
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" />
</button>
<span className="text-sm text-secondary-600">
{selectedImage || '未选择文件'}
</span>
<div className="space-y-2">
<div className="flex items-center space-x-3">
<button
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" />
</button>
<span className="text-sm text-secondary-600">
{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>
<input
ref={fileInputRef}
@@ -266,18 +303,27 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
type="button"
onClick={async () => {
try {
console.log('Opening output folder dialog...')
const selected = await open({
directory: true,
multiple: false
})
if (selected && typeof selected === 'string') {
setOutputFolder(selected)
console.log('Output folder dialog result:', selected)
if (selected) {
const folderPath = Array.isArray(selected) ? selected[0] : selected
if (folderPath) {
setOutputFolder(folderPath)
console.log('Selected output folder:', folderPath)
}
}
} catch (error) {
console.error('Failed to select output folder:', error)
alert(`输出文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}`)
}
}}
className="btn-secondary px-3 py-2"
title="选择输出文件夹"
>
<Folder size={16} />
</button>