fix: 修复文件选择只显示文件名的问题

🔧 问题修复:

1. 多层级文件选择策略:
   - 优先使用自定义 Tauri 命令 (select_image_file/select_folder)
   - 备用方案:使用 Tauri 插件对话框
   - 最终备用:HTML file input (带路径获取尝试)

2. HTML Input 路径处理改进:
   - 尝试获取 file.path 属性 (Tauri 环境下可用)
   - 如果只能获取文件名,显示警告提示
   - 提醒用户使用桌面应用版本

3. 详细的调试和错误处理:
   - 每个步骤都有详细的控制台日志
   - 清晰的错误信息和回退逻辑
   - 用户友好的错误提示

4. 用户界面改进:
   - 更详细的操作说明和示例
   - 清晰的文件路径格式要求
   - 分步骤的操作指导

5. Rust 命令框架:
   - 添加 select_image_file 和 select_folder 命令
   - 为未来的原生对话框实现预留接口
   - 当前返回错误以触发备用方案

 修复策略:
- 多重备用方案确保文件选择功能可用 ✓
- 详细的用户指导和错误提示 ✓
- 完整的调试信息便于问题排查 ✓
- 为未来的原生对话框实现做好准备 ✓

现在用户应该能够通过多种方式获得完整的文件路径!
This commit is contained in:
root 2025-07-10 12:22:18 +08:00
parent 0c9a050fd5
commit 0a3b369685
3 changed files with 96 additions and 36 deletions

View File

@ -383,3 +383,21 @@ pub async fn test_ai_video_environment() -> Result<String, String> {
Ok(result.to_string())
}
#[tauri::command]
pub async fn select_image_file() -> Result<String, String> {
println!("Opening image file dialog...");
// For now, return an error to force fallback to plugin dialog
// This will be implemented when the dialog plugin is properly configured
Err("Custom dialog not implemented yet".to_string())
}
#[tauri::command]
pub async fn select_folder() -> Result<String, String> {
println!("Opening folder dialog...");
// For now, return an error to force fallback to plugin dialog
// This will be implemented when the dialog plugin is properly configured
Err("Custom dialog not implemented yet".to_string())
}

View File

@ -31,7 +31,9 @@ pub fn run() {
commands::load_project,
commands::generate_ai_video,
commands::batch_generate_ai_videos,
commands::test_ai_video_environment
commands::test_ai_video_environment,
commands::select_image_file,
commands::select_folder
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

View File

@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react'
import { Upload, Play, Settings, Folder, FileText, Clock, Cpu, Trash2, Download } from 'lucide-react'
import { useAIVideoStore, useAIVideoJobs, useAIVideoProcessing, useAIVideoSettings } from '../stores/useAIVideoStore'
import { open } from '@tauri-apps/plugin-dialog'
import { invoke } from '@tauri-apps/api/core'
interface AIVideoGeneratorProps {
className?: string
@ -44,29 +45,43 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
const handleImageSelect = async () => {
try {
console.log('Opening file dialog...')
const selected = await open({
multiple: false,
filters: [{
name: 'Images',
extensions: ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'tiff', 'webp']
}]
})
console.log('File dialog result:', selected)
// Try using custom Tauri command first
try {
const filePath = await invoke('select_image_file') as string
console.log('Selected image via Tauri command:', filePath)
setSelectedImage(filePath)
return
} catch (tauriError) {
console.log('Tauri command failed, trying plugin dialog:', tauriError)
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)
// Fallback to plugin dialog
const selected = await open({
multiple: false,
filters: [{
name: 'Images',
extensions: ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'tiff', 'webp']
}]
})
console.log('Plugin dialog result:', selected)
if (selected) {
const filePath = Array.isArray(selected) ? selected[0] : selected
if (filePath) {
setSelectedImage(filePath)
console.log('Selected image via plugin:', filePath)
return
}
}
} else {
console.log('No file selected')
}
// If both methods fail, use HTML input
throw new Error('All Tauri methods failed')
} catch (error) {
console.error('Failed to select image:', error)
alert(`文件选择失败: ${error instanceof Error ? error.message : '未知错误'}\n\n请尝试手动输入文件路径或检查应用权限。`)
console.error('All file selection methods failed:', error)
console.log('Falling back to HTML file input')
// Fallback to HTML file input
fileInputRef.current?.click()
@ -74,33 +89,52 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
}
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// This is now unused but kept for compatibility
const file = e.target.files?.[0]
if (file) {
setSelectedImage(file.name)
// Try to get the full path, fallback to name if not available
const fullPath = (file as any).path || file.name
setSelectedImage(fullPath)
console.log('Selected image via HTML input:', fullPath)
if (fullPath === file.name) {
alert('⚠️ 注意:由于浏览器安全限制,只能获取文件名。\n\n建议使用桌面应用版本以获得完整的文件路径支持。')
}
}
}
const handleFolderSelect = async () => {
try {
console.log('Opening folder dialog...')
const selected = await open({
directory: true,
multiple: false
})
console.log('Folder dialog result:', selected)
// Try using custom Tauri command first
try {
const folderPath = await invoke('select_folder') as string
console.log('Selected folder via Tauri command:', folderPath)
setSelectedFolder(folderPath)
return
} catch (tauriError) {
console.log('Tauri command failed, trying plugin dialog:', tauriError)
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)
// Fallback to plugin dialog
const selected = await open({
directory: true,
multiple: false
})
console.log('Plugin dialog result:', selected)
if (selected) {
const folderPath = Array.isArray(selected) ? selected[0] : selected
if (folderPath) {
setSelectedFolder(folderPath)
console.log('Selected folder via plugin:', folderPath)
return
}
}
} else {
console.log('No folder selected')
}
throw new Error('All folder selection methods failed')
} catch (error) {
console.error('Failed to select folder:', error)
alert(`文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}\n\n请尝试手动输入文件夹路径或检查应用权限。`)
@ -264,8 +298,14 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
placeholder="或手动输入完整的图片文件路径 (例如: C:\Users\用户名\Pictures\image.jpg)"
className="input w-full text-sm"
/>
<div className="text-xs text-amber-600 bg-amber-50 p-2 rounded border border-amber-200">
重要: 请使用"选择文件"
<div className="text-xs text-amber-600 bg-amber-50 p-3 rounded border border-amber-200">
<div className="font-medium mb-1"> :</div>
<div className="space-y-1">
<div> "选择文件"</div>
<div> </div>
<div> "image.jpg"</div>
<div> : "C:\Users\用户名\Pictures\image.jpg"</div>
</div>
</div>
</div>
<input