fix: 修复新版前端夜间模式

This commit is contained in:
2026-05-15 16:23:02 +08:00
parent 12b8a11f17
commit 01c97615cd
5 changed files with 74 additions and 9 deletions
+8
View File
@@ -1,5 +1,13 @@
# 项目上下文记录
## 2026-05-15:修复新版前端夜间模式组件颜色
- 新增 `frontend/src/composables/theme.ts` 作为前端共享主题状态,统一读写 `localStorage.theme` 并同步 `document.documentElement[data-theme]`。
- `App.vue` 的 `n-config-provider` 现在会在夜间模式下使用 Naive UI `darkTheme`,修复 `n-card`、`n-input`、`n-form`、`n-menu`、`n-button` 等组件仍按亮色主题渲染的问题。
- `AppShell.vue` 的主题切换改为调用共享主题逻辑,避免 AppShell 和 Naive Provider 各自维护主题状态。
- `styles.css` 补充工作卡片、卡片标题、表单标签、菜单图标和菜单文字颜色兜底,防止局部组件样式覆盖暗色文本。
- 已执行 `npm run build`,构建通过,仅有 Monaco/Vite 大 chunk 体积警告;已用 headless Chrome 打开新版 `/settings` 并预置 `theme=dark` 验证:页面、卡片标题、表单标签、输入框文字、字段映射标题和顶部按钮均为暗色主题可读颜色。
## 2026-05-15:对齐新版前端主体布局并修复全局拖拽默认行为
- 新版 Vue 前端主体内容继续按旧版 `frontend_old/` 的工作区布局对齐:上传页保留旧版上传区尺寸和文件列表结构,数据库页恢复左侧表列表 + 右侧数据区,设置页恢复左右列,脚本页恢复编辑器容器和底部状态栏。
+8 -3
View File
@@ -1,5 +1,5 @@
<template>
<n-config-provider>
<n-config-provider :theme="naiveTheme">
<n-message-provider>
<n-dialog-provider>
<AppShell />
@@ -9,6 +9,11 @@
</template>
<script setup lang="ts">
import AppShell from './AppShell.vue';
</script>
import { computed } from 'vue';
import { darkTheme } from 'naive-ui';
import AppShell from './AppShell.vue';
import { themeName } from './composables/theme';
const naiveTheme = computed(() => (themeName.value === 'dark' ? darkTheme : undefined));
</script>
+2 -6
View File
@@ -98,6 +98,7 @@ import { apiPost, clearToken, getToken, setToken, setUnauthorizedHandler } from
import type { LoginResponse } from './types';
import LoginView from './components/LoginView.vue';
import { pageHeader, resetPageHeader, resolveHeaderValue, type PageHeaderAction } from './composables/pageHeader';
import { toggleAppTheme } from './composables/theme';
const message = useMessage();
const route = useRoute();
@@ -105,7 +106,6 @@ const router = useRouter();
const token = ref(getToken());
const loginLoading = ref(false);
const sidebarCollapsed = ref(localStorage.getItem('sidebarCollapsed') === 'true');
const theme = ref(localStorage.getItem('theme') || 'light');
const menuKeys = ['workflow', 'history', 'database', 'script', 'settings'] as const;
type MenuKey = (typeof menuKeys)[number];
@@ -126,8 +126,6 @@ const currentTitle = computed(() => {
});
const pageSubtitle = computed(() => resolveHeaderValue(pageHeader.subtitle || '', ''));
document.documentElement.setAttribute('data-theme', theme.value);
onMounted(() => {
window.addEventListener('dragover', preventWindowFileDrop, { capture: true });
window.addEventListener('drop', preventWindowFileDrop, { capture: true });
@@ -173,9 +171,7 @@ function toggleSidebar() {
}
function toggleTheme() {
theme.value = theme.value === 'dark' ? 'light' : 'dark';
localStorage.setItem('theme', theme.value);
document.documentElement.setAttribute('data-theme', theme.value);
toggleAppTheme();
}
async function restartService() {
+40
View File
@@ -0,0 +1,40 @@
import { ref } from 'vue';
export type AppTheme = 'light' | 'dark';
const THEME_KEY = 'theme';
export const themeName = ref<AppTheme>(readStoredTheme());
applyTheme(themeName.value);
export function setAppTheme(nextTheme: AppTheme) {
themeName.value = nextTheme;
applyTheme(nextTheme);
}
export function toggleAppTheme() {
setAppTheme(themeName.value === 'dark' ? 'light' : 'dark');
}
function readStoredTheme(): AppTheme {
try {
const stored = typeof localStorage === 'undefined' ? null : localStorage.getItem(THEME_KEY);
return stored === 'dark' ? 'dark' : 'light';
} catch {
return 'light';
}
}
function applyTheme(nextTheme: AppTheme) {
if (typeof document !== 'undefined') {
document.documentElement.setAttribute('data-theme', nextTheme);
}
try {
if (typeof localStorage !== 'undefined') {
localStorage.setItem(THEME_KEY, nextTheme);
}
} catch {
// Ignore storage failures so theme switching never blocks the UI.
}
}
+16
View File
@@ -210,6 +210,11 @@ select {
transition: all var(--td-transition);
}
.app-menu .n-menu-item-content-header,
.app-menu .n-menu-item-content__icon {
color: inherit;
}
.app-menu .n-menu-item-content:hover {
color: var(--td-text-color-primary);
background: var(--td-bg-color-container-hover);
@@ -371,8 +376,14 @@ select {
border-bottom: 1px solid var(--td-border-color-light);
}
.work-card > .n-card-header .n-card-header__main,
.work-card > .n-card-header .n-card-header__extra {
color: var(--td-text-color-primary);
}
.work-card > .n-card__content {
padding: 16px 20px;
color: var(--td-text-color-primary);
}
.work-card > .n-card__footer {
@@ -381,6 +392,11 @@ select {
border-top: 1px solid var(--td-border-color-light);
}
.work-card .n-form-item-label,
.work-card .n-form-item-label__text {
color: var(--td-text-color-secondary);
}
.login-page {
display: grid;
min-height: 100vh;