From 9ed641c6e3fb85a9738647402a662419c52bc52f Mon Sep 17 00:00:00 2001 From: zhonghua Date: Wed, 15 Apr 2026 18:34:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/utils/aicreate/status.ts | 25 +-- .../public/create/views/BookReaderView.vue | 80 +++++++-- .../views/public/create/views/DubbingView.vue | 154 ++++++++++++------ .../views/public/create/views/PreviewView.vue | 2 + 4 files changed, 186 insertions(+), 75 deletions(-) diff --git a/lesingle-creation-frontend/src/utils/aicreate/status.ts b/lesingle-creation-frontend/src/utils/aicreate/status.ts index b2cdfe5..d3ffa5e 100644 --- a/lesingle-creation-frontend/src/utils/aicreate/status.ts +++ b/lesingle-creation-frontend/src/utils/aicreate/status.ts @@ -11,28 +11,33 @@ export const STATUS = { PROCESSING: 2, COMPLETED: 3, CATALOGED: 4, - DUBBED: 5 -} as const + DUBBED: 5, +} as const; -export type StatusValue = typeof STATUS[keyof typeof STATUS] +export type StatusValue = (typeof STATUS)[keyof typeof STATUS]; /** * 根据作品状态决定应导航到的路由 */ -export function getRouteByStatus(status: StatusValue, workId: string): { name: string; params?: Record } | null { +export function getRouteByStatus( + status: StatusValue, + workId: string, +): { name: string; params?: Record } | null { switch (status) { case STATUS.PENDING: case STATUS.PROCESSING: - return { name: 'PublicCreateCreating' } + return { name: "PublicCreateCreating" }; case STATUS.COMPLETED: - return { name: 'PublicCreatePreview', params: { workId } } + return { name: "PublicCreatePreview", params: { workId } }; case STATUS.CATALOGED: - return { name: 'PublicCreateDubbing', params: { workId } } + return { name: "PublicCreatePreview", params: { workId } }; + // return { name: 'PublicCreateDubbing', params: { workId } } case STATUS.DUBBED: - return { name: 'PublicCreateEditInfo', params: { workId } } + return { name: "PublicCreatePreview", params: { workId } }; + // return { name: 'PublicCreateEditInfo', params: { workId } } case STATUS.FAILED: - return null + return null; default: - return null + return null; } } diff --git a/lesingle-creation-frontend/src/views/public/create/views/BookReaderView.vue b/lesingle-creation-frontend/src/views/public/create/views/BookReaderView.vue index dfca943..60d0803 100644 --- a/lesingle-creation-frontend/src/views/public/create/views/BookReaderView.vue +++ b/lesingle-creation-frontend/src/views/public/create/views/BookReaderView.vue @@ -13,11 +13,8 @@ export default { name: 'BookReaderView' }
-
+
@@ -181,9 +178,9 @@ const goHome = () => { function applyWork(work: any) { title.value = work.title || '我的绘本' const list: any[] = [{ pageNum: 0, text: work.title || '我的绘本', type: 'cover' }] - ;(work.pageList || []).forEach((p: any) => { - if (p.pageNum > 0) list.push({ pageNum: p.pageNum, text: p.text, imageUrl: p.imageUrl }) - }) + ; (work.pageList || []).forEach((p: any) => { + if (p.pageNum > 0) list.push({ pageNum: p.pageNum, text: p.text, imageUrl: p.imageUrl }) + }) if (work.pageList?.[0]?.imageUrl) coverImageUrl.value = work.pageList[0].imageUrl if (work.author) authorDisplay.value = work.author if (Array.isArray(work.tags) && work.tags.length > 0) workTags.value = work.tags @@ -235,6 +232,7 @@ onActivated(loadReader) border-bottom: 1px solid rgba(99, 102, 241, 0.08); position: relative; } + .back-btn { position: absolute; left: 12px; @@ -248,8 +246,11 @@ onActivated(loadReader) justify-content: center; color: var(--ai-primary); - :deep(.anticon) { font-size: 16px; } + :deep(.anticon) { + font-size: 16px; + } } + .top-title { font-size: 15px; font-weight: 700; @@ -281,8 +282,13 @@ onActivated(loadReader) 0 14px 36px rgba(99, 102, 241, 0.2); transition: transform 0.25s ease; - &.flip-left { transform: perspective(800px) rotateY(8deg); } - &.flip-right { transform: perspective(800px) rotateY(-8deg); } + &.flip-left { + transform: perspective(800px) rotateY(8deg); + } + + &.flip-right { + transform: perspective(800px) rotateY(-8deg); + } } .book-spine { @@ -307,6 +313,7 @@ onActivated(loadReader) text-align: center; position: relative; } + .cover-image { width: 100%; aspect-ratio: 16 / 9; @@ -320,15 +327,18 @@ onActivated(loadReader) backdrop-filter: blur(8px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); } + .cover-real-img { width: 100%; height: 100%; object-fit: cover; } + .cover-placeholder { font-size: 56px; color: rgba(255, 255, 255, 0.5); } + .cover-title { font-size: 22px; font-weight: 900; @@ -337,6 +347,7 @@ onActivated(loadReader) line-height: 1.3; letter-spacing: 1.5px; } + .cover-divider { width: 50px; height: 2px; @@ -344,12 +355,14 @@ onActivated(loadReader) border-radius: 2px; margin: 14px 0 12px; } + .cover-brand { font-size: 12px; color: rgba(255, 255, 255, 0.88); font-weight: 500; letter-spacing: 0.5px; } + .cover-author { margin-top: 12px; display: inline-flex; @@ -363,7 +376,9 @@ onActivated(loadReader) padding: 4px 12px; backdrop-filter: blur(4px); - :deep(.anticon) { font-size: 11px; } + :deep(.anticon) { + font-size: 11px; + } } /* ---------- 正文页 ---------- */ @@ -373,6 +388,7 @@ onActivated(loadReader) display: flex; flex-direction: column; } + .content-image { flex: 1; display: flex; @@ -382,16 +398,19 @@ onActivated(loadReader) position: relative; background: #1e1b4b; } + .content-real-img { width: 100%; height: 100%; object-fit: contain; border-radius: 10px; } + .content-placeholder { font-size: 48px; color: rgba(255, 255, 255, 0.3); } + .page-num { position: absolute; top: 14px; @@ -405,6 +424,7 @@ onActivated(loadReader) color: #fff; letter-spacing: 0.5px; } + .content-text { padding: 14px 22px 18px; text-align: center; @@ -428,12 +448,14 @@ onActivated(loadReader) padding: 32px 28px; text-align: center; } + .back-icon { font-size: 44px; color: #fff; margin-bottom: 16px; filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.18)); } + .back-title { font-size: 20px; font-weight: 900; @@ -441,6 +463,7 @@ onActivated(loadReader) text-shadow: 0 2px 8px rgba(0, 0, 0, 0.18); letter-spacing: 1px; } + .back-divider { width: 36px; height: 2px; @@ -448,6 +471,7 @@ onActivated(loadReader) border-radius: 2px; margin: 12px 0; } + .back-desc { font-size: 13px; color: rgba(255, 255, 255, 0.92); @@ -463,6 +487,7 @@ onActivated(loadReader) margin-top: 14px; padding: 0 16px; } + .book-tag { display: inline-block; padding: 4px 12px; @@ -490,9 +515,13 @@ onActivated(loadReader) box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18); transition: transform 0.2s; - :deep(.anticon) { font-size: 13px; } + :deep(.anticon) { + font-size: 13px; + } - &:active { transform: scale(0.96); } + &:active { + transform: scale(0.96); + } } .back-brand { @@ -513,6 +542,7 @@ onActivated(loadReader) margin-top: 16px; padding: 0 4px; } + .nav-btn { width: 40px; height: 40px; @@ -523,7 +553,9 @@ onActivated(loadReader) cursor: pointer; transition: all 0.2s; - :deep(.anticon) { font-size: 16px; } + :deep(.anticon) { + font-size: 16px; + } &.prev { background: #fff; @@ -531,20 +563,27 @@ onActivated(loadReader) border: 1px solid rgba(99, 102, 241, 0.2); box-shadow: 0 2px 10px rgba(99, 102, 241, 0.08); - &:hover { border-color: var(--ai-primary); } + &:hover { + border-color: var(--ai-primary); + } } + &.next { background: var(--ai-gradient); color: #fff; box-shadow: 0 4px 14px rgba(99, 102, 241, 0.32); - &:hover { transform: scale(1.05); } + &:hover { + transform: scale(1.05); + } } + &.disabled { opacity: 0; pointer-events: none; } } + .nav-label { font-size: 12px; color: var(--ai-text-sub); @@ -562,6 +601,7 @@ onActivated(loadReader) margin-top: 12px; overflow: hidden; } + .progress-bar-fill { height: 100%; background: var(--ai-gradient); @@ -577,6 +617,7 @@ onActivated(loadReader) background: #fff; border-top: 1px solid rgba(99, 102, 241, 0.08); } + .again-btn { display: flex !important; align-items: center; @@ -587,8 +628,11 @@ onActivated(loadReader) padding: 14px 0 !important; border-radius: 28px !important; - :deep(.anticon) { font-size: 16px; } + :deep(.anticon) { + font-size: 16px; + } } + .bottom-hint { margin-top: 8px; font-size: 12px; diff --git a/lesingle-creation-frontend/src/views/public/create/views/DubbingView.vue b/lesingle-creation-frontend/src/views/public/create/views/DubbingView.vue index 45270d9..2138e16 100644 --- a/lesingle-creation-frontend/src/views/public/create/views/DubbingView.vue +++ b/lesingle-creation-frontend/src/views/public/create/views/DubbingView.vue @@ -52,34 +52,19 @@ export default { name: 'DubbingView' }
-
- - @@ -93,13 +78,8 @@ export default { name: 'DubbingView' } {{ voicedCount }} / {{ pages.length }} 已完成
-
+
@@ -535,6 +515,7 @@ async function finish() { // --- Load --- async function loadWork() { + store.reset(); loading.value = true try { if (!store.workDetail || store.workDetail.workId !== workId.value) { @@ -586,11 +567,13 @@ onBeforeUnmount(() => { align-items: center; justify-content: center; } + .loading-icon { font-size: 44px; color: var(--ai-primary); margin-bottom: 14px; } + .loading-text { font-size: 14px; color: var(--ai-text-sub); @@ -612,6 +595,7 @@ onBeforeUnmount(() => { flex-direction: column; gap: 0; } + .page-image-wrap { position: relative; border-radius: 18px; @@ -619,6 +603,7 @@ onBeforeUnmount(() => { background: #1e1b4b; box-shadow: 0 8px 24px rgba(99, 102, 241, 0.18); } + .page-image { width: 100%; display: block; @@ -626,6 +611,7 @@ onBeforeUnmount(() => { max-height: 32vh; background: #1e1b4b; } + .placeholder { height: 180px; display: flex; @@ -634,6 +620,7 @@ onBeforeUnmount(() => { font-size: 48px; color: rgba(255, 255, 255, 0.3); } + .page-badge-pill { position: absolute; top: 10px; @@ -676,6 +663,7 @@ onBeforeUnmount(() => { padding: 12px 14px; box-shadow: 0 2px 12px rgba(99, 102, 241, 0.05); } + .play-btn { width: 42px; height: 42px; @@ -692,24 +680,30 @@ onBeforeUnmount(() => { box-shadow: 0 4px 14px rgba(99, 102, 241, 0.32); transition: transform 0.2s; - &:active { transform: scale(0.92); } + &:active { + transform: scale(0.92); + } } + .audio-progress { flex: 1; min-width: 0; } + .audio-bar { height: 5px; background: rgba(99, 102, 241, 0.1); border-radius: 3px; overflow: hidden; } + .audio-bar-fill { height: 100%; background: var(--ai-gradient); border-radius: 3px; transition: width 0.3s; } + .audio-time { font-size: 11px; color: var(--ai-text-sub); @@ -717,6 +711,7 @@ onBeforeUnmount(() => { display: block; font-weight: 500; } + .audio-source-tag { display: inline-flex; align-items: center; @@ -728,13 +723,17 @@ onBeforeUnmount(() => { white-space: nowrap; flex-shrink: 0; - :deep(.anticon) { font-size: 11px; } + :deep(.anticon) { + font-size: 11px; + } &.ai { background: rgba(99, 102, 241, 0.1); color: var(--ai-primary); } - &.local, &.human { + + &.local, + &.human { background: rgba(239, 68, 68, 0.1); color: #ef4444; } @@ -751,7 +750,9 @@ onBeforeUnmount(() => { color: var(--ai-text-sub); font-weight: 500; - :deep(.anticon) { font-size: 16px; } + :deep(.anticon) { + font-size: 16px; + } } /* ---------- 配音操作按钮 ---------- */ @@ -780,7 +781,9 @@ onBeforeUnmount(() => { justify-content: center; gap: 10px; - :deep(.anticon) { font-size: 18px; } + :deep(.anticon) { + font-size: 18px; + } &:active, &.recording { @@ -800,6 +803,7 @@ onBeforeUnmount(() => { display: flex; gap: 10px; } + .ai-btn { flex: 1; padding: 13px 8px; @@ -813,7 +817,9 @@ onBeforeUnmount(() => { gap: 6px; transition: all 0.2s; - :deep(.anticon) { font-size: 14px; } + :deep(.anticon) { + font-size: 14px; + } &.single { background: var(--ai-gradient); @@ -821,7 +827,9 @@ onBeforeUnmount(() => { border: none; box-shadow: 0 4px 14px rgba(99, 102, 241, 0.32); - &:hover { transform: translateY(-1px); } + &:hover { + transform: translateY(-1px); + } } &.all { @@ -836,12 +844,21 @@ onBeforeUnmount(() => { } } - &:active { transform: scale(0.97); } - &:disabled { opacity: 0.4; pointer-events: none; } + &:active { + transform: scale(0.97); + } + + &:disabled { + opacity: 0.4; + pointer-events: none; + } } /* ---------- 缩略图横向胶卷 ---------- */ -.thumb-section { margin-top: 2px; } +.thumb-section { + margin-top: 2px; +} + .thumb-header { display: flex; justify-content: space-between; @@ -849,11 +866,13 @@ onBeforeUnmount(() => { margin-bottom: 8px; padding: 0 2px; } + .thumb-title { font-size: 13px; font-weight: 700; color: var(--ai-text); } + .thumb-count { font-size: 12px; color: var(--ai-primary); @@ -871,7 +890,10 @@ onBeforeUnmount(() => { scrollbar-width: none; -ms-overflow-style: none; - &::-webkit-scrollbar { display: none; } + + &::-webkit-scrollbar { + display: none; + } } .thumb-item { @@ -903,12 +925,14 @@ onBeforeUnmount(() => { background: linear-gradient(to top, rgba(16, 185, 129, 0.85), transparent); } } + .thumb-img { width: 100%; height: 100%; object-fit: cover; display: block; } + .thumb-placeholder { width: 100%; height: 100%; @@ -918,6 +942,7 @@ onBeforeUnmount(() => { font-size: 22px; color: rgba(255, 255, 255, 0.3); } + .thumb-num { position: absolute; bottom: 0; @@ -933,6 +958,7 @@ onBeforeUnmount(() => { line-height: 1; transition: background 0.3s; } + .thumb-voice-flag { position: absolute; top: 4px; @@ -953,6 +979,7 @@ onBeforeUnmount(() => { .dubbing-bottom { border-top: 1px solid rgba(99, 102, 241, 0.06); } + .finish-btn { display: flex !important; align-items: center; @@ -962,8 +989,11 @@ onBeforeUnmount(() => { padding: 14px 0 !important; border-radius: 28px !important; - :deep(.anticon) { font-size: 16px; } + :deep(.anticon) { + font-size: 16px; + } } + .local-hint { display: flex; align-items: center; @@ -975,7 +1005,10 @@ onBeforeUnmount(() => { margin-top: 8px; font-weight: 500; - :deep(.anticon) { font-size: 13px; color: var(--ai-primary); } + :deep(.anticon) { + font-size: 13px; + color: var(--ai-primary); + } } /* ---------- Toast ---------- */ @@ -996,10 +1029,16 @@ onBeforeUnmount(() => { box-shadow: 0 6px 20px rgba(15, 12, 41, 0.25); backdrop-filter: blur(8px); } + .fade-enter-active, -.fade-leave-active { transition: opacity 0.3s; } +.fade-leave-active { + transition: opacity 0.3s; +} + .fade-enter-from, -.fade-leave-to { opacity: 0; } +.fade-leave-to { + opacity: 0; +} /* ---------- 确认弹窗 ---------- */ .confirm-overlay { @@ -1013,6 +1052,7 @@ onBeforeUnmount(() => { justify-content: center; padding: 20px; } + .confirm-modal { background: #fff; border-radius: 20px; @@ -1023,27 +1063,39 @@ onBeforeUnmount(() => { box-shadow: 0 20px 60px rgba(15, 12, 41, 0.25); animation: confirmFadeIn 0.2s ease; } + @keyframes confirmFadeIn { - from { opacity: 0; transform: scale(0.95); } - to { opacity: 1; transform: scale(1); } + from { + opacity: 0; + transform: scale(0.95); + } + + to { + opacity: 1; + transform: scale(1); + } } + .confirm-title { font-size: 16px; font-weight: 700; color: var(--ai-text); margin-bottom: 10px; } + .confirm-content { font-size: 14px; color: var(--ai-text-sub); line-height: 1.6; margin-bottom: 22px; } + .confirm-actions { display: flex; gap: 10px; justify-content: flex-end; } + .confirm-btn { padding: 8px 18px; border-radius: 16px; @@ -1056,14 +1108,22 @@ onBeforeUnmount(() => { border: 1px solid rgba(99, 102, 241, 0.2); background: #fff; color: var(--ai-text-sub); - &:hover { border-color: var(--ai-primary); color: var(--ai-primary); } + + &:hover { + border-color: var(--ai-primary); + color: var(--ai-primary); + } } + &.ok { border: none; background: var(--ai-gradient); color: #fff; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.32); - &:hover { transform: translateY(-1px); } + + &:hover { + transform: translateY(-1px); + } } } diff --git a/lesingle-creation-frontend/src/views/public/create/views/PreviewView.vue b/lesingle-creation-frontend/src/views/public/create/views/PreviewView.vue index e5586ed..32d0f16 100644 --- a/lesingle-creation-frontend/src/views/public/create/views/PreviewView.vue +++ b/lesingle-creation-frontend/src/views/public/create/views/PreviewView.vue @@ -92,6 +92,7 @@ import { STATUS, getRouteByStatus } from '@/utils/aicreate/status' const router = useRouter() const route = useRoute() +const store = useAicreateStore() const loading = ref(true) const error = ref('') const pages = ref([]) @@ -122,6 +123,7 @@ function scrollThumbIntoView(i: number) { const workId = computed(() => route.params.workId) async function loadWork() { + store.reset(); loading.value = true error.value = ''