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 { Link } from 'react-router-dom'
|
||||||
import { Plus, FolderOpen, Video, Music, Zap, Sparkles, Database } from 'lucide-react'
|
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 HomePage: React.FC = () => {
|
||||||
const recentProjects = [
|
const [recentProjects, setRecentProjects] = useState<Project[]>([])
|
||||||
{ id: 1, name: '旅行视频剪辑', lastModified: '2 小时前', thumbnail: '/placeholder-video.jpg' },
|
const [loading, setLoading] = useState(true)
|
||||||
{ id: 2, name: '产品宣传片', lastModified: '1 天前', thumbnail: '/placeholder-video.jpg' },
|
|
||||||
{ id: 3, name: '音乐MV制作', lastModified: '3 天前', thumbnail: '/placeholder-video.jpg' },
|
// 加载最近的项目
|
||||||
]
|
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 = [
|
const quickActions = [
|
||||||
{ icon: Sparkles, label: 'AI 视频生成', description: '使用 AI 将图片转换为动态视频', path: '/ai-video' },
|
{ icon: Sparkles, label: 'AI 视频生成', description: '使用 AI 将图片转换为动态视频', path: '/ai-video' },
|
||||||
|
|
@ -73,20 +161,55 @@ const HomePage: React.FC = () => {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{recentProjects.map((project) => (
|
{loading ? (
|
||||||
<div
|
// 加载状态
|
||||||
|
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}
|
key={project.id}
|
||||||
|
to={`/projects/${project.id}`}
|
||||||
className="card overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
|
className="card overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
|
||||||
>
|
>
|
||||||
<div className="aspect-video bg-secondary-200 flex items-center justify-center">
|
<div className="aspect-video overflow-hidden">
|
||||||
<Video className="text-secondary-400" size={48} />
|
<ProjectImage
|
||||||
|
imagePath={project.product_image}
|
||||||
|
alt={project.name}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<h3 className="font-medium text-secondary-900 mb-1">{project.name}</h3>
|
<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>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue