fix: 大纲确认/修改时显示 loading + novel 消息使用 typewriter 逐字流式输出
- confirmOutline / modifyOutline 中设置 pendingNextResponse.value = true 解决大纲确认/修改后等待响应期间无 loading 的问题 - novel_start 时为 novel 消息创建独立 typewriter writer 实例(novelWriters Map) - novel_delta 时通过 writer.push() 推送增量,逐字渲染 visibleText - novel_done 时 finish writer 并同步最终文本,清理 writer 实例 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -290,6 +290,8 @@ const currentVersionMessageId = ref('')
|
|||||||
const novelSessionId = ref('')
|
const novelSessionId = ref('')
|
||||||
const novelFullText = ref('')
|
const novelFullText = ref('')
|
||||||
const novelOutline = ref(null)
|
const novelOutline = ref(null)
|
||||||
|
// novel 消息 id → typewriter writer 实例映射(用于真正的逐字流式输出)
|
||||||
|
const novelWriters = new Map()
|
||||||
const clarificationCard = ref(null)
|
const clarificationCard = ref(null)
|
||||||
const currentStreamTask = ref(null)
|
const currentStreamTask = ref(null)
|
||||||
const answeringClarification = ref(false)
|
const answeringClarification = ref(false)
|
||||||
@@ -1705,16 +1707,27 @@ const handleShortNovelEvent = (event) => {
|
|||||||
generationStatus.value = 'idle'
|
generationStatus.value = 'idle'
|
||||||
pendingNextResponse.value = false
|
pendingNextResponse.value = false
|
||||||
break
|
break
|
||||||
case 'novel_start':
|
case 'novel_start': {
|
||||||
addResultMessage({ role: 'assistant', kind: 'novel', content: '', pending: true })
|
const novelMsg = addResultMessage({ role: 'assistant', kind: 'novel', content: '', pending: true })
|
||||||
|
// 为这条 novel 消息创建独立的 typewriter writer,实现逐字流式输出
|
||||||
|
novelWriters.set(novelMsg.id, useTypewriterStream({ interval: 32, step: 1 }))
|
||||||
generationStatus.value = 'streaming'
|
generationStatus.value = 'streaming'
|
||||||
pendingNextResponse.value = false
|
pendingNextResponse.value = false
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'novel_delta': {
|
case 'novel_delta': {
|
||||||
// 往最后一条 novel 消息追加 delta
|
// 往最后一条 pending novel 消息推送 delta 到 typewriter writer
|
||||||
const lastNovel = [...resultMessages.value].reverse().find(m => m.kind === 'novel' && m.pending)
|
const lastNovel = [...resultMessages.value].reverse().find(m => m.kind === 'novel' && m.pending)
|
||||||
if (lastNovel) {
|
if (lastNovel) {
|
||||||
lastNovel.content += payload.delta || ''
|
const writer = novelWriters.get(lastNovel.id)
|
||||||
|
const delta = payload.delta || ''
|
||||||
|
if (writer) {
|
||||||
|
writer.push((writer.targetText?.value || '') + delta)
|
||||||
|
lastNovel.content = writer.visibleText.value
|
||||||
|
// writer 的 visibleText 是 reactive,需用 watch;但这里直接读取值即可
|
||||||
|
} else {
|
||||||
|
lastNovel.content += delta
|
||||||
|
}
|
||||||
}
|
}
|
||||||
keepResultAtBottom()
|
keepResultAtBottom()
|
||||||
break
|
break
|
||||||
@@ -1723,7 +1736,17 @@ const handleShortNovelEvent = (event) => {
|
|||||||
// 标记最后一条 novel 消息完成
|
// 标记最后一条 novel 消息完成
|
||||||
const lastNovel = [...resultMessages.value].reverse().find(m => m.kind === 'novel')
|
const lastNovel = [...resultMessages.value].reverse().find(m => m.kind === 'novel')
|
||||||
if (lastNovel) {
|
if (lastNovel) {
|
||||||
if (payload.full_text) lastNovel.content = payload.full_text
|
const writer = novelWriters.get(lastNovel.id)
|
||||||
|
const finalText = payload.full_text || ''
|
||||||
|
if (writer) {
|
||||||
|
writer.finish(finalText)
|
||||||
|
// finish 后 typewriter 会逐字排空到 targetText,等排空完成再写回 content
|
||||||
|
// 但为了持久化和立即显示,这里直接同步设值
|
||||||
|
lastNovel.content = finalText
|
||||||
|
novelWriters.delete(lastNovel.id)
|
||||||
|
} else if (finalText) {
|
||||||
|
lastNovel.content = finalText
|
||||||
|
}
|
||||||
lastNovel.pending = false
|
lastNovel.pending = false
|
||||||
}
|
}
|
||||||
scriptId.value = payload.scriptId || ''
|
scriptId.value = payload.scriptId || ''
|
||||||
@@ -1775,6 +1798,7 @@ const submitClarification = (answer) => {
|
|||||||
|
|
||||||
const confirmOutline = (msg) => {
|
const confirmOutline = (msg) => {
|
||||||
if (msg) msg.confirmed = true
|
if (msg) msg.confirmed = true
|
||||||
|
pendingNextResponse.value = true
|
||||||
addResultMessage({ role: 'user', kind: 'text', content: '确认大纲' })
|
addResultMessage({ role: 'user', kind: 'text', content: '确认大纲' })
|
||||||
currentStreamTask.value = followupStream({
|
currentStreamTask.value = followupStream({
|
||||||
sessionId: novelSessionId.value,
|
sessionId: novelSessionId.value,
|
||||||
@@ -1792,6 +1816,7 @@ const modifyOutline = (msg) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (msg) msg.confirmed = true
|
if (msg) msg.confirmed = true
|
||||||
|
pendingNextResponse.value = true
|
||||||
addResultMessage({ role: 'user', kind: 'text', content: feedback })
|
addResultMessage({ role: 'user', kind: 'text', content: feedback })
|
||||||
currentStreamTask.value = followupStream({
|
currentStreamTask.value = followupStream({
|
||||||
sessionId: novelSessionId.value,
|
sessionId: novelSessionId.value,
|
||||||
|
|||||||
Reference in New Issue
Block a user