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