style(web): 改暖色木色调,跟 Android 端对齐

Web 端原本是冷色蓝调(#2080f0 + #fafbfc),
Android 端已经是暖色木色(#8B6B45 + #F5E9D0),两端不一致。

统一到暖色木色调(跟 logo "D" 木质方块的视觉延伸):

style.css 重写:
- CSS 变量: --color-primary #8B6B45, --color-bg #F5E9D0 等
- 字体栈: serif 用于标题(Georgia / Songti), sans-serif 用于正文
- 卡片: 圆角 12px, 边框 + 浅阴影, hover 浮起 1px
- 滚动条: 木色风格
- 按钮 / 分页 / TopBar 主题色统一

Feed.vue:
- 中文标题字体 18px -> 20px, 字重 600 -> 700
- 原标题字号 13, 颜色淡木色
- 插图圆角 4px -> 8px, 高度自适应 max 280px
- 评论钩子: 淡木色背景 + 木色左边框 3px(与 Android 一致)
- 标签全部用 round

ArticleDetail.vue:
- 中文主标题大字号, 衬线粗体
- 原标题灰色辅助
- 操作按钮全部 round
- 卡片标题统一用 serif

Login.vue:
- 登录卡 圆角 16px, 木色渐变背景
- 标题用 serif, 按钮 round

未提交 Android 端改动 — 在 D:/selftools/diary-news-android/ 独立目录,
会重新 build APK 后单独交付。
This commit is contained in:
Mavis
2026-06-10 18:12:05 +08:00
parent 921e674a30
commit 759eefabc3
4 changed files with 395 additions and 120 deletions

View File

@@ -2,7 +2,7 @@
import { onMounted, ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
NCard, NSpace, NTag, NText, NButton, NSpin, NEmpty, NDivider, NAlert, NSkeleton, NImage, useMessage,
NCard, NSpace, NTag, NText, NButton, NSpin, NEmpty, NAlert, NSkeleton, NImage, useMessage,
} from 'naive-ui'
import { articlesApi, adminApi, type ArticleDetail } from '@/api/articles'
import { http } from '@/api/client'
@@ -103,51 +103,92 @@ onMounted(load)
</script>
<template>
<NSpace vertical>
<NButton text @click="router.back()"> 返回</NButton>
<NSpace vertical :size="16">
<NButton text @click="router.back()" class="back-button"> 返回</NButton>
<NSpin :show="loading">
<NSkeleton v-if="loading" :repeat="6" />
<NEmpty v-else-if="!article" description="文章不存在" />
<div v-else>
<NCard>
<NSpace vertical :size="8">
<NSpace align="center">
<NTag type="info">{{ article.source.name }}</NTag>
<NText depth="3" style="font-size: 12px">{{ fmtTime(publishedAt) }}</NText>
<NTag v-if="article.translation_status !== 'ok'" size="small" type="warning">
翻译: {{ article.translation_status }}
<!-- 顶部信息卡 -->
<NCard class="article-detail-card">
<NSpace vertical :size="14">
<!-- tag -->
<NSpace align="center" :size="6" :wrap="false" style="overflow: hidden">
<NTag type="primary" :bordered="false" round>{{ article.source.name }}</NTag>
<NTag v-if="article.lang_src" :bordered="false" round>{{ article.lang_src.toUpperCase() }}</NTag>
<NTag v-if="article.translation_status !== 'ok'" size="small" type="warning" :bordered="false" round>
翻译:{{ article.translation_status }}
</NTag>
<NTag v-if="article.translation_engine" size="small">
<NTag v-if="article.translation_engine" size="small" :bordered="false" round>
{{ article.translation_engine }}
</NTag>
<NTag v-for="c in categories" :key="c" type="success" size="small">{{ c }}</NTag>
<NTag v-for="c in categories" :key="c" type="success" size="small" :bordered="false" round>
{{ c }}
</NTag>
<NText :depth="3" style="font-size: 12px; margin-left: auto">
{{ fmtTime(publishedAt) }}
</NText>
</NSpace>
<h1 style="margin: 0">{{ article.title }}</h1>
<h2 v-if="article.title_zh" style="margin: 0; color: #2080f0">{{ article.title_zh }}</h2>
<NSpace>
<NButton :type="starred ? 'warning' : 'default'" @click="toggleStar">
<!-- 主标题(中文) -->
<h1 v-if="article.title_zh" style="margin: 0; color: var(--color-letter)">
{{ article.title_zh }}
</h1>
<h2 v-else style="margin: 0; color: var(--color-letter)">
{{ article.title }}
</h2>
<!-- 原标题(英文/外文) -->
<div
v-if="article.title_zh && article.title"
style="font-size: 14px; color: var(--color-text-faint); line-height: 1.5;"
>
{{ article.title }}
</div>
<!-- 操作按钮行 -->
<NSpace :size="8" :wrap="false">
<NButton
:type="starred ? 'warning' : 'primary'"
:ghost="!starred"
@click="toggleStar"
round
>
{{ starred ? '★ 已收藏' : '☆ 收藏' }}
</NButton>
<NButton text @click="showOriginal = !showOriginal">
<NButton text @click="showOriginal = !showOriginal" round>
{{ showOriginal ? '隐藏原文' : '显示原文' }}
</NButton>
<NButton text @click="showTranslation = !showTranslation">
<NButton text @click="showTranslation = !showTranslation" round>
{{ showTranslation ? '隐藏译文' : '显示译文' }}
</NButton>
<NButton v-if="isOwner" type="error" ghost @click="rerunTranslation">
<NButton v-if="isOwner" type="error" ghost @click="rerunTranslation" round>
重译
</NButton>
<NButton v-if="isOwner" type="info" ghost :loading="enriching" @click="triggerEnrich">
<NButton v-if="isOwner" type="info" ghost :loading="enriching" @click="triggerEnrich" round>
LLM 增强
</NButton>
<NButton tag="a" :href="article.url" target="_blank" rel="noopener">原文链接 </NButton>
<NButton tag="a" :href="article.url" target="_blank" rel="noopener" ghost round>
原文链接
</NButton>
</NSpace>
<!-- owner 专属:LLM 状态 -->
<NSpace v-if="isOwner" size="small" align="center">
<NText depth="3" style="font-size: 12px">LLM 状态:</NText>
<NTag size="tiny" :type="statusTagType(article.format_status)">排版:{{ article.format_status || 'n/a' }}</NTag>
<NTag size="tiny" :type="statusTagType(article.classify_status)">分类:{{ article.classify_status || 'n/a' }}</NTag>
<NTag size="tiny" :type="statusTagType(article.image_ai_status)">插图:{{ article.image_ai_status || 'n/a' }}</NTag>
<NTag size="tiny" :type="statusTagType(article.commentary_status)">点评:{{ article.commentary_status || 'n/a' }}</NTag>
<NText :depth="3" style="font-size: 12px">LLM 状态:</NText>
<NTag size="tiny" :type="statusTagType(article.format_status)" :bordered="false" round>
排版:{{ article.format_status || 'n/a' }}
</NTag>
<NTag size="tiny" :type="statusTagType(article.classify_status)" :bordered="false" round>
分类:{{ article.classify_status || 'n/a' }}
</NTag>
<NTag size="tiny" :type="statusTagType(article.image_ai_status)" :bordered="false" round>
插图:{{ article.image_ai_status || 'n/a' }}
</NTag>
<NTag size="tiny" :type="statusTagType(article.commentary_status)" :bordered="false" round>
点评:{{ article.commentary_status || 'n/a' }}
</NTag>
</NSpace>
</NSpace>
</NCard>
@@ -156,58 +197,111 @@ onMounted(load)
本条翻译失败,可点 "重译" 重试,或查看后端日志
</NAlert>
<!-- 三段式:评论(顶部) / 译文() / 原文() -->
<!-- 1) 评论(LLM 点评) -->
<NCard v-if="article.commentary" style="margin-top: 16px" title="💬 评论">
<template #header-extra>
<NTag size="tiny" :type="statusTagType(article.commentary_status)">{{ article.commentary_status || 'n/a' }}</NTag>
<NCard v-if="article.commentary" class="detail-card" style="margin-top: 16px">
<template #header>
<span class="card-header-title">💬 评论</span>
</template>
<p style="white-space: pre-wrap; line-height: 1.8; margin: 0">{{ article.commentary }}</p>
<template #header-extra>
<NTag size="tiny" :type="statusTagType(article.commentary_status)" :bordered="false" round>
{{ article.commentary_status || 'n/a' }}
</NTag>
</template>
<p class="commentary-text-detail">{{ article.commentary }}</p>
</NCard>
<!-- 2) 译文(优先 LLM 排版版,fallback 原始译文) -->
<!-- 2) 译文(优先 LLM 排版版) -->
<div v-if="showTranslation" style="margin-top: 16px">
<NCard v-if="article.body_zh_formatted" title="📖 文章译文">
<NCard v-if="article.body_zh_formatted" class="detail-card">
<template #header>
<span class="card-header-title">📖 文章译文</span>
</template>
<template #header-extra>
<NSpace align="center">
<NTag size="tiny" :type="statusTagType(article.format_status)">排版:{{ article.format_status || 'n/a' }}</NTag>
<NButton text size="tiny" @click="showFormatted = !showFormatted">
<NSpace align="center" :size="4">
<NTag size="tiny" :type="statusTagType(article.format_status)" :bordered="false" round>
排版:{{ article.format_status || 'n/a' }}
</NTag>
<NButton text size="tiny" @click="showFormatted = !showFormatted" round>
{{ showFormatted ? '隐藏排版' : '显示排版' }}
</NButton>
</NSpace>
</template>
<div v-if="showFormatted" v-html="article.body_zh_formatted" />
<NText v-else depth="3" style="font-size: 12px">已隐藏排版版(点击右上角显示)</NText>
<NText v-else :depth="3" style="font-size: 12px">已隐藏排版版(点击右上角显示)</NText>
</NCard>
<NCard v-else title="📖 文章译文(原始)" style="margin-top: 16px">
<div v-if="article.body_zh_html" v-html="article.body_zh_html" style="line-height: 1.8" />
<div v-else-if="article.body_zh_text" style="white-space: pre-wrap; line-height: 1.8">
{{ article.body_zh_text }}
</div>
<NText v-else depth="3">暂无译文</NText>
<NCard v-else class="detail-card">
<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>
<NText v-else :depth="3">暂无译文</NText>
</NCard>
</div>
<!-- AI 插图(挂在译文卡片下,作附属) -->
<div v-if="article.image_ai_url" style="margin-top: 16px">
<NCard title="🎨 AI 插图">
<NImage :src="article.image_ai_url" object-fit="cover" style="max-width: 100%; border-radius: 6px" />
</NCard>
</div>
<!-- AI 插图 -->
<NCard v-if="article.image_ai_url" class="detail-card" style="margin-top: 16px">
<template #header>
<span class="card-header-title">🎨 AI 插图</span>
</template>
<NImage :src="article.image_ai_url" object-fit="cover" class="article-image" />
</NCard>
<!-- 3) 原文 -->
<div v-if="showOriginal" style="margin-top: 16px">
<NCard title="📄 文章原文">
<div v-if="article.body_html" v-html="article.body_html" style="line-height: 1.8" />
<div v-else style="white-space: pre-wrap; line-height: 1.8">{{ article.body_text }}</div>
<NCard class="detail-card">
<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>
</NCard>
</div>
<NCard v-if="article.entities" style="margin-top: 16px" title="🔍 实体(预留)">
<NCard v-if="article.entities" class="detail-card" style="margin-top: 16px">
<template #header>
<span class="card-header-title">🔍 实体(预留)</span>
</template>
<code style="font-size: 12px">{{ JSON.stringify(article.entities) }}</code>
</NCard>
</div>
</NSpin>
</NSpace>
</template>
<style scoped>
.back-button {
font-size: 14px;
color: var(--color-primary) !important;
}
.card-header-title {
font-family: var(--font-serif);
font-weight: 700;
font-size: 18px;
color: var(--color-letter);
}
.commentary-text-detail {
white-space: pre-wrap;
line-height: 1.85;
margin: 0;
color: var(--color-letter);
font-size: 15px;
}
.article-body-fallback {
white-space: pre-wrap;
line-height: 1.85;
color: var(--color-letter);
font-size: 16px;
font-family: var(--font-sans);
}
.article-image {
max-width: 100%;
border-radius: 8px;
background: var(--color-surface-variant);
}
</style>