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:
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user