diff --git a/prompt.html b/prompt.html
new file mode 100644
index 0000000..277370c
--- /dev/null
+++ b/prompt.html
@@ -0,0 +1,598 @@
+
+
+
+
+
+ 全能AI绘画提示词工厂 公众号:AI星河
+
+
+
+
+ 🎨 AI绘画提示词超级工厂
+
+
+
+
1. 主体描述 (自由输入)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/AICreationChat.tsx b/src/components/AICreationChat.tsx
new file mode 100644
index 0000000..673d2b7
--- /dev/null
+++ b/src/components/AICreationChat.tsx
@@ -0,0 +1,356 @@
+import React, { useState, useRef, useEffect } from 'react'
+import { Sparkles, Send, User, Bot, Wand2, Play, Download, Loader } from 'lucide-react'
+import { Project } from '../services/projectService'
+import { Model } from '../services/modelService'
+
+interface AICreationChatProps {
+ project: Project
+ models: Model[]
+ onMaterialCreated: () => void
+}
+
+interface ChatMessage {
+ id: string
+ type: 'user' | 'assistant' | 'system'
+ content: string
+ timestamp: Date
+ creationTask?: CreationTask
+}
+
+interface CreationTask {
+ id: string
+ model: Model
+ prompt: string
+ status: 'pending' | 'processing' | 'completed' | 'failed'
+ progress: number
+ result?: {
+ videoPath: string
+ thumbnailPath: string
+ duration: number
+ }
+ error?: string
+}
+
+const AICreationChat: React.FC = ({
+ project,
+ models,
+ onMaterialCreated
+}) => {
+ const [messages, setMessages] = useState([
+ {
+ id: '1',
+ type: 'system',
+ content: `欢迎使用AI创作助手!我可以帮您为项目"${project.product_name}"创作视频素材。请告诉我您想要什么样的内容。`,
+ timestamp: new Date()
+ }
+ ])
+ const [inputText, setInputText] = useState('')
+ const [selectedModel, setSelectedModel] = useState(models[0] || null)
+ const [isCreating, setIsCreating] = useState(false)
+ const messagesEndRef = useRef(null)
+
+ useEffect(() => {
+ scrollToBottom()
+ }, [messages])
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
+ }
+
+ const handleSendMessage = async () => {
+ if (!inputText.trim() || !selectedModel) return
+
+ const userMessage: ChatMessage = {
+ id: Date.now().toString(),
+ type: 'user',
+ content: inputText.trim(),
+ timestamp: new Date()
+ }
+
+ setMessages(prev => [...prev, userMessage])
+ setInputText('')
+ setIsCreating(true)
+
+ // 模拟AI响应
+ setTimeout(() => {
+ const assistantMessage: ChatMessage = {
+ id: (Date.now() + 1).toString(),
+ type: 'assistant',
+ content: `好的!我将使用模特"${selectedModel.model_number}"为您创作关于"${project.product_name}"的视频素材。正在开始创作...`,
+ timestamp: new Date()
+ }
+ setMessages(prev => [...prev, assistantMessage])
+
+ // 开始创作任务
+ startCreationTask(inputText.trim(), selectedModel)
+ }, 1000)
+ }
+
+ const startCreationTask = async (prompt: string, model: Model) => {
+ const taskId = Date.now().toString()
+ const creationTask: CreationTask = {
+ id: taskId,
+ model,
+ prompt,
+ status: 'pending',
+ progress: 0
+ }
+
+ const taskMessage: ChatMessage = {
+ id: taskId + '_task',
+ type: 'assistant',
+ content: '正在创作中...',
+ timestamp: new Date(),
+ creationTask
+ }
+
+ setMessages(prev => [...prev, taskMessage])
+
+ // 模拟创作过程
+ const steps = [
+ { progress: 10, status: '初始化AI模型...' },
+ { progress: 30, status: '分析模特特征...' },
+ { progress: 50, status: '生成视频内容...' },
+ { progress: 70, status: '渲染视频帧...' },
+ { progress: 90, status: '后处理优化...' },
+ { progress: 100, status: '创作完成!' }
+ ]
+
+ for (const step of steps) {
+ await new Promise(resolve => setTimeout(resolve, 1500))
+
+ const updatedTask = {
+ ...creationTask,
+ status: step.progress === 100 ? 'completed' as const : 'processing' as const,
+ progress: step.progress,
+ result: step.progress === 100 ? {
+ videoPath: `${project.local_directory}/ai_generated_${taskId}.mp4`,
+ thumbnailPath: `${project.local_directory}/ai_generated_${taskId}_thumb.jpg`,
+ duration: 15
+ } : undefined
+ }
+
+ setMessages(prev => prev.map(msg =>
+ msg.id === taskId + '_task'
+ ? { ...msg, creationTask: updatedTask }
+ : msg
+ ))
+ }
+
+ // 添加完成消息
+ setTimeout(() => {
+ const completionMessage: ChatMessage = {
+ id: (Date.now() + 2).toString(),
+ type: 'assistant',
+ content: '✨ 视频创作完成!您可以预览或下载生成的素材。需要调整什么吗?',
+ timestamp: new Date()
+ }
+ setMessages(prev => [...prev, completionMessage])
+ setIsCreating(false)
+ onMaterialCreated()
+ }, 500)
+ }
+
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ handleSendMessage()
+ }
+ }
+
+ const getStatusColor = (status: CreationTask['status']) => {
+ switch (status) {
+ case 'pending':
+ return 'text-yellow-600'
+ case 'processing':
+ return 'text-blue-600'
+ case 'completed':
+ return 'text-green-600'
+ case 'failed':
+ return 'text-red-600'
+ default:
+ return 'text-gray-600'
+ }
+ }
+
+ return (
+
+ {/* 头部 */}
+
+
+
+
AI创作助手
+
+
+ {/* 模特选择 */}
+
+
+
+
+
+
+ 为项目"{project.product_name}"创作专属视频素材
+
+
+
+ {/* 消息列表 */}
+
+ {messages.map((message) => (
+
+
+ {/* 消息头部 */}
+
+
+
+ {message.type === 'user' ? (
+
+ ) : message.type === 'system' ? (
+
+ ) : (
+
+ )}
+
+
+ {message.timestamp.toLocaleTimeString()}
+
+
+
+
+ {/* 消息内容 */}
+
+
{message.content}
+
+ {/* 创作任务卡片 */}
+ {message.creationTask && (
+
+
+
+
+
+ {message.creationTask.model.model_number}
+
+
+
+ {message.creationTask.status === 'pending' && '等待中'}
+ {message.creationTask.status === 'processing' && '创作中'}
+ {message.creationTask.status === 'completed' && '已完成'}
+ {message.creationTask.status === 'failed' && '失败'}
+
+
+
+
+ {message.creationTask.prompt}
+
+
+ {/* 进度条 */}
+ {message.creationTask.status === 'processing' && (
+
+
+ 进度
+ {message.creationTask.progress}%
+
+
+
+ )}
+
+ {/* 结果操作 */}
+ {message.creationTask.status === 'completed' && message.creationTask.result && (
+
+
+
+
+ {message.creationTask.result.duration}s
+
+
+ )}
+
+ )}
+
+
+
+ ))}
+
+ {isCreating && (
+
+ )}
+
+
+
+
+ {/* 输入区域 */}
+
+
+
+
+ 按 Enter 发送,Shift + Enter 换行
+
+
+
+ )
+}
+
+export default AICreationChat
diff --git a/src/components/ProjectMaterialsCenter.tsx b/src/components/ProjectMaterialsCenter.tsx
new file mode 100644
index 0000000..bbf7710
--- /dev/null
+++ b/src/components/ProjectMaterialsCenter.tsx
@@ -0,0 +1,312 @@
+import React, { useState } from 'react'
+import { Video, Play, Search, Filter, Tag, Clock, HardDrive, Eye, User } from 'lucide-react'
+import { Project } from '../services/projectService'
+import { VideoSegment, MediaService } from '../services/mediaService'
+import { Model } from '../services/modelService'
+import VideoPlayer from './VideoPlayer'
+
+interface ProjectMaterialsCenterProps {
+ project: Project
+ materials: VideoSegment[]
+ models: Model[]
+ onMaterialsChange: (materials: VideoSegment[]) => void
+}
+
+const ProjectMaterialsCenter: React.FC = ({
+ project,
+ materials,
+ models,
+ onMaterialsChange
+}) => {
+ const [searchTerm, setSearchTerm] = useState('')
+ const [selectedModelIds, setSelectedModelIds] = useState([])
+ const [filterType, setFilterType] = useState<'all' | 'original' | 'segmented'>('all')
+ const [selectedMaterial, setSelectedMaterial] = useState(null)
+ const [showVideoPlayer, setShowVideoPlayer] = useState(false)
+
+ // 过滤素材
+ const filteredMaterials = materials.filter(material => {
+ // 搜索过滤
+ const matchesSearch = material.filename.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ (material.tags || []).some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()))
+
+ // 模特过滤(如果选择了模特)
+ const matchesModel = selectedModelIds.length === 0 ||
+ selectedModelIds.some(modelId => {
+ const model = models.find(m => m.id === modelId)
+ return model && (material.tags || []).includes(model.model_number)
+ })
+
+ // 类型过滤
+ let matchesType = true
+ if (filterType === 'original') {
+ matchesType = material.segment_index === 0
+ } else if (filterType === 'segmented') {
+ matchesType = material.segment_index > 0
+ }
+
+ return matchesSearch && matchesModel && matchesType
+ })
+
+ const handleModelToggle = (modelId: string) => {
+ setSelectedModelIds(prev =>
+ prev.includes(modelId)
+ ? prev.filter(id => id !== modelId)
+ : [...prev, modelId]
+ )
+ }
+
+ const handleUseMaterial = async (material: VideoSegment) => {
+ try {
+ await MediaService.incrementSegmentUsage(material.id)
+ const updatedMaterials = materials.map(m =>
+ m.id === material.id ? { ...m, use_count: m.use_count + 1 } : m
+ )
+ onMaterialsChange(updatedMaterials)
+ } catch (error) {
+ console.error('Failed to use material:', error)
+ }
+ }
+
+ const handlePlayMaterial = (material: VideoSegment) => {
+ setSelectedMaterial(material)
+ setShowVideoPlayer(true)
+ }
+
+ const formatDuration = (seconds: number) => {
+ const minutes = Math.floor(seconds / 60)
+ const remainingSeconds = Math.floor(seconds % 60)
+ return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`
+ }
+
+ const formatFileSize = (bytes: number) => {
+ const sizes = ['B', 'KB', 'MB', 'GB']
+ if (bytes === 0) return '0 B'
+ const i = Math.floor(Math.log(bytes) / Math.log(1024))
+ return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
+ }
+
+ return (
+
+ {/* 头部 */}
+
+
+
项目素材
+
+ {filteredMaterials.length} / {materials.length} 个素材
+
+
+
+ {/* 搜索栏 */}
+
+
+ setSearchTerm(e.target.value)}
+ className="pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent w-full"
+ />
+
+
+ {/* 模特筛选标签 */}
+
+
+
+ 按模特筛选:
+
+
+ {models.map((model) => (
+
+ ))}
+ {selectedModelIds.length > 0 && (
+
+ )}
+
+
+
+ {/* 类型过滤 */}
+
+
+
+ 类型:
+
+
+ {[
+ { value: 'all', label: '全部' },
+ { value: 'original', label: '原始素材' },
+ { value: 'segmented', label: '已分镜头' }
+ ].map((option) => (
+
+ ))}
+
+
+
+
+ {/* 素材网格 */}
+
+ {filteredMaterials.length === 0 ? (
+
+
+
+ {searchTerm || selectedModelIds.length > 0 || filterType !== 'all'
+ ? '没有找到匹配的素材'
+ : '暂无项目素材'
+ }
+
+
+ {searchTerm || selectedModelIds.length > 0 || filterType !== 'all'
+ ? '尝试调整搜索条件或筛选器'
+ : `上传包含"${project.product_name}"标签的视频素材`
+ }
+
+
+ ) : (
+
+ {filteredMaterials.map((material) => (
+
+ {/* 视频缩略图 */}
+
+ {material.thumbnail_path ? (
+

+ ) : (
+
+
+
+ )}
+
+ {/* 播放按钮 */}
+
+
+
+
+ {/* 类型标识 */}
+
+
+ {material.segment_index === 0 ? '原始' : `片段${material.segment_index}`}
+
+
+
+ {/* 使用次数 */}
+
+
+ 使用 {material.use_count} 次
+
+
+
+
+ {/* 素材信息 */}
+
+
+ {material.filename}
+
+
+ {/* 基本信息 */}
+
+
+ {formatDuration(material.duration)}
+ •
+
+ {formatFileSize(material.file_size)}
+
+
+ {/* 标签 */}
+
+ {(material.tags || []).slice(0, 3).map((tag, index) => (
+
+
+ {tag}
+
+ ))}
+ {(material.tags || []).length > 3 && (
+ +{(material.tags || []).length - 3}
+ )}
+
+
+ {/* 操作按钮 */}
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ {/* 视频播放器 */}
+ {selectedMaterial && (
+
{
+ setShowVideoPlayer(false)
+ setSelectedMaterial(null)
+ }}
+ title={`${selectedMaterial.filename} - ${
+ selectedMaterial.segment_index === 0 ? '原始素材' : `片段 ${selectedMaterial.segment_index}`
+ }`}
+ />
+ )}
+
+ )
+}
+
+export default ProjectMaterialsCenter
diff --git a/src/pages/ProjectDetailPage.tsx b/src/pages/ProjectDetailPage.tsx
index e4e887b..e8191d8 100644
--- a/src/pages/ProjectDetailPage.tsx
+++ b/src/pages/ProjectDetailPage.tsx
@@ -1,14 +1,11 @@
import React, { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
-import { ArrowLeft, User, Layout, Video, Sparkles, FolderOpen, Plus } from 'lucide-react'
+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 ProjectModels from '../components/ProjectModels'
-import ProjectTemplates from '../components/ProjectTemplates'
-import ProjectMaterials from '../components/ProjectMaterials'
-import ProjectFiles from '../components/ProjectFiles'
-import AICreationPanel from '../components/AICreationPanel'
+import ProjectMaterialsCenter from '../components/ProjectMaterialsCenter'
+import AICreationChat from '../components/AICreationChat'
const ProjectDetailPage: React.FC = () => {
const { projectId } = useParams<{ projectId: string }>()
@@ -16,13 +13,10 @@ const ProjectDetailPage: React.FC = () => {
const [project, setProject] = useState(null)
const [loading, setLoading] = useState(true)
- const [activeTab, setActiveTab] = useState<'models' | 'templates' | 'materials' | 'files' | 'ai'>('models')
-
+
// 数据状态
const [projectModels, setProjectModels] = useState([])
- const [availableTemplates, setAvailableTemplates] = useState([])
const [projectMaterials, setProjectMaterials] = useState([])
- const [projectFiles, setProjectFiles] = useState([])
useEffect(() => {
if (projectId) {
@@ -43,10 +37,8 @@ const ProjectDetailPage: React.FC = () => {
// 加载项目相关数据
await Promise.all([
- loadProjectModels(projectResponse.data),
- loadAvailableTemplates(projectResponse.data),
- loadProjectMaterials(projectResponse.data),
- loadProjectFiles(projectResponse.data)
+ loadProjectModels(),
+ loadProjectMaterials(projectResponse.data)
])
} else {
console.error('Failed to load project:', projectResponse.msg)
@@ -60,7 +52,7 @@ const ProjectDetailPage: React.FC = () => {
}
}
- const loadProjectModels = async (project: Project) => {
+ const loadProjectModels = async () => {
try {
// 获取所有模特,后续可以添加项目关联逻辑
const response = await ModelService.getAllModels()
@@ -72,16 +64,6 @@ const ProjectDetailPage: React.FC = () => {
}
}
- const loadAvailableTemplates = async (project: Project) => {
- try {
- // 获取所有模板,后续可以添加项目关联逻辑
- // 这里需要调用模板服务
- setAvailableTemplates([])
- } catch (error) {
- console.error('Failed to load available templates:', error)
- }
- }
-
const loadProjectMaterials = async (project: Project) => {
try {
// 获取与项目商品名相关的素材
@@ -98,23 +80,7 @@ const ProjectDetailPage: React.FC = () => {
}
}
- const loadProjectFiles = async (project: Project) => {
- try {
- // 获取项目文件夹下的所有文件
- // 这里需要添加文件系统扫描功能
- setProjectFiles([])
- } catch (error) {
- console.error('Failed to load project files:', error)
- }
- }
- const tabs = [
- { id: 'models', label: '使用模特', icon: User, count: projectModels.length },
- { id: 'templates', label: '可用模板', icon: Layout, count: availableTemplates.length },
- { id: 'materials', label: '项目素材', icon: Video, count: projectMaterials.length },
- { id: 'files', label: '项目文件', icon: FolderOpen, count: projectFiles.length },
- { id: 'ai', label: 'AI创作', icon: Sparkles, count: 0 }
- ] as const
if (loading) {
return (
@@ -146,133 +112,59 @@ const ProjectDetailPage: React.FC = () => {
}
return (
-
- {/* 页面头部 */}
-
-
-
-
{project.name}
-
商品:{project.product_name}
-
-
-
- {/* 项目信息卡片 */}
-
-
- {/* 商品图片 */}
-
- {project.product_image ? (
-

- ) : (
-
-
-
- )}
+
+ {/* 顶部导航栏 */}
+
+
+
+
+
+
{project.name}
+
商品:{project.product_name}
+
- {/* 项目信息 */}
-
-
-
-
-
{project.name}
-
-
-
-
{project.product_name}
-
-
-
-
{project.local_directory}
-
-
-
-
{new Date(project.created_at).toLocaleDateString()}
-
+ {/* 项目信息概览 */}
+
+
+
+ {projectMaterials.length} 个素材
+
+
+
+ {projectModels.length} 个模特
+
+
+ 创建于 {new Date(project.created_at).toLocaleDateString()}
- {/* 标签页导航 */}
-
-
-
+ {/* 主要内容区域 - 三栏布局 */}
+
+ {/* 中间主要区域 - 项目素材管理 */}
+
- {/* 标签页内容 */}
-
- {activeTab === 'models' && (
-
- )}
-
- {activeTab === 'templates' && (
-
- )}
-
- {activeTab === 'materials' && (
-
- )}
-
- {activeTab === 'files' && (
-
- )}
-
- {activeTab === 'ai' && (
-
loadProjectMaterials(project)}
- />
- )}
+ {/* 右侧 AI 创作面板 */}
+
+
loadProjectMaterials(project)}
+ />