diff --git a/src/components/BookListPage.test.ts b/src/components/BookListPage.test.ts index 1b82e28..898e987 100644 --- a/src/components/BookListPage.test.ts +++ b/src/components/BookListPage.test.ts @@ -24,7 +24,7 @@ describe('BookListPage', () => { await flushPromises() expect(wrapper.text()).toContain('Web 前端开发') - expect(wrapper.text()).toContain('更新于 2026/01/01 08:00 CST') + expect(wrapper.text()).toContain('更新于 2026/01/01') expect(wrapper.text()).not.toContain('2026-01-01T00:00:00.000Z') expect(wrapper.text()).toContain('3 课') }) diff --git a/src/components/WorkspaceView.vue b/src/components/WorkspaceView.vue index 8f287be..ec7d1cd 100644 --- a/src/components/WorkspaceView.vue +++ b/src/components/WorkspaceView.vue @@ -4,6 +4,8 @@ import { type DuplicateStrategy, useTeachingBook } from '../composables/useTeach import type { TeachingDesign } from '../domain/teachingDesign' import { createBookZip, downloadBlob } from '../services/zipExporter' import A4Workspace from './A4Workspace.vue' +import BatchGenerateDialog from './BatchGenerateDialog.vue' +import FixBrokenDialog from './FixBrokenDialog.vue' import GenerateLessonDialog from './GenerateLessonDialog.vue' import ImportConflictDialog from './ImportConflictDialog.vue' import LessonSidebar from './LessonSidebar.vue' @@ -17,6 +19,7 @@ defineEmits<{ back: [] }>() const { book, + bookName, loadStatus, loadError, saveStatus, @@ -33,6 +36,7 @@ const { updateDesign, clearBook, generateLesson, + regenerateLesson, } = useTeachingBook(props.bookId) const pendingFiles = ref([]) @@ -44,6 +48,22 @@ const showGenerateDialog = ref(false) const generateLoading = ref(false) const generateError = ref(null) +const showBatchDialog = ref(false) +const batchRunning = ref(false) +const batchDone = ref(0) +const batchTotal = ref(0) +const batchCurrentTopic = ref('') +const batchError = ref(null) +const batchCancelled = ref(false) + +const showFixDialog = ref(false) +const fixRunning = ref(false) +const fixDone = ref(0) +const fixTotal = ref(0) +const fixCurrentTopic = ref('') +const fixError = ref(null) +const fixCancelled = ref(false) + async function runImport(files: File[], strategy: DuplicateStrategy): Promise { const result = await importFiles(files, strategy) if (result.failed.length > 0) { @@ -76,7 +96,10 @@ function triggerUpload(): void { } function handlePrint(): void { + const prev = document.title + document.title = bookName.value || prev window.print() + document.title = prev } async function handleExport(): Promise { @@ -123,6 +146,75 @@ function cancelGenerate(): void { showGenerateDialog.value = false generateError.value = null } + +async function handleBatchStart(topics: string[]): Promise { + batchRunning.value = true + batchCancelled.value = false + batchDone.value = 0 + batchTotal.value = topics.length + batchError.value = null + + for (const topic of topics) { + if (batchCancelled.value) break + batchCurrentTopic.value = topic + const result = await generateLesson(topic) + if (!result.ok) { + batchError.value = result.message + break + } + batchDone.value++ + } + + batchRunning.value = false +} + +function handleBatchCancel(): void { + batchCancelled.value = true +} + +function closeBatchDialog(): void { + showBatchDialog.value = false + batchDone.value = 0 + batchTotal.value = 0 + batchError.value = null +} + +function openFixDialog(): void { + fixTotal.value = book.value.designs.filter((d) => d.warnings.length > 0).length + fixDone.value = 0 + fixError.value = null + showFixDialog.value = true +} + +async function handleFixStart(): Promise { + const broken = book.value.designs.filter((d) => d.warnings.length > 0) + fixCancelled.value = false + fixRunning.value = true + + for (const lesson of broken) { + if (fixCancelled.value) break + fixCurrentTopic.value = lesson.originalFilename.replace(/\.md$/i, '') + const result = await regenerateLesson(lesson.id) + if (!result.ok) { + fixError.value = result.message + break + } + fixDone.value++ + } + + fixRunning.value = false +} + +function handleFixCancel(): void { + fixCancelled.value = true +} + +function closeFixDialog(): void { + showFixDialog.value = false + fixDone.value = 0 + fixTotal.value = 0 + fixError.value = null +} - +