Files
backend/frontend/src/App.vue
T
34047007@qq.com 62ca8fa6b8
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
chore: batch commit remaining changes
Includes search engine improvements, Alembic migrations,
new services (pubmed_daily_update, query_expansion),
frontend updates, and documentation sync.
2026-07-27 08:35:12 +08:00

238 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { watchEffect } from 'vue'
import { NConfigProvider, NMessageProvider, NDialogProvider, darkTheme, zhCN, dateZhCN } from 'naive-ui'
import { themeOverrides } from './theme'
import { useUiStore } from './stores/ui'
import NetworkStatus from './components/common/NetworkStatus.vue'
const ui = useUiStore()
// 暗色模式:在 html 上加 class,CSS 变量驱动自定义样式
watchEffect(() => {
document.documentElement.classList.toggle('dark', ui.isDark)
})
// 会话初始化在 router.beforeEach 中触发(确保路由守卫前完成)
</script>
<template>
<NConfigProvider :theme="ui.isDark ? darkTheme : null" :theme-overrides="themeOverrides" :locale="zhCN" :date-locale="dateZhCN">
<NMessageProvider>
<NDialogProvider>
<NetworkStatus />
<RouterView />
</NDialogProvider>
</NMessageProvider>
</NConfigProvider>
</template>
<style>
/* 暗色模式 CSS 变量 — 所有自定义样式使用这些变量即可自动适配 */
:root {
--bg-card: #fff;
--bg-page: #f5f7fa;
--bg-hover: #fafafa;
--text-primary: #333;
--text-secondary: #666;
--text-muted: #999;
--border-color: #eee;
--shadow: rgba(0,0,0,0.06);
--preview-ai-bg: #f8f9fb;
--kw-pill-bg: #f0f7ff;
--kw-pill-color: #1a5276;
--diff-delete-bg: #ffeef0;
--diff-delete-text: #cf1322;
--diff-insert-bg: #e6ffed;
--diff-insert-text: #389e0d;
}
html.dark {
--bg-card: #1e1e1e;
--bg-page: #141414;
--bg-hover: #2a2a2a;
--text-primary: #e0e0e0;
--text-secondary: #b0b0b0;
--text-muted: #808080;
--border-color: #333;
--shadow: rgba(0,0,0,0.3);
--preview-ai-bg: #252525;
--kw-pill-bg: #1a2a3a;
--kw-pill-color: #6ab0e0;
--diff-delete-bg: #4a2020;
--diff-delete-text: #ff8a80;
--diff-insert-bg: #1a3a1a;
--diff-insert-text: #a5d6a7;
--hl-toolbar-bg: #2a2a2a;
--hl-tb-btn-color: #e0e0e0;
--hl-tb-btn-hover: #444;
--badge-warning-bg: #4a3520;
--badge-warning-text: #ffb74d;
--badge-info-bg: #1a2a4a;
--badge-info-text: #64b5f6;
--badge-error-bg: #4a2020;
--badge-error-text: #ef9a9a;
--badge-success-bg: #1a3a1a;
--badge-success-text: #a5d6a7;
--accent-border: #8a6a30;
}
/* ═══ 全局按钮统一样式(class .sky-btn ═══ */
/* 使用 class 而非全局 .n-button,避免影响 text 按钮(naive-ui text 按钮无 --text class 可排除) */
.sky-btn {
--n-color: #e6f4ff !important;
--n-color-hover: #d0ebff !important;
--n-color-pressed: #b7dfff !important;
--n-color-focus: #d0ebff !important;
--n-text-color: var(--text-primary) !important;
--n-text-color-hover: #1a1a1a !important;
--n-text-color-pressed: #1a1a1a !important;
--n-border: 1px solid #b3d8ff !important;
--n-border-hover: 1px solid #8ac0ff !important;
--n-border-pressed: 1px solid #6ba8ff !important;
}
/* ghost */
.sky-btn.n-button--ghost {
--n-text-color: #2a6a9a !important;
--n-border: 1px solid #b3d8ff !important;
--n-border-hover: 1px solid #8ac0ff !important;
--n-border-pressed: 1px solid #6ba8ff !important;
--n-color: transparent !important;
}
/* error */
.sky-btn.n-button--error-type {
--n-color: #ffeef0 !important;
--n-color-hover: #ffdbdd !important;
--n-color-pressed: #ffc8cb !important;
--n-color-focus: #ffdbdd !important;
--n-border: 1px solid #e8b4b4 !important;
--n-border-hover: 1px solid #d08080 !important;
--n-border-pressed: 1px solid #c06060 !important;
}
.sky-btn.n-button--ghost.n-button--error-type {
--n-text-color: #d03050 !important;
--n-border: 1px solid #e8b4b4 !important;
--n-border-hover: 1px solid #d08080 !important;
--n-border-pressed: 1px solid #c06060 !important;
}
/* warning */
.sky-btn.n-button--warning-type {
--n-color: #fff7e6 !important;
--n-color-hover: #ffefd0 !important;
--n-color-pressed: #ffe6b8 !important;
--n-color-focus: #ffefd0 !important;
--n-border: 1px solid #e8c888 !important;
--n-border-hover: 1px solid #d0a850 !important;
--n-border-pressed: 1px solid #c09030 !important;
}
.sky-btn.n-button--ghost.n-button--warning-type {
--n-text-color: #d48806 !important;
--n-border: 1px solid #e8c888 !important;
--n-border-hover: 1px solid #d0a850 !important;
--n-border-pressed: 1px solid #c09030 !important;
}
/* dark mode */
html.dark .sky-btn {
--n-color: #1a3a5a !important;
--n-color-hover: #2471a3 !important;
--n-color-pressed: #154360 !important;
--n-color-focus: #2471a3 !important;
--n-text-color: #fff !important;
--n-text-color-hover: #fff !important;
--n-text-color-pressed: #fff !important;
--n-text-color-focus: #fff !important;
--n-border: 1px solid #4a7a9a !important;
--n-border-hover: 1px solid #6ab0e0 !important;
--n-border-pressed: 1px solid #8ac0ff !important;
--n-border-focus: 1px solid #8ac0ff !important;
}
html.dark .sky-btn.n-button--ghost {
--n-text-color: #6ab0e0 !important;
--n-text-color-hover: #8ac8ff !important;
--n-text-color-pressed: #8ac8ff !important;
--n-border: 1px solid #4a7a9a !important;
--n-border-hover: 1px solid #6ab0e0 !important;
--n-border-pressed: 1px solid #8ac0ff !important;
}
/* Dark mode NSelect text visibility */
html.dark .n-select .n-base-selection-label {
--n-text-color: #e0e0e0 !important;
}
/* Dark mode NSelect popup — hover/selected 选项文字颜色 */
html.dark .n-base-select-menu .n-base-select-option--hover {
color: #fff !important;
}
html.dark .n-base-select-menu .n-base-select-option--selected {
color: #fff !important;
}
html.dark .n-base-select-menu .n-base-select-option--hover .n-base-select-option__check,
html.dark .n-base-select-menu .n-base-select-option--selected .n-base-select-option__check {
color: #fff !important;
}
html.dark .sky-btn:focus,
html.dark .sky-btn:focus-visible,
html.dark .sky-btn:active {
color: #fff !important;
}
html.dark ::selection {
background: #1a6bb0;
color: #fff;
}
html.dark .sky-btn.n-button--error-type {
--n-color: #4a2020 !important;
--n-color-hover: #6a3030 !important;
--n-color-pressed: #3a1010 !important;
--n-color-focus: #6a3030 !important;
--n-text-color: #ef9a9a !important;
--n-text-color-hover: #fff !important;
--n-text-color-pressed: #fff !important;
--n-border: 1px solid #c06060 !important;
--n-border-hover: 1px solid #ef9a9a !important;
--n-border-pressed: 1px solid #ffb0b0 !important;
}
html.dark .sky-btn.n-button--ghost.n-button--error-type {
--n-text-color: #ef9a9a !important;
--n-border: 1px solid #c06060 !important;
--n-border-hover: 1px solid #ef9a9a !important;
--n-border-pressed: 1px solid #ffb0b0 !important;
}
html.dark .sky-btn.n-button--warning-type {
--n-color: #4a3520 !important;
--n-color-hover: #6a4530 !important;
--n-color-pressed: #3a2510 !important;
--n-color-focus: #6a4530 !important;
--n-text-color: #ffb74d !important;
--n-text-color-hover: #fff !important;
--n-text-color-pressed: #fff !important;
--n-border: 1px solid #c08030 !important;
--n-border-hover: 1px solid #ffb74d !important;
--n-border-pressed: 1px solid #ffcc80 !important;
}
html.dark .sky-btn.n-button--ghost.n-button--warning-type {
--n-text-color: #ffb74d !important;
--n-border: 1px solid #c08030 !important;
--n-border-hover: 1px solid #ffb74d !important;
--n-border-pressed: 1px solid #ffcc80 !important;
}
/* 移动端:防止任何水平溢出(html + body 双保险) */
@media (max-width: 768px) {
html, body { overflow-x: hidden; width: 100%; max-width: 100vw; }
#app { overflow-x: hidden; max-width: 100vw; }
}
/* SEO: 视觉隐藏但搜索引擎可读 */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>