Files
mxivideo/src/pages/HomePage.tsx
root 4cdd6560fc feat: 完成 MixVideo V2 基础架构和核心功能
🎬 主要功能:
-  完整的 Tauri + React + Python 分层架构
-  视频处理核心模块(剪切、调整、特效)
-  音频处理集成(节拍检测、频谱分析)
-  Tauri-Python 桥接通信
-  项目管理和文件管理服务
-  现代化 UI 组件(时间轴、预览、媒体库)

🛠️ 技术栈:
- Frontend: Tauri 2.6.2 + React 18 + TypeScript + Tailwind CSS
- Backend: Python 3.10 + MoviePy + FFmpeg + Librosa + OpenCV
- State: Zustand stores for project and media management
- Build: Vite + pnpm

📦 新增组件:
- VideoPreview: 视频预览和播放控制
- Timeline: 多轨道时间轴编辑器
- MediaLibrary: 媒体文件管理和拖拽上传
- TauriService: 前端与 Python 通信服务
- ProjectStore/MediaStore: 状态管理

🧪 已测试功能:
- Python 视频处理模块正常工作
- 项目创建和管理功能
- 前端构建成功
- Tauri 应用启动正常
2025-07-10 10:00:37 +08:00

163 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react'
import { Link } from 'react-router-dom'
import { Plus, FolderOpen, Video, Music, Zap, TestTube } from 'lucide-react'
import { TauriService } from '../services/tauri'
import { useProjectStore } from '../stores/useProjectStore'
const HomePage: React.FC = () => {
const [testResult, setTestResult] = useState<string>('')
const [isTestingConnection, setIsTestingConnection] = useState(false)
const { createProject } = useProjectStore()
const recentProjects = [
{ id: 1, name: '旅行视频剪辑', lastModified: '2 小时前', thumbnail: '/placeholder-video.jpg' },
{ id: 2, name: '产品宣传片', lastModified: '1 天前', thumbnail: '/placeholder-video.jpg' },
{ id: 3, name: '音乐MV制作', lastModified: '3 天前', thumbnail: '/placeholder-video.jpg' },
]
const quickActions = [
{ icon: Video, label: '新建视频项目', description: '创建一个新的视频编辑项目' },
{ icon: Music, label: '音频处理', description: '处理音频文件,添加效果' },
{ icon: Zap, label: 'AI 自动剪辑', description: '使用 AI 自动生成视频剪辑' },
{ icon: FolderOpen, label: '导入媒体', description: '导入视频、音频和图片文件' },
]
const testTauriConnection = async () => {
setIsTestingConnection(true)
setTestResult('')
try {
const result = await TauriService.greet('MixVideo V2')
setTestResult(`✅ 连接成功: ${result}`)
} catch (error) {
setTestResult(`❌ 连接失败: ${error instanceof Error ? error.message : '未知错误'}`)
} finally {
setIsTestingConnection(false)
}
}
const handleCreateProject = async () => {
try {
await createProject('新项目', '使用 MixVideo V2 创建的项目')
} catch (error) {
console.error('Failed to create project:', error)
}
}
return (
<div className="p-6 space-y-8">
{/* Welcome Section */}
<div className="text-center py-8">
<h1 className="text-4xl font-bold text-secondary-900 mb-4">
使 MixVideo V2
</h1>
<p className="text-lg text-secondary-600 mb-8">
</p>
<div className="flex items-center space-x-4">
<Link
to="/editor"
className="btn-primary px-8 py-3 text-lg"
>
<Plus className="mr-2" size={20} />
</Link>
<button
onClick={handleCreateProject}
className="btn-secondary px-6 py-3"
>
</button>
<button
onClick={testTauriConnection}
disabled={isTestingConnection}
className="btn-ghost px-6 py-3 border border-secondary-300"
>
<TestTube className="mr-2" size={16} />
{isTestingConnection ? '测试中...' : '测试连接'}
</button>
</div>
{testResult && (
<div className="mt-4 p-3 bg-secondary-100 rounded-lg text-sm">
{testResult}
</div>
)}
</div>
{/* Quick Actions */}
<div>
<h2 className="text-2xl font-semibold text-secondary-900 mb-4"></h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{quickActions.map((action, index) => {
const Icon = action.icon
return (
<div
key={index}
className="card p-6 hover:shadow-md transition-shadow cursor-pointer group"
>
<div className="flex flex-col items-center text-center space-y-3">
<div className="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center group-hover:bg-primary-200 transition-colors">
<Icon className="text-primary-600" size={24} />
</div>
<h3 className="font-medium text-secondary-900">{action.label}</h3>
<p className="text-sm text-secondary-600">{action.description}</p>
</div>
</div>
)
})}
</div>
</div>
{/* Recent Projects */}
<div>
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-semibold text-secondary-900"></h2>
<Link to="/projects" className="btn-ghost px-4 py-2">
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{recentProjects.map((project) => (
<div
key={project.id}
className="card overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
>
<div className="aspect-video bg-secondary-200 flex items-center justify-center">
<Video className="text-secondary-400" size={48} />
</div>
<div className="p-4">
<h3 className="font-medium text-secondary-900 mb-1">{project.name}</h3>
<p className="text-sm text-secondary-600">{project.lastModified}</p>
</div>
</div>
))}
</div>
</div>
{/* Features Highlight */}
<div className="bg-gradient-to-r from-primary-500 to-primary-600 rounded-lg p-8 text-white">
<h2 className="text-2xl font-bold mb-4"></h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<h3 className="font-semibold mb-2">🎬 </h3>
<p className="text-primary-100"></p>
</div>
<div>
<h3 className="font-semibold mb-2">🎵 </h3>
<p className="text-primary-100"></p>
</div>
<div>
<h3 className="font-semibold mb-2">🤖 AI </h3>
<p className="text-primary-100"></p>
</div>
</div>
</div>
</div>
)
}
export default HomePage