feat: 实现全局目录设置功能
- 添加目录设置模态框,支持管理所有导入导出的默认目录 - 实现自动记忆目录功能,可开启/关闭 - 优化目录设置开关的视觉反馈,增加明显的状态区别 - 使用统一的Modal组件替代自定义弹框,确保正确渲染到modal-root - 在各个导入导出界面添加目录设置按钮: * 素材导入对话框 * 模板匹配结果管理(剪影导出) * 导出记录管理 * 批量模板导入 * 项目详情页面 * 模板管理页面 - 修改相关导出功能使用新的目录选择命令 - 创建QuickDirectoryButton组件用于快速目录选择 - 改进DirectorySettingsButton的视觉效果和交互体验
This commit is contained in:
@@ -723,6 +723,9 @@ pub async fn select_material_folders(app_handle: tauri::AppHandle) -> Result<Vec
|
|||||||
let default_dir = service.get_directory_setting(DirectorySettingType::MaterialImport)
|
let default_dir = service.get_directory_setting(DirectorySettingType::MaterialImport)
|
||||||
.unwrap_or(None);
|
.unwrap_or(None);
|
||||||
|
|
||||||
|
// 添加调试日志
|
||||||
|
tracing::info!("素材导入默认目录: {:?}", default_dir);
|
||||||
|
|
||||||
// 使用带选项的目录选择功能
|
// 使用带选项的目录选择功能
|
||||||
match select_directory_with_options(
|
match select_directory_with_options(
|
||||||
app_handle,
|
app_handle,
|
||||||
|
|||||||
@@ -34,9 +34,15 @@ pub fn select_directory_with_options(
|
|||||||
|
|
||||||
// 设置默认目录
|
// 设置默认目录
|
||||||
if let Some(default_dir) = default_directory {
|
if let Some(default_dir) = default_directory {
|
||||||
|
tracing::info!("尝试设置默认目录: {}", default_dir);
|
||||||
if std::path::Path::new(&default_dir).exists() {
|
if std::path::Path::new(&default_dir).exists() {
|
||||||
|
tracing::info!("默认目录存在,设置成功: {}", default_dir);
|
||||||
dialog = dialog.set_directory(&default_dir);
|
dialog = dialog.set_directory(&default_dir);
|
||||||
|
} else {
|
||||||
|
tracing::warn!("默认目录不存在: {}", default_dir);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
tracing::info!("没有设置默认目录");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一个简单的阻塞实现
|
// 创建一个简单的阻塞实现
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ const DirectorySettingsButton: React.FC<DirectorySettingsButtonProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
primary: 'bg-blue-600 text-white hover:bg-blue-700 shadow-sm hover:shadow-md',
|
||||||
secondary: 'bg-gray-200 text-gray-700 hover:bg-gray-300',
|
secondary: 'bg-gray-200 text-gray-700 hover:bg-gray-300 border border-gray-300 hover:border-gray-400',
|
||||||
ghost: 'bg-transparent text-gray-600 hover:bg-gray-100',
|
ghost: 'bg-transparent text-gray-600 hover:bg-gray-100 hover:text-gray-800',
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconSizes = {
|
const iconSizes = {
|
||||||
@@ -38,14 +38,15 @@ const DirectorySettingsButton: React.FC<DirectorySettingsButtonProps> = ({
|
|||||||
<button
|
<button
|
||||||
onClick={() => setIsModalOpen(true)}
|
onClick={() => setIsModalOpen(true)}
|
||||||
className={`
|
className={`
|
||||||
flex items-center space-x-2 rounded transition-colors
|
flex items-center space-x-2 rounded transition-all duration-200
|
||||||
|
hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
||||||
${sizeClasses[size]}
|
${sizeClasses[size]}
|
||||||
${variantClasses[variant]}
|
${variantClasses[variant]}
|
||||||
${className}
|
${className}
|
||||||
`}
|
`}
|
||||||
title="目录设置"
|
title="目录设置 - 管理导入导出的默认目录"
|
||||||
>
|
>
|
||||||
<Settings className={iconSizes[size]} />
|
<Settings className={`${iconSizes[size]} transition-transform duration-200 group-hover:rotate-90`} />
|
||||||
<span>目录设置</span>
|
<span>目录设置</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
144
apps/desktop/src/components/DirectorySettingsDemo.tsx
Normal file
144
apps/desktop/src/components/DirectorySettingsDemo.tsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import DirectorySettingsButton from './DirectorySettingsButton';
|
||||||
|
import QuickDirectoryButton from './QuickDirectoryButton';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
|
const DirectorySettingsDemo: React.FC = () => {
|
||||||
|
const [selectedDirectory, setSelectedDirectory] = useState<string>('');
|
||||||
|
const [autoRememberStatus, setAutoRememberStatus] = useState<boolean | null>(null);
|
||||||
|
|
||||||
|
const checkAutoRememberStatus = async () => {
|
||||||
|
try {
|
||||||
|
const status = await invoke<boolean>('is_auto_remember_enabled');
|
||||||
|
setAutoRememberStatus(status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查自动记忆状态失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDirectorySelected = (directory: string) => {
|
||||||
|
setSelectedDirectory(directory);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
checkAutoRememberStatus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 max-w-4xl mx-auto">
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900 mb-6">目录设置功能演示</h1>
|
||||||
|
|
||||||
|
{/* 状态显示 */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-6">
|
||||||
|
<h2 className="text-lg font-semibold text-gray-800 mb-3">当前状态</h2>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="text-sm text-gray-600">自动记忆功能:</span>
|
||||||
|
<span className={`text-sm font-medium px-2 py-1 rounded ${
|
||||||
|
autoRememberStatus === true
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: autoRememberStatus === false
|
||||||
|
? 'bg-red-100 text-red-800'
|
||||||
|
: 'bg-gray-100 text-gray-600'
|
||||||
|
}`}>
|
||||||
|
{autoRememberStatus === true ? '已启用' : autoRememberStatus === false ? '已关闭' : '检查中...'}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={checkAutoRememberStatus}
|
||||||
|
className="text-xs text-blue-600 hover:text-blue-800 underline"
|
||||||
|
>
|
||||||
|
刷新状态
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{selectedDirectory && (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="text-sm text-gray-600">最后选择的目录:</span>
|
||||||
|
<span className="text-sm font-mono bg-gray-100 px-2 py-1 rounded">
|
||||||
|
{selectedDirectory}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 按钮演示 */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
{/* 目录设置按钮 */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800 mb-3">目录设置按钮</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
|
点击打开目录设置对话框,可以管理所有导入导出的默认目录
|
||||||
|
</p>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<span className="text-sm text-gray-500 w-16">小号:</span>
|
||||||
|
<DirectorySettingsButton size="sm" variant="primary" />
|
||||||
|
<DirectorySettingsButton size="sm" variant="secondary" />
|
||||||
|
<DirectorySettingsButton size="sm" variant="ghost" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<span className="text-sm text-gray-500 w-16">中号:</span>
|
||||||
|
<DirectorySettingsButton size="md" variant="primary" />
|
||||||
|
<DirectorySettingsButton size="md" variant="secondary" />
|
||||||
|
<DirectorySettingsButton size="md" variant="ghost" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<span className="text-sm text-gray-500 w-16">大号:</span>
|
||||||
|
<DirectorySettingsButton size="lg" variant="primary" />
|
||||||
|
<DirectorySettingsButton size="lg" variant="secondary" />
|
||||||
|
<DirectorySettingsButton size="lg" variant="ghost" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 快速目录选择按钮 */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800 mb-3">快速目录选择</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
|
直接选择特定类型的目录,会自动保存到对应的设置中
|
||||||
|
</p>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<QuickDirectoryButton
|
||||||
|
settingType="material_import"
|
||||||
|
onDirectorySelected={handleDirectorySelected}
|
||||||
|
size="md"
|
||||||
|
variant="primary"
|
||||||
|
/>
|
||||||
|
<QuickDirectoryButton
|
||||||
|
settingType="template_import"
|
||||||
|
onDirectorySelected={handleDirectorySelected}
|
||||||
|
size="md"
|
||||||
|
variant="secondary"
|
||||||
|
/>
|
||||||
|
<QuickDirectoryButton
|
||||||
|
settingType="jianying_export"
|
||||||
|
onDirectorySelected={handleDirectorySelected}
|
||||||
|
size="md"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
<QuickDirectoryButton
|
||||||
|
settingType="project_export"
|
||||||
|
onDirectorySelected={handleDirectorySelected}
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
|
showLabel={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 使用说明 */}
|
||||||
|
<div className="bg-blue-50 rounded-lg border border-blue-200 p-4 mt-6">
|
||||||
|
<h3 className="text-lg font-semibold text-blue-900 mb-2">使用说明</h3>
|
||||||
|
<ul className="text-sm text-blue-800 space-y-1">
|
||||||
|
<li>• <strong>目录设置按钮</strong>:打开完整的目录设置对话框,可以管理所有类型的默认目录</li>
|
||||||
|
<li>• <strong>快速目录选择</strong>:直接选择特定类型的目录,立即保存到设置中</li>
|
||||||
|
<li>• <strong>自动记忆功能</strong>:启用后,每次选择文件时会自动更新对应的默认目录</li>
|
||||||
|
<li>• <strong>视觉反馈</strong>:开关状态有明显的颜色和图标区别,便于识别当前状态</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectorySettingsDemo;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { X, Folder, Settings, RotateCcw, Save } from 'lucide-react';
|
import { Folder, Settings, RotateCcw } from 'lucide-react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { Modal } from './Modal';
|
||||||
|
|
||||||
interface DirectorySettings {
|
interface DirectorySettings {
|
||||||
material_import_directory: string | null;
|
material_import_directory: string | null;
|
||||||
@@ -146,104 +147,131 @@ const DirectorySettingsModal: React.FC<DirectorySettingsModalProps> = ({
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<Modal
|
||||||
<div className="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[80vh] overflow-hidden">
|
isOpen={isOpen}
|
||||||
{/* 标题栏 */}
|
onClose={onClose}
|
||||||
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
title="目录设置"
|
||||||
<div className="flex items-center space-x-2">
|
subtitle="管理导入导出的默认目录"
|
||||||
<Settings className="w-5 h-5 text-blue-600" />
|
icon={<Settings className="w-6 h-6 text-white" />}
|
||||||
<h2 className="text-xl font-semibold text-gray-900">目录设置</h2>
|
size="lg"
|
||||||
|
variant="default"
|
||||||
|
closeOnBackdropClick={true}
|
||||||
|
closeOnEscape={true}
|
||||||
|
>
|
||||||
|
<div className="p-6">
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex items-center justify-center py-8">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||||
|
<span className="ml-2 text-gray-600">加载设置中...</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
) : (
|
||||||
onClick={onClose}
|
<div className="space-y-6">
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
{/* 自动记忆设置 */}
|
||||||
>
|
<div className={`p-4 rounded-lg border-2 transition-all duration-200 ${settings.auto_remember_directories
|
||||||
<X className="w-6 h-6" />
|
? 'bg-blue-50 border-blue-200'
|
||||||
</button>
|
: 'bg-gray-50 border-gray-200'
|
||||||
</div>
|
}`}>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
{/* 内容区域 */}
|
<div>
|
||||||
<div className="p-6 overflow-y-auto max-h-[60vh]">
|
<h3 className={`text-sm font-medium ${settings.auto_remember_directories ? 'text-blue-900' : 'text-gray-700'
|
||||||
{loading ? (
|
}`}>
|
||||||
<div className="flex items-center justify-center py-8">
|
自动记忆目录
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
</h3>
|
||||||
<span className="ml-2 text-gray-600">加载设置中...</span>
|
<p className={`text-sm mt-1 ${settings.auto_remember_directories ? 'text-blue-700' : 'text-gray-600'
|
||||||
</div>
|
}`}>
|
||||||
) : (
|
启用后,选择文件时会自动记住所选目录作为下次的默认位置
|
||||||
<div className="space-y-6">
|
</p>
|
||||||
{/* 自动记忆设置 */}
|
|
||||||
<div className="bg-blue-50 p-4 rounded-lg">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-medium text-blue-900">自动记忆目录</h3>
|
|
||||||
<p className="text-sm text-blue-700 mt-1">
|
|
||||||
启用后,选择文件时会自动记住所选目录作为下次的默认位置
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<label className="relative inline-flex items-center cursor-pointer">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={settings.auto_remember_directories}
|
|
||||||
onChange={(e) => saveAutoRememberSetting(e.target.checked)}
|
|
||||||
disabled={saving}
|
|
||||||
className="sr-only"
|
|
||||||
/>
|
|
||||||
<div className={`w-11 h-6 bg-gray-200 rounded-full peer ${
|
|
||||||
settings.auto_remember_directories ? 'bg-blue-600' : ''
|
|
||||||
} ${saving ? 'opacity-50' : ''}`}>
|
|
||||||
<div className={`absolute top-[2px] left-[2px] bg-white border border-gray-300 rounded-full h-5 w-5 transition-transform ${
|
|
||||||
settings.auto_remember_directories ? 'translate-x-5' : ''
|
|
||||||
}`}></div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<label className="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
{/* 目录设置列表 */}
|
type="checkbox"
|
||||||
<div className="space-y-4">
|
checked={settings.auto_remember_directories}
|
||||||
<h3 className="text-lg font-medium text-gray-900">默认目录设置</h3>
|
onChange={(e) => saveAutoRememberSetting(e.target.checked)}
|
||||||
{directoryItems.map((item) => (
|
disabled={saving}
|
||||||
<div key={item.key} className="border border-gray-200 rounded-lg p-4">
|
className="sr-only"
|
||||||
<div className="flex items-start justify-between">
|
/>
|
||||||
<div className="flex-1">
|
<div className={`relative w-11 h-6 rounded-full transition-colors duration-200 ease-in-out ${settings.auto_remember_directories
|
||||||
<h4 className="text-sm font-medium text-gray-900">{item.label}</h4>
|
? 'bg-blue-600'
|
||||||
<p className="text-sm text-gray-500 mt-1">{item.description}</p>
|
: 'bg-gray-300'
|
||||||
{item.value && (
|
} ${saving ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}>
|
||||||
<div className="mt-2 p-2 bg-gray-50 rounded text-sm text-gray-700 font-mono break-all">
|
<div className={`absolute top-0.5 left-0.5 bg-white rounded-full h-5 w-5 transition-transform duration-200 ease-in-out shadow-sm ${settings.auto_remember_directories ? 'translate-x-5' : 'translate-x-0'
|
||||||
{item.value}
|
}`}>
|
||||||
</div>
|
{/* 添加图标指示器 */}
|
||||||
)}
|
<div className="flex items-center justify-center h-full w-full">
|
||||||
</div>
|
{settings.auto_remember_directories ? (
|
||||||
<div className="flex items-center space-x-2 ml-4">
|
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<button
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
onClick={() => selectDirectory(item.key, item.label)}
|
</svg>
|
||||||
className="flex items-center space-x-1 px-3 py-1.5 text-sm bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
|
) : (
|
||||||
>
|
<svg className="w-3 h-3 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<Folder className="w-4 h-4" />
|
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||||
<span>{item.value ? '更改' : '选择'}</span>
|
</svg>
|
||||||
</button>
|
|
||||||
{item.value && (
|
|
||||||
<button
|
|
||||||
onClick={() => clearDirectory(item.key)}
|
|
||||||
className="px-3 py-1.5 text-sm bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition-colors"
|
|
||||||
>
|
|
||||||
清除
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
{/* 状态文字 */}
|
||||||
|
<span className={`ml-3 text-sm font-medium ${settings.auto_remember_directories ? 'text-blue-600' : 'text-gray-500'
|
||||||
|
}`}>
|
||||||
|
{settings.auto_remember_directories ? '已启用' : '已关闭'}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 额外的状态说明 */}
|
||||||
|
<div className={`mt-3 p-2 rounded text-xs ${settings.auto_remember_directories
|
||||||
|
? 'bg-blue-100 text-blue-800'
|
||||||
|
: 'bg-gray-100 text-gray-600'
|
||||||
|
}`}>
|
||||||
|
{settings.auto_remember_directories
|
||||||
|
? '✓ 系统将自动记住您选择的目录路径'
|
||||||
|
: '○ 每次都需要手动选择目录路径'
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
{/* 目录设置列表 */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="text-lg font-medium text-gray-900">默认目录设置</h3>
|
||||||
|
{directoryItems.map((item) => (
|
||||||
|
<div key={item.key} className="border border-gray-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="text-sm font-medium text-gray-900">{item.label}</h4>
|
||||||
|
<p className="text-sm text-gray-500 mt-1">{item.description}</p>
|
||||||
|
{item.value && (
|
||||||
|
<div className="mt-2 p-2 bg-gray-50 rounded text-sm text-gray-700 font-mono break-all">
|
||||||
|
{item.value}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 ml-4">
|
||||||
|
<button
|
||||||
|
onClick={() => selectDirectory(item.key, item.label)}
|
||||||
|
className="flex items-center space-x-1 px-3 py-1.5 text-sm bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
|
||||||
|
>
|
||||||
|
<Folder className="w-4 h-4" />
|
||||||
|
<span>{item.value ? '更改' : '选择'}</span>
|
||||||
|
</button>
|
||||||
|
{item.value && (
|
||||||
|
<button
|
||||||
|
onClick={() => clearDirectory(item.key)}
|
||||||
|
className="px-3 py-1.5 text-sm bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition-colors"
|
||||||
|
>
|
||||||
|
清除
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 底部操作栏 */}
|
{/* 底部操作栏 */}
|
||||||
<div className="flex items-center justify-between p-6 border-t border-gray-200 bg-gray-50">
|
<div className="flex items-center justify-between pt-6 border-t border-gray-200">
|
||||||
<button
|
<button
|
||||||
onClick={resetAllSettings}
|
onClick={resetAllSettings}
|
||||||
className="flex items-center space-x-2 px-4 py-2 text-sm bg-red-600 text-white rounded hover:bg-red-700 transition-colors"
|
className="flex items-center space-x-2 px-4 py-2 text-sm bg-red-600 text-white rounded hover:bg-red-700 transition-colors"
|
||||||
@@ -261,7 +289,7 @@ const DirectorySettingsModal: React.FC<DirectorySettingsModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
FileJson,
|
FileJson,
|
||||||
Film
|
Film
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import DirectorySettingsButton from './DirectorySettingsButton';
|
||||||
|
|
||||||
interface ExportRecordManagerProps {
|
interface ExportRecordManagerProps {
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
@@ -143,22 +144,9 @@ const ExportRecordManager: React.FC<ExportRecordManagerProps> = ({
|
|||||||
// 重新导出
|
// 重新导出
|
||||||
const handleReExport = async (recordId: string) => {
|
const handleReExport = async (recordId: string) => {
|
||||||
try {
|
try {
|
||||||
// 打开文件选择对话框
|
// 使用新的剪影导出路径选择命令
|
||||||
const { open } = await import('@tauri-apps/plugin-dialog');
|
const selected = await invoke<string | null>('select_jianying_export_path', {
|
||||||
const selected = await open({
|
defaultFilename: 'draft_content_reexport.json'
|
||||||
title: '选择导出路径',
|
|
||||||
directory: false,
|
|
||||||
multiple: false,
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: '剪映项目',
|
|
||||||
extensions: ['json']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '所有文件',
|
|
||||||
extensions: ['*']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
@@ -545,6 +533,7 @@ const ExportRecordManager: React.FC<ExportRecordManagerProps> = ({
|
|||||||
>
|
>
|
||||||
清理过期
|
清理过期
|
||||||
</InteractiveButton>
|
</InteractiveButton>
|
||||||
|
<DirectorySettingsButton size="sm" variant="secondary" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CreateMaterialRequest, MaterialImportResult } from '../types/material';
|
|||||||
import { Model } from '../types/model';
|
import { Model } from '../types/model';
|
||||||
import { MaterialModelBindingService } from '../services/materialModelBindingService';
|
import { MaterialModelBindingService } from '../services/materialModelBindingService';
|
||||||
import { CustomSelect } from './CustomSelect';
|
import { CustomSelect } from './CustomSelect';
|
||||||
|
import DirectorySettingsButton from './DirectorySettingsButton';
|
||||||
|
|
||||||
interface MaterialImportDialogProps {
|
interface MaterialImportDialogProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -314,6 +315,11 @@ export const MaterialImportDialog: React.FC<MaterialImportDialogProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 目录设置按钮 */}
|
||||||
|
<div className="flex justify-center pt-4 border-t border-gray-200">
|
||||||
|
<DirectorySettingsButton size="sm" variant="ghost" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
110
apps/desktop/src/components/QuickDirectoryButton.tsx
Normal file
110
apps/desktop/src/components/QuickDirectoryButton.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Folder, Settings } from 'lucide-react';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
|
interface QuickDirectoryButtonProps {
|
||||||
|
settingType: 'material_import' | 'template_import' | 'jianying_export' | 'project_export' | 'thumbnail_export';
|
||||||
|
label?: string;
|
||||||
|
onDirectorySelected?: (directory: string) => void;
|
||||||
|
className?: string;
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
variant?: 'primary' | 'secondary' | 'ghost';
|
||||||
|
showLabel?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuickDirectoryButton: React.FC<QuickDirectoryButtonProps> = ({
|
||||||
|
settingType,
|
||||||
|
label,
|
||||||
|
onDirectorySelected,
|
||||||
|
className = '',
|
||||||
|
size = 'md',
|
||||||
|
variant = 'secondary',
|
||||||
|
showLabel = true,
|
||||||
|
}) => {
|
||||||
|
const sizeClasses = {
|
||||||
|
sm: 'px-2 py-1 text-xs',
|
||||||
|
md: 'px-3 py-2 text-sm',
|
||||||
|
lg: 'px-4 py-3 text-base',
|
||||||
|
};
|
||||||
|
|
||||||
|
const variantClasses = {
|
||||||
|
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
||||||
|
secondary: 'bg-gray-200 text-gray-700 hover:bg-gray-300',
|
||||||
|
ghost: 'bg-transparent text-gray-600 hover:bg-gray-100',
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconSizes = {
|
||||||
|
sm: 'w-3 h-3',
|
||||||
|
md: 'w-4 h-4',
|
||||||
|
lg: 'w-5 h-5',
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDisplayName = () => {
|
||||||
|
if (label) return label;
|
||||||
|
|
||||||
|
switch (settingType) {
|
||||||
|
case 'material_import':
|
||||||
|
return '素材目录';
|
||||||
|
case 'template_import':
|
||||||
|
return '模板目录';
|
||||||
|
case 'jianying_export':
|
||||||
|
return '导出目录';
|
||||||
|
case 'project_export':
|
||||||
|
return '项目目录';
|
||||||
|
case 'thumbnail_export':
|
||||||
|
return '缩略图目录';
|
||||||
|
default:
|
||||||
|
return '选择目录';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTitle = () => {
|
||||||
|
switch (settingType) {
|
||||||
|
case 'material_import':
|
||||||
|
return '选择素材导入目录';
|
||||||
|
case 'template_import':
|
||||||
|
return '选择模板导入目录';
|
||||||
|
case 'jianying_export':
|
||||||
|
return '选择剪影导出目录';
|
||||||
|
case 'project_export':
|
||||||
|
return '选择项目导出目录';
|
||||||
|
case 'thumbnail_export':
|
||||||
|
return '选择缩略图导出目录';
|
||||||
|
default:
|
||||||
|
return '选择目录';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
try {
|
||||||
|
const result = await invoke<string | null>('select_and_save_directory', {
|
||||||
|
settingType,
|
||||||
|
title: getTitle(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && onDirectorySelected) {
|
||||||
|
onDirectorySelected(result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('选择目录失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleClick}
|
||||||
|
className={`
|
||||||
|
flex items-center space-x-2 rounded transition-colors
|
||||||
|
${sizeClasses[size]}
|
||||||
|
${variantClasses[variant]}
|
||||||
|
${className}
|
||||||
|
`}
|
||||||
|
title={getTitle()}
|
||||||
|
>
|
||||||
|
<Folder className={iconSizes[size]} />
|
||||||
|
{showLabel && <span>{getDisplayName()}</span>}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QuickDirectoryButton;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { save } from '@tauri-apps/plugin-dialog';
|
|
||||||
import {
|
import {
|
||||||
TemplateMatchingResult,
|
TemplateMatchingResult,
|
||||||
TemplateMatchingResultQueryOptions,
|
TemplateMatchingResultQueryOptions,
|
||||||
@@ -14,6 +13,7 @@ import { DeleteConfirmDialog } from './DeleteConfirmDialog';
|
|||||||
import { CustomSelect } from './CustomSelect';
|
import { CustomSelect } from './CustomSelect';
|
||||||
import { TemplateMatchingResultCard } from './TemplateMatchingResultCard';
|
import { TemplateMatchingResultCard } from './TemplateMatchingResultCard';
|
||||||
import { TemplateMatchingResultDetailModal } from './TemplateMatchingResultDetailModal';
|
import { TemplateMatchingResultDetailModal } from './TemplateMatchingResultDetailModal';
|
||||||
|
import DirectorySettingsButton from './DirectorySettingsButton';
|
||||||
import { TemplateMatchingResultStatsPanel } from './TemplateMatchingResultStatsPanel';
|
import { TemplateMatchingResultStatsPanel } from './TemplateMatchingResultStatsPanel';
|
||||||
import { useNotifications } from './NotificationSystem';
|
import { useNotifications } from './NotificationSystem';
|
||||||
|
|
||||||
@@ -200,16 +200,9 @@ export const TemplateMatchingResultManager: React.FC<TemplateMatchingResultManag
|
|||||||
// 导出到剪映 (V1版本)
|
// 导出到剪映 (V1版本)
|
||||||
const handleExportToJianying = async (result: TemplateMatchingResult) => {
|
const handleExportToJianying = async (result: TemplateMatchingResult) => {
|
||||||
try {
|
try {
|
||||||
// 选择保存路径
|
// 使用新的剪影导出路径选择命令
|
||||||
const filePath = await save({
|
const filePath = await invoke<string | null>('select_jianying_export_path', {
|
||||||
title: '导出到剪映 (V1)',
|
defaultFilename: `draft_content_v1_${result.result_name}.json`
|
||||||
defaultPath: `draft_content_v1_${result.result_name}.json`,
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: 'JSON文件',
|
|
||||||
extensions: ['json']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
@@ -235,16 +228,9 @@ export const TemplateMatchingResultManager: React.FC<TemplateMatchingResultManag
|
|||||||
// 导出到剪映 (V2版本)
|
// 导出到剪映 (V2版本)
|
||||||
const handleExportToJianyingV2 = async (result: TemplateMatchingResult) => {
|
const handleExportToJianyingV2 = async (result: TemplateMatchingResult) => {
|
||||||
try {
|
try {
|
||||||
// 选择保存路径
|
// 使用新的剪影导出路径选择命令
|
||||||
const filePath = await save({
|
const filePath = await invoke<string | null>('select_jianying_export_path', {
|
||||||
title: '导出到剪映',
|
defaultFilename: `draft_content_${result.result_name}.json`
|
||||||
defaultPath: 'draft_content.json',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: 'JSON文件',
|
|
||||||
extensions: ['json']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
@@ -429,6 +415,7 @@ export const TemplateMatchingResultManager: React.FC<TemplateMatchingResultManag
|
|||||||
>
|
>
|
||||||
刷新
|
刷新
|
||||||
</button>
|
</button>
|
||||||
|
<DirectorySettingsButton size="sm" variant="secondary" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { X, FolderOpen, AlertCircle, CheckCircle, Settings } from 'lucide-react'
|
|||||||
import { BatchImportRequest } from '../../types/template';
|
import { BatchImportRequest } from '../../types/template';
|
||||||
import { CustomSelect } from '../CustomSelect';
|
import { CustomSelect } from '../CustomSelect';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import DirectorySettingsButton from '../DirectorySettingsButton';
|
||||||
|
|
||||||
interface BatchImportModalProps {
|
interface BatchImportModalProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -28,8 +29,11 @@ export const BatchImportModal: React.FC<BatchImportModalProps> = ({
|
|||||||
// 处理文件夹选择
|
// 处理文件夹选择
|
||||||
const handleFolderSelect = async () => {
|
const handleFolderSelect = async () => {
|
||||||
try {
|
try {
|
||||||
// 使用 Tauri 的文件夹选择对话框
|
// 使用带默认目录的文件夹选择对话框
|
||||||
const selected = await invoke<string | null>('select_directory');
|
const selected = await invoke<string | null>('select_and_save_directory', {
|
||||||
|
settingType: 'template_import',
|
||||||
|
title: '选择包含剪映草稿的文件夹'
|
||||||
|
});
|
||||||
if (selected) {
|
if (selected) {
|
||||||
setSelectedFolder(selected);
|
setSelectedFolder(selected);
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
@@ -152,6 +156,11 @@ export const BatchImportModal: React.FC<BatchImportModalProps> = ({
|
|||||||
{errors.folder_path}
|
{errors.folder_path}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 目录设置按钮 */}
|
||||||
|
<div className="flex justify-end mt-3">
|
||||||
|
<DirectorySettingsButton size="sm" variant="ghost" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 自动上传选项 */}
|
{/* 自动上传选项 */}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
UpdateProjectTemplateBindingRequest
|
UpdateProjectTemplateBindingRequest
|
||||||
} from '../types/projectTemplateBinding';
|
} from '../types/projectTemplateBinding';
|
||||||
import { MaterialMatchingService } from '../services/materialMatchingService';
|
import { MaterialMatchingService } from '../services/materialMatchingService';
|
||||||
|
import DirectorySettingsButton from '../components/DirectorySettingsButton';
|
||||||
import { MaterialMatchingResult, MaterialMatchingRequest } from '../types/materialMatching';
|
import { MaterialMatchingResult, MaterialMatchingRequest } from '../types/materialMatching';
|
||||||
import { MaterialSegmentView } from '../components/MaterialSegmentView';
|
import { MaterialSegmentView } from '../components/MaterialSegmentView';
|
||||||
import { BatchMatchingService } from '../services/batchMatchingService';
|
import { BatchMatchingService } from '../services/batchMatchingService';
|
||||||
@@ -959,6 +960,8 @@ export const ProjectDetails: React.FC = () => {
|
|||||||
导入素材
|
导入素材
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<DirectorySettingsButton size="md" variant="secondary" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleBatchClassification}
|
onClick={handleBatchClassification}
|
||||||
disabled={classificationLoading}
|
disabled={classificationLoading}
|
||||||
@@ -1412,6 +1415,7 @@ export const ProjectDetails: React.FC = () => {
|
|||||||
<Upload className="w-4 h-4 mr-2" />
|
<Upload className="w-4 h-4 mr-2" />
|
||||||
导入素材
|
导入素材
|
||||||
</button>
|
</button>
|
||||||
|
<DirectorySettingsButton size="sm" variant="secondary" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { TemplateDetailModal } from '../components/template/TemplateDetailModal'
|
|||||||
import { ImportProgressModal } from '../components/template/ImportProgressModal';
|
import { ImportProgressModal } from '../components/template/ImportProgressModal';
|
||||||
import { BatchImportProgressModal } from '../components/template/BatchImportProgressModal';
|
import { BatchImportProgressModal } from '../components/template/BatchImportProgressModal';
|
||||||
import { CustomSelect } from '../components/CustomSelect';
|
import { CustomSelect } from '../components/CustomSelect';
|
||||||
|
import DirectorySettingsButton from '../components/DirectorySettingsButton';
|
||||||
import { DeleteConfirmDialog } from '../components/DeleteConfirmDialog';
|
import { DeleteConfirmDialog } from '../components/DeleteConfirmDialog';
|
||||||
import { TemplateCardSkeleton } from '../components/SkeletonLoader';
|
import { TemplateCardSkeleton } from '../components/SkeletonLoader';
|
||||||
import { useTemplateStore } from '../stores/templateStore';
|
import { useTemplateStore } from '../stores/templateStore';
|
||||||
@@ -219,6 +220,8 @@ const TemplateManagement: React.FC = () => {
|
|||||||
<Upload className="w-4 h-4" />
|
<Upload className="w-4 h-4" />
|
||||||
批量导入
|
批量导入
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<DirectorySettingsButton size="md" variant="secondary" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { AdvancedFilterDemo } from '../../components/outfit/AdvancedFilterDemo';
|
import { AdvancedFilterDemo } from '../../components/outfit/AdvancedFilterDemo';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,12 +6,6 @@ import { AdvancedFilterDemo } from '../../components/outfit/AdvancedFilterDemo';
|
|||||||
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
||||||
*/
|
*/
|
||||||
const AdvancedFilterTool: React.FC = () => {
|
const AdvancedFilterTool: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const handleGoBack = () => {
|
|
||||||
navigate('/tools');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50">
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50">
|
||||||
{/* 页面头部 */}
|
{/* 页面头部 */}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
|
||||||
Play,
|
Play,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
XCircle,
|
XCircle,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Sparkles,
|
Sparkles,
|
||||||
ArrowLeft,
|
|
||||||
Wand2,
|
Wand2,
|
||||||
Shirt,
|
Shirt,
|
||||||
Download,
|
Download,
|
||||||
@@ -10,7 +9,6 @@ import {
|
|||||||
Settings,
|
Settings,
|
||||||
Info
|
Info
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import OutfitRecommendationService from '../../services/outfitRecommendationService';
|
import OutfitRecommendationService from '../../services/outfitRecommendationService';
|
||||||
import { OutfitRecommendation, STYLE_OPTIONS, OCCASION_OPTIONS, SEASON_OPTIONS } from '../../types/outfitRecommendation';
|
import { OutfitRecommendation, STYLE_OPTIONS, OCCASION_OPTIONS, SEASON_OPTIONS } from '../../types/outfitRecommendation';
|
||||||
import OutfitRecommendationList from '../../components/outfit/OutfitRecommendationList';
|
import OutfitRecommendationList from '../../components/outfit/OutfitRecommendationList';
|
||||||
@@ -22,8 +20,6 @@ import { MaterialSearchPanel, MaterialDetailModal } from '../../components/mater
|
|||||||
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
||||||
*/
|
*/
|
||||||
const OutfitRecommendationTool: React.FC = () => {
|
const OutfitRecommendationTool: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [targetStyle, setTargetStyle] = useState<string>('');
|
const [targetStyle, setTargetStyle] = useState<string>('');
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ import {
|
|||||||
RotateCcw,
|
RotateCcw,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
Zap,
|
Zap,
|
||||||
ArrowLeft,
|
|
||||||
Loader2,
|
Loader2,
|
||||||
X,
|
X,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Copy
|
Copy
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import {
|
import {
|
||||||
useSimilaritySearchStore,
|
useSimilaritySearchStore,
|
||||||
useSimilaritySearchSelectors,
|
useSimilaritySearchSelectors,
|
||||||
@@ -30,8 +28,6 @@ import { SearchConfig, DEFAULT_SEARCH_CONFIG } from '../../types/outfitSearch';
|
|||||||
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
||||||
*/
|
*/
|
||||||
const SimilaritySearchTool: React.FC = () => {
|
const SimilaritySearchTool: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// 本地状态
|
// 本地状态
|
||||||
const [selectedResult, setSelectedResult] = useState<any>(null);
|
const [selectedResult, setSelectedResult] = useState<any>(null);
|
||||||
const [showDetailModal, setShowDetailModal] = useState(false);
|
const [showDetailModal, setShowDetailModal] = useState(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user