fix(detail): 原始译文/原文标签字号 16→19px,加段落切分+行高 1.95

问题:'文章译文(原始)' 和 '文章原文' 标签下,内容是 body_zh_text / body_text 一坨没分段,
走 .article-body-fallback 16px,排版难看。

修复:
1) .article-body-fallback 字号 16→19,行高 1.85→1.95,加 max-width 720px 居中 + 0.02em letter-spacing
2) 加 splitIntoParagraphs: 已经是 HTML 标签的不动;否则按 \n 切;一坨纯文字按 [。!?!?] 切
3) 段首 text-indent 2em(中文书报排版)
4) translationBody / originalBody computed 替代内联三元

LLM 排版过的 (body_zh_formatted) 已经 <p> 段标签,不受影响。
This commit is contained in:
Mavis
2026-06-11 23:54:32 +08:00
parent e4733ab495
commit 3ab6e4c7d0

View File

@@ -64,6 +64,64 @@ const publishedAt = computed(() => article.value?.published_at || article.value?
const isOwner = computed(() => auth.isOwner)
const categories = computed(() => (article.value?.category || '').split(',').filter(Boolean))
/** 把"一坨"译文/原文按"中文句号"切成 <p> 段,改善"挤在一起"的观感。
* 优先按 \n 切(LLM 排版过的),没有换行再按句号/问号/感叹号切。
* 句中常见的"Mr./U.S."等缩写不会出现在中文译文里,按 6+ 字符才切,避免半句话被切。
*/
function splitIntoParagraphs(html: string): string {
// 已经是 HTML 标签(LLM 排版过),不动
if (/<(p|div|br)\b/i.test(html)) return html
// 按 \n 切
if (/\n/.test(html)) {
return html
.split(/\n+/)
.map((p) => p.trim())
.filter(Boolean)
.map((p) => `<p>${escapeHtml(p)}</p>`)
.join('')
}
// 一坨纯文字:按句号/问号/感叹号切,每段最多 140 字
const text = html.trim()
if (!text) return ''
const chunks: string[] = []
const re = /([。!?!?])(?=[^。!?!?]{0,140})/g
let last = 0
let m: RegExpExecArray | null
while ((m = re.exec(text)) !== null) {
const end = m.index + 1
if (end - last > 6) {
chunks.push(text.slice(last, end))
last = end
}
}
if (last < text.length) chunks.push(text.slice(last))
return chunks.map((p) => `<p>${escapeHtml(p.trim())}</p>`).join('')
}
function escapeHtml(s: string): string {
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}
const translationBody = computed(() => {
const a = article.value
if (!a) return ''
if (a.body_zh_formatted) return a.body_zh_formatted
if (a.body_zh_html) return splitIntoParagraphs(a.body_zh_html)
if (a.body_zh_text) return splitIntoParagraphs(a.body_zh_text)
return ''
})
const originalBody = computed(() => {
const a = article.value
if (!a) return ''
if (a.body_html) return splitIntoParagraphs(a.body_html)
if (a.body_text) return splitIntoParagraphs(a.body_text)
return ''
})
async function rerunTranslation() {
if (!article.value) return
if (!confirm('重新翻译会消耗配额,确认?')) return
@@ -234,8 +292,7 @@ onMounted(load)
<template #header>
<span class="card-header-title">📖 文章译文(原始)</span>
</template>
<div v-if="article.body_zh_html" v-html="article.body_zh_html" />
<div v-else-if="article.body_zh_text" class="article-body-fallback">{{ article.body_zh_text }}</div>
<div v-if="translationBody" class="article-body-fallback" v-html="translationBody" />
<NText v-else :depth="3">暂无译文</NText>
</NCard>
</div>
@@ -254,8 +311,7 @@ onMounted(load)
<template #header>
<span class="card-header-title">📄 文章原文</span>
</template>
<div v-if="article.body_html" v-html="article.body_html" class="article-body-fallback" />
<div v-else class="article-body-fallback">{{ article.body_text }}</div>
<div v-if="originalBody" class="article-body-fallback" v-html="originalBody" />
</NCard>
</div>
@@ -293,10 +349,28 @@ onMounted(load)
.article-body-fallback {
white-space: pre-wrap;
line-height: 1.85;
line-height: 1.95;
color: var(--color-letter);
font-size: 16px;
font-size: 19px;
font-family: var(--font-sans);
max-width: 720px;
margin: 0 auto;
letter-spacing: 0.02em;
text-align: justify;
text-justify: inter-ideograph;
}
/* 原始译文里的"中文句号"或"换行"在浏览器里经常被压成一坨,
强制段间加空行 + 段首缩进 2 字符,接近纸质书阅读感 */
.article-body-fallback :deep(p),
.article-body-fallback :deep(div) {
margin: 0 0 1.1em 0;
text-indent: 2em;
}
.article-body-fallback :deep(p:last-child),
.article-body-fallback :deep(div:last-child) {
margin-bottom: 0;
}
.article-image {