fix: 直接修复 Tauri 对话框插件,移除回退机制
🎯 核心修复: 1. 正确实现 Tauri 对话框命令: - 使用 tauri_plugin_dialog::DialogExt 正确的 API - 实现 select_image_file 和 select_folder 命令 - 使用 blocking_pick_file 和 blocking_pick_folder 方法 - 修复路径转换问题 (FilePath.to_string()) 2. 简化前端文件选择逻辑: - 移除复杂的多重备用方案 - 直接调用 Tauri 命令 invoke('select_image_file') - 清晰的错误处理,不再回退到 HTML input - 统一的文件和文件夹选择逻辑 3. 清理代码: - 移除不需要的 HTML input 元素 - 移除 useRef 和相关的备用代码 - 移除 @tauri-apps/plugin-dialog 直接导入 - 清理未使用的导入和函数 4. 配置优化: - 确保 Tauri 对话框插件正确注册 - 移除不必要的配置复杂性 - 专注于核心功能实现 ✅ 修复效果: - 文件选择:直接使用原生 Tauri 对话框 ✓ - 路径获取:确保返回完整的文件路径 ✓ - 代码简洁:移除复杂的回退逻辑 ✓ - 错误处理:清晰的错误信息 ✓ 现在文件选择功能应该直接工作,返回完整的文件路径!
This commit is contained in:
parent
0a3b369685
commit
a6dbd37526
|
|
@ -385,19 +385,48 @@ pub async fn test_ai_video_environment() -> Result<String, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn select_image_file() -> Result<String, String> {
|
pub async fn select_image_file(app: tauri::AppHandle) -> Result<String, String> {
|
||||||
|
use tauri_plugin_dialog::DialogExt;
|
||||||
|
|
||||||
println!("Opening image file dialog...");
|
println!("Opening image file dialog...");
|
||||||
|
|
||||||
// For now, return an error to force fallback to plugin dialog
|
let file_path = app.dialog()
|
||||||
// This will be implemented when the dialog plugin is properly configured
|
.file()
|
||||||
Err("Custom dialog not implemented yet".to_string())
|
.add_filter("Images", &["jpg", "jpeg", "png", "bmp", "gif", "tiff", "webp"])
|
||||||
|
.blocking_pick_file();
|
||||||
|
|
||||||
|
match file_path {
|
||||||
|
Some(path) => {
|
||||||
|
let path_str = path.to_string();
|
||||||
|
println!("Selected image file: {}", path_str);
|
||||||
|
Ok(path_str)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
println!("No file selected");
|
||||||
|
Err("No file selected".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn select_folder() -> Result<String, String> {
|
pub async fn select_folder(app: tauri::AppHandle) -> Result<String, String> {
|
||||||
|
use tauri_plugin_dialog::{DialogExt};
|
||||||
|
|
||||||
println!("Opening folder dialog...");
|
println!("Opening folder dialog...");
|
||||||
|
|
||||||
// For now, return an error to force fallback to plugin dialog
|
let folder_path = app.dialog()
|
||||||
// This will be implemented when the dialog plugin is properly configured
|
.file()
|
||||||
Err("Custom dialog not implemented yet".to_string())
|
.blocking_pick_folder();
|
||||||
|
|
||||||
|
match folder_path {
|
||||||
|
Some(path) => {
|
||||||
|
let path_str = path.to_string();
|
||||||
|
println!("Selected folder: {}", path_str);
|
||||||
|
Ok(path_str)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
println!("No folder selected");
|
||||||
|
Err("No folder selected".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useState, useRef } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Upload, Play, Settings, Folder, FileText, Clock, Cpu, Trash2, Download } from 'lucide-react'
|
import { Upload, Play, Folder, FileText, Clock, Cpu, Trash2, Download } from 'lucide-react'
|
||||||
import { useAIVideoStore, useAIVideoJobs, useAIVideoProcessing, useAIVideoSettings } from '../stores/useAIVideoStore'
|
import { useAIVideoStore, useAIVideoJobs, useAIVideoProcessing, useAIVideoSettings } from '../stores/useAIVideoStore'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
interface AIVideoGeneratorProps {
|
interface AIVideoGeneratorProps {
|
||||||
|
|
@ -9,8 +8,6 @@ interface AIVideoGeneratorProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) => {
|
const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
||||||
const folderInputRef = useRef<HTMLInputElement>(null)
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const [mode, setMode] = useState<'single' | 'batch'>('single')
|
const [mode, setMode] = useState<'single' | 'batch'>('single')
|
||||||
|
|
@ -45,115 +42,30 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
|
||||||
const handleImageSelect = async () => {
|
const handleImageSelect = async () => {
|
||||||
try {
|
try {
|
||||||
console.log('Opening file dialog...')
|
console.log('Opening file dialog...')
|
||||||
|
|
||||||
// Try using custom Tauri command first
|
|
||||||
try {
|
|
||||||
const filePath = await invoke('select_image_file') as string
|
const filePath = await invoke('select_image_file') as string
|
||||||
console.log('Selected image via Tauri command:', filePath)
|
console.log('Selected image:', filePath)
|
||||||
setSelectedImage(filePath)
|
setSelectedImage(filePath)
|
||||||
return
|
|
||||||
} catch (tauriError) {
|
|
||||||
console.log('Tauri command failed, trying plugin dialog:', tauriError)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both methods fail, use HTML input
|
|
||||||
throw new Error('All Tauri methods failed')
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('All file selection methods failed:', error)
|
console.error('Failed to select image:', error)
|
||||||
console.log('Falling back to HTML file input')
|
alert(`文件选择失败: ${error instanceof Error ? error.message : '未知错误'}`)
|
||||||
|
|
||||||
// Fallback to HTML file input
|
|
||||||
fileInputRef.current?.click()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const file = e.target.files?.[0]
|
|
||||||
if (file) {
|
|
||||||
// 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 () => {
|
const handleFolderSelect = async () => {
|
||||||
try {
|
try {
|
||||||
console.log('Opening folder dialog...')
|
console.log('Opening folder dialog...')
|
||||||
|
|
||||||
// Try using custom Tauri command first
|
|
||||||
try {
|
|
||||||
const folderPath = await invoke('select_folder') as string
|
const folderPath = await invoke('select_folder') as string
|
||||||
console.log('Selected folder via Tauri command:', folderPath)
|
console.log('Selected folder:', folderPath)
|
||||||
setSelectedFolder(folderPath)
|
setSelectedFolder(folderPath)
|
||||||
return
|
|
||||||
} catch (tauriError) {
|
|
||||||
console.log('Tauri command failed, trying plugin dialog:', tauriError)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('All folder selection methods failed')
|
|
||||||
|
|
||||||
} 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请尝试手动输入文件夹路径或检查应用权限。`)
|
alert(`文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}`)
|
||||||
|
|
||||||
// Fallback to HTML folder input
|
|
||||||
folderInputRef.current?.click()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFolderChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
// This is now unused but kept for compatibility
|
|
||||||
const files = e.target.files
|
|
||||||
if (files && files.length > 0) {
|
|
||||||
const firstFile = files[0]
|
|
||||||
const path = (firstFile as any).webkitRelativePath || firstFile.name
|
|
||||||
const folderPath = path.split('/')[0]
|
|
||||||
setSelectedFolder(folderPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle generation
|
// Handle generation
|
||||||
const handleGenerate = async () => {
|
const handleGenerate = async () => {
|
||||||
|
|
@ -308,13 +220,7 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input
|
|
||||||
ref={fileInputRef}
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
onChange={handleImageChange}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -334,14 +240,7 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
|
||||||
{selectedFolder || '未选择文件夹'}
|
{selectedFolder || '未选择文件夹'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
|
||||||
ref={folderInputRef}
|
|
||||||
type="file"
|
|
||||||
{...({ webkitdirectory: "" } as any)}
|
|
||||||
multiple
|
|
||||||
onChange={handleFolderChange}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -361,19 +260,9 @@ const AIVideoGenerator: React.FC<AIVideoGeneratorProps> = ({ className = '' }) =
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
console.log('Opening output folder dialog...')
|
console.log('Opening output folder dialog...')
|
||||||
const selected = await open({
|
const folderPath = await invoke('select_folder') as string
|
||||||
directory: true,
|
|
||||||
multiple: false
|
|
||||||
})
|
|
||||||
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)
|
console.log('Selected output folder:', folderPath)
|
||||||
}
|
setOutputFolder(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 : '未知错误'}`)
|
alert(`输出文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}`)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue