fix
This commit is contained in:
parent
61782a77b3
commit
bdc68bf733
|
|
@ -0,0 +1,44 @@
|
|||
import React from 'react'
|
||||
import { Sparkles, Zap, Video } from 'lucide-react'
|
||||
|
||||
const FeaturesHighlight: React.FC = () => {
|
||||
const features = [
|
||||
{
|
||||
icon: Sparkles,
|
||||
title: 'AI 智能剪辑',
|
||||
description: '使用先进的 AI 技术,自动识别精彩片段,智能生成视频剪辑'
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
title: '高效处理',
|
||||
description: '优化的渲染引擎,支持多种格式,快速导出高质量视频'
|
||||
},
|
||||
{
|
||||
icon: Video,
|
||||
title: '专业工具',
|
||||
description: '丰富的编辑工具和特效,满足从入门到专业的各种需求'
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<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">
|
||||
{features.map((feature, index) => {
|
||||
const IconComponent = feature.icon
|
||||
return (
|
||||
<div key={index} className="text-center">
|
||||
<div className="inline-flex items-center justify-center w-12 h-12 bg-white/20 rounded-lg mb-4">
|
||||
<IconComponent size={24} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">{feature.title}</h3>
|
||||
<p className="text-primary-100 text-sm">{feature.description}</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeaturesHighlight
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Project } from '../services/projectService'
|
||||
import ProjectImage from './ProjectImage'
|
||||
|
||||
interface ProjectCardProps {
|
||||
project: Project
|
||||
formatTimeAgo: (dateString: string) => string
|
||||
}
|
||||
|
||||
const ProjectCard: React.FC<ProjectCardProps> = ({ project, formatTimeAgo }) => {
|
||||
return (
|
||||
<Link
|
||||
to={`/projects/${project.id}`}
|
||||
className="card overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
|
||||
>
|
||||
<div className="aspect-video overflow-hidden">
|
||||
<ProjectImage
|
||||
imagePath={project.product_image}
|
||||
alt={project.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<h3 className="font-medium text-secondary-900 mb-1">{project.name}</h3>
|
||||
<p className="text-sm text-secondary-600">{formatTimeAgo(project.updated_at)}</p>
|
||||
{project.product_name && (
|
||||
<p className="text-xs text-secondary-500 mt-1">{project.product_name}</p>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectCard
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { Video } from 'lucide-react'
|
||||
|
||||
interface ProjectImageProps {
|
||||
imagePath: string
|
||||
alt: string
|
||||
className: string
|
||||
}
|
||||
|
||||
const ProjectImage: React.FC<ProjectImageProps> = ({ imagePath, alt, className }) => {
|
||||
const [imageSrc, setImageSrc] = useState<string>('')
|
||||
const [imageLoading, setImageLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const loadImage = async () => {
|
||||
if (!imagePath) {
|
||||
setImageLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (imagePath.startsWith('http') || imagePath.startsWith('data:')) {
|
||||
setImageSrc(imagePath)
|
||||
setImageLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = await invoke<string>('read_image_as_data_url', { filePath: imagePath })
|
||||
setImageSrc(dataUrl)
|
||||
} catch (error) {
|
||||
console.error('Failed to read image:', error)
|
||||
} finally {
|
||||
setImageLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
loadImage()
|
||||
}, [imagePath])
|
||||
|
||||
if (imageLoading) {
|
||||
return <div className={`${className} bg-secondary-200 animate-pulse`}></div>
|
||||
}
|
||||
|
||||
if (!imageSrc) {
|
||||
return (
|
||||
<div className={`${className} bg-secondary-200 flex items-center justify-center`}>
|
||||
<Video className="text-secondary-400" size={48} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <img src={imageSrc} alt={alt} className={className} />
|
||||
}
|
||||
|
||||
export default ProjectImage
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { FolderOpen, Music, Zap, Sparkles, Database, LucideIcon } from 'lucide-react'
|
||||
|
||||
interface QuickAction {
|
||||
icon: LucideIcon
|
||||
label: string
|
||||
description: string
|
||||
path: string
|
||||
}
|
||||
|
||||
const QuickActions: React.FC = () => {
|
||||
const quickActions: QuickAction[] = [
|
||||
{ icon: Sparkles, label: 'AI 视频生成', description: '使用 AI 将图片转换为动态视频', path: '/ai-video' },
|
||||
{ icon: Music, label: '音频处理', description: '处理音频文件,添加效果', path: '/audio' },
|
||||
{ icon: Zap, label: 'AI 自动剪辑', description: '使用 AI 自动生成视频剪辑', path: '/editor' },
|
||||
{ icon: FolderOpen, label: '导入媒体', description: '导入视频、音频和图片文件', path: '/media' },
|
||||
{ icon: Database, label: 'KV 存储测试', description: '测试 Cloudflare KV 键值存储功能', path: '/kv-test' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-secondary-900 mb-6">快速操作</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{quickActions.map((action, index) => {
|
||||
const IconComponent = action.icon
|
||||
return (
|
||||
<Link
|
||||
key={index}
|
||||
to={action.path}
|
||||
className="card p-6 hover:shadow-md transition-shadow group"
|
||||
>
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="p-3 bg-primary-100 rounded-lg group-hover:bg-primary-200 transition-colors">
|
||||
<IconComponent className="text-primary-600" size={24} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-medium text-secondary-900 mb-1">{action.label}</h3>
|
||||
<p className="text-sm text-secondary-600">{action.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QuickActions
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Plus, Video } from 'lucide-react'
|
||||
import { ProjectService, Project } from '../services/projectService'
|
||||
import ProjectCard from './ProjectCard'
|
||||
|
||||
const RecentProjects: React.FC = () => {
|
||||
const [recentProjects, setRecentProjects] = useState<Project[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// 加载最近的项目
|
||||
useEffect(() => {
|
||||
const loadRecentProjects = async () => {
|
||||
try {
|
||||
const response = await ProjectService.getAllProjects()
|
||||
if (response.status && response.data) {
|
||||
// 按更新时间排序,取最近的3个项目
|
||||
const sortedProjects = response.data
|
||||
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
|
||||
.slice(0, 3)
|
||||
setRecentProjects(sortedProjects)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load recent projects:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
loadRecentProjects()
|
||||
}, [])
|
||||
|
||||
// 格式化时间显示
|
||||
const formatTimeAgo = (dateString: string): string => {
|
||||
const now = new Date()
|
||||
const date = new Date(dateString)
|
||||
const diffInMs = now.getTime() - date.getTime()
|
||||
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60))
|
||||
const diffInDays = Math.floor(diffInHours / 24)
|
||||
|
||||
if (diffInHours < 1) {
|
||||
return '刚刚'
|
||||
} else if (diffInHours < 24) {
|
||||
return `${diffInHours} 小时前`
|
||||
} else if (diffInDays < 7) {
|
||||
return `${diffInDays} 天前`
|
||||
} else {
|
||||
return date.toLocaleDateString('zh-CN')
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态骨架屏
|
||||
const LoadingSkeleton = () => (
|
||||
<>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div key={index} className="card overflow-hidden">
|
||||
<div className="aspect-video bg-secondary-200 animate-pulse"></div>
|
||||
<div className="p-4">
|
||||
<div className="h-4 bg-secondary-200 rounded animate-pulse mb-2"></div>
|
||||
<div className="h-3 bg-secondary-200 rounded animate-pulse w-2/3"></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
||||
// 空状态
|
||||
const EmptyState = () => (
|
||||
<div className="col-span-full text-center py-8">
|
||||
<Video className="mx-auto text-secondary-400 mb-4" size={48} />
|
||||
<p className="text-secondary-600 mb-4">还没有项目</p>
|
||||
<Link
|
||||
to="/projects"
|
||||
className="btn-primary inline-flex items-center"
|
||||
>
|
||||
<Plus size={16} className="mr-2" />
|
||||
创建第一个项目
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-semibold text-secondary-900">最近项目</h2>
|
||||
<Link
|
||||
to="/projects"
|
||||
className="text-primary-600 hover:text-primary-700 text-sm font-medium"
|
||||
>
|
||||
查看全部
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{loading ? (
|
||||
<LoadingSkeleton />
|
||||
) : recentProjects.length > 0 ? (
|
||||
recentProjects.map((project) => (
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
project={project}
|
||||
formatTimeAgo={formatTimeAgo}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<EmptyState />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecentProjects
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Plus } from 'lucide-react'
|
||||
|
||||
const WelcomeSection: React.FC = () => {
|
||||
return (
|
||||
<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 justify-center space-x-4">
|
||||
<Link
|
||||
to="/projects"
|
||||
className="btn-primary flex items-center"
|
||||
>
|
||||
<Plus size={20} className="mr-2" />
|
||||
创建新项目
|
||||
</Link>
|
||||
<Link
|
||||
to="/templates"
|
||||
className="btn-secondary"
|
||||
>
|
||||
浏览模板
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WelcomeSection
|
||||
|
|
@ -1,296 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Plus, FolderOpen, Video, Music, Zap, Sparkles, Database } from 'lucide-react'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { ProjectService, Project } from '../services/projectService'
|
||||
|
||||
const HomePage: React.FC = () => {
|
||||
const [recentProjects, setRecentProjects] = useState<Project[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// 加载最近的项目
|
||||
useEffect(() => {
|
||||
const loadRecentProjects = async () => {
|
||||
try {
|
||||
const response = await ProjectService.getAllProjects()
|
||||
if (response.status && response.data) {
|
||||
// 按更新时间排序,取最近的3个项目
|
||||
const sortedProjects = response.data
|
||||
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
|
||||
.slice(0, 3)
|
||||
setRecentProjects(sortedProjects)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load recent projects:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
loadRecentProjects()
|
||||
}, [])
|
||||
|
||||
// 简化的图片组件
|
||||
const ProjectImage: React.FC<{ imagePath: string; alt: string; className: string }> = ({
|
||||
imagePath, alt, className
|
||||
}) => {
|
||||
const [imageSrc, setImageSrc] = useState<string>('')
|
||||
const [imageLoading, setImageLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const loadImage = async () => {
|
||||
if (!imagePath) {
|
||||
setImageLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (imagePath.startsWith('http') || imagePath.startsWith('data:')) {
|
||||
setImageSrc(imagePath)
|
||||
setImageLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = await invoke<string>('read_image_as_data_url', { filePath: imagePath })
|
||||
setImageSrc(dataUrl)
|
||||
} catch (error) {
|
||||
console.error('Failed to read image:', error)
|
||||
} finally {
|
||||
setImageLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
loadImage()
|
||||
}, [imagePath])
|
||||
|
||||
if (imageLoading) {
|
||||
return <div className={`${className} bg-secondary-200 animate-pulse`}></div>
|
||||
}
|
||||
|
||||
if (!imageSrc) {
|
||||
return (
|
||||
<div className={`${className} bg-secondary-200 flex items-center justify-center`}>
|
||||
<Video className="text-secondary-400" size={48} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <img src={imageSrc} alt={alt} className={className} />
|
||||
}
|
||||
|
||||
// 格式化时间显示
|
||||
const formatTimeAgo = (dateString: string): string => {
|
||||
const now = new Date()
|
||||
const date = new Date(dateString)
|
||||
const diffInMs = now.getTime() - date.getTime()
|
||||
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60))
|
||||
const diffInDays = Math.floor(diffInHours / 24)
|
||||
|
||||
if (diffInHours < 1) {
|
||||
return '刚刚'
|
||||
} else if (diffInHours < 24) {
|
||||
return `${diffInHours} 小时前`
|
||||
} else if (diffInDays < 7) {
|
||||
return `${diffInDays} 天前`
|
||||
} else {
|
||||
return date.toLocaleDateString('zh-CN')
|
||||
}
|
||||
}
|
||||
|
||||
const quickActions = [
|
||||
{ icon: Sparkles, label: 'AI 视频生成', description: '使用 AI 将图片转换为动态视频', path: '/ai-video' },
|
||||
{ icon: Music, label: '音频处理', description: '处理音频文件,添加效果', path: '/audio' },
|
||||
{ icon: Zap, label: 'AI 自动剪辑', description: '使用 AI 自动生成视频剪辑', path: '/editor' },
|
||||
{ icon: FolderOpen, label: '导入媒体', description: '导入视频、音频和图片文件', path: '/media' },
|
||||
{ icon: Database, label: 'KV 存储测试', description: '测试 Cloudflare KV 键值存储功能', path: '/kv-test' },
|
||||
]
|
||||
|
||||
|
||||
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="/projects"
|
||||
className="btn-primary px-8 py-3 text-lg"
|
||||
>
|
||||
<Plus className="mr-2" size={20} />
|
||||
开始新项目
|
||||
</Link>
|
||||
</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 (
|
||||
<Link
|
||||
key={index}
|
||||
to={action.path}
|
||||
className="card p-6 hover:shadow-md transition-shadow cursor-pointer group block"
|
||||
>
|
||||
<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>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</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">
|
||||
{loading ? (
|
||||
// 加载状态
|
||||
Array.from({ length: 3 }).map((_, index) => (
|
||||
<div key={index} className="card overflow-hidden">
|
||||
<div className="aspect-video bg-secondary-200 animate-pulse"></div>
|
||||
<div className="p-4">
|
||||
<div className="h-4 bg-secondary-200 rounded animate-pulse mb-2"></div>
|
||||
<div className="h-3 bg-secondary-200 rounded animate-pulse w-2/3"></div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : recentProjects.length > 0 ? (
|
||||
// 显示真实项目
|
||||
recentProjects.map((project) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
to={`/projects/${project.id}`}
|
||||
className="card overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
|
||||
>
|
||||
<div className="aspect-video overflow-hidden">
|
||||
<ProjectImage
|
||||
imagePath={project.product_image}
|
||||
alt={project.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<h3 className="font-medium text-secondary-900 mb-1">{project.name}</h3>
|
||||
<p className="text-sm text-secondary-600">{formatTimeAgo(project.updated_at)}</p>
|
||||
{project.product_name && (
|
||||
<p className="text-xs text-secondary-500 mt-1">{project.product_name}</p>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
))
|
||||
) : (
|
||||
// 空状态
|
||||
<div className="col-span-full text-center py-8">
|
||||
<Video className="mx-auto text-secondary-400 mb-4" size={48} />
|
||||
<p className="text-secondary-600 mb-4">还没有项目</p>
|
||||
<Link
|
||||
to="/projects"
|
||||
className="btn-primary inline-flex items-center"
|
||||
>
|
||||
<Plus size={16} className="mr-2" />
|
||||
创建第一个项目
|
||||
</Link>
|
||||
</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>
|
||||
|
||||
{/* Additional Content for Testing Scroll */}
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-semibold text-secondary-900">更多功能</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="card p-6">
|
||||
<h3 className="text-lg font-semibold mb-3">视频效果</h3>
|
||||
<ul className="space-y-2 text-secondary-600">
|
||||
<li>• 滤镜和颜色调整</li>
|
||||
<li>• 转场效果</li>
|
||||
<li>• 动画和运动图形</li>
|
||||
<li>• 绿幕抠像</li>
|
||||
<li>• 画中画效果</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="card p-6">
|
||||
<h3 className="text-lg font-semibold mb-3">音频功能</h3>
|
||||
<ul className="space-y-2 text-secondary-600">
|
||||
<li>• 降噪和音频修复</li>
|
||||
<li>• 音频均衡器</li>
|
||||
<li>• 音频同步</li>
|
||||
<li>• 语音识别</li>
|
||||
<li>• 音频可视化</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="card p-6">
|
||||
<h3 className="text-lg font-semibold mb-3">导出选项</h3>
|
||||
<ul className="space-y-2 text-secondary-600">
|
||||
<li>• 多种格式支持</li>
|
||||
<li>• 自定义分辨率</li>
|
||||
<li>• 批量导出</li>
|
||||
<li>• 云端渲染</li>
|
||||
<li>• 直播推流</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="card p-6">
|
||||
<h3 className="text-lg font-semibold mb-3">协作功能</h3>
|
||||
<ul className="space-y-2 text-secondary-600">
|
||||
<li>• 团队协作</li>
|
||||
<li>• 版本控制</li>
|
||||
<li>• 评论和反馈</li>
|
||||
<li>• 云端同步</li>
|
||||
<li>• 权限管理</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="text-center py-8 border-t border-secondary-200">
|
||||
<p className="text-secondary-600">
|
||||
© 2025 MixVideo V2. 专业视频编辑软件,让创作更简单。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomePage
|
||||
Loading…
Reference in New Issue