style: 放大章节阅读区与编辑区,使其随窗口宽度自适应

阅读区:正文列上限由固定 820px 改为 min(1040px, 92vw);打开查词面板时的容器由 1120px 改为
min(1240px, 96vw),大屏上正文列不再比不开面板时更窄。编辑区:章节编辑对话框由固定 620px 改为
min(1180px, 94vw),正文输入框最小高度改为 62vh(行数仅作初始值),书籍对话框 460px 改为 560px。

新增验证:reading 用例检查 768/1024/1280 三档宽度不横向溢出,并在 1280 下断言正文列宽 > 820px;
edit 用例断言章节对话框宽 > 900px、正文输入框高 > 400px 且不溢出。手机(≤760px)的底部抽屉样式未改动。

测试:学习端 159 单测、26 项 e2e、vue-tsc、build 全部通过。无长期文档影响(仅样式数值)。
This commit is contained in:
ila
2026-09-16 23:33:50 +08:00
parent 42945dec0a
commit f2fdf405a9
4 changed files with 31 additions and 5 deletions
+11
View File
@@ -80,9 +80,20 @@ test('rename the book, edit a chapter into a new version and delete both', async
await expect(page.getByRole('heading', { name: 'A long step' })).toBeVisible()
// Edit the chapter text: the new version re-processes and later becomes ready again.
// The chapter editor is the main writing surface, so on a wide window it must use that width and
// a tall body instead of a fixed 620px box.
await page.setViewportSize({ width: 1280, height: 900 })
await page.getByTestId('edit-chapter-9').click()
await expectModalOverlay(page, page.getByTestId('chapter-dialog'))
await expect(page.getByLabel('章节标题', { exact: true })).toHaveValue('First chapter')
await expect(page.getByLabel('正文', { exact: true })).toHaveValue(firstText)
const dialog = await page.getByTestId('chapter-dialog').boundingBox()
expect(dialog!.width, 'the chapter editor follows the window').toBeGreaterThan(900)
const body = await page.getByLabel('正文', { exact: true }).boundingBox()
expect(body!.height, 'the text area is tall enough to write in').toBeGreaterThan(400)
const noOverflow = await page.evaluate(() => ({ scroll: document.documentElement.scrollWidth, inner: window.innerWidth }))
expect(noOverflow.scroll, 'the wide editor must not overflow').toBeLessThanOrEqual(noOverflow.inner)
await page.setViewportSize({ width: 1280, height: 900 })
await page.getByLabel('正文', { exact: true }).fill('A replacement body.\n')
await page.getByTestId('save-chapter').click()
await expect(page.getByTestId('book-notice')).toContainText('已保存为新版本,正在重新处理')
+12
View File
@@ -137,6 +137,18 @@ test('paste English text, watch a chapter finish processing, then read it verbat
await expect(page.locator('.lookup-panel')).toContainText('Princeton WordNet')
expect(await readerText.evaluate(element => element.textContent)).toBe(pasted)
// The reading and editing surfaces must grow with the window instead of staying at a fixed
// 820px column, and no intermediate width may overflow sideways (the blind spot before this).
for (const width of [768, 1024, 1280]) {
await page.setViewportSize({ width, height: 900 })
await page.waitForTimeout(60)
const overflow = await page.evaluate(() => ({ scroll: document.documentElement.scrollWidth, inner: window.innerWidth }))
expect(overflow.scroll, `reader must not overflow at ${width}px`).toBeLessThanOrEqual(overflow.inner)
}
await page.setViewportSize({ width: 1280, height: 900 })
await page.waitForTimeout(60)
const wideBody = await page.locator('.reader-body').boundingBox()
expect(wideBody!.width, 'the text column follows the window').toBeGreaterThan(820)
// Browser narrow viewport check only; this is not real-device acceptance.
await page.setViewportSize({ width: 390, height: 844 })
await word.click()
+5 -2
View File
@@ -227,12 +227,12 @@ h1 { font-size: 30px; font-weight: 600; margin: 14px 0; letter-spacing: 1px; }
a.chapter-name:hover { color: var(--accent); text-decoration: underline; }
.chapter-meta { margin: 4px 0 0; color: var(--text-muted); font-size: 13px; overflow-wrap: anywhere; }
/* Reader keeps the pasted text exactly as it was, including line breaks and tabs. */
.reader-page { max-width: 820px; }
.reader-page { max-width: min(1040px, 92vw); }
.chapter-author { margin: -6px 0 0; color: var(--text-muted); font-size: 14px; }
.reader-text { white-space: pre-wrap; overflow-wrap: break-word; margin: 26px 0 0; font-family: Georgia, 'Times New Roman', 'Microsoft YaHei', serif; font-size: calc(17px * var(--reader-font-scale)); line-height: 2; }
.processing-hint { padding: 36px 0; color: var(--text-muted); }
.reader-nav { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-top: 36px; padding-top: 22px; border-top: 1px solid var(--border); }
.reader-page.has-lookup { max-width: 1120px; }
.reader-page.has-lookup { max-width: min(1240px, 96vw); }
.reader-workspace { display: grid; grid-template-columns: minmax(0, 1fr); gap: 32px; align-items: start; }
.has-lookup .reader-workspace { grid-template-columns: minmax(0, 1fr) 320px; }
.reader-body { min-width: 0; }
@@ -270,6 +270,9 @@ a.chapter-name:hover { color: var(--accent); text-decoration: underline; }
.lookup-term .el-radio-group { margin-top: 8px; flex-wrap: wrap; gap: 4px 12px; }
.lookup-term .el-textarea { margin-top: 8px; }
.lookup-term .el-textarea textarea { font: inherit; line-height: 1.6; }
/* The chapter body is the main editing surface, so it grows with the window instead of staying
fixed at its rows attribute; the dialog is sized by viewport so a small laptop still fits. */
.chapter-text-input .el-textarea__inner { min-height: 62vh; line-height: 1.9; }
.lookup-saved { color: var(--saved); font-size: 14px; margin: 12px 0 0; }
.lookup-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.review-page { max-width: 680px; }
+3 -3
View File
@@ -398,7 +398,7 @@ onUnmounted(() => library.closeBook())
</section>
</template>
<ElDialog v-model="bookDialog" title="编辑书籍" width="460" data-testid="book-dialog">
<ElDialog v-model="bookDialog" title="编辑书籍" width="560" data-testid="book-dialog">
<div class="field-row">
<label for="book-title">书名</label>
<ElInput id="book-title" v-model="bookTitle" type="text" maxlength="200" :disabled="saving" />
@@ -414,7 +414,7 @@ onUnmounted(() => library.closeBook())
</template>
</ElDialog>
<ElDialog v-model="chapterDialog" title="编辑章节" width="620" data-testid="chapter-dialog">
<ElDialog v-model="chapterDialog" title="编辑章节" width="min(1180px, 94vw)" data-testid="chapter-dialog">
<p v-if="chapterLoading" role="status" class="loading">正在加载…</p>
<template v-else-if="chapterSource">
<div class="field-row">
@@ -426,7 +426,7 @@ onUnmounted(() => library.closeBook())
<ElInput id="chapter-author" v-model="chapterAuthor" type="text" maxlength="120" placeholder="可留空" :disabled="saving" data-testid="chapter-author" />
</div>
<label for="chapter-text">正文</label>
<ElInput id="chapter-text" v-model="chapterText" type="textarea" :rows="18" :disabled="saving" />
<ElInput id="chapter-text" v-model="chapterText" type="textarea" class="chapter-text-input" :rows="18" :disabled="saving" />
<p class="counter">{{ chapterLength }} / {{ TEXT_MAX_CODE_POINTS }} 字符 · 保存后重新处理这一章</p>
<div class="attachment-block">