fix:AI创作优化

This commit is contained in:
zhonghua 2026-04-13 16:40:46 +08:00
parent 0d7063077f
commit 93c1f0d497
2 changed files with 94 additions and 56 deletions

View File

@ -207,7 +207,9 @@ const startWebSocket = (workId: string) => {
closeWebSocket() closeWebSocket()
saveWorkId('') saveWorkId('')
const route = getRouteByStatus(STATUS.COMPLETED, workId) const route = getRouteByStatus(STATUS.COMPLETED, workId)
if (route) setTimeout(() => router.replace(route), 800) if (route) {
setTimeout(() => router.replace(route), 800)
}
} else if (data.progress < 0) { } else if (data.progress < 0) {
closeWebSocket() closeWebSocket()
saveWorkId('') saveWorkId('')
@ -246,52 +248,59 @@ const closeWebSocket = () => {
stompClient = null stompClient = null
} }
} }
async function getWorkDetailApi(workId: string) {
try {
const work = await getWorkDetail(workId)
if (!work) return
if (consecutiveErrors > 0 || networkWarn.value) {
consecutiveErrors = 0
networkWarn.value = false
}
if (work.progress != null && work.progress > progress.value) progress.value = work.progress
if (work.progressMessage) stage.value = friendlyStage(progress.value, work.progressMessage)
if (work.status >= STATUS.COMPLETED) {
progress.value = 100
stage.value = '绘本创作完成'
clearInterval(pollTimer!)
pollTimer = null
saveWorkId('')
const route = getRouteByStatus(work.status, workId)
if (route) replaceWhenCreationAdvances(route)
} else if (work.status === STATUS.FAILED) {
clearInterval(pollTimer!)
pollTimer = null
saveWorkId('')
error.value = sanitizeError(work.failReason)
}
} catch {
consecutiveErrors++
if (consecutiveErrors > MAX_POLL_ERRORS) {
clearInterval(pollTimer!)
pollTimer = null
networkWarn.value = false
error.value = '网络连接异常,创作仍在后台进行中'
} else if (consecutiveErrors > MAX_SILENT_ERRORS) {
networkWarn.value = true
}
}
}
// B2 // B2
const startPolling = (workId: string) => { const startPolling = async (workId: string) => {
if (pollTimer) clearInterval(pollTimer) if (pollTimer) clearInterval(pollTimer)
consecutiveErrors = 0 consecutiveErrors = 0
networkWarn.value = false networkWarn.value = false
pollTimer = setInterval(async () => { pollTimer = setInterval(async () => {
try { await getWorkDetailApi(workId);
const work = await getWorkDetail(workId) }, 8000);
if (!work) return try {
await getWorkDetailApi(workId);
if (consecutiveErrors > 0 || networkWarn.value) { } catch (error) {
consecutiveErrors = 0 console.log('error', error);
networkWarn.value = false }
}
if (work.progress != null && work.progress > progress.value) progress.value = work.progress
if (work.progressMessage) stage.value = friendlyStage(progress.value, work.progressMessage)
if (work.status >= STATUS.COMPLETED) {
progress.value = 100
stage.value = '绘本创作完成'
clearInterval(pollTimer!)
pollTimer = null
saveWorkId('')
const route = getRouteByStatus(work.status, workId)
if (route) replaceWhenCreationAdvances(route)
} else if (work.status === STATUS.FAILED) {
clearInterval(pollTimer!)
pollTimer = null
saveWorkId('')
error.value = sanitizeError(work.failReason)
}
} catch {
consecutiveErrors++
if (consecutiveErrors > MAX_POLL_ERRORS) {
clearInterval(pollTimer!)
pollTimer = null
networkWarn.value = false
error.value = '网络连接异常,创作仍在后台进行中'
} else if (consecutiveErrors > MAX_SILENT_ERRORS) {
networkWarn.value = true
}
}
}, 8000)
} }
const startCreation = async () => { const startCreation = async () => {
@ -323,9 +332,10 @@ const startCreation = async () => {
saveWorkId(workId) saveWorkId(workId)
progress.value = 10 progress.value = 10
stage.value = '故事构思中…' stage.value = '故事构思中…'
startWebSocket(workId) // startWebSocket(workId)
startPolling(store.workId)
} catch (e: any) { } catch (e: any) {
console.error('e', e);
if (store.workId) { if (store.workId) {
progress.value = 10 progress.value = 10
stage.value = '创作已提交到后台…' stage.value = '创作已提交到后台…'
@ -366,15 +376,19 @@ onMounted(() => {
tipTimer = setInterval(() => { tipTimer = setInterval(() => {
currentTipIdx.value = (currentTipIdx.value + 1) % creatingTips.length currentTipIdx.value = (currentTipIdx.value + 1) % creatingTips.length
}, 3500) }, 3500)
// workId // workId
const urlWorkId = new URLSearchParams(window.location.search).get('workId') const urlWorkId = new URLSearchParams(window.location.search).get('workId')
console.log('store.workId', urlWorkId, window.location.search)
if (urlWorkId) { if (urlWorkId) {
saveWorkId(urlWorkId) saveWorkId(urlWorkId)
} else { } else {
restoreWorkId() restoreWorkId()
} }
try {
getWorkDetailApi(store.workId)
} catch (error) {
console.log('error', error);
}
if (store.workId) { if (store.workId) {
submitted = true submitted = true
progress.value = 10 progress.value = 10

View File

@ -53,13 +53,7 @@ export default { name: 'PreviewView' }
<!-- 4. 缩略图横向胶卷 --> <!-- 4. 缩略图横向胶卷 -->
<div class="thumb-strip" ref="thumbStrip"> <div class="thumb-strip" ref="thumbStrip">
<div <div v-for="(p, i) in pages" :key="i" class="thumb-item" :class="{ active: i === idx }" @click="idx = i">
v-for="(p, i) in pages"
:key="i"
class="thumb-item"
:class="{ active: i === idx }"
@click="idx = i"
>
<img v-if="p.imageUrl" :src="p.imageUrl" class="thumb-img" /> <img v-if="p.imageUrl" :src="p.imageUrl" class="thumb-img" />
<div v-else class="thumb-placeholder"> <div v-else class="thumb-placeholder">
<picture-outlined /> <picture-outlined />
@ -156,6 +150,9 @@ async function loadWork() {
imageUrl: p.imageUrl, imageUrl: p.imageUrl,
audioUrl: p.audioUrl, audioUrl: p.audioUrl,
})) }))
// workId
store.reset()
} catch (e: any) { } catch (e: any) {
error.value = e.message || '加载失败' error.value = e.message || '加载失败'
} finally { } finally {
@ -185,11 +182,13 @@ onMounted(loadWork)
text-align: center; text-align: center;
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.18); box-shadow: 0 6px 20px rgba(99, 102, 241, 0.18);
} }
.top-title { .top-title {
font-size: 20px; font-size: 20px;
font-weight: 800; font-weight: 800;
letter-spacing: 1px; letter-spacing: 1px;
} }
.top-sub { .top-sub {
font-size: 13px; font-size: 13px;
opacity: 0.92; opacity: 0.92;
@ -207,26 +206,31 @@ onMounted(loadWork)
justify-content: center; justify-content: center;
padding: 60px 24px; padding: 60px 24px;
} }
.loading-icon { .loading-icon {
font-size: 44px; font-size: 44px;
color: var(--ai-primary); color: var(--ai-primary);
margin-bottom: 14px; margin-bottom: 14px;
} }
.loading-text { .loading-text {
color: var(--ai-text-sub); color: var(--ai-text-sub);
font-size: 14px; font-size: 14px;
} }
.error-icon { .error-icon {
font-size: 48px; font-size: 48px;
color: var(--ai-text-sub); color: var(--ai-text-sub);
margin-bottom: 14px; margin-bottom: 14px;
} }
.error-title { .error-title {
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
color: var(--ai-text); color: var(--ai-text);
margin-bottom: 6px; margin-bottom: 6px;
} }
.error-msg { .error-msg {
color: var(--ai-text-sub); color: var(--ai-text-sub);
font-size: 13px; font-size: 13px;
@ -234,6 +238,7 @@ onMounted(loadWork)
text-align: center; text-align: center;
max-width: 280px; max-width: 280px;
} }
.error-btn { .error-btn {
width: auto !important; width: auto !important;
padding: 10px 32px !important; padding: 10px 32px !important;
@ -252,9 +257,11 @@ onMounted(loadWork)
position: relative; position: relative;
border-radius: 18px; border-radius: 18px;
overflow: hidden; overflow: hidden;
background: #1e1b4b; /* 深紫黑底,电影画幅感 */ background: #1e1b4b;
/* 深紫黑底,电影画幅感 */
box-shadow: 0 8px 28px rgba(99, 102, 241, 0.2); box-shadow: 0 8px 28px rgba(99, 102, 241, 0.2);
} }
.page-image { .page-image {
width: 100%; width: 100%;
display: block; display: block;
@ -262,6 +269,7 @@ onMounted(loadWork)
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
background: #1e1b4b; background: #1e1b4b;
} }
.placeholder-img { .placeholder-img {
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
display: flex; display: flex;
@ -271,6 +279,7 @@ onMounted(loadWork)
color: rgba(255, 255, 255, 0.3); color: rgba(255, 255, 255, 0.3);
background: #1e1b4b; background: #1e1b4b;
} }
.page-badge { .page-badge {
position: absolute; position: absolute;
top: 12px; top: 12px;
@ -298,6 +307,7 @@ onMounted(loadWork)
box-shadow: 0 4px 16px rgba(99, 102, 241, 0.06); box-shadow: 0 4px 16px rgba(99, 102, 241, 0.06);
min-height: 64px; min-height: 64px;
} }
.story-text { .story-text {
font-size: 15px; font-size: 15px;
line-height: 1.8; line-height: 1.8;
@ -313,6 +323,7 @@ onMounted(loadWork)
gap: 22px; gap: 22px;
margin-top: 18px; margin-top: 18px;
} }
.nav-btn { .nav-btn {
width: 38px; width: 38px;
height: 38px; height: 38px;
@ -332,9 +343,17 @@ onMounted(loadWork)
border-color: var(--ai-primary); border-color: var(--ai-primary);
background: rgba(99, 102, 241, 0.04); background: rgba(99, 102, 241, 0.04);
} }
&:active { transform: scale(0.92); }
&.invisible { opacity: 0; pointer-events: none; } &:active {
transform: scale(0.92);
}
&.invisible {
opacity: 0;
pointer-events: none;
}
} }
.page-counter { .page-counter {
font-size: 12px; font-size: 12px;
color: var(--ai-text-sub); color: var(--ai-text-sub);
@ -355,7 +374,10 @@ onMounted(loadWork)
/* 隐藏滚动条 */ /* 隐藏滚动条 */
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
&::-webkit-scrollbar { display: none; }
&::-webkit-scrollbar {
display: none;
}
} }
.thumb-item { .thumb-item {
@ -427,6 +449,8 @@ onMounted(loadWork)
padding: 14px 0 !important; padding: 14px 0 !important;
border-radius: 28px !important; border-radius: 28px !important;
:deep(.anticon) { font-size: 16px; } :deep(.anticon) {
font-size: 16px;
}
} }
</style> </style>