199 lines
6.7 KiB
TypeScript
199 lines
6.7 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
||
import { useParams, useNavigate } from 'react-router-dom'
|
||
import { ArrowLeft, User, Video } from 'lucide-react'
|
||
import { Project, ProjectService } from '../services/projectService'
|
||
import { VideoSegment, MediaService } from '../services/mediaService'
|
||
import { Model, ModelService } from '../services/modelService'
|
||
import { MixEditResult } from '../services/mixEditService'
|
||
import ProjectMaterialsCenter from '../components/ProjectMaterialsCenter'
|
||
import AGUIChat from '../components/AGUIChat'
|
||
import MixEditFloatingButton from '../components/MixEditFloatingButton'
|
||
|
||
const ProjectDetailPage: React.FC = () => {
|
||
const { projectId } = useParams<{ projectId: string }>()
|
||
const navigate = useNavigate()
|
||
|
||
const [project, setProject] = useState<Project | null>(null)
|
||
const [loading, setLoading] = useState(true)
|
||
|
||
// 数据状态
|
||
const [projectModels, setProjectModels] = useState<Model[]>([])
|
||
const [projectMaterials, setProjectMaterials] = useState<VideoSegment[]>([])
|
||
|
||
useEffect(() => {
|
||
if (projectId) {
|
||
loadProjectDetail()
|
||
}
|
||
}, [projectId])
|
||
|
||
const loadProjectDetail = async () => {
|
||
if (!projectId) return
|
||
|
||
try {
|
||
setLoading(true)
|
||
|
||
// 加载项目基本信息
|
||
const projectResponse = await ProjectService.getProjectById(projectId)
|
||
if (projectResponse.status && projectResponse.data) {
|
||
setProject(projectResponse.data)
|
||
|
||
// 加载项目相关数据
|
||
await Promise.all([
|
||
loadProjectModels(),
|
||
loadProjectMaterials(projectResponse.data)
|
||
])
|
||
} else {
|
||
console.error('Failed to load project:', projectResponse.msg)
|
||
navigate('/projects')
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to load project detail:', error)
|
||
navigate('/projects')
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const loadProjectModels = async () => {
|
||
try {
|
||
// 获取所有模特,后续可以添加项目关联逻辑
|
||
const response = await ModelService.getAllModels()
|
||
if (response.status && response.data) {
|
||
setProjectModels(response.data)
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to load project models:', error)
|
||
}
|
||
}
|
||
|
||
const loadProjectMaterials = async (project: Project) => {
|
||
try {
|
||
// 获取与项目商品名相关的素材
|
||
const response = await MediaService.getAllSegments()
|
||
if (response.status && response.data) {
|
||
// 过滤包含商品名标签的素材(包括已使用和未使用的)
|
||
console.log(`素材:`, response.data)
|
||
const filteredMaterials = response.data.filter(segment =>
|
||
segment.tags.includes(project.product_name)
|
||
)
|
||
setProjectMaterials(filteredMaterials)
|
||
|
||
console.log(`项目 "${project.name}" 找到 ${filteredMaterials.length} 个相关素材`)
|
||
console.log('项目商品名:', project.product_name)
|
||
console.log('素材标签示例:', filteredMaterials.slice(0, 3).map(m => ({ filename: m.filename, tags: m.tags })))
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to load project materials:', error)
|
||
}
|
||
}
|
||
|
||
// 处理混剪完成
|
||
const handleMixEditComplete = (result: MixEditResult) => {
|
||
if (result.success) {
|
||
alert(`混剪完成!\n输出路径: ${result.outputPath}\n时长: ${result.duration}秒`)
|
||
// 可以在这里添加更多处理逻辑,比如刷新素材列表、显示预览等
|
||
} else {
|
||
alert(`混剪失败: ${result.message}`)
|
||
}
|
||
}
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="p-6">
|
||
<div className="animate-pulse">
|
||
<div className="h-8 bg-gray-200 rounded w-1/4 mb-6"></div>
|
||
<div className="h-32 bg-gray-200 rounded mb-6"></div>
|
||
<div className="h-64 bg-gray-200 rounded"></div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (!project) {
|
||
return (
|
||
<div className="p-6">
|
||
<div className="text-center py-12">
|
||
<h2 className="text-xl font-semibold text-gray-900 mb-2">项目不存在</h2>
|
||
<p className="text-gray-600 mb-4">找不到指定的项目</p>
|
||
<button
|
||
onClick={() => navigate('/projects')}
|
||
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
|
||
>
|
||
返回项目列表
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50">
|
||
{/* 顶部导航栏 */}
|
||
<div className="bg-white border-b border-gray-200 px-6 py-4">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center">
|
||
<button
|
||
onClick={() => navigate('/projects')}
|
||
className="mr-4 p-2 text-gray-400 hover:text-gray-600 transition-colors"
|
||
>
|
||
<ArrowLeft size={24} />
|
||
</button>
|
||
<div>
|
||
<h1 className="text-xl font-bold text-gray-900">{project.name}</h1>
|
||
<p className="text-sm text-gray-600">商品:{project.product_name}</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 项目信息概览 */}
|
||
<div className="flex items-center space-x-6 text-sm text-gray-600">
|
||
<div className="flex items-center">
|
||
<Video size={16} className="mr-1" />
|
||
<span>{projectMaterials.length} 个素材</span>
|
||
</div>
|
||
<div className="flex items-center">
|
||
<User size={16} className="mr-1" />
|
||
<span>{projectModels.length} 个模特</span>
|
||
</div>
|
||
<div className="text-xs">
|
||
创建于 {new Date(project.created_at).toLocaleDateString()}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 主要内容区域 - 三栏布局 */}
|
||
<div className="flex h-[calc(100vh-120px)]">
|
||
{/* 左侧 AI 聊天面板 */}
|
||
<div className="w-80 bg-white flex flex-col">
|
||
<AGUIChat
|
||
project={project}
|
||
models={projectModels}
|
||
onMaterialCreated={() => loadProjectMaterials(project)}
|
||
/>
|
||
</div>
|
||
|
||
{/* 右侧主要区域 - 项目素材管理 */}
|
||
<div className="flex-1 px-4 py-3 overflow-hidden">
|
||
<ProjectMaterialsCenter
|
||
project={project}
|
||
materials={projectMaterials}
|
||
models={projectModels}
|
||
onMaterialsChange={setProjectMaterials}
|
||
onRefreshMaterials={() => loadProjectMaterials(project)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 一键混剪悬浮按钮 */}
|
||
<MixEditFloatingButton
|
||
project={project}
|
||
materials={projectMaterials}
|
||
models={projectModels}
|
||
onMixEditComplete={handleMixEditComplete}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default ProjectDetailPage
|