feat:20260126修改
This commit is contained in:
parent
482fd2d292
commit
d72991aa95
@ -8,7 +8,12 @@
|
|||||||
"Bash(npx tsc:*)",
|
"Bash(npx tsc:*)",
|
||||||
"Bash(findstr:*)",
|
"Bash(findstr:*)",
|
||||||
"Bash(tree:*)",
|
"Bash(tree:*)",
|
||||||
"Bash(npx eslint:*)"
|
"Bash(npx eslint:*)",
|
||||||
|
"Bash(curl:*)",
|
||||||
|
"Bash(node:*)",
|
||||||
|
"Bash(netstat:*)",
|
||||||
|
"Bash(tail:*)",
|
||||||
|
"Bash(tasklist:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,7 +103,7 @@ async def execute_generate_characters(
|
|||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
],
|
],
|
||||||
temperature=0.7
|
temperature=0.9
|
||||||
)
|
)
|
||||||
|
|
||||||
task_manager.update_task_progress(
|
task_manager.update_task_progress(
|
||||||
@ -188,7 +188,7 @@ async def execute_generate_outline(
|
|||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
],
|
],
|
||||||
temperature=0.7
|
temperature=0.9
|
||||||
)
|
)
|
||||||
|
|
||||||
task_manager.update_task_progress(
|
task_manager.update_task_progress(
|
||||||
@ -268,7 +268,7 @@ async def execute_generate_world(
|
|||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
],
|
],
|
||||||
temperature=0.7
|
temperature=0.9
|
||||||
)
|
)
|
||||||
|
|
||||||
task_manager.update_task_progress(
|
task_manager.update_task_progress(
|
||||||
|
|||||||
@ -56,6 +56,8 @@ class GlobalContext(BaseModel):
|
|||||||
sceneSettings: Dict[str, SceneSetting] = Field(default_factory=dict)
|
sceneSettings: Dict[str, SceneSetting] = Field(default_factory=dict)
|
||||||
overallOutline: str = ""
|
overallOutline: str = ""
|
||||||
styleGuide: str = ""
|
styleGuide: str = ""
|
||||||
|
uploadedScript: str = "" # 上传的剧本内容
|
||||||
|
inspiration: str = "" # 创意灵感内容
|
||||||
|
|
||||||
|
|
||||||
class Memory(BaseModel):
|
class Memory(BaseModel):
|
||||||
|
|||||||
@ -71,7 +71,7 @@ class TaskCreateRequest(BaseModel):
|
|||||||
|
|
||||||
class TaskResponse(BaseModel):
|
class TaskResponse(BaseModel):
|
||||||
"""任务响应"""
|
"""任务响应"""
|
||||||
id: str
|
id: str = Field(serialization_alias="taskId")
|
||||||
type: TaskType
|
type: TaskType
|
||||||
status: TaskStatus
|
status: TaskStatus
|
||||||
progress: TaskProgress
|
progress: TaskProgress
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export const TaskProgressTracker = ({
|
|||||||
clearTimeout(pollTimer)
|
clearTimeout(pollTimer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [taskId, pollInterval, onComplete, onError])
|
}, [taskId, pollInterval])
|
||||||
|
|
||||||
// 获取状态图标
|
// 获取状态图标
|
||||||
const getStatusIcon = (status: string) => {
|
const getStatusIcon = (status: string) => {
|
||||||
@ -284,7 +284,7 @@ export const StageProgressTracker = ({
|
|||||||
clearTimeout(pollTimer)
|
clearTimeout(pollTimer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [taskId, pollInterval, onComplete, onError])
|
}, [taskId, pollInterval])
|
||||||
|
|
||||||
// 获取当前阶段索引
|
// 获取当前阶段索引
|
||||||
const getCurrentStageIndex = () => {
|
const getCurrentStageIndex = () => {
|
||||||
|
|||||||
@ -7,19 +7,18 @@ import { useState, useEffect } from 'react'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
Form, Input, InputNumber, Button, Card, Steps, message,
|
Form, Input, InputNumber, Button, Card, Steps, message,
|
||||||
Upload, Select, Space, Divider, Tag, Alert, Popover, Progress, Modal, Radio
|
Upload, Select, Space, Divider, Tag, Alert, Popover, Progress, Radio
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import {
|
import {
|
||||||
UploadOutlined, FileTextOutlined, RobotOutlined,
|
UploadOutlined, FileTextOutlined, RobotOutlined,
|
||||||
CheckCircleOutlined, ArrowRightOutlined, SettingOutlined,
|
CheckCircleOutlined, ArrowRightOutlined, SettingOutlined,
|
||||||
ThunderboltOutlined, CloseOutlined
|
ThunderboltOutlined, EditOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { useProjectStore } from '@/stores/projectStore'
|
import { useProjectStore } from '@/stores/projectStore'
|
||||||
import { useSkillStore } from '@/stores/skillStore'
|
import { useSkillStore } from '@/stores/skillStore'
|
||||||
import { api } from '@/services'
|
import { api } from '@/services'
|
||||||
import { ProjectCreateRequest } from '@/services/projectService'
|
import { ProjectCreateRequest } from '@/services/projectService'
|
||||||
import { taskService } from '@/services/taskService'
|
import { taskService } from '@/services/taskService'
|
||||||
import { TaskProgressTracker, TaskResultDisplay } from '@/components/TaskProgressTracker'
|
|
||||||
|
|
||||||
const { TextArea } = Input
|
const { TextArea } = Input
|
||||||
const { Step } = Steps
|
const { Step } = Steps
|
||||||
@ -253,8 +252,6 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
// 异步任务状态
|
// 异步任务状态
|
||||||
const [currentTaskId, setCurrentTaskId] = useState<string | null>(null)
|
const [currentTaskId, setCurrentTaskId] = useState<string | null>(null)
|
||||||
const [showTaskProgress, setShowTaskProgress] = useState(false)
|
|
||||||
const [taskResult, setTaskResult] = useState<any>(null)
|
|
||||||
const [taskType, setTaskType] = useState<string>('')
|
const [taskType, setTaskType] = useState<string>('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -378,9 +375,13 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AI 生成人物设定
|
// AI 生成人物设定
|
||||||
// 启动异步AI生成人物任务
|
|
||||||
const handleAIGenerateCharacters = async () => {
|
const handleAIGenerateCharacters = async () => {
|
||||||
|
// 防止连续触发
|
||||||
|
if (aiGeneratingCharacters) return
|
||||||
|
|
||||||
setAiGeneratingCharacters(true)
|
setAiGeneratingCharacters(true)
|
||||||
|
message.loading('AI 正在生成人物设定...', 0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const projectName = form.getFieldValue('name') || '未命名项目'
|
const projectName = form.getFieldValue('name') || '未命名项目'
|
||||||
const totalEpisodes = form.getFieldValue('totalEpisodes') || 30
|
const totalEpisodes = form.getFieldValue('totalEpisodes') || 30
|
||||||
@ -401,67 +402,92 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
customPrompt: characterPrompt || undefined
|
customPrompt: characterPrompt || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
// 显示任务进度
|
// 设置任务ID和类型,用于后续查询结果
|
||||||
setCurrentTaskId(response.taskId)
|
setCurrentTaskId(response.taskId)
|
||||||
setTaskType('generate_characters')
|
setTaskType('generate_characters')
|
||||||
setShowTaskProgress(true)
|
|
||||||
|
|
||||||
message.info('AI 生成任务已启动,正在后台执行...')
|
// 轮询任务结果
|
||||||
|
let attempts = 0
|
||||||
|
const maxAttempts = 60 // 最多等待60秒
|
||||||
|
|
||||||
|
const pollResult = async (): Promise<any> => {
|
||||||
|
attempts++
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await api.get(`/tasks/${response.taskId}`)
|
||||||
|
|
||||||
|
if (result.status === 'completed') {
|
||||||
|
return result.result
|
||||||
|
} else if (result.status === 'failed') {
|
||||||
|
throw new Error(result.error || '任务执行失败')
|
||||||
|
} else if (attempts >= maxAttempts) {
|
||||||
|
throw new Error('任务执行超时')
|
||||||
|
} else {
|
||||||
|
// 继续轮询
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return pollResult()
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('创建任务失败:', error)
|
throw error
|
||||||
message.error(`创建任务失败: ${error.message || '未知错误'}`)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pollResult()
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
let characterText = ''
|
||||||
|
|
||||||
|
if (Array.isArray(result?.characters)) {
|
||||||
|
characterText = result.characters.map((c: any) => {
|
||||||
|
if (typeof c === 'string') return c
|
||||||
|
if (c.name) {
|
||||||
|
return c.lines ? `【${c.name}】出场 ${c.lines} 次` : `【${c.name}】`
|
||||||
|
}
|
||||||
|
return JSON.stringify(c)
|
||||||
|
}).join('\n')
|
||||||
|
} else if (typeof result?.characters === 'string') {
|
||||||
|
characterText = result.characters
|
||||||
|
} else if (typeof result === 'string') {
|
||||||
|
characterText = result
|
||||||
|
} else if (Array.isArray(result)) {
|
||||||
|
characterText = result.map((c: any) => {
|
||||||
|
if (typeof c === 'string') return c
|
||||||
|
if (c.name) {
|
||||||
|
return c.lines ? `【${c.name}】出场 ${c.lines} 次` : `【${c.name}】`
|
||||||
|
}
|
||||||
|
return JSON.stringify(c)
|
||||||
|
}).join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characterText) {
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, characters: characterText }))
|
||||||
|
form.setFieldsValue({ characters: characterText })
|
||||||
|
|
||||||
|
// 关闭loading并显示成功提示
|
||||||
|
message.destroy()
|
||||||
|
message.success('人物设定生成完成!', 1.5)
|
||||||
|
} else {
|
||||||
|
message.destroy()
|
||||||
|
message.warning('生成完成,但未返回有效内容')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('AI 生成人物设定失败:', error)
|
||||||
|
message.destroy()
|
||||||
|
message.error(`生成失败: ${error.message || '未知错误'}`)
|
||||||
} finally {
|
} finally {
|
||||||
setAiGeneratingCharacters(false)
|
setAiGeneratingCharacters(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 任务完成回调
|
|
||||||
const handleTaskComplete = (result: any) => {
|
|
||||||
setShowTaskProgress(false)
|
|
||||||
|
|
||||||
if (taskType === 'generate_characters') {
|
|
||||||
const characters = result?.characters || result
|
|
||||||
if (characters) {
|
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, characters }))
|
|
||||||
form.setFieldsValue({ characters })
|
|
||||||
message.success('人物设定生成完成!')
|
|
||||||
}
|
|
||||||
} else if (taskType === 'generate_outline') {
|
|
||||||
const outline = result?.outline || result
|
|
||||||
if (outline) {
|
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, outline }))
|
|
||||||
form.setFieldsValue({ overallOutline: outline })
|
|
||||||
message.success('剧情大纲生成完成!')
|
|
||||||
}
|
|
||||||
} else if (taskType === 'generate_world') {
|
|
||||||
const worldSetting = result?.worldSetting || result
|
|
||||||
if (worldSetting) {
|
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, worldSetting }))
|
|
||||||
form.setFieldsValue({ worldSetting })
|
|
||||||
message.success('世界观设定生成完成!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTaskResult(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 任务失败回调
|
|
||||||
const handleTaskError = (error: string) => {
|
|
||||||
setShowTaskProgress(false)
|
|
||||||
message.error(`任务执行失败: ${error}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭任务进度对话框
|
|
||||||
const handleCloseTaskProgress = () => {
|
|
||||||
setShowTaskProgress(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AI 分析上传的剧本
|
// AI 分析上传的剧本
|
||||||
const handleAIAnalyzeScript = async () => {
|
const handleAIAnalyzeScript = async () => {
|
||||||
setAiAnalyzingScript(true)
|
// 防止连续触发
|
||||||
try {
|
if (aiAnalyzingScript) return
|
||||||
console.log('开始调用 AI 分析剧本...')
|
|
||||||
|
|
||||||
|
setAiAnalyzingScript(true)
|
||||||
|
message.loading('AI 正在分析剧本...', 0)
|
||||||
|
|
||||||
|
try {
|
||||||
// 获取剧本分析配置的技能信息
|
// 获取剧本分析配置的技能信息
|
||||||
const selectedSkillsInfo = skills.filter(s => analyzeSkills.includes(s.id))
|
const selectedSkillsInfo = skills.filter(s => analyzeSkills.includes(s.id))
|
||||||
|
|
||||||
@ -477,8 +503,6 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
customPrompt: analyzePrompt || undefined
|
customPrompt: analyzePrompt || undefined
|
||||||
}) as any
|
}) as any
|
||||||
|
|
||||||
console.log('AI 剧本分析响应:', JSON.stringify(data, null, 2))
|
|
||||||
|
|
||||||
// 提取分析结果
|
// 提取分析结果
|
||||||
const characters = data?.characters || []
|
const characters = data?.characters || []
|
||||||
const outline = data?.outline || ''
|
const outline = data?.outline || ''
|
||||||
@ -492,21 +516,23 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
).join('\n')
|
).join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('分析结果 - 人物:', characterText, '概要:', summary)
|
|
||||||
|
|
||||||
// 更新表单字段
|
// 更新表单字段
|
||||||
if (characterText) {
|
if (characterText) {
|
||||||
form.setFieldsValue({ characters: characterText })
|
form.setFieldsValue({ characters: characterText })
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, characters: characterText }))
|
setAiGeneratedContent((prev: any) => ({ ...prev, characters: characterText }))
|
||||||
}
|
}
|
||||||
if (outline) {
|
if (outline) {
|
||||||
form.setFieldsValue({ overallOutline: outline })
|
const outlineText = typeof outline === 'string' ? outline : JSON.stringify(outline, null, 2)
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, outline }))
|
form.setFieldsValue({ overallOutline: outlineText })
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, outline: outlineText }))
|
||||||
}
|
}
|
||||||
|
|
||||||
message.success(`AI 分析完成!${summary}`)
|
// 关闭loading并显示成功提示
|
||||||
|
message.destroy()
|
||||||
|
message.success(`AI 分析完成!${summary}`, 1.5)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('AI 分析失败,完整错误:', error)
|
console.error('AI 分析失败,完整错误:', error)
|
||||||
|
message.destroy()
|
||||||
message.error(`AI 分析失败: ${error.message || '未知错误'}`)
|
message.error(`AI 分析失败: ${error.message || '未知错误'}`)
|
||||||
} finally {
|
} finally {
|
||||||
setAiAnalyzingScript(false)
|
setAiAnalyzingScript(false)
|
||||||
@ -515,10 +541,13 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
// AI 从剧本分析世界观
|
// AI 从剧本分析世界观
|
||||||
const handleAIAnalyzeWorldFromScript = async () => {
|
const handleAIAnalyzeWorldFromScript = async () => {
|
||||||
setAiAnalyzingWorldFromScript(true)
|
// 防止连续触发
|
||||||
try {
|
if (aiAnalyzingWorldFromScript) return
|
||||||
console.log('开始调用 AI 分析世界观...')
|
|
||||||
|
|
||||||
|
setAiAnalyzingWorldFromScript(true)
|
||||||
|
message.loading('AI 正在分析世界观...', 0)
|
||||||
|
|
||||||
|
try {
|
||||||
// 获取世界观生成配置的技能信息
|
// 获取世界观生成配置的技能信息
|
||||||
const selectedSkillsInfo = skills.filter(s => worldSkills.includes(s.id))
|
const selectedSkillsInfo = skills.filter(s => worldSkills.includes(s.id))
|
||||||
|
|
||||||
@ -535,13 +564,10 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
customPrompt: worldPrompt || undefined
|
customPrompt: worldPrompt || undefined
|
||||||
}) as any
|
}) as any
|
||||||
|
|
||||||
console.log('AI 世界观分析响应:', JSON.stringify(data, null, 2))
|
|
||||||
|
|
||||||
const worldSetting = data?.worldSetting || data?.result || data?.content || ''
|
const worldSetting = data?.worldSetting || data?.result || data?.content || ''
|
||||||
console.log('提取的世界观:', worldSetting?.substring(0, 100) + '...')
|
|
||||||
|
|
||||||
if (!worldSetting) {
|
if (!worldSetting) {
|
||||||
console.error('未能从响应中提取世界观,响应数据:', data)
|
message.destroy()
|
||||||
message.error('AI 分析失败:未返回有效内容')
|
message.error('AI 分析失败:未返回有效内容')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -550,9 +576,12 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
form.setFieldsValue({ worldSetting })
|
form.setFieldsValue({ worldSetting })
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, worldSetting }))
|
setAiGeneratedContent((prev: any) => ({ ...prev, worldSetting }))
|
||||||
|
|
||||||
message.success('AI 世界观分析完成!')
|
// 关闭loading并显示成功提示
|
||||||
|
message.destroy()
|
||||||
|
message.success('AI 世界观分析完成!', 1.5)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('AI 世界观分析失败,完整错误:', error)
|
console.error('AI 世界观分析失败,完整错误:', error)
|
||||||
|
message.destroy()
|
||||||
message.error(`AI 分析失败: ${error.message || '未知错误'}`)
|
message.error(`AI 分析失败: ${error.message || '未知错误'}`)
|
||||||
} finally {
|
} finally {
|
||||||
setAiAnalyzingWorldFromScript(false)
|
setAiAnalyzingWorldFromScript(false)
|
||||||
@ -561,7 +590,12 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
// AI 生成世界观
|
// AI 生成世界观
|
||||||
const handleAIGenerateWorldSetting = async () => {
|
const handleAIGenerateWorldSetting = async () => {
|
||||||
|
// 防止连续触发
|
||||||
|
if (aiGeneratingWorldSetting) return
|
||||||
|
|
||||||
setAiGeneratingWorldSetting(true)
|
setAiGeneratingWorldSetting(true)
|
||||||
|
message.loading('AI 正在生成世界观设定...', 0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const projectName = form.getFieldValue('name') || '未命名项目'
|
const projectName = form.getFieldValue('name') || '未命名项目'
|
||||||
const selectedSkillsInfo = skills.filter(s => worldSkills.includes(s.id))
|
const selectedSkillsInfo = skills.filter(s => worldSkills.includes(s.id))
|
||||||
@ -581,15 +615,57 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
customPrompt: worldPrompt || undefined
|
customPrompt: worldPrompt || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
// 显示任务进度
|
// 设置任务ID和类型,用于后续查询结果
|
||||||
setCurrentTaskId(response.taskId)
|
setCurrentTaskId(response.taskId)
|
||||||
setTaskType('generate_world')
|
setTaskType('generate_world')
|
||||||
setShowTaskProgress(true)
|
|
||||||
|
|
||||||
message.info('AI 生成任务已启动,正在后台执行...')
|
// 轮询任务结果
|
||||||
|
let attempts = 0
|
||||||
|
const maxAttempts = 60 // 最多等待60秒
|
||||||
|
|
||||||
|
const pollResult = async (): Promise<any> => {
|
||||||
|
attempts++
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await api.get(`/tasks/${response.taskId}`)
|
||||||
|
|
||||||
|
if (result.status === 'completed') {
|
||||||
|
return result.result
|
||||||
|
} else if (result.status === 'failed') {
|
||||||
|
throw new Error(result.error || '任务执行失败')
|
||||||
|
} else if (attempts >= maxAttempts) {
|
||||||
|
throw new Error('任务执行超时')
|
||||||
|
} else {
|
||||||
|
// 继续轮询
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return pollResult()
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('创建任务失败:', error)
|
throw error
|
||||||
message.error(`创建任务失败: ${error.message || '未知错误'}`)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pollResult()
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
const worldSetting = result?.worldSetting || result
|
||||||
|
const worldText = typeof worldSetting === 'string' ? worldSetting : JSON.stringify(worldSetting, null, 2)
|
||||||
|
|
||||||
|
if (worldText) {
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, worldSetting: worldText }))
|
||||||
|
form.setFieldsValue({ worldSetting: worldText })
|
||||||
|
|
||||||
|
// 关闭loading并显示成功提示
|
||||||
|
message.destroy()
|
||||||
|
message.success('世界观设定生成完成!', 1.5)
|
||||||
|
} else {
|
||||||
|
message.destroy()
|
||||||
|
message.warning('生成完成,但未返回有效内容')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('AI 生成世界观设定失败:', error)
|
||||||
|
message.destroy()
|
||||||
|
message.error(`生成失败: ${error.message || '未知错误'}`)
|
||||||
} finally {
|
} finally {
|
||||||
setAiGeneratingWorldSetting(false)
|
setAiGeneratingWorldSetting(false)
|
||||||
}
|
}
|
||||||
@ -597,7 +673,12 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
// AI 生成大纲
|
// AI 生成大纲
|
||||||
const handleAIGenerateOutline = async () => {
|
const handleAIGenerateOutline = async () => {
|
||||||
|
// 防止连续触发
|
||||||
|
if (aiGeneratingOutline) return
|
||||||
|
|
||||||
setAiGeneratingOutline(true)
|
setAiGeneratingOutline(true)
|
||||||
|
message.loading('AI 正在生成剧情大纲...', 0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const projectName = form.getFieldValue('name') || '未命名项目'
|
const projectName = form.getFieldValue('name') || '未命名项目'
|
||||||
const totalEpisodes = form.getFieldValue('totalEpisodes') || 30
|
const totalEpisodes = form.getFieldValue('totalEpisodes') || 30
|
||||||
@ -619,15 +700,57 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
customPrompt: outlinePrompt || undefined
|
customPrompt: outlinePrompt || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
// 显示任务进度
|
// 设置任务ID和类型,用于后续查询结果
|
||||||
setCurrentTaskId(response.taskId)
|
setCurrentTaskId(response.taskId)
|
||||||
setTaskType('generate_outline')
|
setTaskType('generate_outline')
|
||||||
setShowTaskProgress(true)
|
|
||||||
|
|
||||||
message.info('AI 生成任务已启动,正在后台执行...')
|
// 轮询任务结果
|
||||||
|
let attempts = 0
|
||||||
|
const maxAttempts = 60 // 最多等待60秒
|
||||||
|
|
||||||
|
const pollResult = async (): Promise<any> => {
|
||||||
|
attempts++
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await api.get(`/tasks/${response.taskId}`)
|
||||||
|
|
||||||
|
if (result.status === 'completed') {
|
||||||
|
return result.result
|
||||||
|
} else if (result.status === 'failed') {
|
||||||
|
throw new Error(result.error || '任务执行失败')
|
||||||
|
} else if (attempts >= maxAttempts) {
|
||||||
|
throw new Error('任务执行超时')
|
||||||
|
} else {
|
||||||
|
// 继续轮询
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return pollResult()
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('创建任务失败:', error)
|
throw error
|
||||||
message.error(`创建任务失败: ${error.message || '未知错误'}`)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pollResult()
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
const outline = result?.outline || result
|
||||||
|
const outlineText = typeof outline === 'string' ? outline : JSON.stringify(outline, null, 2)
|
||||||
|
|
||||||
|
if (outlineText) {
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, outline: outlineText }))
|
||||||
|
form.setFieldsValue({ overallOutline: outlineText })
|
||||||
|
|
||||||
|
// 关闭loading并显示成功提示
|
||||||
|
message.destroy()
|
||||||
|
message.success('剧情大纲生成完成!', 1.5)
|
||||||
|
} else {
|
||||||
|
message.destroy()
|
||||||
|
message.warning('生成完成,但未返回有效内容')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('AI 生成剧情大纲失败:', error)
|
||||||
|
message.destroy()
|
||||||
|
message.error(`生成失败: ${error.message || '未知错误'}`)
|
||||||
} finally {
|
} finally {
|
||||||
setAiGeneratingOutline(false)
|
setAiGeneratingOutline(false)
|
||||||
}
|
}
|
||||||
@ -665,8 +788,9 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
const worldSetting = data?.worldSetting || data?.result || data?.content || ''
|
const worldSetting = data?.worldSetting || data?.result || data?.content || ''
|
||||||
if (worldSetting) {
|
if (worldSetting) {
|
||||||
form.setFieldsValue({ worldSetting })
|
const worldText = typeof worldSetting === 'string' ? worldSetting : JSON.stringify(worldSetting, null, 2)
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, worldSetting }))
|
form.setFieldsValue({ worldSetting: worldText })
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, worldSetting: worldText }))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('生成世界观失败:', error)
|
console.error('生成世界观失败:', error)
|
||||||
@ -718,8 +842,22 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
const characters = data?.characters || data?.result || data?.content || ''
|
const characters = data?.characters || data?.result || data?.content || ''
|
||||||
if (characters) {
|
if (characters) {
|
||||||
form.setFieldsValue({ characters })
|
let characterText = ''
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, characters }))
|
if (typeof characters === 'string') {
|
||||||
|
characterText = characters
|
||||||
|
} else if (Array.isArray(characters)) {
|
||||||
|
characterText = characters.map((c: any) => {
|
||||||
|
if (typeof c === 'string') return c
|
||||||
|
if (c.name) {
|
||||||
|
return c.lines ? `【${c.name}】出场 ${c.lines} 次` : `【${c.name}】`
|
||||||
|
}
|
||||||
|
return JSON.stringify(c)
|
||||||
|
}).join('\n')
|
||||||
|
} else {
|
||||||
|
characterText = JSON.stringify(characters, null, 2)
|
||||||
|
}
|
||||||
|
form.setFieldsValue({ characters: characterText })
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, characters: characterText }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -749,8 +887,9 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
const outline = data?.outline || data?.analysis || ''
|
const outline = data?.outline || data?.analysis || ''
|
||||||
if (outline) {
|
if (outline) {
|
||||||
form.setFieldsValue({ overallOutline: outline })
|
const outlineText = typeof outline === 'string' ? outline : JSON.stringify(outline, null, 2)
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, outline }))
|
form.setFieldsValue({ overallOutline: outlineText })
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, outline: outlineText }))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 从头创作模式:生成大纲
|
// 从头创作模式:生成大纲
|
||||||
@ -769,8 +908,9 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
|
|
||||||
const outline = data?.outline || data?.result || data?.content || ''
|
const outline = data?.outline || data?.result || data?.content || ''
|
||||||
if (outline) {
|
if (outline) {
|
||||||
form.setFieldsValue({ overallOutline: outline })
|
const outlineText = typeof outline === 'string' ? outline : JSON.stringify(outline, null, 2)
|
||||||
setAiGeneratedContent((prev: any) => ({ ...prev, outline }))
|
form.setFieldsValue({ overallOutline: outlineText })
|
||||||
|
setAiGeneratedContent((prev: any) => ({ ...prev, outline: outlineText }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -849,12 +989,29 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理灵感内容(如果是文件上传,需要读取内容)
|
||||||
|
let inspirationContent = directTextInput || ''
|
||||||
|
if (createSubMode === 'file' && inspirationFile) {
|
||||||
|
// 灵感文件内容已经在 handleInspirationUpload 中读取
|
||||||
|
// 这里使用 originFileObj 来读取
|
||||||
|
const reader = new FileReader()
|
||||||
|
const fileContent = await new Promise<string>((resolve) => {
|
||||||
|
reader.onload = (e) => resolve(e.target?.result as string || '')
|
||||||
|
reader.readAsText(inspirationFile.originFileObj || inspirationFile)
|
||||||
|
})
|
||||||
|
inspirationContent = fileContent
|
||||||
|
}
|
||||||
|
|
||||||
const projectData: ProjectCreateRequest = {
|
const projectData: ProjectCreateRequest = {
|
||||||
name: values.name,
|
name: values.name,
|
||||||
totalEpisodes: values.totalEpisodes || 30,
|
totalEpisodes: values.totalEpisodes || 30,
|
||||||
globalContext: {
|
globalContext: {
|
||||||
worldSetting: values.worldSetting || '',
|
worldSetting: values.worldSetting || '',
|
||||||
overallOutline: values.overallOutline || '',
|
overallOutline: values.overallOutline || '',
|
||||||
|
// 保存创作内容
|
||||||
|
uploadedScript: uploadedScript || '', // 上传的剧本
|
||||||
|
inspiration: inspirationContent, // 灵感内容(直接输入或文件)
|
||||||
|
styleGuide: values.characters || '', // 人物设定
|
||||||
characterProfiles: {},
|
characterProfiles: {},
|
||||||
sceneSettings: {}
|
sceneSettings: {}
|
||||||
},
|
},
|
||||||
@ -1589,7 +1746,11 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
maxHeight: '200px',
|
maxHeight: '200px',
|
||||||
overflowY: 'auto'
|
overflowY: 'auto'
|
||||||
}}>
|
}}>
|
||||||
{aiGeneratedContent.characters || '未设置'}
|
{typeof aiGeneratedContent.characters === 'string'
|
||||||
|
? aiGeneratedContent.characters
|
||||||
|
: aiGeneratedContent.characters
|
||||||
|
? JSON.stringify(aiGeneratedContent.characters, null, 2)
|
||||||
|
: '未设置'}
|
||||||
</div>
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
@ -1602,7 +1763,11 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
maxHeight: '200px',
|
maxHeight: '200px',
|
||||||
overflowY: 'auto'
|
overflowY: 'auto'
|
||||||
}}>
|
}}>
|
||||||
{aiGeneratedContent.outline || '未设置'}
|
{typeof aiGeneratedContent.outline === 'string'
|
||||||
|
? aiGeneratedContent.outline
|
||||||
|
: aiGeneratedContent.outline
|
||||||
|
? JSON.stringify(aiGeneratedContent.outline, null, 2)
|
||||||
|
: '未设置'}
|
||||||
</div>
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
@ -1639,25 +1804,6 @@ export const ProjectCreateEnhanced = () => {
|
|||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 异步任务进度对话框 */}
|
|
||||||
<Modal
|
|
||||||
title="AI 生成进度"
|
|
||||||
open={showTaskProgress}
|
|
||||||
onCancel={handleCloseTaskProgress}
|
|
||||||
footer={null}
|
|
||||||
width={600}
|
|
||||||
closeIcon={<CloseOutlined />}
|
|
||||||
>
|
|
||||||
{currentTaskId && (
|
|
||||||
<TaskProgressTracker
|
|
||||||
taskId={currentTaskId}
|
|
||||||
onComplete={handleTaskComplete}
|
|
||||||
onError={handleTaskError}
|
|
||||||
pollInterval={1000}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,9 +16,8 @@ import { useProjectStore } from '@/stores/projectStore'
|
|||||||
import { useSkillStore } from '@/stores/skillStore'
|
import { useSkillStore } from '@/stores/skillStore'
|
||||||
import { Episode } from '@/services/projectService'
|
import { Episode } from '@/services/projectService'
|
||||||
import { taskService } from '@/services/taskService'
|
import { taskService } from '@/services/taskService'
|
||||||
import { TaskProgressTracker } from '@/components/TaskProgressTracker'
|
|
||||||
|
|
||||||
const { TextParagraph } = Typography
|
const { Paragraph } = Typography
|
||||||
const { TabPane } = Tabs
|
const { TabPane } = Tabs
|
||||||
const { TextArea } = Input
|
const { TextArea } = Input
|
||||||
|
|
||||||
@ -129,10 +128,9 @@ export const ProjectDetail = () => {
|
|||||||
|
|
||||||
// 全局设定生成相关状态
|
// 全局设定生成相关状态
|
||||||
const [globalForm] = Form.useForm()
|
const [globalForm] = Form.useForm()
|
||||||
const [generating, setGenerating] = useState(false)
|
const [generatingWorld, setGeneratingWorld] = useState(false)
|
||||||
const [currentTaskId, setCurrentTaskId] = useState<string | null>(null)
|
const [generatingCharacters, setGeneratingCharacters] = useState(false)
|
||||||
const [showTaskProgress, setShowTaskProgress] = useState(false)
|
const [generatingOutline, setGeneratingOutline] = useState(false)
|
||||||
const [taskType, setTaskType] = useState<string | null>(null)
|
|
||||||
|
|
||||||
// Skills 配置
|
// Skills 配置
|
||||||
const [worldSkills, setWorldSkills] = useState<string[]>([])
|
const [worldSkills, setWorldSkills] = useState<string[]>([])
|
||||||
@ -180,7 +178,7 @@ export const ProjectDetail = () => {
|
|||||||
// 初始化全局设定表单
|
// 初始化全局设定表单
|
||||||
globalForm.setFieldsValue({
|
globalForm.setFieldsValue({
|
||||||
worldSetting: currentProject.globalContext?.worldSetting || '',
|
worldSetting: currentProject.globalContext?.worldSetting || '',
|
||||||
characters: currentProject.globalContext?.characterProfiles || '',
|
characters: currentProject.globalContext?.styleGuide || '',
|
||||||
overallOutline: currentProject.globalContext?.overallOutline || ''
|
overallOutline: currentProject.globalContext?.overallOutline || ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -273,19 +271,29 @@ export const ProjectDetail = () => {
|
|||||||
// AI生成/分析世界观
|
// AI生成/分析世界观
|
||||||
const handleGenerateWorld = async () => {
|
const handleGenerateWorld = async () => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
if (generatingWorld) return // 防止重复点击
|
||||||
|
|
||||||
const projectName = currentProject?.name || '未命名项目'
|
const projectName = currentProject?.name || '未命名项目'
|
||||||
const selectedSkillsInfo = skills.filter(s => worldSkills.includes(s.id))
|
const selectedSkillsInfo = skills.filter(s => worldSkills.includes(s.id))
|
||||||
|
|
||||||
// 根据创作方式决定是分析还是生成
|
// 根据创作方式决定是分析还是生成(从项目数据中实时获取最新内容)
|
||||||
|
const projectScript = currentProject?.globalContext?.uploadedScript || ''
|
||||||
|
const projectInspiration = currentProject?.globalContext?.inspiration || ''
|
||||||
|
|
||||||
|
// 如果有编辑中的内容,优先使用;否则使用项目中保存的内容
|
||||||
|
const finalScript = scriptContent || projectScript
|
||||||
|
const finalInspiration = inspirationContent || projectInspiration
|
||||||
|
|
||||||
const isAnalysis = creationMode === 'script'
|
const isAnalysis = creationMode === 'script'
|
||||||
const baseContent = isAnalysis ? scriptContent : inspirationContent
|
const baseContent = isAnalysis ? finalScript : finalInspiration
|
||||||
const idea = isAnalysis
|
const idea = isAnalysis
|
||||||
? `分析以下剧本,提取世界观设定:\n${baseContent?.substring(0, 2000)}`
|
? `分析以下剧本,提取世界观设定:\n${baseContent?.substring(0, 2000)}`
|
||||||
: `项目名称:${projectName}\n创意灵感:\n${baseContent}`
|
: `项目名称:${projectName}\n创意灵感:\n${baseContent}`
|
||||||
|
|
||||||
|
const hideMessage = message.loading(isAnalysis ? '正在分析剧本...' : '正在生成世界观设定...', 0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setGenerating(true)
|
setGeneratingWorld(true)
|
||||||
const response = await taskService.generateWorld({
|
const response = await taskService.generateWorld({
|
||||||
idea,
|
idea,
|
||||||
projectName,
|
projectName,
|
||||||
@ -299,31 +307,58 @@ export const ProjectDetail = () => {
|
|||||||
projectId: id
|
projectId: id
|
||||||
})
|
})
|
||||||
|
|
||||||
setCurrentTaskId(response.taskId)
|
// 轮询任务直到完成
|
||||||
setTaskType('generate_world')
|
const result = await taskService.pollTask(response.taskId)
|
||||||
setShowTaskProgress(true)
|
|
||||||
|
const worldSetting = result?.worldSetting || result
|
||||||
|
const worldText = typeof worldSetting === 'string' ? worldSetting : JSON.stringify(worldSetting, null, 2)
|
||||||
|
globalForm.setFieldsValue({ worldSetting: worldText })
|
||||||
|
|
||||||
|
hideMessage()
|
||||||
|
message.success(isAnalysis ? '世界观分析完成!' : '世界观设定生成完成!')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`创建任务失败: ${error.message || '未知错误'}`)
|
hideMessage()
|
||||||
setGenerating(false)
|
message.error(`生成失败: ${error.message || '未知错误'}`)
|
||||||
|
} finally {
|
||||||
|
setGeneratingWorld(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI生成/分析人物设定
|
// AI生成/分析人物设定
|
||||||
const handleGenerateCharacters = async () => {
|
const handleGenerateCharacters = async () => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
if (generatingCharacters) return // 防止重复点击
|
||||||
|
|
||||||
const projectName = currentProject?.name || '未命名项目'
|
const projectName = currentProject?.name || '未命名项目'
|
||||||
const totalEpisodes = currentProject?.totalEpisodes || 30
|
const totalEpisodes = currentProject?.totalEpisodes || 30
|
||||||
const selectedSkillsInfo = skills.filter(s => characterSkills.includes(s.id))
|
const selectedSkillsInfo = skills.filter(s => characterSkills.includes(s.id))
|
||||||
|
|
||||||
|
// 获取已生成的世界观设定,保证一致性
|
||||||
|
const worldSetting = globalForm.getFieldValue('worldSetting') || ''
|
||||||
|
|
||||||
|
// 从项目数据中实时获取最新内容
|
||||||
|
const projectScript = currentProject?.globalContext?.uploadedScript || ''
|
||||||
|
const projectInspiration = currentProject?.globalContext?.inspiration || ''
|
||||||
|
|
||||||
|
// 如果有编辑中的内容,优先使用;否则使用项目中保存的内容
|
||||||
|
const finalScript = scriptContent || projectScript
|
||||||
|
const finalInspiration = inspirationContent || projectInspiration
|
||||||
|
|
||||||
const isAnalysis = creationMode === 'script'
|
const isAnalysis = creationMode === 'script'
|
||||||
const baseContent = isAnalysis ? scriptContent : inspirationContent
|
const baseContent = isAnalysis ? finalScript : finalInspiration
|
||||||
const idea = isAnalysis
|
let idea = isAnalysis
|
||||||
? `分析以下剧本,提取人物设定:\n${baseContent?.substring(0, 2000)}`
|
? `分析以下剧本,提取人物设定:\n${baseContent?.substring(0, 2000)}`
|
||||||
: `项目名称:${projectName},总集数:${totalEpisodes}集\n创意灵感:\n${baseContent}`
|
: `项目名称:${projectName},总集数:${totalEpisodes}集\n创意灵感:\n${baseContent}`
|
||||||
|
|
||||||
|
// 如果已有世界观设定,将其作为上下文
|
||||||
|
if (worldSetting && !isAnalysis) {
|
||||||
|
idea += `\n\n【世界观设定】\n${worldSetting}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideMessage = message.loading(isAnalysis ? '正在分析人物设定...' : '正在生成人物设定...', 0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setGenerating(true)
|
setGeneratingCharacters(true)
|
||||||
const response = await taskService.generateCharacters({
|
const response = await taskService.generateCharacters({
|
||||||
idea,
|
idea,
|
||||||
projectName,
|
projectName,
|
||||||
@ -337,31 +372,77 @@ export const ProjectDetail = () => {
|
|||||||
projectId: id
|
projectId: id
|
||||||
})
|
})
|
||||||
|
|
||||||
setCurrentTaskId(response.taskId)
|
// 轮询任务直到完成
|
||||||
setTaskType('generate_characters')
|
const result = await taskService.pollTask(response.taskId)
|
||||||
setShowTaskProgress(true)
|
|
||||||
|
const characters = result?.characters || result
|
||||||
|
let characterText = ''
|
||||||
|
if (typeof characters === 'string') {
|
||||||
|
characterText = characters
|
||||||
|
} else if (Array.isArray(characters)) {
|
||||||
|
characterText = characters.map((c: any) => {
|
||||||
|
if (typeof c === 'string') return c
|
||||||
|
if (c.name) {
|
||||||
|
return c.lines ? `【${c.name}】出场 ${c.lines} 次` : `【${c.name}】`
|
||||||
|
}
|
||||||
|
return JSON.stringify(c)
|
||||||
|
}).join('\n')
|
||||||
|
} else {
|
||||||
|
characterText = JSON.stringify(characters, null, 2)
|
||||||
|
}
|
||||||
|
globalForm.setFieldsValue({ characters: characterText })
|
||||||
|
|
||||||
|
hideMessage()
|
||||||
|
message.success(isAnalysis ? '人物设定分析完成!' : '人物设定生成完成!')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`创建任务失败: ${error.message || '未知错误'}`)
|
hideMessage()
|
||||||
setGenerating(false)
|
message.error(`生成失败: ${error.message || '未知错误'}`)
|
||||||
|
} finally {
|
||||||
|
setGeneratingCharacters(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI生成/分析大纲
|
// AI生成/分析大纲
|
||||||
const handleGenerateOutline = async () => {
|
const handleGenerateOutline = async () => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
if (generatingOutline) return // 防止重复点击
|
||||||
|
|
||||||
const projectName = currentProject?.name || '未命名项目'
|
const projectName = currentProject?.name || '未命名项目'
|
||||||
const totalEpisodes = currentProject?.totalEpisodes || 30
|
const totalEpisodes = currentProject?.totalEpisodes || 30
|
||||||
const selectedSkillsInfo = skills.filter(s => outlineSkills.includes(s.id))
|
const selectedSkillsInfo = skills.filter(s => outlineSkills.includes(s.id))
|
||||||
|
|
||||||
|
// 获取已生成的世界观设定和人物设定,保证一致性
|
||||||
|
const worldSetting = globalForm.getFieldValue('worldSetting') || ''
|
||||||
|
const characters = globalForm.getFieldValue('characters') || ''
|
||||||
|
|
||||||
|
// 从项目数据中实时获取最新内容
|
||||||
|
const projectScript = currentProject?.globalContext?.uploadedScript || ''
|
||||||
|
const projectInspiration = currentProject?.globalContext?.inspiration || ''
|
||||||
|
|
||||||
|
// 如果有编辑中的内容,优先使用;否则使用项目中保存的内容
|
||||||
|
const finalScript = scriptContent || projectScript
|
||||||
|
const finalInspiration = inspirationContent || projectInspiration
|
||||||
|
|
||||||
const isAnalysis = creationMode === 'script'
|
const isAnalysis = creationMode === 'script'
|
||||||
const baseContent = isAnalysis ? scriptContent : inspirationContent
|
const baseContent = isAnalysis ? finalScript : finalInspiration
|
||||||
const idea = isAnalysis
|
let idea = isAnalysis
|
||||||
? `分析以下剧本,提取整体大纲:\n${baseContent?.substring(0, 2000)}`
|
? `分析以下剧本,提取整体大纲:\n${baseContent?.substring(0, 2000)}`
|
||||||
: `项目名称:${projectName},总集数:${totalEpisodes}集\n创意灵感:\n${baseContent}`
|
: `项目名称:${projectName},总集数:${totalEpisodes}集\n创意灵感:\n${baseContent}`
|
||||||
|
|
||||||
|
// 如果已有世界观设定和人物设定,将其作为上下文
|
||||||
|
if (!isAnalysis) {
|
||||||
|
if (worldSetting) {
|
||||||
|
idea += `\n\n【世界观设定】\n${worldSetting}`
|
||||||
|
}
|
||||||
|
if (characters) {
|
||||||
|
idea += `\n\n【人物设定】\n${characters}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideMessage = message.loading(isAnalysis ? '正在分析整体大纲...' : '正在生成整体大纲...', 0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setGenerating(true)
|
setGeneratingOutline(true)
|
||||||
const response = await taskService.generateOutline({
|
const response = await taskService.generateOutline({
|
||||||
idea,
|
idea,
|
||||||
totalEpisodes,
|
totalEpisodes,
|
||||||
@ -376,40 +457,21 @@ export const ProjectDetail = () => {
|
|||||||
projectId: id
|
projectId: id
|
||||||
})
|
})
|
||||||
|
|
||||||
setCurrentTaskId(response.taskId)
|
// 轮询任务直到完成
|
||||||
setTaskType('generate_outline')
|
const result = await taskService.pollTask(response.taskId)
|
||||||
setShowTaskProgress(true)
|
|
||||||
} catch (error: any) {
|
|
||||||
message.error(`创建任务失败: ${error.message || '未知错误'}`)
|
|
||||||
setGenerating(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 任务完成回调
|
|
||||||
const handleTaskComplete = (result: any) => {
|
|
||||||
setShowTaskProgress(false)
|
|
||||||
setGenerating(false)
|
|
||||||
|
|
||||||
if (taskType === 'generate_world') {
|
|
||||||
const worldSetting = result?.worldSetting || result
|
|
||||||
globalForm.setFieldsValue({ worldSetting })
|
|
||||||
message.success('世界观设定生成完成!')
|
|
||||||
} else if (taskType === 'generate_characters') {
|
|
||||||
const characters = result?.characters || result
|
|
||||||
globalForm.setFieldsValue({ characters })
|
|
||||||
message.success('人物设定生成完成!')
|
|
||||||
} else if (taskType === 'generate_outline') {
|
|
||||||
const outline = result?.outline || result
|
const outline = result?.outline || result
|
||||||
globalForm.setFieldsValue({ overallOutline: outline })
|
const outlineText = typeof outline === 'string' ? outline : JSON.stringify(outline, null, 2)
|
||||||
message.success('大纲生成完成!')
|
globalForm.setFieldsValue({ overallOutline: outlineText })
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 任务失败回调
|
hideMessage()
|
||||||
const handleTaskError = (error: string) => {
|
message.success(isAnalysis ? '整体大纲分析完成!' : '整体大纲生成完成!')
|
||||||
setShowTaskProgress(false)
|
} catch (error: any) {
|
||||||
setGenerating(false)
|
hideMessage()
|
||||||
message.error(`任务执行失败: ${error}`)
|
message.error(`生成失败: ${error.message || '未知错误'}`)
|
||||||
|
} finally {
|
||||||
|
setGeneratingOutline(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存全局设定
|
// 保存全局设定
|
||||||
@ -424,8 +486,9 @@ export const ProjectDetail = () => {
|
|||||||
...currentProject?.globalContext,
|
...currentProject?.globalContext,
|
||||||
worldSetting: worldSetting || '',
|
worldSetting: worldSetting || '',
|
||||||
overallOutline: overallOutline || '',
|
overallOutline: overallOutline || '',
|
||||||
characterProfiles: characters ? {} : {},
|
styleGuide: characters || '',
|
||||||
sceneSettings: {}
|
characterProfiles: currentProject?.globalContext?.characterProfiles || {},
|
||||||
|
sceneSettings: currentProject?.globalContext?.sceneSettings || {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -812,9 +875,9 @@ export const ProjectDetail = () => {
|
|||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
icon={generating ? <LoadingOutlined /> : <RobotOutlined />}
|
icon={generatingWorld ? <LoadingOutlined /> : <RobotOutlined />}
|
||||||
onClick={handleGenerateWorld}
|
onClick={handleGenerateWorld}
|
||||||
disabled={generating}
|
disabled={generatingWorld}
|
||||||
>
|
>
|
||||||
{isAnalysisMode ? 'AI 分析' : 'AI 生成'}
|
{isAnalysisMode ? 'AI 分析' : 'AI 生成'}
|
||||||
</Button>
|
</Button>
|
||||||
@ -852,9 +915,9 @@ export const ProjectDetail = () => {
|
|||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
icon={generating ? <LoadingOutlined /> : <RobotOutlined />}
|
icon={generatingCharacters ? <LoadingOutlined /> : <RobotOutlined />}
|
||||||
onClick={handleGenerateCharacters}
|
onClick={handleGenerateCharacters}
|
||||||
disabled={generating}
|
disabled={generatingCharacters}
|
||||||
>
|
>
|
||||||
{isAnalysisMode ? 'AI 分析' : 'AI 生成'}
|
{isAnalysisMode ? 'AI 分析' : 'AI 生成'}
|
||||||
</Button>
|
</Button>
|
||||||
@ -892,9 +955,9 @@ export const ProjectDetail = () => {
|
|||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
icon={generating ? <LoadingOutlined /> : <RobotOutlined />}
|
icon={generatingOutline ? <LoadingOutlined /> : <RobotOutlined />}
|
||||||
onClick={handleGenerateOutline}
|
onClick={handleGenerateOutline}
|
||||||
disabled={generating}
|
disabled={generatingOutline}
|
||||||
>
|
>
|
||||||
{isAnalysisMode ? 'AI 分析' : 'AI 生成'}
|
{isAnalysisMode ? 'AI 分析' : 'AI 生成'}
|
||||||
</Button>
|
</Button>
|
||||||
@ -1059,25 +1122,6 @@ export const ProjectDetail = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
{/* AI生成任务进度弹窗 */}
|
|
||||||
<Modal
|
|
||||||
title="AI 生成进度"
|
|
||||||
open={showTaskProgress}
|
|
||||||
onCancel={() => setShowTaskProgress(false)}
|
|
||||||
footer={null}
|
|
||||||
width={600}
|
|
||||||
closable={!generating}
|
|
||||||
>
|
|
||||||
{currentTaskId && (
|
|
||||||
<TaskProgressTracker
|
|
||||||
taskId={currentTaskId}
|
|
||||||
onComplete={handleTaskComplete}
|
|
||||||
onError={handleTaskError}
|
|
||||||
pollInterval={1000}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ interface ProjectStore {
|
|||||||
currentProject: SeriesProject | null
|
currentProject: SeriesProject | null
|
||||||
episodes: Episode[]
|
episodes: Episode[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
|
loadingEpisodes: boolean
|
||||||
error: string | null
|
error: string | null
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@ -27,6 +28,7 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
|
|||||||
currentProject: null,
|
currentProject: null,
|
||||||
episodes: [],
|
episodes: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
loadingEpisodes: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
|
||||||
fetchProjects: async () => {
|
fetchProjects: async () => {
|
||||||
@ -93,12 +95,12 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fetchEpisodes: async (projectId: string) => {
|
fetchEpisodes: async (projectId: string) => {
|
||||||
set({ loading: true, error: null })
|
set({ loadingEpisodes: true, error: null })
|
||||||
try {
|
try {
|
||||||
const episodes = await projectService.listEpisodes(projectId)
|
const episodes = await projectService.listEpisodes(projectId)
|
||||||
set({ episodes, loading: false })
|
set({ episodes, loadingEpisodes: false })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
set({ error: (error as Error).message, loading: false })
|
set({ error: (error as Error).message, loadingEpisodes: false })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user