diff --git a/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs index d7c87d5..46b0735 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/material_commands.rs @@ -723,6 +723,9 @@ pub async fn select_material_folders(app_handle: tauri::AppHandle) -> Result = ({ }; 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', + 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 border border-gray-300 hover:border-gray-400', + ghost: 'bg-transparent text-gray-600 hover:bg-gray-100 hover:text-gray-800', }; const iconSizes = { @@ -38,14 +38,15 @@ const DirectorySettingsButton: React.FC = ({ diff --git a/apps/desktop/src/components/DirectorySettingsDemo.tsx b/apps/desktop/src/components/DirectorySettingsDemo.tsx new file mode 100644 index 0000000..baddfda --- /dev/null +++ b/apps/desktop/src/components/DirectorySettingsDemo.tsx @@ -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(''); + const [autoRememberStatus, setAutoRememberStatus] = useState(null); + + const checkAutoRememberStatus = async () => { + try { + const status = await invoke('is_auto_remember_enabled'); + setAutoRememberStatus(status); + } catch (error) { + console.error('检查自动记忆状态失败:', error); + } + }; + + const handleDirectorySelected = (directory: string) => { + setSelectedDirectory(directory); + }; + + React.useEffect(() => { + checkAutoRememberStatus(); + }, []); + + return ( +
+

目录设置功能演示

+ + {/* 状态显示 */} +
+

当前状态

+
+
+ 自动记忆功能: + + {autoRememberStatus === true ? '已启用' : autoRememberStatus === false ? '已关闭' : '检查中...'} + + +
+ {selectedDirectory && ( +
+ 最后选择的目录: + + {selectedDirectory} + +
+ )} +
+
+ + {/* 按钮演示 */} +
+ {/* 目录设置按钮 */} +
+

目录设置按钮

+

+ 点击打开目录设置对话框,可以管理所有导入导出的默认目录 +

+
+
+ 小号: + + + +
+
+ 中号: + + + +
+
+ 大号: + + + +
+
+
+ + {/* 快速目录选择按钮 */} +
+

快速目录选择

+

+ 直接选择特定类型的目录,会自动保存到对应的设置中 +

+
+ + + + +
+
+
+ + {/* 使用说明 */} +
+

使用说明

+
    +
  • 目录设置按钮:打开完整的目录设置对话框,可以管理所有类型的默认目录
  • +
  • 快速目录选择:直接选择特定类型的目录,立即保存到设置中
  • +
  • 自动记忆功能:启用后,每次选择文件时会自动更新对应的默认目录
  • +
  • 视觉反馈:开关状态有明显的颜色和图标区别,便于识别当前状态
  • +
+
+
+ ); +}; + +export default DirectorySettingsDemo; diff --git a/apps/desktop/src/components/DirectorySettingsModal.tsx b/apps/desktop/src/components/DirectorySettingsModal.tsx index 50cfda1..f978583 100644 --- a/apps/desktop/src/components/DirectorySettingsModal.tsx +++ b/apps/desktop/src/components/DirectorySettingsModal.tsx @@ -1,6 +1,7 @@ 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 { Modal } from './Modal'; interface DirectorySettings { material_import_directory: string | null; @@ -146,104 +147,131 @@ const DirectorySettingsModal: React.FC = ({ }, ]; - if (!isOpen) return null; - return ( -
-
- {/* 标题栏 */} -
-
- -

目录设置

+ } + size="lg" + variant="default" + closeOnBackdropClick={true} + closeOnEscape={true} + > +
+ {loading ? ( +
+
+ 加载设置中...
- -
- - {/* 内容区域 */} -
- {loading ? ( -
-
- 加载设置中... -
- ) : ( -
- {/* 自动记忆设置 */} -
-
-
-

自动记忆目录

-

- 启用后,选择文件时会自动记住所选目录作为下次的默认位置 -

-
- + ) : ( +
+ {/* 自动记忆设置 */} +
+
+
+

+ 自动记忆目录 +

+

+ 启用后,选择文件时会自动记住所选目录作为下次的默认位置 +

-
- - {/* 目录设置列表 */} -
-

默认目录设置

- {directoryItems.map((item) => ( -
-
-
-

{item.label}

-

{item.description}

- {item.value && ( -
- {item.value} -
- )} -
-
- - {item.value && ( - + +
+ + {/* 额外的状态说明 */} +
+ {settings.auto_remember_directories + ? '✓ 系统将自动记住您选择的目录路径' + : '○ 每次都需要手动选择目录路径' + }
- )} -
+ + {/* 目录设置列表 */} +
+

默认目录设置

+ {directoryItems.map((item) => ( +
+
+
+

{item.label}

+

{item.description}

+ {item.value && ( +
+ {item.value} +
+ )} +
+
+ + {item.value && ( + + )} +
+
+
+ ))} +
+
+ )} {/* 底部操作栏 */} -
+
-
+ ); }; diff --git a/apps/desktop/src/components/ExportRecordManager.tsx b/apps/desktop/src/components/ExportRecordManager.tsx index d58ecd8..eabc338 100644 --- a/apps/desktop/src/components/ExportRecordManager.tsx +++ b/apps/desktop/src/components/ExportRecordManager.tsx @@ -27,6 +27,7 @@ import { FileJson, Film } from 'lucide-react'; +import DirectorySettingsButton from './DirectorySettingsButton'; interface ExportRecordManagerProps { projectId?: string; @@ -143,22 +144,9 @@ const ExportRecordManager: React.FC = ({ // 重新导出 const handleReExport = async (recordId: string) => { try { - // 打开文件选择对话框 - const { open } = await import('@tauri-apps/plugin-dialog'); - const selected = await open({ - title: '选择导出路径', - directory: false, - multiple: false, - filters: [ - { - name: '剪映项目', - extensions: ['json'] - }, - { - name: '所有文件', - extensions: ['*'] - } - ] + // 使用新的剪影导出路径选择命令 + const selected = await invoke('select_jianying_export_path', { + defaultFilename: 'draft_content_reexport.json' }); if (selected) { @@ -545,6 +533,7 @@ const ExportRecordManager: React.FC = ({ > 清理过期 +
diff --git a/apps/desktop/src/components/MaterialImportDialog.tsx b/apps/desktop/src/components/MaterialImportDialog.tsx index 97d135e..627f9c9 100644 --- a/apps/desktop/src/components/MaterialImportDialog.tsx +++ b/apps/desktop/src/components/MaterialImportDialog.tsx @@ -6,6 +6,7 @@ import { CreateMaterialRequest, MaterialImportResult } from '../types/material'; import { Model } from '../types/model'; import { MaterialModelBindingService } from '../services/materialModelBindingService'; import { CustomSelect } from './CustomSelect'; +import DirectorySettingsButton from './DirectorySettingsButton'; interface MaterialImportDialogProps { isOpen: boolean; @@ -314,6 +315,11 @@ export const MaterialImportDialog: React.FC = ({

+ + {/* 目录设置按钮 */} +
+ +
)} diff --git a/apps/desktop/src/components/QuickDirectoryButton.tsx b/apps/desktop/src/components/QuickDirectoryButton.tsx new file mode 100644 index 0000000..44f61d1 --- /dev/null +++ b/apps/desktop/src/components/QuickDirectoryButton.tsx @@ -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 = ({ + 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('select_and_save_directory', { + settingType, + title: getTitle(), + }); + + if (result && onDirectorySelected) { + onDirectorySelected(result); + } + } catch (error) { + console.error('选择目录失败:', error); + } + }; + + return ( + + ); +}; + +export default QuickDirectoryButton; diff --git a/apps/desktop/src/components/TemplateMatchingResultManager.tsx b/apps/desktop/src/components/TemplateMatchingResultManager.tsx index e720cb8..4bc214c 100644 --- a/apps/desktop/src/components/TemplateMatchingResultManager.tsx +++ b/apps/desktop/src/components/TemplateMatchingResultManager.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from 'react'; import { invoke } from '@tauri-apps/api/core'; -import { save } from '@tauri-apps/plugin-dialog'; import { TemplateMatchingResult, TemplateMatchingResultQueryOptions, @@ -14,6 +13,7 @@ import { DeleteConfirmDialog } from './DeleteConfirmDialog'; import { CustomSelect } from './CustomSelect'; import { TemplateMatchingResultCard } from './TemplateMatchingResultCard'; import { TemplateMatchingResultDetailModal } from './TemplateMatchingResultDetailModal'; +import DirectorySettingsButton from './DirectorySettingsButton'; import { TemplateMatchingResultStatsPanel } from './TemplateMatchingResultStatsPanel'; import { useNotifications } from './NotificationSystem'; @@ -200,16 +200,9 @@ export const TemplateMatchingResultManager: React.FC { try { - // 选择保存路径 - const filePath = await save({ - title: '导出到剪映 (V1)', - defaultPath: `draft_content_v1_${result.result_name}.json`, - filters: [ - { - name: 'JSON文件', - extensions: ['json'] - } - ] + // 使用新的剪影导出路径选择命令 + const filePath = await invoke('select_jianying_export_path', { + defaultFilename: `draft_content_v1_${result.result_name}.json` }); if (!filePath) { @@ -235,16 +228,9 @@ export const TemplateMatchingResultManager: React.FC { try { - // 选择保存路径 - const filePath = await save({ - title: '导出到剪映', - defaultPath: 'draft_content.json', - filters: [ - { - name: 'JSON文件', - extensions: ['json'] - } - ] + // 使用新的剪影导出路径选择命令 + const filePath = await invoke('select_jianying_export_path', { + defaultFilename: `draft_content_${result.result_name}.json` }); if (!filePath) { @@ -429,6 +415,7 @@ export const TemplateMatchingResultManager: React.FC 刷新 +
diff --git a/apps/desktop/src/components/template/BatchImportModal.tsx b/apps/desktop/src/components/template/BatchImportModal.tsx index 6882527..0b85dbe 100644 --- a/apps/desktop/src/components/template/BatchImportModal.tsx +++ b/apps/desktop/src/components/template/BatchImportModal.tsx @@ -3,6 +3,7 @@ import { X, FolderOpen, AlertCircle, CheckCircle, Settings } from 'lucide-react' import { BatchImportRequest } from '../../types/template'; import { CustomSelect } from '../CustomSelect'; import { invoke } from '@tauri-apps/api/core'; +import DirectorySettingsButton from '../DirectorySettingsButton'; interface BatchImportModalProps { onClose: () => void; @@ -28,8 +29,11 @@ export const BatchImportModal: React.FC = ({ // 处理文件夹选择 const handleFolderSelect = async () => { try { - // 使用 Tauri 的文件夹选择对话框 - const selected = await invoke('select_directory'); + // 使用带默认目录的文件夹选择对话框 + const selected = await invoke('select_and_save_directory', { + settingType: 'template_import', + title: '选择包含剪映草稿的文件夹' + }); if (selected) { setSelectedFolder(selected); setFormData(prev => ({ @@ -152,6 +156,11 @@ export const BatchImportModal: React.FC = ({ {errors.folder_path} )} + + {/* 目录设置按钮 */} +
+ +
{/* 自动上传选项 */} diff --git a/apps/desktop/src/pages/ProjectDetails.tsx b/apps/desktop/src/pages/ProjectDetails.tsx index c8c436a..f6be30c 100644 --- a/apps/desktop/src/pages/ProjectDetails.tsx +++ b/apps/desktop/src/pages/ProjectDetails.tsx @@ -28,6 +28,7 @@ import { UpdateProjectTemplateBindingRequest } from '../types/projectTemplateBinding'; import { MaterialMatchingService } from '../services/materialMatchingService'; +import DirectorySettingsButton from '../components/DirectorySettingsButton'; import { MaterialMatchingResult, MaterialMatchingRequest } from '../types/materialMatching'; import { MaterialSegmentView } from '../components/MaterialSegmentView'; import { BatchMatchingService } from '../services/batchMatchingService'; @@ -959,6 +960,8 @@ export const ProjectDetails: React.FC = () => { 导入素材 + + + diff --git a/apps/desktop/src/pages/TemplateManagement.tsx b/apps/desktop/src/pages/TemplateManagement.tsx index c11dc30..d7cd617 100644 --- a/apps/desktop/src/pages/TemplateManagement.tsx +++ b/apps/desktop/src/pages/TemplateManagement.tsx @@ -8,6 +8,7 @@ import { TemplateDetailModal } from '../components/template/TemplateDetailModal' import { ImportProgressModal } from '../components/template/ImportProgressModal'; import { BatchImportProgressModal } from '../components/template/BatchImportProgressModal'; import { CustomSelect } from '../components/CustomSelect'; +import DirectorySettingsButton from '../components/DirectorySettingsButton'; import { DeleteConfirmDialog } from '../components/DeleteConfirmDialog'; import { TemplateCardSkeleton } from '../components/SkeletonLoader'; import { useTemplateStore } from '../stores/templateStore'; @@ -219,6 +220,8 @@ const TemplateManagement: React.FC = () => { 批量导入 + + diff --git a/apps/desktop/src/pages/tools/AdvancedFilterTool.tsx b/apps/desktop/src/pages/tools/AdvancedFilterTool.tsx index 882ce35..420dd2e 100644 --- a/apps/desktop/src/pages/tools/AdvancedFilterTool.tsx +++ b/apps/desktop/src/pages/tools/AdvancedFilterTool.tsx @@ -1,6 +1,4 @@ import React from 'react'; -import { ArrowLeft } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; import { AdvancedFilterDemo } from '../../components/outfit/AdvancedFilterDemo'; /** @@ -8,12 +6,6 @@ import { AdvancedFilterDemo } from '../../components/outfit/AdvancedFilterDemo'; * 遵循 Tauri 开发规范和 UI/UX 设计标准 */ const AdvancedFilterTool: React.FC = () => { - const navigate = useNavigate(); - - const handleGoBack = () => { - navigate('/tools'); - }; - return (
{/* 页面头部 */} diff --git a/apps/desktop/src/pages/tools/ChatTestPage.tsx b/apps/desktop/src/pages/tools/ChatTestPage.tsx index 44f2117..e378ec4 100644 --- a/apps/desktop/src/pages/tools/ChatTestPage.tsx +++ b/apps/desktop/src/pages/tools/ChatTestPage.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { - ArrowLeft, Play, CheckCircle, XCircle, diff --git a/apps/desktop/src/pages/tools/OutfitRecommendationTool.tsx b/apps/desktop/src/pages/tools/OutfitRecommendationTool.tsx index 7df1853..2d60a2f 100644 --- a/apps/desktop/src/pages/tools/OutfitRecommendationTool.tsx +++ b/apps/desktop/src/pages/tools/OutfitRecommendationTool.tsx @@ -1,7 +1,6 @@ import React, { useState, useCallback } from 'react'; import { Sparkles, - ArrowLeft, Wand2, Shirt, Download, @@ -10,7 +9,6 @@ import { Settings, Info } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; import OutfitRecommendationService from '../../services/outfitRecommendationService'; import { OutfitRecommendation, STYLE_OPTIONS, OCCASION_OPTIONS, SEASON_OPTIONS } from '../../types/outfitRecommendation'; import OutfitRecommendationList from '../../components/outfit/OutfitRecommendationList'; @@ -22,8 +20,6 @@ import { MaterialSearchPanel, MaterialDetailModal } from '../../components/mater * 遵循 Tauri 开发规范和 UI/UX 设计标准 */ const OutfitRecommendationTool: React.FC = () => { - const navigate = useNavigate(); - // 状态管理 const [query, setQuery] = useState(''); const [targetStyle, setTargetStyle] = useState(''); diff --git a/apps/desktop/src/pages/tools/SimilaritySearchTool.tsx b/apps/desktop/src/pages/tools/SimilaritySearchTool.tsx index 5d9ac91..bffb173 100644 --- a/apps/desktop/src/pages/tools/SimilaritySearchTool.tsx +++ b/apps/desktop/src/pages/tools/SimilaritySearchTool.tsx @@ -4,13 +4,11 @@ import { RotateCcw, TrendingUp, Zap, - ArrowLeft, Loader2, X, ExternalLink, Copy } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; import { useSimilaritySearchStore, useSimilaritySearchSelectors, @@ -30,8 +28,6 @@ import { SearchConfig, DEFAULT_SEARCH_CONFIG } from '../../types/outfitSearch'; * 遵循 Tauri 开发规范和 UI/UX 设计标准 */ const SimilaritySearchTool: React.FC = () => { - const navigate = useNavigate(); - // 本地状态 const [selectedResult, setSelectedResult] = useState(null); const [showDetailModal, setShowDetailModal] = useState(false);