fix: 修复新版前端夜间模式
This commit is contained in:
@@ -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/` 的工作区布局对齐:上传页保留旧版上传区尺寸和文件列表结构,数据库页恢复左侧表列表 + 右侧数据区,设置页恢复左右列,脚本页恢复编辑器容器和底部状态栏。
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user