This commit is contained in:
root 2025-07-10 20:32:31 +08:00
parent 2c609c04f1
commit 1435c72ba6
3 changed files with 91 additions and 23 deletions

View File

@ -1,14 +1,23 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, List, Play, Clock, CheckCircle, XCircle, Layout } from 'lucide-react' import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, Layout, Clock, CheckCircle, XCircle, List } from 'lucide-react'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { useAIVideoJobs } from '../stores/useAIVideoStore'
const Sidebar: React.FC = () => { const Sidebar: React.FC = () => {
const location = useLocation() const location = useLocation()
const jobs = useAIVideoJobs()
const [activeTab, setActiveTab] = useState<'nav' | 'tasks'>('nav') const [activeTab, setActiveTab] = useState<'nav' | 'tasks'>('nav')
// Mock jobs data - this should be replaced with actual state management
const jobs: Array<{
id: string
type: 'single' | 'batch'
status: 'processing' | 'completed' | 'failed'
progress: number
startTime: number
result?: { success_count?: number }
error?: string
}> = []
const navItems = [ const navItems = [
{ path: '/', icon: Home, label: '首页' }, { path: '/', icon: Home, label: '首页' },
{ path: '/editor', icon: Video, label: '编辑器' }, { path: '/editor', icon: Video, label: '编辑器' },

View File

@ -1,28 +1,38 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { Sparkles, Info, Settings, HelpCircle, TestTube } from 'lucide-react' import { Sparkles, Info, Settings, HelpCircle, List, BookOpen } from 'lucide-react'
import AIVideoGenerator from '../components/AIVideoGenerator' import AIVideoGenerator from '../components/AIVideoGenerator'
import { AIVideoService } from '../services/tauri' import VideoJobList from '../components/ai-video/VideoJobList'
import { useAIVideoStore } from '../stores/useAIVideoStore'
const AIVideoPage: React.FC = () => { const AIVideoPage: React.FC = () => {
const [testResult, setTestResult] = useState<any>(null) const [activeTab, setActiveTab] = useState<'tasks' | 'guide'>('tasks')
const [isTesting, setIsTesting] = useState(false)
const handleTestEnvironment = async () => { // Get jobs from store
setIsTesting(true) const jobs = useAIVideoStore(state => state.jobs)
setTestResult(null) const removeJob = useAIVideoStore(state => state.removeJob)
const generateSingleVideo = useAIVideoStore(state => state.generateSingleVideo)
const batchGenerateVideos = useAIVideoStore(state => state.batchGenerateVideos)
// Job handlers
const handlePreviewJob = (job: any) => {
// TODO: Implement job preview
console.log('Preview job:', job)
}
const handleDeleteJob = (jobId: string) => {
removeJob(jobId)
}
const handleRetryJob = async (job: any) => {
try { try {
const result = await AIVideoService.testEnvironment() if (job.type === 'single') {
setTestResult(result) await generateSingleVideo(job.request)
console.log('Environment test result:', result) } else if (job.type === 'batch') {
await batchGenerateVideos(job.request)
}
} catch (error) { } catch (error) {
setTestResult({ console.error('Retry failed:', error)
status: 'error', alert(`重试失败: ${error instanceof Error ? error.message : '未知错误'}`)
error: error instanceof Error ? error.message : String(error)
})
console.error('Environment test failed:', error)
} finally {
setIsTesting(false)
} }
} }
@ -62,7 +72,53 @@ const AIVideoPage: React.FC = () => {
</div> </div>
{/* Sidebar */} {/* Sidebar */}
<div className="w-64 bg-white border-l border-secondary-200 p-6 overflow-y-auto"> <div className="w-80 bg-white border-l border-secondary-200 flex flex-col">
{/* Tab Headers */}
<div className="border-b border-secondary-200">
<div className="flex">
<button
onClick={() => setActiveTab('tasks')}
className={`flex-1 px-4 py-3 text-sm font-medium border-b-2 transition-colors flex items-center justify-center gap-2 ${
activeTab === 'tasks'
? 'border-primary-500 text-primary-600 bg-primary-50'
: 'border-transparent text-secondary-600 hover:text-secondary-900 hover:bg-secondary-50'
}`}
>
<List size={16} />
{jobs.length > 0 && (
<span className="bg-primary-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
{jobs.length}
</span>
)}
</button>
<button
onClick={() => setActiveTab('guide')}
className={`flex-1 px-4 py-3 text-sm font-medium border-b-2 transition-colors flex items-center justify-center gap-2 ${
activeTab === 'guide'
? 'border-primary-500 text-primary-600 bg-primary-50'
: 'border-transparent text-secondary-600 hover:text-secondary-900 hover:bg-secondary-50'
}`}
>
<BookOpen size={16} />
使
</button>
</div>
</div>
{/* Tab Content */}
<div className="flex-1 overflow-hidden">
{activeTab === 'tasks' ? (
<div className="h-full p-4">
<VideoJobList
jobs={jobs}
onPreview={handlePreviewJob}
onDelete={handleDeleteJob}
onRetry={handleRetryJob}
/>
</div>
) : (
<div className="h-full p-6 overflow-y-auto">
<h3 className="text-lg font-semibold text-secondary-900 mb-4">使</h3> <h3 className="text-lg font-semibold text-secondary-900 mb-4">使</h3>
<div className="space-y-4"> <div className="space-y-4">
@ -203,6 +259,9 @@ const AIVideoPage: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
)}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -64,7 +64,7 @@ const TemplateManagePage: React.FC = () => {
const handleBatchImport = async () => { const handleBatchImport = async () => {
try { try {
// Select folder using Tauri dialog // Select folder using Tauri dialog
const folderResult = await invoke('select_folder') const folderResult = await invoke<string>('select_folder')
if (!folderResult) return if (!folderResult) return
setImporting(true) setImporting(true)