mxivideo/src/pages/ProjectDetailPage.tsx

199 lines
6.7 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, 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