fix
This commit is contained in:
parent
c0fa5c503c
commit
61782a77b3
|
|
@ -1,13 +1,101 @@
|
|||
import React from 'react'
|
||||
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 = [
|
||||
{ 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 [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' },
|
||||
|
|
@ -73,20 +161,55 @@ const HomePage: React.FC = () => {
|
|||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{recentProjects.map((project) => (
|
||||
<div
|
||||
{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 bg-secondary-200 flex items-center justify-center">
|
||||
<Video className="text-secondary-400" size={48} />
|
||||
<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">{project.lastModified}</p>
|
||||
<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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue