feat(管理端): 深色模式接入 Element Plus 官方深色变量并动态计算主色色阶

This commit is contained in:
sugar1569 2026-09-29 00:55:44 +08:00
parent a632aafaf9
commit e815e1714f
6 changed files with 119 additions and 25 deletions

View File

@ -22,6 +22,7 @@ import config from '../package.json';
import bus from '@/utils/bus';
import { useMediaStore } from '@/store/module/media';
import { useThemeConfigStore } from '@/store/module/themeConfig';
import { filterThemeCssText } from '@/utils/theme.js';
defineOptions({ name: 'app' });
@ -69,7 +70,9 @@ function onOpenSetingsDrawer() {
function getLayoutThemeConfig() {
if (Local.get('themeConfigPrev')) {
themeConfigStore.setThemeConfig(Local.get('themeConfigPrev'));
document.documentElement.style.cssText = Local.get('themeConfigStyle');
// 恢复时过滤主色浅/深阶(历史脏数据清洗):这些变量由样式表 color-mix 动态计算,
// 行内持久化会压过 dark.scss 暗色档,导致深色模式表头/浅底仍为浅色
document.documentElement.style.cssText = filterThemeCssText(Local.get('themeConfigStyle'));
} else {
Local.set('themeConfigPrev', themeConfigStore.themeConfig);
}

View File

@ -176,12 +176,12 @@
</div>
</div>
<!-- 暗黑模式 -->
<!-- <div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsDark') }}</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isIsDark" :width="35" @change="onAddDarkChange"> </el-switch>
</div>
</div> -->
</div>
<!-- 其它设置 -->
<el-divider :content-position="contentPosotion">{{ $t('message.layout.fiveTitle') }}</el-divider>
<div class="layout-breadcrumb-seting-bar-flex mt15">
@ -243,7 +243,7 @@ import { ref, computed, nextTick, onMounted } from 'vue';
import ClipboardJS from 'clipboard';
import { ElMessage } from 'element-plus';
import { Local } from '@/utils/storage.js';
import { useChangeColor } from '@/utils/theme.js';
import { filterThemeCssText } from '@/utils/theme.js';
import config from '../../../../package.json';
import { themeList } from './theme';
import bus from '@/utils/bus';
@ -279,16 +279,9 @@ function onLayoutMobileResize(res) {
// 全局主题
function onColorPickerChange() {
// if (!getThemeConfig.value.primary) return;
// 颜色加深
// document.documentElement.style.setProperty('--prev-color-primary', getThemeConfig.value.primary);
// 颜色变浅
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--prev-color-primary-light-${i}`,
`${useChangeColor().getLightColor(getThemeConfig.value.primary, i / 10)}`,
);
}
// 主色浅色阶不再写行内样式:app.scss 的 color-mix 表达式引用 --prev-color-primary,
// 换肤只改主色即可全档自动重算;此前行内写死向白混色,会压过 dark.scss 的深色档,
// 导致深色模式下表头/选中行/标签浅底仍是刺眼浅蓝
setLocalThemeConfig();
}
@ -455,8 +448,14 @@ function onMenuBgColorChange() {
// 深色模式
function onAddDarkChange() {
const body = document.documentElement;
if (getThemeConfig.value.isIsDark) body.setAttribute('data-theme', 'dark');
const isDark = getThemeConfig.value.isIsDark;
if (isDark) body.setAttribute('data-theme', 'dark');
else body.setAttribute('data-theme', '');
// Element Plus 官方深色变量表挂在 html.dark 上(main.js 引入的 dark/css-vars.css),同步切换
body.classList.toggle('dark', isDark);
// vxe-table / vxe-pc-ui 自带深色主题挂 data-vxe-ui-theme 属性(树形表格等 vxe 组件),同步切换
if (isDark) body.setAttribute('data-vxe-ui-theme', 'dark');
else body.removeAttribute('data-vxe-ui-theme');
setLocalThemeConfig();
}
@ -485,7 +484,8 @@ function setLocalThemeConfig() {
// 存储布局配置全局主题样式(html根标签)
function setLocalThemeConfigStyle() {
Local.set('themeConfigStyle', document.documentElement.style.cssText);
// 过滤主色浅/深阶:由样式表 color-mix 动态计算,行内持久化会压过深色暗色档
Local.set('themeConfigStyle', filterThemeCssText(document.documentElement.style.cssText));
}
// 布局配置弹窗打开

View File

@ -11,7 +11,6 @@ export const themeList = {
'--prev-bg-columnsMenuBarColor': '#fff',
'--prev-MenuActiveColor': '#0256FF',
'--prev-tag-active-color': '#0256FF',
'--prev-color-primary-light-9': '#e5eeff',
'--prev-bg-columnsMenuActiveColor': '#fff',
},
'theme-2': {
@ -28,7 +27,6 @@ export const themeList = {
'--prev-MenuActiveColor': '#0256FF',
'--prev-bg-columnsMenuActiveBgColor': '#0256FF',
'--prev-tag-active-color': '#0256FF',
'--prev-color-primary-light-9': '#e5eeff',
},
'theme-3': {
'--prev-bg-menuBar': '#282c34',
@ -42,9 +40,7 @@ export const themeList = {
'--prev-bg-columnsMenuBarColor': '#fff',
'--prev-bg-columnsMenuActiveColor': '#fff',
'--prev-MenuActiveColor': '#41b584',
'--prev-color-primary-light-9': '#ecf8f3',
'--prev-tag-active-color': '#41b584',
'--prev-color-primary-light-9': '#ecf8f3',
},
'theme-4': {
'--prev-bg-menuBar': '#ffffff',
@ -60,7 +56,6 @@ export const themeList = {
'--prev-MenuActiveColor': '#41b584',
'--prev-tag-active-color': '#41b584',
'--prev-color-primary-light-9': '#ecf8f3',
},
'theme-5': {
'--prev-bg-menuBar': '#282c34',
@ -74,7 +69,6 @@ export const themeList = {
'--prev-bg-columnsMenuBarColor': '#fff',
'--prev-bg-columnsMenuActiveColor': '#fff',
'--prev-MenuActiveColor': '#6954f0',
'--prev-color-primary-light-9': '#f0eefe',
'--prev-tag-active-color': '#6954f0',
},
'theme-6': {
@ -89,7 +83,6 @@ export const themeList = {
'--prev-bg-columnsMenuBarColor': '#515a6e',
'--prev-bg-columnsMenuActiveColor': '#fff',
'--prev-MenuActiveColor': '#6954f0',
'--prev-color-primary-light-9': '#f0eefe',
'--prev-tag-active-color': '#6954f0',
},
'theme-7': {
@ -117,7 +110,6 @@ export const themeList = {
'--prev-bg-columnsMenuBar': '#fff',
'--prev-bg-columnsMenuBarColor': '#515a6e',
'--prev-bg-columnsMenuActiveColor': '#fff',
'--prev-color-primary-light-9': '#feedeb',
'--prev-MenuActiveColor': '#f34d37',
'--prev-tag-active-color': '#f34d37',
},
@ -134,6 +126,5 @@ export const themeList = {
'--prev-bg-columnsMenuActiveColor': '#fff',
'--prev-tag-active-color': '#0256FF',
'--prev-MenuActiveColor': '#0256FF',
'--prev-color-primary-light-9': '#e5eeff',
},
};

View File

@ -27,6 +27,9 @@ import timeOptions from '@/libs/timeOptions';
import ElementPlus, { ElMessage } from 'element-plus';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import 'element-plus/dist/index.css';
// Element Plus 官方深色变量表(挂 html.dark,由 setings.vue 的 onAddDarkChange 切换);
// 须在 @/theme/index.scss 之前引入,保证同特异性时项目主题层(dark.scss 重桥接)后加载胜出
import 'element-plus/theme-chalk/dark/css-vars.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
// element-ui 字体图标类名兼容层(CSS mask 方案,自动生成)
import './styles/el-icon-compat.css';

View File

@ -7,6 +7,8 @@
--prev-bg-topBarColor: #dadada !important;
--prev-bg-columnsMenuBar: #191919 !important;
--prev-bg-columnsMenuBarColor: #dadada !important;
/* 换肤(setLocalTheme)会按主题行内写入浅色 hover 底(#e5eeff/#ecf8f3 等),深色下需压制 */
--prev-bg-menu-hover-ba-color: rgba(255, 255, 255, 0.1) !important;
--prev-bg-main-color: #1f1f1f !important;
--prev-bg-color: rgba(0, 0, 0, 0.3) !important;
--prev-bg-white: #191919 !important;
@ -24,6 +26,20 @@
--prev-border-color-lighter: #333333 !important;
--prev-border-color-extra-light: #333333 !important;
/* 主色浅色阶暗色档:与 app.scss 浅色档同算法(color-mix),混色底由白色换成暗底
#141414(Element Plus 官方深色变量同款底色)。表头(app.scss 的
--el-table-header-bg-color)、选中行、标签浅底等引用方自动跟随;
行内主色换肤时 color-mix 同样自动重算,无需 JS 参与 */
--prev-color-primary-light-1: color-mix(in srgb, var(--prev-color-primary) 90%, #141414);
--prev-color-primary-light-2: color-mix(in srgb, var(--prev-color-primary) 80%, #141414);
--prev-color-primary-light-3: color-mix(in srgb, var(--prev-color-primary) 70%, #141414);
--prev-color-primary-light-4: color-mix(in srgb, var(--prev-color-primary) 60%, #141414);
--prev-color-primary-light-5: color-mix(in srgb, var(--prev-color-primary) 50%, #141414);
--prev-color-primary-light-6: color-mix(in srgb, var(--prev-color-primary) 40%, #141414);
--prev-color-primary-light-7: color-mix(in srgb, var(--prev-color-primary) 30%, #141414);
--prev-color-primary-light-8: color-mix(in srgb, var(--prev-color-primary) 20%, #141414);
--prev-color-primary-light-9: color-mix(in srgb, var(--prev-color-primary) 10%, #141414);
// menu
.layout-aside {
border-right: 1px solid var(--prev-border-color-lighter) !important;
@ -112,4 +128,63 @@
.el-calendar {
border: 1px solid var(--prev-border-color-lighter);
}
// 底部固定提交卡:浅色为白色毛玻璃,深色换黑色毛玻璃(style.scss 同规则带 !important,
// 此处块内选择器特异性更高,可覆盖)
.fixed-card {
background-color: rgba(25, 25, 25, 0.6) !important;
}
// AI 对话工作台:会话面板底改不透明层次色。
// 浅色档 --prev-bg-color 是实色,深色档是 rgba(0,0,0,0.3) 半透明,
// 面板用半透明底会随下垫内容/页面底色变化产生观感分层
.conversation-panel {
background-color: #202020;
}
// form-create 上传占位框(elm-frame/elm-upload 组件默认 #fff 底 + #c0ccda 边)
._fc-frame ._fc-upload-btn,
._fc-frame ._fc-files,
._fc-upload ._fc-upload-btn,
._fc-upload ._fc-files {
background-color: var(--prev-bg-white) !important;
border-color: var(--prev-border-color-base) !important;
}
}
/* Element Plus 官方深色变量重桥接
------------------------------- */
/* main.js 引入的官方 dark/css-vars.css 挂在 html.dark(特异性 0,1,1)上,会:
1. 把主色系接管为 EP 默认蓝 #409eff,与项目主色(默认 #0256FF)漂移;
2. 把 --el-bg-color 系接管为 #141414 体系,与本项目 #191919 深色体系并存两种黑。
此处以同特异性 html[data-theme='dark'] 重桥接回 --prev-* 体系(本文件后于官方
文件加载,同特异性按源顺序胜出);未列出的官方深色值(语义色 light 阶、遮罩、
阴影、fill-color/-lighter 等)保持官方值兜底 */
html[data-theme='dark'] {
/* 背景体系:统一回 #191919(卡片/弹层/表格行底/输入框内底) */
--el-bg-color: var(--prev-bg-white);
--el-bg-color-overlay: var(--prev-bg-white);
--el-fill-color-blank: var(--prev-bg-white);
--el-fill-color-light: var(--prev-color-hover);
/* 文本体系 */
--el-text-color-primary: var(--prev-color-text-primary);
--el-text-color-regular: var(--prev-color-text-regular);
--el-text-color-secondary: var(--prev-color-text-secondary);
--el-text-color-placeholder: var(--prev-color-text-placeholder);
/* 边框体系 */
--el-border-color: var(--prev-border-color-base);
--el-border-color-light: var(--prev-border-color-light);
--el-border-color-lighter: var(--prev-border-color-lighter);
--el-border-color-extra-light: var(--prev-border-color-extra-light);
/* 主色系:浅色阶向暗底 #141414 混色(同 EP 官方深色算法),保持项目主色不漂移 */
--el-color-primary: var(--prev-color-primary);
--el-color-primary-light-3: color-mix(in srgb, var(--prev-color-primary) 70%, #141414);
--el-color-primary-light-5: color-mix(in srgb, var(--prev-color-primary) 50%, #141414);
--el-color-primary-light-7: color-mix(in srgb, var(--prev-color-primary) 30%, #141414);
--el-color-primary-light-8: color-mix(in srgb, var(--prev-color-primary) 20%, #141414);
--el-color-primary-light-9: color-mix(in srgb, var(--prev-color-primary) 10%, #141414);
--el-color-primary-dark-2: color-mix(in srgb, var(--prev-color-primary) 80%, #000000);
}

View File

@ -1,5 +1,27 @@
import { ElMessage } from 'element-plus';
/**
* 过滤主题行内样式中的主色浅/深阶变量
*
* 主色浅色阶/深色阶由样式表 color-mix 表达式动态计算(亮色档在 theme/app.scss,
* 暗色档在 theme/dark.scss),引用 --prev-color-primary,换肤时自动重算。
* 这类变量不允许持久化为行内样式:行内样式优先级最高,会压过深色模式的暗色档,
* 导致深色下表头/选中行/标签浅底仍是刺眼浅色(2026-09 深色模式适配遗留问题)。
* @param {string} cssText document.documentElement.style.cssText
* @returns {string} 过滤后的 cssText
*/
export function filterThemeCssText(cssText) {
if (!cssText) return '';
const dynamicVarRe = /^--(prev|el)-color-primary-(light|dark)-\d+$/;
return cssText
.split(';')
.filter((item) => {
const name = (item.split(':')[0] || '').trim();
return name && !dynamicVarRe.test(name);
})
.join(';');
}
/**
* 颜色转换函数
* @method hexToRgb hex 颜色转 rgb 颜色