refactor(captcha): 调整验证码字体大小并增强验证码生成逻辑

- 修改验证码字体大小从40调整为35
- 增加验证码生成的难度参数,支持不同难度级别的运算
- 优化验证码图像生成逻辑,增加随机噪点和干扰线,提升安全性和可读性
This commit is contained in:
zhangtao
2026-03-25 01:18:24 +08:00
parent fd8ee5d3af
commit 565663be4a
48 changed files with 1216 additions and 1326 deletions
@@ -103,7 +103,6 @@
:border="true"
:max-height="250"
:row-key="pk"
:highlight-current-row="true"
:class="{ radio: !isMultiple }"
@select="handleSelect"
@select-all="handleSelectAll"
@@ -27,7 +27,7 @@ defineProps({
.logo {
width: 100%;
height: $navbar-height;
background-color: var(--sidebar-logo-background);
background-color: var(--layout-logo-bg);
.title {
max-width: 100px;
@@ -36,7 +36,7 @@ defineProps({
text-overflow: ellipsis;
font-size: 14px;
font-weight: bold;
color: var(--sidebar-logo-text-color);
color: var(--layout-logo-text);
white-space: nowrap;
}
}
@@ -50,7 +50,7 @@ defineProps({
background-color: transparent !important;
.title {
color: var(--sidebar-logo-text-color);
color: var(--layout-logo-text);
}
}
}
@@ -41,7 +41,7 @@ const appMainHeight = computed(() => {
.app-main {
position: relative;
overflow-y: auto;
background-color: var(--el-bg-color);
background-color: var(--layout-content-canvas, var(--el-bg-color-page));
/* 布局切换动画优化 */
/* fade */
@@ -33,7 +33,6 @@ import type { RouteRecordRaw } from "vue-router";
import { useSettingsStore, useAppStore } from "@/store";
import { isExternal } from "@/utils/index";
import MenuItem from "./components/MenuItem.vue";
import variables from "@/styles/variables.module.scss";
const props = defineProps({
data: {
@@ -64,13 +63,13 @@ const expandedMenuIndexes = ref<string[]>([]);
const theme = computed(() => settingsStore.theme);
// 菜单主题属性
// 顶栏横向菜单背景由布局 CSS 设为 transparent;若此处传 CSS 变量,EP 内部 TinyColor 无法解析会退成 #000,hover 背景发黑
const menuThemeProps = computed(() => {
const isDark = theme.value === "dark";
const isHorizontal = props.menuMode === "horizontal";
return {
backgroundColor: isDark ? variables["menu-background"] : undefined,
textColor: isDark ? variables["menu-text"] : undefined,
activeTextColor: isDark ? variables["menu-active-text"] : undefined,
backgroundColor: isHorizontal ? "" : "var(--layout-sidebar-bg)",
textColor: "var(--layout-sidebar-text)",
activeTextColor: "var(--layout-sidebar-active-text)",
};
});
@@ -3,9 +3,8 @@
<el-menu
mode="horizontal"
:default-active="activeTopMenuPath"
:background-color="theme === 'dark' ? variables['menu-background'] : undefined"
:text-color="theme === 'dark' ? variables['menu-text'] : undefined"
:active-text-color="theme === 'dark' ? variables['menu-active-text'] : undefined"
text-color="var(--layout-menu-text)"
active-text-color="var(--layout-menu-active-text)"
@select="handleTopMenuSelect"
>
<el-menu-item v-for="item in topMenuItems" :key="item.path" :index="item.path">
@@ -22,16 +21,12 @@ defineOptions({
});
import { LocationQueryRaw, RouteRecordRaw } from "vue-router";
import { usePermissionStore, useAppStore, useSettingsStore } from "@/store";
import variables from "@/styles/variables.module.scss";
import { usePermissionStore, useAppStore } from "@/store";
const router = useRouter();
const route = useRoute();
const appStore = useAppStore();
const permissionStore = usePermissionStore();
const settingsStore = useSettingsStore();
// 获取主题
const theme = computed(() => settingsStore.theme);
// 顶部菜单列表
const topMenus = ref<RouteRecordRaw[]>([]);
@@ -120,10 +115,7 @@ const activeTopMenuPath = computed(() => appStore.activeTopMenuPath);
onMounted(() => {
topMenus.value = permissionStore.routes.filter((item) => !item.meta || !item.meta.hidden);
// 初始化顶部菜单
const currentTopMenuPath =
useRoute().path.split("/").filter(Boolean).length > 1
? useRoute().path.match(/^\/[^/]+/)?.[0] || "/"
: "/";
const currentTopMenuPath = getTopMenuPath(route.path);
appStore.activeTopMenu(currentTopMenuPath); // 设置激活的顶部菜单
permissionStore.setMixLayoutSideMenus(currentTopMenuPath); // 设置混合布局左侧菜单
});
@@ -133,15 +125,17 @@ watch(
() => router.currentRoute.value.path,
(newPath) => {
if (newPath) {
// 提取顶级路径
const topMenuPath =
newPath.split("/").filter(Boolean).length > 1 ? newPath.match(/^\/[^/]+/)?.[0] || "/" : "/";
const topMenuPath = getTopMenuPath(newPath);
// 使用公共方法更新菜单状态,但跳过导航(因为路由已经变化)
updateMenuState(topMenuPath, true);
}
}
);
function getTopMenuPath(path: string): string {
return path.split("/").filter(Boolean).length > 1 ? path.match(/^\/[^/]+/)?.[0] || "/" : "/";
}
</script>
<style lang="scss" scoped>
@@ -182,17 +182,17 @@ function resolvePath(routePath: string) {
html.dark {
.el-menu-item:hover {
background-color: var(--menu-hover);
background-color: var(--layout-menu-hover);
}
}
// 激活菜单项样式 - 只有文字变色,无背景色
.el-menu-item.is-active {
font-weight: 500;
color: var(--sidebar-active-text-color, var(--el-color-primary-dark-2)) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-dark-2)) !important;
.menu-icon {
color: var(--sidebar-active-text-color, var(--el-color-primary-dark-2)) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-dark-2)) !important;
}
}
@@ -200,44 +200,44 @@ html.dark {
.el-sub-menu {
// 当父菜单包含激活子菜单时的样式
&.has-active-child .el-sub-menu__title {
color: var(--sidebar-active-text-color, var(--el-color-primary-dark-2)) !important;
background-color: var(--sidebar-active-bg) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-dark-2)) !important;
background-color: var(--layout-sidebar-active-bg) !important;
.menu-icon {
color: var(--sidebar-active-text-color, var(--el-color-primary-dark-2)) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-dark-2)) !important;
}
}
&.has-active-child .el-sub-menu__title:hover {
background-color: var(--sidebar-active-hover-bg) !important;
background-color: var(--layout-sidebar-active-hover-bg) !important;
}
// 深蓝色侧边栏配色下的父菜单激活状态
html.sidebar-color-blue &.has-active-child .el-sub-menu__title {
color: var(--sidebar-active-text-color, var(--el-color-primary-light-3)) !important;
background-color: var(--sidebar-active-bg) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-light-3)) !important;
background-color: var(--layout-sidebar-active-bg) !important;
.menu-icon {
color: var(--sidebar-active-text-color, var(--el-color-primary-light-3)) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-light-3)) !important;
}
}
html.sidebar-color-blue &.has-active-child .el-sub-menu__title:hover {
background-color: var(--sidebar-active-hover-bg) !important;
background-color: var(--layout-sidebar-active-hover-bg) !important;
}
// 深色主题下的父菜单激活状态
html.dark &.has-active-child .el-sub-menu__title {
color: var(--sidebar-active-text-color, var(--el-color-primary-light-3)) !important;
background-color: var(--sidebar-active-bg) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-light-3)) !important;
background-color: var(--layout-sidebar-active-bg) !important;
.menu-icon {
color: var(--sidebar-active-text-color, var(--el-color-primary-light-3)) !important;
color: var(--layout-sidebar-active-text, var(--el-color-primary-light-3)) !important;
}
}
html.dark &.has-active-child .el-sub-menu__title:hover {
background-color: var(--sidebar-active-hover-bg) !important;
background-color: var(--layout-sidebar-active-hover-bg) !important;
}
}
</style>
@@ -26,10 +26,7 @@ const isSidebarOpened = computed(() => appStore.sidebar.opened);
// 切换侧边栏展开/折叠状态
function toggleSideBar() {
console.log("🔄 Hamburger clicked! Current state:", isSidebarOpened.value);
console.log("🔄 Device type:", appStore.device);
appStore.toggleSidebar();
console.log("🔄 New state:", appStore.sidebar.opened);
}
</script>
@@ -257,7 +257,7 @@ const layoutOptions: LayoutOption[] = [
const settingsStore = useSettingsStore();
// 主题颜色选择器相关
const displayColorPresets = computed(() => themeColorPresets.slice(0, 7)); // 只显示前9个预设颜色
const displayColorPresets = computed(() => themeColorPresets.slice(0, 9)); // 只显示前9个预设颜色
const allColorPresets = themeColorPresets; // 所有颜色预设,用于自定义颜色选择器
const isDark = ref<boolean>(settingsStore.theme === ThemeMode.DARK);
@@ -651,11 +651,11 @@ const handleCloseDrawer = () => {
/* 主题颜色选择器样式 */
.theme-color-selector {
display: flex;
flex-direction: column;
gap: 12px;
align-items: center;
align-items: stretch;
.color-label {
flex-shrink: 0;
min-width: 60px;
}
@@ -785,10 +785,11 @@ onUnmounted(() => {
align-items: center;
width: 100%;
height: $tags-view-height;
background-color: var(--bg-primary);
border-top: 1px solid var(--el-border-color-light);
border-bottom: 1px solid var(--el-border-color-light);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
/* 与 theme.ts layoutTagsBg 一致;介于顶栏与主内容之间的桥接层 */
background-color: var(--layout-tags-bg, var(--el-bg-color-overlay));
border-top: 1px solid var(--layout-tags-border);
border-bottom: 1px solid var(--layout-tags-border);
box-shadow: var(--el-box-shadow-light);
.btn {
display: flex;
@@ -797,11 +798,11 @@ onUnmounted(() => {
width: $tags-view-height;
height: $tags-view-height;
cursor: pointer;
border: 1px solid var(--el-border-color-light);
border: 1px solid var(--layout-tags-border);
&:hover {
color: var(--el-color-primary);
background-color: var(--el-fill-color-light);
background-color: var(--layout-tags-item-hover-bg, var(--layout-tags-item-bg));
.el-icon {
transform: scale(1.1);
@@ -826,12 +827,24 @@ onUnmounted(() => {
padding: 0 8px;
margin-left: 5px;
font-size: 12px;
line-height: 26px;
color: var(--el-text-color-primary);
background: var(--el-bg-color);
border: 1px solid var(--el-border-color);
line-height: 1;
vertical-align: middle;
color: var(--layout-tags-item-text);
background: var(--layout-tags-item-bg);
border: 1px solid var(--layout-tags-border);
transition: all 0.2s ease;
/* 与激活态同宽占位,避免仅 active 有圆点导致整条标签行垂直错位 */
&::before {
flex-shrink: 0;
width: 8px;
height: 8px;
margin-right: 6px;
content: "";
background: transparent;
border-radius: 50%;
}
&:first-of-type {
margin-left: 5px;
}
@@ -839,13 +852,19 @@ onUnmounted(() => {
margin-right: 5px;
}
&:hover {
background-color: var(--el-fill-color);
&:hover:not(.active) {
background-color: var(--layout-tags-item-hover-bg, var(--layout-tags-item-bg));
border-color: var(--layout-tags-item-hover-border, var(--layout-tags-border));
}
:deep(.el-dropdown) {
display: inline-flex;
align-items: center;
line-height: 1;
}
.tag-text {
display: inline-block;
vertical-align: middle;
line-height: 1.25;
}
.tag-close-btn {
@@ -869,21 +888,16 @@ onUnmounted(() => {
}
&.active {
background: var(--layout-tags-item-active-bg);
border-color: var(--el-color-primary);
&::before {
position: relative;
display: inline-block;
width: 8px;
height: 8px;
margin-right: 2px;
content: "";
background: var(--el-color-primary);
border-radius: 50%;
}
.tag-text {
color: var(--el-color-primary);
font-weight: 500;
color: var(--layout-tags-item-active-text);
}
.tag-close-btn {
+5 -3
View File
@@ -55,7 +55,7 @@ const { routes } = useLayoutMenu();
left: 0;
z-index: 999;
width: $sidebar-width;
background-color: var(--menu-background);
background-color: var(--layout-sidebar-bg);
transition: width 0.28s;
&--collapsed {
@@ -65,7 +65,7 @@ const { routes } = useLayoutMenu();
.layout-sidebar {
position: relative;
height: 100%;
background-color: var(--menu-background);
background-color: var(--layout-sidebar-bg);
transition: width 0.28s;
&.has-logo {
@@ -95,7 +95,9 @@ const { routes } = useLayoutMenu();
position: sticky;
top: 0;
z-index: 9;
background-color: var(--bg-secondary);
color: var(--layout-header-text);
background-color: var(--layout-header-bg);
border-bottom: 1px solid var(--layout-header-border);
transition: width 0.28s;
}
}
+12 -9
View File
@@ -30,9 +30,9 @@
:collapse="!isSidebarOpen"
:collapse-transition="false"
:unique-opened="false"
:background-color="variables['menu-background']"
:text-color="variables['menu-text']"
:active-text-color="variables['menu-active-text']"
background-color="var(--layout-menu-bg)"
text-color="var(--layout-menu-text)"
active-text-color="var(--layout-menu-active-text)"
>
<MenuItem
v-for="item in sideMenuRoutes"
@@ -70,7 +70,6 @@ import TagsView from "../components/TagsView/index.vue";
import AppMain from "../components/AppMain/index.vue";
import MenuItem from "../components/Menu/components/MenuItem.vue";
import Hamburger from "@/components/Hamburger/index.vue";
import variables from "@/styles/variables.module.scss";
import { isExternal } from "@/utils/index";
import { computed, watch } from "vue";
import { useAppStore, usePermissionStore } from "@/store";
@@ -148,7 +147,9 @@ watch(
z-index: 999;
width: 100%;
height: $navbar-height;
background-color: var(--menu-background);
color: var(--layout-header-text);
background-color: var(--layout-header-bg);
border-bottom: 1px solid var(--layout-header-border);
&-content {
display: flex;
@@ -190,8 +191,9 @@ watch(
border-bottom: none;
&.is-active {
background-color: rgba(255, 255, 255, 0.12);
border-bottom: 2px solid var(--el-color-primary);
color: var(--layout-menu-active-text);
background-color: var(--layout-menu-active-bg);
border-bottom: 2px solid var(--layout-menu-active-text);
}
}
}
@@ -214,7 +216,7 @@ watch(
position: relative;
width: $sidebar-width;
height: 100%;
background-color: var(--menu-background);
background-color: var(--layout-menu-bg);
transition: width 0.28s;
&.layout__sidebar--collapsed {
@@ -239,7 +241,7 @@ watch(
width: 100%;
height: 50px;
line-height: 50px;
background-color: var(--menu-background);
background-color: var(--layout-menu-bg);
box-shadow: 0 0 6px -2px var(--el-color-primary);
}
}
@@ -250,6 +252,7 @@ watch(
height: 100%;
margin-left: 0;
overflow-y: auto;
background-color: var(--layout-content-canvas, var(--layout-content-bg));
}
}
}
+7 -4
View File
@@ -67,7 +67,9 @@ const isLogoCollapsed = computed(() => width.value < 768);
justify-content: space-between;
width: 100%;
height: $navbar-height;
background-color: var(--menu-background);
color: var(--layout-header-text);
background-color: var(--layout-header-bg);
border-bottom: 1px solid var(--layout-header-border);
&-left {
display: flex;
@@ -124,11 +126,11 @@ const isLogoCollapsed = computed(() => width.value < 768);
&.has-active-child {
.el-sub-menu__title {
color: var(--el-color-primary) !important;
border-bottom: 2px solid var(--el-color-primary) !important;
color: var(--layout-menu-active-text) !important;
border-bottom: 2px solid var(--layout-menu-active-text) !important;
.menu-icon {
color: var(--el-color-primary) !important;
color: var(--layout-menu-active-text) !important;
}
}
}
@@ -152,6 +154,7 @@ const isLogoCollapsed = computed(() => width.value < 768);
&__main {
height: calc(100vh - $navbar-height);
overflow-y: auto;
background-color: var(--layout-content-canvas, var(--layout-content-bg));
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import { createTerminal } from "vue-web-terminal";
import "element-plus/theme-chalk/dark/css-vars.css";
import "element-plus/dist/index.css";
// 主题变量(包含亮色和暗黑模式)
import "@/styles/index.scss";
import "./styles/index.scss";
import "uno.css";
+1 -52
View File
@@ -66,64 +66,13 @@ export const defaultSettings: AppSettings = {
*/
export const themeColorPresets = [
// ==================== 精选推荐 - 最受欢迎的颜色 ====================
"#5dc0ff", // 淡蓝色 - 清新明亮,亮暗主题皆宜
"#3b82f6", // 现代蓝 - 默认主题色,专业稳重
"#5dc0ff", // 淡蓝色 - 清新明亮,亮暗主题皆宜
"#10b981", // 翡翠绿 - 清新自然,活力健康
"#8b5cf6", // 紫罗兰 - 创意优雅,现代时尚
"#f59e0b", // 琥珀橙 - 温暖活力,积极向上
"#06b6d4", // 青蓝色 - 科技清新,年轻活力
"#ef4444", // 鲜红色 - 醒目强烈,热情奔放
"#ec4899", // 玫瑰粉 - 浪漫温馨,时尚个性
"#14b8a6", // 青绿色 - 清新自然,平和舒适
// ==================== 蓝色系 - 科技与专业 ====================
"#60a5fa", // 天空蓝 - 清新明亮,轻松愉悦
"#3b82f6", // 现代蓝 - 专业稳重,值得信赖
"#2563eb", // 深蓝色 - 商务专业,稳重大气
"#1d4ed8", // 皇家蓝 - 高端商务,权威专业
"#1e40af", // 深海蓝 - 商务精英,深沉内敛
// ==================== 绿色系 - 自然与活力 ====================
"#34d399", // 浅绿色 - 清新明亮,生机盎然
"#10b981", // 翡翠绿 - 清新自然,活力健康
"#22c55e", // 草绿色 - 健康活力,积极向上
"#16a34a", // 森林绿 - 生态环保,自然和谐
"#15803d", // 深绿色 - 稳重大气,成熟稳重
// ==================== 紫色系 - 创意与优雅 ====================
"#a78bfa", // 浅紫色 - 时尚现代,温柔浪漫
"#8b5cf6", // 紫罗兰 - 创意优雅,现代时尚
"#7c3aed", // 葡萄紫 - 创意无限,神秘优雅
"#6d28d9", // 深紫色 - 神秘高端,独特个性
"#5b21b6", // 皇家紫 - 王者风范,尊贵典雅
// ==================== 橙色系 - 温暖与活力 ====================
"#fb923c", // 浅橙色 - 温暖亲切,阳光开朗
"#f59e0b", // 琥珀橙 - 温暖活力,积极向上
"#f97316", // 火橙色 - 热情奔放,活力四射
"#ea580c", // 深橙色 - 阳光活力,热情洋溢
// ==================== 青色系 - 科技与清新 ====================
"#22d3ee", // 浅青色 - 清新明亮,年轻活力
"#06b6d4", // 青蓝色 - 科技清新,年轻活力
"#14b8a6", // 青绿色 - 清新自然,平和舒适
"#0891b2", // 天蓝色 - 清新自然,专业科技
"#0e7490", // 深青色 - 专业科技,沉稳内敛
// ==================== 红色系 - 激情与警示 ====================
"#f87171", // 浅红色 - 柔和温暖,亲和力强
"#ef4444", // 鲜红色 - 醒目强烈,热情奔放
"#dc2626", // 猩红色 - 激情四射,力量感强
"#b91c1c", // 深红色 - 庄重严肃,权威感强
// ==================== 粉色系 - 温柔与时尚 ====================
"#f472b6", // 浅粉色 - 柔美可爱,青春活力
"#ec4899", // 玫瑰粉 - 浪漫温馨,时尚个性
"#db2777", // 玫瑰红 - 浪漫优雅,热情奔放
"#be185d", // 深粉色 - 高贵典雅,成熟魅力
// ==================== 灰色系 - 简约与现代 ====================
"#9ca3af", // 浅灰色 - 简约现代,低调内敛
"#6b7280", // 经典灰 - 中性稳重,专业可靠
"#4b5563", // 深灰色 - 商务专业,成熟稳重
];
@@ -98,7 +98,6 @@ export const useSettingsStore = defineStore("setting", () => {
const themeColor = useStorage<string>(SETTINGS_KEYS.THEME_COLOR, defaultSettings.themeColor);
// 主题模式
const theme = useStorage<ThemeMode>(SETTINGS_KEYS.THEME, defaultSettings.theme);
// 是否开启灰色模式
const grayMode = useStorage<boolean>(SETTINGS_KEYS.GRAY_MODE, defaultSettings.grayMode);
// 是否开启AI助手
+365
View File
@@ -0,0 +1,365 @@
/* 暗黑模式通过 CSS 自定义变量,官方链接:https://element-plus.org/zh-CN/guide/dark-mode.html#%E9%80%9A%E8%BF%87-css */
/**
* Element Plus 主题最小回退变量(合并版)
*
* === 配色层级(同一主色:哪里深、哪里浅)===
* L0 页面画布:--el-bg-color / --el-bg-color-page / --layout-content-canvas(最浅透气,大面积)
* L1 内容区块:--layout-content-bg(与 L0 略分阶)
* L2 卡片/表格容器面:--el-bg-color-overlay(抬升中性面)
* L3 表头/侧栏条:color-mix(primary, fill) 明显高于 L2(表格见下文「表格」)
* L4 浮层(下拉/弹窗):面板底 = overlay,不用整板灌主色;强调在选项/按钮/focus
* L5 再嵌套浅填充:--el-fill-color-light(hover 槽、禁用底等)
* 标签栏 TagsView:介于顶栏与内容区 — 整条背景 = --layout-tags-bg(theme.ts blend);pill 默认/激活
* = --layout-tags-item-bg / --layout-tags-item-active-*;非激活 hover = 下文 --layout-tags-item-hover-*
* 主色强度:点线控件(按钮、focus、选中字)用实色 primary;面状 hover/范围/斑马只用 color-mix 低比例
*
* === 本文区块 ===
* :root 亮色 / html.dark 暗色 / 运行时 theme.ts 覆盖
* 弹层、浮层 Popper、Select 选项、DatePicker 范围、级联、表格见对应注释
* 其余排期:Message、图表取 CSS 变量、登录全屏页;Tabs border-card 顶条 / Steps simple / Progress 轨道见下文
*/
:root {
/* 品牌与语义 */
--el-color-primary: #1677ff;
--el-color-success: #10b981;
--el-color-warning: #f59e0b;
--el-color-danger: #ef4444;
--el-color-info: #2f86ff;
/* 基础中性色 */
--el-bg-color: #f5f7fb;
--el-bg-color-page: #f5f7fb;
--el-bg-color-overlay: #ffffff;
--el-fill-color: #eef3fb;
--el-fill-color-light: #f7f9fd;
--el-border-color: #d8e2f0;
--el-border-color-light: #e7edf5;
--el-text-color-primary: #0f172a;
--el-text-color-regular: #334155;
--el-text-color-secondary: #64748b;
--el-text-color-placeholder: #94a3b8;
/* 阴影/遮罩 */
--el-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--el-box-shadow-light: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-overlay-color: rgba(15, 23, 42, 0.5);
--el-overlay-color-light: rgba(15, 23, 42, 0.3);
--el-overlay-color-lighter: rgba(15, 23, 42, 0.15);
/* 菜单 hover 兼容 */
--el-menu-hover-bg-color: #f3f6fb;
/* 主内容区画布:组件间隙可见区域,随 --el-color-primary / --el-bg-color-page(theme.ts)变化 */
--layout-content-canvas: color-mix(
in srgb,
var(--el-color-primary) 3.5%,
var(--el-bg-color-page)
);
/* 搜索区等与间隙呼应的分割线 */
--layout-block-border-themed: color-mix(
in srgb,
var(--el-color-primary) 18%,
var(--el-border-color-light)
);
/* TagsView:未激活标签 hover(theme 只算默认/激活底,hover 在此补主色透气) */
--layout-tags-item-hover-bg: color-mix(
in srgb,
var(--el-color-primary) 9%,
var(--layout-tags-item-bg)
);
--layout-tags-item-hover-border: color-mix(
in srgb,
var(--el-color-primary) 22%,
var(--layout-tags-border)
);
/* Select / TreeSelect 选项行(EP 默认 hover=fill-light、选中无底,与主题脱节) */
--el-select-option-hover-bg: color-mix(
in srgb,
var(--el-color-primary) 11%,
var(--el-bg-color-overlay)
);
--el-select-option-selected-bg: color-mix(
in srgb,
var(--el-color-primary) 18%,
var(--el-bg-color-overlay)
);
/* DatePicker 区间内底(EP 默认 inrange≈极浅灰,与主题脱节) */
--el-datepicker-range-bg: color-mix(
in srgb,
var(--el-color-primary) 10%,
var(--el-bg-color-overlay)
);
--el-datepicker-range-hover-bg: color-mix(
in srgb,
var(--el-color-primary) 16%,
var(--el-bg-color-overlay)
);
/*
* 弹层
* - 遮罩:--el-overlay-color
* - 面板底:纯中性 overlay(不要混主色);主题色交给内部 primary 按钮、表单 focus、链接等
* - Drawer 继承 --el-dialog-bg-color;Dialog 见下方 html .el-dialog(压过 EP 的 var(--el-bg-color))
*/
--el-dialog-bg-color: var(--el-bg-color-overlay);
--el-popover-bg-color: var(--el-dialog-bg-color);
--el-popup-modal-bg-color: var(--el-overlay-color);
--el-popup-modal-opacity: 1;
}
html.dark {
/* 品牌与语义 */
--el-color-primary: #1677ff;
--el-color-success: #10b981;
--el-color-warning: #f59e0b;
--el-color-danger: #ef4444;
--el-color-info: #2f86ff;
/* 基础中性色 */
--el-bg-color: #0b1220;
--el-bg-color-page: #0b1220;
--el-bg-color-overlay: #111a2c;
--el-fill-color: #141f33;
--el-fill-color-light: #1b2a45;
--el-border-color: #253858;
--el-border-color-light: #31486b;
--el-text-color-primary: #f8fafc;
--el-text-color-regular: #e2e8f0;
--el-text-color-secondary: #94a3b8;
--el-text-color-placeholder: #64748b;
/* 阴影/遮罩 */
--el-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
--el-box-shadow-light: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-overlay-color: rgba(0, 0, 0, 0.7);
--el-overlay-color-light: rgba(0, 0, 0, 0.5);
--el-overlay-color-lighter: rgba(0, 0, 0, 0.3);
/* 菜单 hover 兼容 */
--el-menu-hover-bg-color: #1b2a45;
--layout-content-canvas: color-mix(in srgb, var(--el-color-primary) 7%, var(--el-bg-color-page));
--layout-block-border-themed: color-mix(
in srgb,
var(--el-color-primary) 24%,
var(--el-border-color-light)
);
--layout-tags-item-hover-bg: color-mix(
in srgb,
var(--el-color-primary) 14%,
var(--layout-tags-item-bg)
);
--layout-tags-item-hover-border: color-mix(
in srgb,
var(--el-color-primary) 30%,
var(--layout-tags-border)
);
--el-select-option-hover-bg: color-mix(
in srgb,
var(--el-color-primary) 14%,
var(--el-bg-color-overlay)
);
--el-select-option-selected-bg: color-mix(
in srgb,
var(--el-color-primary) 24%,
var(--el-bg-color-overlay)
);
--el-datepicker-range-bg: color-mix(
in srgb,
var(--el-color-primary) 13%,
var(--el-bg-color-overlay)
);
--el-datepicker-range-hover-bg: color-mix(
in srgb,
var(--el-color-primary) 20%,
var(--el-bg-color-overlay)
);
--el-dialog-bg-color: var(--el-bg-color-overlay);
--el-popover-bg-color: var(--el-dialog-bg-color);
--el-popup-modal-bg-color: var(--el-overlay-color);
--el-popup-modal-opacity: 1;
}
/* Dialog:EP 在 .el-dialog 上写死 --el-dialog-bg-color: var(--el-bg-color),改为抬升中性底 */
html .el-dialog {
--el-dialog-bg-color: var(--el-bg-color-overlay);
}
/*
* 浮层 Popper(与弹窗同一 token:--el-dialog-bg-color,现为中性 overlay)
* EP 部分组件写死 --el-bg-color-overlay;显式统一到 --el-dialog-bg-color 便于后续改一处全局同步。
*/
html .el-cascader {
--el-cascader-menu-fill: var(--el-dialog-bg-color);
}
html .el-mention {
--el-mention-bg-color: var(--el-dialog-bg-color);
}
html .el-select__popper.el-popper,
html .el-tree-select__popper.el-popper,
html .el-dropdown__popper.el-popper,
html .el-autocomplete__popper.el-popper,
html .el-mention__popper.el-popper,
html .el-picker__popper.el-popper {
background: var(--el-dialog-bg-color);
}
html .el-select__popper.el-popper .el-popper__arrow:before,
html .el-tree-select__popper.el-popper .el-popper__arrow:before,
html .el-dropdown__popper.el-popper .el-popper__arrow:before,
html .el-autocomplete__popper.el-popper .el-popper__arrow:before,
html .el-mention__popper.el-popper .el-popper__arrow:before,
html .el-picker__popper.el-popper .el-popper__arrow:before {
background: var(--el-dialog-bg-color);
}
html .el-picker-panel {
background: var(--el-dialog-bg-color);
--el-datepicker-inrange-bg-color: var(--el-datepicker-range-bg);
--el-datepicker-inrange-hover-bg-color: var(--el-datepicker-range-hover-bg);
}
html .el-dropdown-menu {
background-color: var(--el-dialog-bg-color);
}
html .el-color-picker-panel {
background: var(--el-dialog-bg-color);
}
html .el-transfer-panel {
background: var(--el-dialog-bg-color);
}
/* Select / TreeSelect:选项 hover、选中背景随主色(浮层底仍为 overlay) */
html .el-select__popper .el-select-dropdown__item.is-hovering,
html .el-tree-select__popper .el-select-dropdown__item.is-hovering {
background-color: var(--el-select-option-hover-bg);
}
html .el-select__popper .el-select-dropdown__item.is-selected,
html .el-tree-select__popper .el-select-dropdown__item.is-selected {
background-color: var(--el-select-option-selected-bg);
}
/* 级联面板节点 hover 与 Select 一致(面板内自带 --el-cascader-node-background-hover) */
html .el-cascader-panel {
--el-cascader-node-background-hover: var(--el-select-option-hover-bg);
}
/* Skeleton 动画条:终点色带一点主色,避免纯灰与整站脱节 */
html .el-skeleton {
--el-skeleton-to-color: color-mix(
in srgb,
var(--el-color-primary) 12%,
var(--el-fill-color-darker)
);
}
/* Tabs border-card:顶栏略混主色(active 条与字仍用 EP 的 --el-color-primary) */
html .el-tabs--border-card > .el-tabs__header {
background: color-mix(in srgb, var(--el-color-primary) 5%, var(--el-fill-color-light));
}
html.dark .el-tabs--border-card > .el-tabs__header {
background: color-mix(in srgb, var(--el-color-primary) 8%, var(--el-fill-color));
}
/* Steps 简约模式底条 */
html .el-steps--simple {
background: color-mix(in srgb, var(--el-color-primary) 6%, var(--el-fill-color-light));
}
html.dark .el-steps--simple {
background: color-mix(in srgb, var(--el-color-primary) 9%, var(--el-fill-color));
}
/* Progress:轨道由极浅灰改为主色轻混,与已填充段同一系 */
html .el-progress-bar__outer {
background-color: color-mix(in srgb, var(--el-color-primary) 8%, var(--el-fill-color-light));
}
html.dark .el-progress-bar__outer {
background-color: color-mix(in srgb, var(--el-color-primary) 11%, var(--el-fill-color-darker));
}
/* ---------- Element Plus 表格(随 :root / html.dark 与 theme.ts 主色变) ---------- */
html .el-table {
--el-table-bg-color: var(--el-bg-color-overlay);
--el-table-tr-bg-color: var(--el-bg-color-overlay);
--el-table-header-bg-color: color-mix(
in srgb,
var(--el-color-primary) 28%,
var(--el-fill-color-light)
);
--el-table-striped-row-bg: color-mix(
in srgb,
var(--el-color-primary) 12%,
var(--el-bg-color-overlay)
);
--el-table-row-hover-bg-color: color-mix(
in srgb,
var(--el-color-primary) 5%,
var(--el-bg-color-overlay)
);
}
html.dark .el-table {
--el-table-header-bg-color: color-mix(
in srgb,
var(--el-color-primary) 24%,
var(--el-fill-color-light)
);
--el-table-striped-row-bg: color-mix(
in srgb,
var(--el-color-primary) 14%,
var(--el-bg-color-overlay)
);
--el-table-row-hover-bg-color: color-mix(
in srgb,
var(--el-color-primary) 7%,
var(--el-bg-color-overlay)
);
}
html .el-table.el-table--striped {
--el-fill-color-lighter: var(--el-table-striped-row-bg);
}
html .el-table .el-table__body tr.hover-row > td.el-table__cell.el-table-fixed-column--left,
html .el-table .el-table__body tr.hover-row > td.el-table__cell.el-table-fixed-column--right {
background-color: var(--el-table-row-hover-bg-color) !important;
}
html
.el-table.el-table--striped
.el-table__body
tr:not(.el-table__row--striped)
> td.el-table__cell.el-table-fixed-column--left,
html
.el-table.el-table--striped
.el-table__body
tr:not(.el-table__row--striped)
> td.el-table__cell.el-table-fixed-column--right {
background-color: var(--el-table-tr-bg-color) !important;
}
html .el-table .el-table__header-wrapper tr th.el-table-fixed-column--left,
html .el-table .el-table__header-wrapper tr th.el-table-fixed-column--right {
background-color: var(--el-table-header-bg-color) !important;
}
html .el-table td.el-table__fixed-right-patch,
html .el-table th.el-table__fixed-right-patch {
background-color: var(--el-table-bg-color) !important;
}
+69 -14
View File
@@ -2,11 +2,8 @@
// 1. 项目自定义变量(侧边栏、布局等)
@use "./variables";
// 2. Element Plus 亮色模式变量
@use "./theme/element-light";
// 3. Element Plus 暗黑模式变量
@use "./theme/element-dark";
// 2. 暗黑模式变量(合并)
@use "./dark/css-vars";
// ==================== 基础样式 ====================
@use "./reset";
@@ -22,41 +19,99 @@
border-radius: 4px;
}
// 主滚动区域滚动条与主题弱对齐(Firefox scrollbar-color + WebKit)
html,
.app-main,
.layout__main,
.chat-messages {
scrollbar-color: color-mix(in srgb, var(--el-color-primary) 32%, var(--el-border-color))
var(--el-fill-color-light);
scrollbar-width: thin;
}
html.dark,
html.dark .app-main,
html.dark .layout__main,
html.dark .chat-messages {
scrollbar-color: color-mix(in srgb, var(--el-color-primary) 26%, var(--el-border-color-light))
var(--el-bg-color);
}
.app-main,
.layout__main,
.chat-messages {
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-track {
background: var(--el-fill-color-light);
}
&::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--el-color-primary) 30%, var(--el-border-color));
border-radius: 4px;
}
&::-webkit-scrollbar-thumb:hover {
background-color: color-mix(in srgb, var(--el-color-primary) 42%, var(--el-border-color));
}
}
html.dark .app-main,
html.dark .layout__main,
html.dark .chat-messages {
&::-webkit-scrollbar-track {
background: var(--el-bg-color);
}
}
// 进度条颜色
#nprogress .bar {
background-color: var(--el-color-primary);
}
// ==================== 混合布局左侧菜单的hover样式 ====================
.layout__sidebar .el-menu {
.el-menu-item:hover,
.el-sub-menu__title:hover {
background-color: var(--layout-sidebar-hover, #f3f6fb) !important;
}
}
.layout-mix .layout__sidebar--left .el-menu {
.el-menu-item {
&:hover {
// 极简白主题:使用浅灰色背景
background-color: var(--el-fill-color-light) !important;
background-color: var(--layout-menu-hover, var(--el-fill-color-light)) !important;
}
}
.el-sub-menu__title {
&:hover {
// 极简白主题:使用浅灰色背景
background-color: var(--el-fill-color-light) !important;
background-color: var(--layout-menu-hover, var(--el-fill-color-light)) !important;
}
}
}
html.dark .layout__sidebar .el-menu {
.el-menu-item:hover,
.el-sub-menu__title:hover {
background-color: var(--layout-sidebar-hover, #1b2a45) !important;
}
}
// 深色主题下的左侧菜单hover样式
html.dark .layout-mix .layout__sidebar--left .el-menu {
.el-menu-item {
&:hover {
// 深色背景:使用CSS变量
background-color: var(--menu-hover) !important;
background-color: var(--layout-sidebar-hover, #1b2a45) !important;
}
}
.el-sub-menu__title {
&:hover {
// 深色背景:使用CSS变量
background-color: var(--menu-hover) !important;
background-color: var(--layout-sidebar-hover, #1b2a45) !important;
}
}
}
@@ -91,7 +146,7 @@ html.dark .layout-mix .layout__sidebar--left .el-menu {
.search-container {
padding: 18px 16px 0;
margin-top: 16px;
border: 1px solid var(--el-border-color-light);
border: 1px solid var(--layout-block-border-themed, var(--el-border-color-light));
.search-buttons {
margin-right: 0;
-343
View File
@@ -1,343 +0,0 @@
/**
* Element Plus 暗黑模式主题变量
*
* 注意:主题色变体(light-1 到 light-9, dark-2)由 theme.ts 动态生成
* 此文件仅提供默认基础值和组件特定样式
*/
html.dark {
--el-color-primary: #3b82f6;
--el-color-success: #10b981;
--el-color-warning: #f59e0b;
--el-color-danger: #ef4444;
--el-color-info: #64748b;
--el-bg-color: #020617;
--el-bg-color-page: #020617;
--el-bg-color-overlay: #0f172a;
--el-fill-color-blank: #020617;
--el-fill-color: #0f172a;
--el-fill-color-light: #1e293b;
--el-fill-color-lighter: #0f172a;
--el-fill-color-extra-light: #020617;
--el-fill-color-dark: #334155;
--el-fill-color-darker: #475569;
--el-text-color-primary: #f8fafc;
--el-text-color-regular: #e2e8f0;
--el-text-color-secondary: #94a3b8;
--el-text-color-placeholder: #64748b;
--el-border-color: #1e293b;
--el-border-color-light: #334155;
--el-border-color-lighter: #475569;
--el-border-color-extra-light: #64748b;
--el-border-color-dark: #0f172a;
--el-border-color-darker: #020617;
--el-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
--el-box-shadow-light: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-box-shadow-lighter: 0 1px 1px 0 rgba(0, 0, 0, 0.12);
--el-box-shadow-dark: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.24);
--el-disabled-bg-color: #0f172a;
--el-disabled-text-color: #475569;
--el-disabled-border-color: #1e293b;
--el-overlay-color: rgba(0, 0, 0, 0.7);
--el-overlay-color-light: rgba(0, 0, 0, 0.5);
--el-overlay-color-lighter: rgba(0, 0, 0, 0.3);
--el-scrollbar-bg-color: #0f172a;
--el-scrollbar-thumb-color: #334155;
--el-scrollbar-thumb-hover-color: #475569;
--el-table-bg-color: var(--el-fill-color-extra-light);
--el-table-tr-bg-color: var(--el-fill-color-extra-light);
--el-table-header-bg-color: var(--el-fill-color-light);
--el-table-row-hover-bg-color: var(--el-fill-color);
--el-table-border-color: var(--el-border-color);
--el-dialog-width: 50%;
--el-dialog-margin-top: 15vh;
--el-dialog-bg-color: var(--el-fill-color);
--el-dialog-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
--el-dialog-title-font-size: 18px;
--el-dialog-content-font-size: 14px;
--el-dialog-font-line-height: 1.5;
--el-dialog-padding-primary: 16px;
--el-dialog-border-radius: 4px;
--el-drawer-bg-color: var(--el-fill-color);
--el-drawer-padding-primary: 20px;
--el-dropdown-menu-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-dropdown-menuItem-hover-fill: var(--el-fill-color-light);
--el-dropdown-menuItem-hover-color: var(--el-color-primary);
--el-dropdown-menu-index: 10;
--el-popover-bg-color: var(--el-fill-color);
--el-popover-font-size: 14px;
--el-popover-border-color: var(--el-border-color);
--el-popover-padding: 12px;
--el-popover-padding-large: 18px 20px;
--el-popover-title-font-size: 16px;
--el-popover-title-text-color: #f8fafc;
--el-popover-border-radius: 4px;
--el-popper-border-radius: 4px;
--el-card-border-color: var(--el-border-color);
--el-card-border-radius: 4px;
--el-card-padding: 20px;
--el-card-bg-color: var(--el-fill-color);
--el-badge-bg-color: var(--el-color-danger);
--el-badge-radius: 10px;
--el-badge-font-size: 12px;
--el-badge-padding: 6px;
--el-badge-size: 18px;
--el-segmented-color: #e2e8f0;
--el-segmented-bg-color: var(--el-fill-color);
--el-segmented-padding: 2px;
--el-segmented-item-selected-color: #f8fafc;
--el-segmented-item-selected-bg-color: var(--el-color-primary);
--el-segmented-item-selected-disabled-bg-color: var(--el-fill-color);
--el-segmented-item-hover-color: #f8fafc;
--el-segmented-item-hover-bg-color: var(--el-fill-color-light);
--el-segmented-item-active-bg-color: var(--el-fill-color-dark);
--el-segmented-item-disabled-color: #64748b;
--el-tour-width: 520px;
--el-tour-padding-primary: 12px;
--el-tour-font-line-height: 1.5;
--el-tour-title-font-size: 16px;
--el-tour-title-text-color: #f8fafc;
--el-tour-title-font-weight: 400;
--el-tour-close-color: #94a3b8;
--el-tour-font-size: 14px;
--el-tour-color: #f8fafc;
--el-tour-bg-color: var(--el-fill-color);
--el-tour-border-radius: 4px;
--el-anchor-bg-color: var(--el-fill-color);
--el-anchor-padding-indent: 14px;
--el-anchor-line-height: 22px;
--el-anchor-font-size: 12px;
--el-anchor-color: #94a3b8;
--el-anchor-active-color: var(--el-color-primary);
--el-anchor-marker-bg-color: var(--el-color-primary);
--el-mention-font-size: 14px;
--el-mention-bg-color: var(--el-fill-color);
--el-mention-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-mention-border: 1px solid var(--el-border-color);
--el-mention-option-color: #e2e8f0;
--el-mention-option-height: 34px;
--el-mention-option-min-width: 100px;
--el-mention-option-hover-background: var(--el-fill-color-light);
--el-mention-option-selected-color: var(--el-color-primary);
--el-mention-option-disabled-color: #64748b;
--el-mention-option-loading-color: #94a3b8;
--el-mention-option-loading-padding: 10px 0;
--el-mention-max-height: 174px;
--el-mention-padding: 6px 0;
--el-mention-header-padding: 10px;
--el-mention-footer-padding: 10px;
--el-skeleton-color: var(--el-fill-color);
--el-skeleton-to-color: var(--el-fill-color-dark);
--el-alert-padding: 8px 16px;
--el-alert-border-radius-base: 4px;
--el-alert-title-font-size: 14px;
--el-alert-title-with-description-font-size: 16px;
--el-alert-description-font-size: 14px;
--el-alert-close-font-size: 16px;
--el-alert-close-customed-font-size: 14px;
--el-alert-icon-size: 16px;
--el-alert-icon-large-size: 28px;
--el-messagebox-title-color: #f8fafc;
--el-messagebox-width: 420px;
--el-messagebox-border-radius: 4px;
--el-messagebox-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
--el-messagebox-font-size: 18px;
--el-messagebox-content-font-size: 14px;
--el-messagebox-content-color: #e2e8f0;
--el-messagebox-error-font-size: 12px;
--el-messagebox-padding-primary: 12px;
--el-messagebox-font-line-height: 1.5;
--el-message-bg-color: var(--el-fill-color-light);
--el-message-border-color: var(--el-border-color);
--el-message-padding: 11px 15px;
--el-message-close-size: 16px;
--el-message-close-icon-color: #94a3b8;
--el-message-close-hover-color: #64748b;
--el-notification-width: 330px;
--el-notification-padding: 14px 26px 14px 13px;
--el-notification-radius: 8px;
--el-notification-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-notification-border-color: var(--el-border-color);
--el-notification-icon-size: 24px;
--el-notification-close-font-size: 16px;
--el-notification-group-margin-left: 13px;
--el-notification-group-margin-right: 8px;
--el-notification-content-font-size: 14px;
--el-notification-content-color: #e2e8f0;
--el-notification-title-font-size: 16px;
--el-notification-title-color: #f8fafc;
--el-notification-close-color: #94a3b8;
--el-notification-close-hover-color: #64748b;
--el-input-text-color: #e2e8f0;
--el-input-border: 1px solid var(--el-border-color);
--el-input-hover-border: 1px solid var(--el-border-color-light);
--el-input-focus-border: 1px solid var(--el-color-primary);
--el-input-transparent-border: 0 0 0 1px transparent inset;
--el-input-border-color: var(--el-border-color);
--el-input-border-radius: 4px;
--el-input-bg-color: var(--el-fill-color);
--el-input-icon-color: #64748b;
--el-input-placeholder-color: #64748b;
--el-input-hover-border-color: var(--el-border-color-light);
--el-input-clear-hover-color: #94a3b8;
--el-input-focus-border-color: var(--el-color-primary);
--el-input-width: 100%;
--el-input-disabled-fill: var(--el-fill-color);
--el-input-disabled-border: var(--el-border-color);
--el-input-disabled-text-color: #64748b;
--el-input-disabled-placeholder-color: #64748b;
--el-select-border-color-hover: var(--el-border-color-light);
--el-select-disabled-color: #64748b;
--el-select-disabled-border: var(--el-border-color);
--el-select-font-size: 14px;
--el-select-close-hover-color: #94a3b8;
--el-select-input-color: #64748b;
--el-select-multiple-input-color: #e2e8f0;
--el-select-input-focus-border-color: var(--el-color-primary);
--el-select-input-font-size: 14px;
--el-select-width: 100%;
--el-select-option-text-color: #e2e8f0;
--el-select-option-disabled-color: #64748b;
--el-select-option-height: 34px;
--el-select-option-hover-background: var(--el-fill-color-light);
--el-select-option-selected-text-color: var(--el-color-primary);
--el-select-group-text-color: #94a3b8;
--el-select-group-height: 34px;
--el-select-group-font-size: 12px;
--el-select-dropdown-bg-color: var(--el-fill-color);
--el-select-dropdown-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-select-dropdown-empty-color: #94a3b8;
--el-select-dropdown-max-height: 274px;
--el-select-dropdown-padding: 6px 0;
--el-select-dropdown-empty-padding: 10px 0;
--el-select-dropdown-header-padding: 10px;
--el-select-dropdown-footer-padding: 10px;
--el-select-dropdown-border: 1px solid var(--el-border-color);
--el-tag-font-size: 12px;
--el-tag-border-radius: 4px;
--el-tag-border-radius-rounded: 9999px;
--el-tag-bg-color: var(--el-fill-color-light);
--el-tag-border-color: var(--el-border-color);
--el-tag-text-color: #e2e8f0;
--el-tag-close-color: #94a3b8;
--el-tag-close-hover-color: #64748b;
--el-button-font-weight: 500;
--el-button-border-color: var(--el-border-color);
--el-button-bg-color: var(--el-fill-color);
--el-button-text-color: #e2e8f0;
--el-button-disabled-text-color: #64748b;
--el-button-disabled-bg-color: var(--el-fill-color);
--el-button-disabled-border-color: var(--el-border-color);
--el-button-divide-border-color: rgba(255, 255, 255, 0.1);
--el-tooltip-font-size: 12px;
--el-tooltip-border-color: var(--el-border-color);
--el-tooltip-fill: var(--el-fill-color-light);
--el-tooltip-light-border-color: var(--el-border-color-light);
--el-tooltip-light-fill: var(--el-fill-color);
--el-cascader-menu-text-color: #e2e8f0;
--el-cascader-menu-selected-text-color: var(--el-color-primary);
--el-cascader-menu-fill: var(--el-fill-color);
--el-cascader-menu-font-size: 14px;
--el-cascader-menu-radius: 4px;
--el-cascader-menu-border: solid 1px var(--el-border-color);
--el-cascader-menu-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
--el-cascader-node-background-hover: var(--el-fill-color-light);
--el-cascader-node-color-disabled: #64748b;
--el-cascader-color-empty: #64748b;
--el-cascader-tag-background: var(--el-fill-color-light);
--el-statistic-title-font-weight: 400;
--el-statistic-title-font-size: 12px;
--el-statistic-title-color: #e2e8f0;
--el-statistic-content-font-weight: 400;
--el-statistic-content-font-size: 24px;
--el-statistic-content-color: #f8fafc;
--el-switch-on-color: var(--el-color-primary);
--el-switch-off-color: #1e293b;
--el-loading-spinner-size: 42px;
--el-loading-fullscreen-spinner-size: 50px;
--el-loading-text-color: var(--el-color-primary);
--el-progress-default-color: var(--el-color-primary);
--el-progress-inside-text-color: #f8fafc;
--el-pagination-font-size: 14px;
--el-pagination-bg-color: var(--el-fill-color);
--el-pagination-text-color: #e2e8f0;
--el-pagination-border-radius: 4px;
--el-pagination-button-color: #e2e8f0;
--el-pagination-button-width: 32px;
--el-pagination-button-height: 32px;
--el-pagination-button-disabled-color: #64748b;
--el-pagination-button-disabled-bg-color: var(--el-fill-color);
--el-pagination-hover-color: var(--el-color-primary);
--el-steps-color: var(--el-border-color);
--el-steps-text-color: #e2e8f0;
--el-steps-border-color: var(--el-border-color);
--el-datepicker-text-color: #e2e8f0;
--el-datepicker-off-text-color: #64748b;
--el-datepicker-header-text-color: #e2e8f0;
--el-datepicker-icon-color: #94a3b8;
--el-datepicker-border-color: var(--el-border-color);
--el-datepicker-inner-border-color: var(--el-border-color-light);
--el-datepicker-inrange-bg-color: var(--el-fill-color-light);
--el-datepicker-inrange-hover-bg-color: var(--el-fill-color-dark);
--el-datepicker-active-color: var(--el-color-primary);
--el-datepicker-hover-text-color: var(--el-color-primary);
--el-datepicker-cell-hover-color: var(--el-fill-color-light);
// 布局相关变量 - 由 theme.ts 动态覆盖
--menu-background: var(--el-fill-color);
--menu-text: #e2e8f0;
--menu-active-text: var(--el-color-primary);
--menu-active-bg: rgba(64, 128, 255, 0.15);
--menu-hover: var(--el-fill-color-light);
--sidebar-background: var(--el-fill-color);
--sidebar-text-color: #e2e8f0;
--sidebar-active-text-color: var(--el-color-primary);
--sidebar-active-bg: rgba(64, 128, 255, 0.15);
--sidebar-active-hover-bg: rgba(64, 128, 255, 0.2);
--sidebar-hover: var(--el-fill-color-light);
--sidebar-logo-text-color: var(--el-color-primary);
--sidebar-logo-background: var(--el-fill-color);
}
@@ -1,338 +0,0 @@
/**
* Element Plus 亮色模式主题变量
*
* 注意:主题色变体(light-1 到 light-9, dark-2)由 theme.ts 动态生成
* 此文件仅提供默认基础值和组件特定样式
*/
:root {
--el-color-primary: #3b82f6;
--el-color-success: #10b981;
--el-color-warning: #f59e0b;
--el-color-danger: #ef4444;
--el-color-info: #64748b;
--el-bg-color: #f8fafc;
--el-bg-color-page: #f8fafc;
--el-bg-color-overlay: #ffffff;
--el-fill-color-blank: #ffffff;
--el-fill-color: #f1f5f9;
--el-fill-color-light: #f8fafc;
--el-fill-color-lighter: #ffffff;
--el-fill-color-extra-light: #ffffff;
--el-fill-color-dark: #e2e8f0;
--el-fill-color-darker: #cbd5e1;
--el-text-color-primary: #0f172a;
--el-text-color-regular: #334155;
--el-text-color-secondary: #64748b;
--el-text-color-placeholder: #94a3b8;
--el-border-color: #e2e8f0;
--el-border-color-light: #f1f5f9;
--el-border-color-lighter: #f8fafc;
--el-border-color-extra-light: #ffffff;
--el-border-color-dark: #cbd5e1;
--el-border-color-darker: #94a3b8;
--el-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--el-box-shadow-light: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-box-shadow-lighter: 0 1px 1px 0 rgba(0, 0, 0, 0.03);
--el-box-shadow-dark: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--el-disabled-bg-color: #f1f5f9;
--el-disabled-text-color: #94a3b8;
--el-disabled-border-color: #e2e8f0;
--el-overlay-color: rgba(15, 23, 42, 0.5);
--el-overlay-color-light: rgba(15, 23, 42, 0.3);
--el-overlay-color-lighter: rgba(15, 23, 42, 0.15);
--el-scrollbar-bg-color: #f1f5f9;
--el-scrollbar-thumb-color: #cbd5e1;
--el-scrollbar-thumb-hover-color: #94a3b8;
--el-table-bg-color: #ffffff;
--el-table-tr-bg-color: #ffffff;
--el-table-header-bg-color: #f8fafc;
--el-table-row-hover-bg-color: #f1f5f9;
--el-table-border-color: #e2e8f0;
--el-dialog-width: 50%;
--el-dialog-margin-top: 15vh;
--el-dialog-bg-color: #ffffff;
--el-dialog-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--el-dialog-title-font-size: 18px;
--el-dialog-content-font-size: 14px;
--el-dialog-font-line-height: 1.5;
--el-dialog-padding-primary: 16px;
--el-dialog-border-radius: 4px;
--el-drawer-bg-color: #ffffff;
--el-drawer-padding-primary: 20px;
--el-dropdown-menu-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-dropdown-menuItem-hover-fill: #f1f5f9;
--el-dropdown-menuItem-hover-color: var(--el-color-primary);
--el-dropdown-menu-index: 10;
--el-popover-bg-color: #ffffff;
--el-popover-font-size: 14px;
--el-popover-border-color: #f1f5f9;
--el-popover-padding: 12px;
--el-popover-padding-large: 18px 20px;
--el-popover-title-font-size: 16px;
--el-popover-title-text-color: #0f172a;
--el-popover-border-radius: 4px;
--el-popper-border-radius: 4px;
--el-card-border-color: #f1f5f9;
--el-card-border-radius: 4px;
--el-card-padding: 20px;
--el-card-bg-color: #ffffff;
--el-badge-bg-color: #ef4444;
--el-badge-radius: 10px;
--el-badge-font-size: 12px;
--el-badge-padding: 6px;
--el-badge-size: 18px;
--el-segmented-color: #334155;
--el-segmented-bg-color: #f8fafc;
--el-segmented-padding: 2px;
--el-segmented-item-selected-color: #ffffff;
--el-segmented-item-selected-bg-color: var(--el-color-primary);
--el-segmented-item-selected-disabled-bg-color: #b1d4fb;
--el-segmented-item-hover-color: #0f172a;
--el-segmented-item-hover-bg-color: #e2e8f0;
--el-segmented-item-active-bg-color: #cbd5e1;
--el-segmented-item-disabled-color: #94a3b8;
--el-tour-width: 520px;
--el-tour-padding-primary: 12px;
--el-tour-font-line-height: 1.5;
--el-tour-title-font-size: 16px;
--el-tour-title-text-color: #0f172a;
--el-tour-title-font-weight: 400;
--el-tour-close-color: #64748b;
--el-tour-font-size: 14px;
--el-tour-color: #0f172a;
--el-tour-bg-color: #ffffff;
--el-tour-border-radius: 4px;
--el-anchor-bg-color: #ffffff;
--el-anchor-padding-indent: 14px;
--el-anchor-line-height: 22px;
--el-anchor-font-size: 12px;
--el-anchor-color: #64748b;
--el-anchor-active-color: var(--el-color-primary);
--el-anchor-marker-bg-color: var(--el-color-primary);
--el-mention-font-size: 14px;
--el-mention-bg-color: #ffffff;
--el-mention-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-mention-border: 1px solid #f1f5f9;
--el-mention-option-color: #334155;
--el-mention-option-height: 34px;
--el-mention-option-min-width: 100px;
--el-mention-option-hover-background: #f8fafc;
--el-mention-option-selected-color: var(--el-color-primary);
--el-mention-option-disabled-color: #94a3b8;
--el-mention-option-loading-color: #64748b;
--el-mention-option-loading-padding: 10px 0;
--el-mention-max-height: 174px;
--el-mention-padding: 6px 0;
--el-mention-header-padding: 10px;
--el-mention-footer-padding: 10px;
--el-skeleton-color: #f1f5f9;
--el-skeleton-to-color: #cbd5e1;
--el-alert-padding: 8px 16px;
--el-alert-border-radius-base: 4px;
--el-alert-title-font-size: 14px;
--el-alert-title-with-description-font-size: 16px;
--el-alert-description-font-size: 14px;
--el-alert-close-font-size: 16px;
--el-alert-close-customed-font-size: 14px;
--el-alert-icon-size: 16px;
--el-alert-icon-large-size: 28px;
--el-messagebox-title-color: #0f172a;
--el-messagebox-width: 420px;
--el-messagebox-border-radius: 4px;
--el-messagebox-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--el-messagebox-font-size: 18px;
--el-messagebox-content-font-size: 14px;
--el-messagebox-content-color: #334155;
--el-messagebox-error-font-size: 12px;
--el-messagebox-padding-primary: 12px;
--el-messagebox-font-line-height: 1.5;
--el-message-bg-color: #ebf5fb;
--el-message-border-color: #f8fafc;
--el-message-padding: 11px 15px;
--el-message-close-size: 16px;
--el-message-close-icon-color: #94a3b8;
--el-message-close-hover-color: #64748b;
--el-notification-width: 330px;
--el-notification-padding: 14px 26px 14px 13px;
--el-notification-radius: 8px;
--el-notification-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-notification-border-color: #f8fafc;
--el-notification-icon-size: 24px;
--el-notification-close-font-size: 16px;
--el-notification-group-margin-left: 13px;
--el-notification-group-margin-right: 8px;
--el-notification-content-font-size: 14px;
--el-notification-content-color: #334155;
--el-notification-title-font-size: 16px;
--el-notification-title-color: #0f172a;
--el-notification-close-color: #64748b;
--el-notification-close-hover-color: #334155;
--el-input-text-color: #334155;
--el-input-border: 1px solid #e2e8f0;
--el-input-hover-border: 1px solid #94a3b8;
--el-input-focus-border: 1px solid var(--el-color-primary);
--el-input-transparent-border: 0 0 0 1px transparent inset;
--el-input-border-color: #e2e8f0;
--el-input-border-radius: 4px;
--el-input-bg-color: #ffffff;
--el-input-icon-color: #94a3b8;
--el-input-placeholder-color: #94a3b8;
--el-input-hover-border-color: #94a3b8;
--el-input-clear-hover-color: #64748b;
--el-input-focus-border-color: var(--el-color-primary);
--el-input-width: 100%;
--el-input-disabled-fill: #f1f5f9;
--el-input-disabled-border: #e2e8f0;
--el-input-disabled-text-color: #94a3b8;
--el-input-disabled-placeholder-color: #94a3b8;
--el-select-border-color-hover: #94a3b8;
--el-select-disabled-color: #94a3b8;
--el-select-disabled-border: #e2e8f0;
--el-select-font-size: 14px;
--el-select-close-hover-color: #64748b;
--el-select-input-color: #94a3b8;
--el-select-multiple-input-color: #334155;
--el-select-input-focus-border-color: var(--el-color-primary);
--el-select-input-font-size: 14px;
--el-select-width: 100%;
--el-select-option-text-color: #334155;
--el-select-option-disabled-color: #94a3b8;
--el-select-option-height: 34px;
--el-select-option-hover-background: #f8fafc;
--el-select-option-selected-text-color: var(--el-color-primary);
--el-select-group-text-color: #64748b;
--el-select-group-height: 34px;
--el-select-group-font-size: 12px;
--el-select-dropdown-bg-color: #ffffff;
--el-select-dropdown-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-select-dropdown-empty-color: #64748b;
--el-select-dropdown-max-height: 274px;
--el-select-dropdown-padding: 6px 0;
--el-select-dropdown-empty-padding: 10px 0;
--el-select-dropdown-header-padding: 10px;
--el-select-dropdown-footer-padding: 10px;
--el-select-dropdown-border: 1px solid #f1f5f9;
--el-tag-font-size: 12px;
--el-tag-border-radius: 4px;
--el-tag-border-radius-rounded: 9999px;
--el-button-font-weight: 500;
--el-button-border-color: #e2e8f0;
--el-button-bg-color: #ffffff;
--el-button-text-color: #334155;
--el-button-disabled-text-color: #94a3b8;
--el-button-disabled-bg-color: #f1f5f9;
--el-button-disabled-border-color: #e2e8f0;
--el-button-divide-border-color: rgba(255, 255, 255, 0.5);
--el-tooltip-font-size: 12px;
--el-tooltip-border-color: #0f172a;
--el-tooltip-fill: #0f172a;
--el-tooltip-light-border-color: #e2e8f0;
--el-tooltip-light-fill: #ffffff;
--el-cascader-menu-text-color: #334155;
--el-cascader-menu-selected-text-color: var(--el-color-primary);
--el-cascader-menu-fill: #ffffff;
--el-cascader-menu-font-size: 14px;
--el-cascader-menu-radius: 4px;
--el-cascader-menu-border: solid 1px #f1f5f9;
--el-cascader-menu-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--el-cascader-node-background-hover: #f8fafc;
--el-cascader-node-color-disabled: #94a3b8;
--el-cascader-color-empty: #94a3b8;
--el-cascader-tag-background: #f1f5f9;
--el-statistic-title-font-weight: 400;
--el-statistic-title-font-size: 12px;
--el-statistic-title-color: #334155;
--el-statistic-content-font-weight: 400;
--el-statistic-content-font-size: 24px;
--el-statistic-content-color: #0f172a;
--el-switch-on-color: var(--el-color-primary);
--el-switch-off-color: #e2e8f0;
--el-loading-spinner-size: 42px;
--el-loading-fullscreen-spinner-size: 50px;
--el-loading-text-color: var(--el-color-primary);
--el-progress-default-color: var(--el-color-primary);
--el-progress-inside-text-color: #ffffff;
--el-pagination-font-size: 14px;
--el-pagination-bg-color: #ffffff;
--el-pagination-text-color: #334155;
--el-pagination-border-radius: 4px;
--el-pagination-button-color: #334155;
--el-pagination-button-width: 32px;
--el-pagination-button-height: 32px;
--el-pagination-button-disabled-color: #94a3b8;
--el-pagination-button-disabled-bg-color: #f1f5f9;
--el-pagination-hover-color: var(--el-color-primary);
--el-steps-color: #e2e8f0;
--el-steps-text-color: #334155;
--el-steps-border-color: #e2e8f0;
--el-datepicker-text-color: #334155;
--el-datepicker-off-text-color: #94a3b8;
--el-datepicker-header-text-color: #334155;
--el-datepicker-icon-color: #64748b;
--el-datepicker-border-color: #e2e8f0;
--el-datepicker-inner-border-color: #f1f5f9;
--el-datepicker-inrange-bg-color: #f8fafc;
--el-datepicker-inrange-hover-bg-color: #f1f5f9;
--el-datepicker-active-color: var(--el-color-primary);
--el-datepicker-hover-text-color: var(--el-color-primary);
--el-datepicker-cell-hover-color: #f8fafc;
// 布局相关变量 - 由 theme.ts 动态覆盖
--menu-background: #ffffff;
--menu-text: #1e293b;
--menu-active-text: var(--el-color-primary);
--menu-active-bg: #f8fafc;
--menu-hover: #fafafa;
--sidebar-background: #ffffff;
--sidebar-text-color: #1e293b;
--sidebar-active-text-color: var(--el-color-primary);
--sidebar-active-bg: #f8fafc;
--sidebar-active-hover-bg: #f1f5f9;
--sidebar-hover: #fafafa;
--sidebar-logo-text-color: var(--el-color-primary);
--sidebar-logo-background: #ffffff;
}
+89 -91
View File
@@ -1,32 +1,3 @@
// 主题颜色 - 跟随主题模式自动变化
// 注意:SCSS 配置需要使用固定颜色值,动态主题色由 theme.ts 在运行时设置
@forward "element-plus/theme-chalk/src/common/var.scss" with (
$colors: (
"primary": (
"base": #3b82f6,
),
"success": (
"base": #10b981,
),
"warning": (
"base": #f59e0b,
),
"danger": (
"base": #ef4444,
),
"info": (
"base": #64748b,
),
"default": (
"base": #f8fafc,
),
),
$bg-color: (
"page": #f8fafc,
)
);
/**
* 简化颜色系统设计原则
*
@@ -39,33 +10,51 @@
:root {
// ==================== 品牌主题色 ====================
// 由 theme.ts 动态设置,这里提供默认值
--brand-primary: var(--el-color-primary, #3b82f6);
--brand-primary: var(--el-color-primary, #1677ff);
--brand-success: #10b981;
--brand-warning: #f59e0b;
--brand-danger: #ef4444;
--brand-info: #64748b;
// ==================== 固定的自定义颜色 ====================
--custom-bg-light: #ffffff;
--custom-bg-dark: #0f172a;
--custom-text-light: #334155;
--custom-text-dark: #f1f5f9;
// ==================== 亮色模式背景层次 ====================
--custom-bg-primary: #f8fafc;
--custom-bg-secondary: #ffffff;
--custom-bg-tertiary: #ffffff;
// ==================== 暗黑模式背景层次 ====================
--custom-bg-primary-dark: var(--el-bg-color, #020617);
--custom-bg-secondary-dark: var(--el-bg-color-overlay, #0f172a);
--custom-bg-tertiary-dark: #1e293b;
--brand-info: #2f86ff;
// ==================== 亮色模式(默认)====================
// 语义化布局变量(推荐优先使用)
--layout-bg-primary: var(--el-bg-color, #f8fafc);
--layout-bg-secondary: var(--el-bg-color-overlay, #ffffff);
--layout-bg-tertiary: #ffffff;
--layout-text-primary: #0f172a;
--layout-text-secondary: #475569;
--layout-text-tertiary: #64748b;
--layout-text-disabled: #94a3b8;
--layout-sidebar-bg: #ffffff;
--layout-sidebar-text: #334155;
--layout-sidebar-active-text: var(--brand-primary);
--layout-sidebar-active-bg: #f8fafc;
--layout-sidebar-active-hover-bg: #f1f5f9;
--layout-sidebar-hover: #f1f5f9;
--layout-menu-bg: var(--layout-sidebar-bg);
--layout-menu-text: var(--layout-sidebar-text);
--layout-menu-active-text: var(--layout-sidebar-active-text);
--layout-menu-active-bg: var(--layout-sidebar-active-bg);
--layout-menu-hover: var(--layout-sidebar-hover);
--layout-logo-bg: var(--layout-sidebar-bg);
--layout-logo-text: var(--layout-sidebar-active-text);
--layout-header-bg: var(--layout-menu-bg);
--layout-header-text: var(--layout-text-primary);
--layout-header-border: var(--el-border-color-light, #e2e8f0);
--layout-tags-bg: var(--layout-bg-primary);
--layout-tags-border: var(--el-border-color-light, #e2e8f0);
--layout-tags-item-bg: var(--el-bg-color, #ffffff);
--layout-tags-item-text: var(--layout-text-primary);
--layout-tags-item-active-bg: var(--el-color-primary-light-9, #dbeafe);
--layout-tags-item-active-text: var(--el-color-primary, #1677ff);
--layout-content-bg: var(--layout-bg-primary);
// 列表页 gutter/留白透出:--layout-content-canvas(dark/css-vars.css,主色轻混 --el-bg-color-page)
--layout-content-surface-bg: var(--el-bg-color-overlay, #ffffff);
// 背景色系
--bg-primary: var(--custom-bg-primary);
--bg-secondary: var(--custom-bg-secondary);
--bg-tertiary: var(--custom-bg-tertiary);
--bg-primary: var(--layout-bg-primary, #f8fafc);
--bg-secondary: var(--layout-bg-secondary, #ffffff);
--bg-tertiary: var(--layout-bg-tertiary, #ffffff);
// 文字色系
--text-primary: #0f172a;
@@ -78,35 +67,57 @@
--border-secondary: #cbd5e1;
--border-tertiary: #f1f5f9;
// 菜单色系
--menu-background: var(--sidebar-background, var(--custom-bg-light));
--menu-text: var(--sidebar-text-color, var(--custom-text-light));
--menu-active-text: var(--sidebar-active-text-color, var(--brand-primary));
--menu-hover: var(--sidebar-hover, #f1f5f9);
--sidebar-logo-background: var(--sidebar-background, var(--custom-bg-light));
--sidebar-logo-text-color: var(--brand-primary);
// 状态色系
--status-success: var(--brand-success);
--status-warning: var(--brand-warning);
--status-danger: var(--brand-danger);
--status-info: var(--brand-info);
// 菜单兼容(仅保留 Element Plus 所需 hover 变量)
--el-menu-hover-bg-color: var(--layout-menu-hover, #f3f6fb);
// 科技感增强
--tech-glow: rgba(var(--brand-primary-rgb, 59, 130, 246), 0.12);
--tech-glow: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.12);
--tech-gradient: linear-gradient(
135deg,
rgba(var(--brand-primary-rgb, 59, 130, 246), 0.02) 0%,
rgba(var(--brand-primary-rgb, 22, 119, 255), 0.02) 0%,
rgba(0, 0, 0, 0) 100%
);
}
/** 暗黑主题 */
html.dark {
// 语义化布局变量(暗色)
--layout-bg-primary: var(--el-bg-color, #020617);
--layout-bg-secondary: var(--el-bg-color-overlay, #0f172a);
--layout-bg-tertiary: #1e293b;
--layout-text-primary: #f8fafc;
--layout-text-secondary: #cbd5e1;
--layout-text-tertiary: #94a3b8;
--layout-text-disabled: #64748b;
--layout-sidebar-bg: #0f172a;
--layout-sidebar-text: #f1f5f9;
--layout-sidebar-active-text: var(--brand-primary);
--layout-sidebar-active-bg: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.2);
--layout-sidebar-active-hover-bg: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.26);
--layout-sidebar-hover: #1e293b;
--layout-menu-bg: var(--layout-sidebar-bg);
--layout-menu-text: var(--layout-sidebar-text);
--layout-menu-active-text: var(--layout-sidebar-active-text);
--layout-menu-active-bg: var(--layout-sidebar-active-bg);
--layout-menu-hover: var(--layout-sidebar-hover);
--layout-logo-bg: var(--layout-sidebar-bg);
--layout-logo-text: var(--layout-sidebar-active-text);
--layout-header-bg: var(--layout-menu-bg);
--layout-header-text: var(--layout-text-primary);
--layout-header-border: var(--el-border-color, #334155);
--layout-tags-bg: var(--layout-bg-primary);
--layout-tags-border: var(--el-border-color, #334155);
--layout-tags-item-bg: var(--el-fill-color, #0f172a);
--layout-tags-item-text: var(--layout-text-primary);
--layout-tags-item-active-bg: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.2);
--layout-tags-item-active-text: var(--el-color-primary, #1677ff);
--layout-content-bg: var(--layout-bg-primary);
--layout-content-surface-bg: var(--el-fill-color, #0f172a);
// 背景色系
--bg-primary: var(--custom-bg-primary-dark);
--bg-secondary: var(--custom-bg-secondary-dark);
--bg-tertiary: var(--custom-bg-tertiary-dark);
--bg-primary: var(--layout-bg-primary, #020617);
--bg-secondary: var(--layout-bg-secondary, #0f172a);
--bg-tertiary: var(--layout-bg-tertiary, #1e293b);
// 文字色系
--text-primary: #f8fafc;
@@ -119,25 +130,14 @@ html.dark {
--border-secondary: var(--el-border-color-light, #334155);
--border-tertiary: var(--el-border-color-lighter, #475569);
// 菜单色系
--menu-background: var(--sidebar-background, var(--custom-bg-dark));
--menu-text: var(--sidebar-text-color, var(--custom-text-dark));
--menu-active-text: var(--sidebar-active-text-color, var(--brand-primary));
--menu-hover: var(--sidebar-hover, #1e293b);
--sidebar-logo-background: var(--sidebar-background, var(--custom-bg-dark));
--sidebar-logo-text-color: var(--brand-primary);
// 状态色系
--status-success: var(--brand-success);
--status-warning: var(--brand-warning);
--status-danger: var(--brand-danger);
--status-info: var(--brand-info);
// 菜单兼容(仅保留 Element Plus 所需 hover 变量)
--el-menu-hover-bg-color: var(--layout-menu-hover, #1b2a45);
// 科技感增强
--tech-glow: rgba(var(--brand-primary-rgb, 59, 130, 246), 0.25);
--tech-glow: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.25);
--tech-gradient: linear-gradient(
135deg,
rgba(var(--brand-primary-rgb, 59, 130, 246), 0.08) 0%,
rgba(var(--brand-primary-rgb, 22, 119, 255), 0.08) 0%,
rgba(0, 0, 0, 0) 100%
);
@@ -148,13 +148,13 @@ html.dark {
--w-e-textarea-slight-border-color: var(--border-secondary);
--w-e-textarea-slight-color: var(--text-tertiary);
--w-e-textarea-slight-bg-color: var(--bg-tertiary);
--w-e-textarea-selected-border-color: rgba(var(--brand-primary-rgb, 59, 130, 246), 0.4);
--w-e-textarea-selected-border-color: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.4);
--w-e-textarea-handler-bg-color: var(--brand-primary);
--w-e-toolbar-color: var(--text-primary);
--w-e-toolbar-bg-color: var(--bg-secondary);
--w-e-toolbar-active-color: var(--brand-primary);
--w-e-toolbar-active-bg-color: rgba(var(--brand-primary-rgb, 59, 130, 246), 0.08);
--w-e-toolbar-active-bg-color: rgba(var(--brand-primary-rgb, 22, 119, 255), 0.08);
--w-e-toolbar-disabled-color: var(--text-disabled);
--w-e-toolbar-border-color: var(--border-primary);
@@ -162,12 +162,10 @@ html.dark {
--w-e-modal-button-border-color: var(--border-secondary);
}
$menu-background: var(--menu-background);
$menu-text: var(--menu-text);
$menu-active-text: var(--menu-active-text);
$menu-hover: var(--menu-hover);
$sidebar-logo-background: var(--sidebar-logo-background);
$sidebar-logo-text-color: var(--sidebar-logo-text-color);
$menu-background: var(--layout-menu-bg);
$menu-text: var(--layout-menu-text);
$menu-active-text: var(--layout-menu-active-text);
$menu-hover: var(--layout-menu-hover);
$sidebar-width: 210px;
$sidebar-width-collapsed: 54px;
+324 -310
View File
@@ -1,8 +1,13 @@
import { ThemeMode } from "@/enums";
const COLOR_VARIANT_LEVELS = [0.08, 0.16, 0.24, 0.32, 0.4, 0.52, 0.64, 0.76, 0.88];
function normalizeHexColor(hex: string): string {
return hex.length === 9 ? hex.slice(0, 7) : hex;
}
function hexToRgb(hex: string): [number, number, number] {
// 处理带alpha通道的颜色值,去除alpha部分
const cleanHex = hex.length === 9 ? hex.slice(0, 7) : hex;
const cleanHex = normalizeHexColor(hex);
const bigint = parseInt(cleanHex.slice(1), 16);
return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
}
@@ -20,10 +25,6 @@ function getLuminance(hex: string): number {
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function isLightColor(hex: string): boolean {
return getLuminance(hex) > 0.5;
}
function getDarkColor(color: string, level: number): string {
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level));
@@ -54,15 +55,13 @@ interface ColorVariants {
}
function generateColorVariants(baseColor: string): ColorVariants {
const lightLevels = [0.08, 0.16, 0.24, 0.32, 0.4, 0.52, 0.64, 0.76, 0.88];
const light = lightLevels.map((level) => getLightColor(baseColor, level));
const light = COLOR_VARIANT_LEVELS.map((level) => getLightColor(baseColor, level));
const dark = getDarkColor(baseColor, 0.15);
return { light, dark };
}
function generateDarkModeVariants(baseColor: string): ColorVariants {
const darkLevels = [0.08, 0.16, 0.24, 0.32, 0.4, 0.52, 0.64, 0.76, 0.88];
const light = darkLevels.map((level) => getDarkColor(baseColor, level));
const light = COLOR_VARIANT_LEVELS.map((level) => getDarkColor(baseColor, level));
const dark = getLightColor(baseColor, 0.15);
return { light, dark };
}
@@ -96,13 +95,6 @@ interface ColorScheme {
dark: string;
darker: string;
};
table: {
bg: string;
trBg: string;
headerBg: string;
hoverBg: string;
border: string;
};
sidebar: {
background: string;
text: string;
@@ -118,221 +110,225 @@ interface ColorScheme {
activeBg: string;
hover: string;
};
}
function generateLightThemeScheme(primary: string, isLightPrimary: boolean): ColorScheme {
if (isLightPrimary) {
return {
bg: {
primary: getLightColor(primary, 0.92),
secondary: "#ffffff",
tertiary: "#ffffff",
},
fill: {
base: getLightColor(primary, 0.88),
light: getLightColor(primary, 0.92),
lighter: getLightColor(primary, 0.96),
extraLight: "#ffffff",
dark: getLightColor(primary, 0.8),
darker: getLightColor(primary, 0.7),
},
shadow: {
base: `0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)`,
light: `0 1px 2px 0 rgba(0, 0, 0, 0.05)`,
lighter: `0 1px 1px 0 rgba(0, 0, 0, 0.03)`,
dark: `0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)`,
},
border: {
base: getLightColor(primary, 0.85),
light: getLightColor(primary, 0.9),
lighter: getLightColor(primary, 0.94),
extraLight: "#ffffff",
dark: getLightColor(primary, 0.75),
darker: getLightColor(primary, 0.65),
},
table: {
bg: "#ffffff",
trBg: "#ffffff",
headerBg: getLightColor(primary, 0.92),
hoverBg: getLightColor(primary, 0.88),
border: getLightColor(primary, 0.85),
},
sidebar: {
background: getLightColor(primary, 0.92),
text: "#1e293b",
activeText: getDarkColor(primary, 0.15),
activeBg: getLightColor(primary, 0.88),
activeHoverBg: getLightColor(primary, 0.65),
hover: "#fafafa",
},
menu: {
background: getLightColor(primary, 0.92),
text: "#1e293b",
activeText: getDarkColor(primary, 0.15),
activeBg: getLightColor(primary, 0.88),
hover: "#fafafa",
},
};
}
return {
bg: {
primary: getLightColor(primary, 0.75),
secondary: "#ffffff",
tertiary: "#ffffff",
},
fill: {
base: "#f1f5f9",
light: "#f8fafc",
lighter: "#ffffff",
extraLight: "#ffffff",
dark: "#e2e8f0",
darker: "#cbd5e1",
},
shadow: {
base: `0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)`,
light: `0 1px 2px 0 rgba(0, 0, 0, 0.05)`,
lighter: `0 1px 1px 0 rgba(0, 0, 0, 0.03)`,
dark: `0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)`,
},
border: {
base: blendTwoColors("#cbd5e1", primary, 0.2),
light: blendTwoColors("#e2e8f0", primary, 0.15),
lighter: blendTwoColors("#f1f5f9", primary, 0.1),
extraLight: "#ffffff",
dark: blendTwoColors("#94a3b8", primary, 0.25),
darker: blendTwoColors("#64748b", primary, 0.3),
},
table: {
bg: blendTwoColors("#ffffff", primary, 0.02),
trBg: blendTwoColors("#ffffff", primary, 0.02),
headerBg: blendTwoColors("#f8fafc", primary, 0.15),
hoverBg: blendTwoColors("#f1f5f9", primary, 0.1),
border: blendTwoColors("#cbd5e1", primary, 0.2),
},
sidebar: {
background: blendTwoColors("#ffffff", primary, 0.05),
text: "#1e293b",
activeText: getDarkColor(primary, 0.1),
activeBg: getLightColor(primary, 0.88),
activeHoverBg: getLightColor(primary, 0.65),
hover: blendTwoColors("#fafafa", primary, 0.1),
},
menu: {
background: blendTwoColors("#ffffff", primary, 0.05),
text: "#1e293b",
activeText: getDarkColor(primary, 0.1),
activeBg: getLightColor(primary, 0.88),
hover: blendTwoColors("#fafafa", primary, 0.1),
},
text: {
primary: string;
secondary: string;
tertiary: string;
disabled: string;
};
}
function generateDarkThemeScheme(primary: string, isLightPrimary: boolean): ColorScheme {
if (isLightPrimary) {
return {
bg: {
primary: getLightColor(primary, 0.85),
secondary: getLightColor(primary, 0.95),
tertiary: getLightColor(primary, 0.97),
overlay: getLightColor(primary, 0.95),
},
fill: {
base: blendTwoColors("#0f172a", primary, 0.1),
light: blendTwoColors("#1e293b", primary, 0.15),
lighter: blendTwoColors("#0f172a", primary, 0.05),
extraLight: blendTwoColors("#020617", primary, 0.02),
dark: blendTwoColors("#334155", primary, 0.2),
darker: blendTwoColors("#475569", primary, 0.25),
},
shadow: {
base: `0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24)`,
light: `0 1px 2px 0 rgba(0, 0, 0, 0.2)`,
lighter: `0 1px 1px 0 rgba(0, 0, 0, 0.12)`,
dark: `0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.24)`,
},
border: {
base: getLightColor(primary, 0.85),
light: getLightColor(primary, 0.9),
lighter: getLightColor(primary, 0.94),
extraLight: getLightColor(primary, 0.97),
dark: getLightColor(primary, 0.78),
darker: getLightColor(primary, 0.68),
},
table: {
bg: blendTwoColors("#020617", primary, 0.02),
trBg: blendTwoColors("#020617", primary, 0.02),
headerBg: blendTwoColors("#1e293b", primary, 0.15),
hoverBg: blendTwoColors("#0f172a", primary, 0.1),
border: blendTwoColors("#1e293b", primary, 0.12),
},
sidebar: {
background: getLightColor(primary, 0.92),
text: "#e2e8f0",
activeText: primary,
activeBg: getLightColor(primary, 0.9),
activeHoverBg: getLightColor(primary, 0.65),
hover: getLightColor(primary, 0.88),
},
menu: {
background: getLightColor(primary, 0.92),
text: "#e2e8f0",
activeText: primary,
activeBg: getLightColor(primary, 0.9),
hover: getLightColor(primary, 0.88),
},
};
}
interface CoreThemeTokens {
brandPrimary: string;
layoutBgPrimary: string;
layoutBgSecondary: string;
layoutBgTertiary: string;
layoutSidebarBg: string;
layoutSidebarText: string;
layoutSidebarActiveText: string;
layoutSidebarActiveBg: string;
layoutSidebarActiveHoverBg: string;
layoutSidebarHover: string;
layoutMenuActiveBg: string;
layoutHeaderBg: string;
layoutHeaderText: string;
layoutHeaderBorder: string;
layoutTagsBg: string;
layoutTagsBorder: string;
layoutTagsItemBg: string;
layoutTagsItemText: string;
layoutTagsItemActiveBg: string;
layoutTagsItemActiveText: string;
layoutContentBg: string;
layoutContentSurfaceBg: string;
}
const LIGHT_TEXT_SCHEME: ColorScheme["text"] = {
primary: "#0f172a",
secondary: "#475569",
tertiary: "#64748b",
disabled: "#94a3b8",
};
const DARK_TEXT_SCHEME: ColorScheme["text"] = {
primary: "#f8fafc",
secondary: "#cbd5e1",
tertiary: "#94a3b8",
disabled: "#64748b",
};
const LIGHT_SHADOW_SCHEME: ColorScheme["shadow"] = {
base: `0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)`,
light: `0 1px 2px 0 rgba(0, 0, 0, 0.05)`,
lighter: `0 1px 1px 0 rgba(0, 0, 0, 0.03)`,
dark: `0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)`,
};
const DARK_SHADOW_SCHEME: ColorScheme["shadow"] = {
base: `0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24)`,
light: `0 1px 2px 0 rgba(0, 0, 0, 0.2)`,
lighter: `0 1px 1px 0 rgba(0, 0, 0, 0.12)`,
dark: `0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.24)`,
};
const ENTERPRISE_LIGHT_NEUTRAL = {
bgPrimary: "#f5f7fb",
bgSecondary: "#ffffff",
bgTertiary: "#f8fafc",
fillBase: "#eef3fb",
fillLight: "#f7f9fd",
fillLighter: "#ffffff",
borderBase: "#d8e2f0",
borderLight: "#e7edf5",
borderLighter: "#f0f4fa",
};
const ENTERPRISE_DARK_NEUTRAL = {
bgPrimary: "#0b1220",
bgSecondary: "#111a2c",
bgTertiary: "#17233a",
fillBase: "#141f33",
fillLight: "#1b2a45",
fillLighter: "#111a2c",
borderBase: "#253858",
borderLight: "#31486b",
borderLighter: "#3f5c86",
};
function createCoreThemeTokens(scheme: ColorScheme, primary: string): CoreThemeTokens {
const isDarkScheme = scheme.text.primary === DARK_TEXT_SCHEME.primary;
const headerBg = blendTwoColors(scheme.menu.background, scheme.bg.secondary, 0.18);
const tagsBg = blendTwoColors(scheme.bg.secondary, scheme.fill.light, 0.28);
const tagsItemBg = blendTwoColors(scheme.bg.secondary, scheme.fill.light, 0.18);
const tagsItemActiveBg = isDarkScheme
? blendTwoColors(scheme.fill.light, primary, 0.28)
: blendTwoColors(scheme.fill.light, primary, 0.2);
const tagsItemActiveText = isDarkScheme
? getLightColor(primary, 0.1)
: getDarkColor(primary, 0.08);
// 内容区分为两层:页面底色 + 容器(padding空白区)底色,提升层次感
const contentBg = blendTwoColors(scheme.bg.primary, scheme.fill.light, 0.08);
const contentSurfaceBg = blendTwoColors(scheme.bg.secondary, scheme.fill.base, 0.22);
return {
brandPrimary: primary,
layoutBgPrimary: scheme.bg.primary,
layoutBgSecondary: scheme.bg.overlay ?? scheme.bg.secondary,
layoutBgTertiary: scheme.bg.tertiary,
layoutSidebarBg: scheme.sidebar.background,
layoutSidebarText: scheme.sidebar.text,
layoutSidebarActiveText: scheme.sidebar.activeText,
layoutSidebarActiveBg: scheme.sidebar.activeBg,
layoutSidebarActiveHoverBg: scheme.sidebar.activeHoverBg,
layoutSidebarHover: scheme.sidebar.hover,
layoutMenuActiveBg: scheme.menu.activeBg,
layoutHeaderBg: headerBg,
layoutHeaderText: scheme.text.primary,
layoutHeaderBorder: scheme.border.light,
layoutTagsBg: tagsBg,
layoutTagsBorder: scheme.border.light,
layoutTagsItemBg: tagsItemBg,
layoutTagsItemText: scheme.text.primary,
layoutTagsItemActiveBg: tagsItemActiveBg,
layoutTagsItemActiveText: tagsItemActiveText,
layoutContentBg: contentBg,
layoutContentSurfaceBg: contentSurfaceBg,
};
}
function generateLightThemeScheme(primary: string): ColorScheme {
const activeBg = blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.fillLight, primary, 0.18);
const activeHoverBg = blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.fillBase, primary, 0.24);
return {
bg: {
primary: blendTwoColors("#020617", primary, 0.05),
secondary: blendTwoColors("#0f172a", primary, 0.08),
tertiary: blendTwoColors("#1e293b", primary, 0.1),
overlay: blendTwoColors("#0f172a", primary, 0.08),
primary: ENTERPRISE_LIGHT_NEUTRAL.bgPrimary,
secondary: ENTERPRISE_LIGHT_NEUTRAL.bgSecondary,
tertiary: ENTERPRISE_LIGHT_NEUTRAL.bgTertiary,
},
fill: {
base: blendTwoColors("#0f172a", primary, 0.1),
light: blendTwoColors("#1e293b", primary, 0.15),
lighter: blendTwoColors("#0f172a", primary, 0.05),
extraLight: blendTwoColors("#020617", primary, 0.02),
dark: blendTwoColors("#334155", primary, 0.2),
darker: blendTwoColors("#475569", primary, 0.25),
},
shadow: {
base: `0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.24)`,
light: `0 1px 2px 0 rgba(0, 0, 0, 0.2)`,
lighter: `0 1px 1px 0 rgba(0, 0, 0, 0.12)`,
dark: `0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.24)`,
base: ENTERPRISE_LIGHT_NEUTRAL.fillBase,
light: ENTERPRISE_LIGHT_NEUTRAL.fillLight,
lighter: ENTERPRISE_LIGHT_NEUTRAL.fillLighter,
extraLight: "#ffffff",
dark: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.fillBase, primary, 0.12),
darker: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.borderBase, primary, 0.16),
},
shadow: LIGHT_SHADOW_SCHEME,
border: {
base: blendTwoColors("#1e293b", primary, 0.12),
light: blendTwoColors("#334155", primary, 0.15),
lighter: blendTwoColors("#475569", primary, 0.2),
extraLight: blendTwoColors("#64748b", primary, 0.25),
dark: blendTwoColors("#0f172a", primary, 0.08),
darker: blendTwoColors("#020617", primary, 0.05),
},
table: {
bg: blendTwoColors("#020617", primary, 0.02),
trBg: blendTwoColors("#020617", primary, 0.02),
headerBg: blendTwoColors("#1e293b", primary, 0.15),
hoverBg: blendTwoColors("#0f172a", primary, 0.1),
border: blendTwoColors("#1e293b", primary, 0.12),
base: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.borderBase, primary, 0.1),
light: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.borderLight, primary, 0.08),
lighter: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.borderLighter, primary, 0.05),
extraLight: "#ffffff",
dark: blendTwoColors("#94a3b8", primary, 0.15),
darker: blendTwoColors("#64748b", primary, 0.2),
},
sidebar: {
background: blendTwoColors("#0f172a", primary, 0.08),
text: "#e2e8f0",
activeText: getLightColor(primary, 0.15),
activeBg: getLightColor(primary, 0.9),
activeHoverBg: getLightColor(primary, 0.65),
hover: blendTwoColors("#1e293b", primary, 0.15),
background: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.bgSecondary, primary, 0.06),
text: "#1e293b",
activeText: getDarkColor(primary, 0.1),
activeBg,
activeHoverBg,
hover: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.fillLight, primary, 0.15),
},
menu: {
background: blendTwoColors("#0f172a", primary, 0.08),
background: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.bgSecondary, primary, 0.04),
text: "#1e293b",
activeText: getDarkColor(primary, 0.1),
activeBg,
hover: blendTwoColors(ENTERPRISE_LIGHT_NEUTRAL.fillLight, primary, 0.08),
},
text: LIGHT_TEXT_SCHEME,
};
}
function generateDarkThemeScheme(primary: string): ColorScheme {
const baseDarkBg = blendTwoColors(ENTERPRISE_DARK_NEUTRAL.bgPrimary, primary, 0.04);
const secondaryDarkBg = blendTwoColors(ENTERPRISE_DARK_NEUTRAL.bgSecondary, primary, 0.06);
const tertiaryDarkBg = blendTwoColors(ENTERPRISE_DARK_NEUTRAL.bgTertiary, primary, 0.08);
const activeBg = blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillLight, primary, 0.22);
const activeHoverBg = blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillLight, primary, 0.28);
return {
bg: {
primary: baseDarkBg,
secondary: secondaryDarkBg,
tertiary: tertiaryDarkBg,
overlay: secondaryDarkBg,
},
fill: {
base: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillBase, primary, 0.08),
light: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillLight, primary, 0.12),
lighter: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillLighter, primary, 0.06),
extraLight: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.bgPrimary, primary, 0.04),
dark: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.borderLight, primary, 0.16),
darker: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.borderLighter, primary, 0.18),
},
shadow: DARK_SHADOW_SCHEME,
border: {
base: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.borderBase, primary, 0.1),
light: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.borderLight, primary, 0.14),
lighter: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.borderLighter, primary, 0.16),
extraLight: blendTwoColors("#64748b", primary, 0.2),
dark: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.bgSecondary, primary, 0.08),
darker: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.bgPrimary, primary, 0.06),
},
sidebar: {
background: secondaryDarkBg,
text: "#e2e8f0",
activeText: getLightColor(primary, 0.15),
activeBg: getLightColor(primary, 0.9),
hover: blendTwoColors("#1e293b", primary, 0.15),
activeBg,
activeHoverBg,
hover: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillLight, primary, 0.14),
},
menu: {
background: secondaryDarkBg,
text: "#e2e8f0",
activeText: getLightColor(primary, 0.15),
activeBg,
hover: blendTwoColors(ENTERPRISE_DARK_NEUTRAL.fillLight, primary, 0.14),
},
text: DARK_TEXT_SCHEME,
};
}
@@ -340,18 +336,85 @@ const SEMANTIC_COLORS = {
success: "#10b981",
warning: "#f59e0b",
danger: "#ef4444",
info: "#64748b",
info: "#2f86ff",
} as const;
function assignThemeRoleColors(colors: Record<string, string>, scheme: ColorScheme): void {
// Element Plus 核心变量
colors["el-bg-color"] = scheme.bg.primary;
colors["el-bg-color-page"] = scheme.bg.primary;
colors["el-bg-color-overlay"] = scheme.bg.overlay ?? scheme.bg.secondary;
// 边框颜色
colors["el-border-color"] = scheme.border.base;
colors["el-border-color-light"] = scheme.border.light;
colors["el-border-color-lighter"] = scheme.border.lighter;
colors["el-border-color-extra-light"] = scheme.border.extraLight;
colors["el-border-color-dark"] = scheme.border.dark;
colors["el-border-color-darker"] = scheme.border.darker;
// 填充颜色
colors["el-fill-color"] = scheme.fill.base;
colors["el-fill-color-light"] = scheme.fill.light;
colors["el-fill-color-lighter"] = scheme.fill.lighter;
colors["el-fill-color-extra-light"] = scheme.fill.extraLight;
colors["el-fill-color-dark"] = scheme.fill.dark;
colors["el-fill-color-darker"] = scheme.fill.darker;
// 阴影效果
colors["el-box-shadow"] = scheme.shadow.base;
colors["el-box-shadow-light"] = scheme.shadow.light;
colors["el-box-shadow-lighter"] = scheme.shadow.lighter;
colors["el-box-shadow-dark"] = scheme.shadow.dark;
// 抽屉背景色
colors["el-drawer-bg-color"] = scheme.fill.base;
// 菜单 / 侧边栏由 layout-* 语义变量统一驱动(见 assignCoreLayoutTokens)
}
function assignCoreLayoutTokens(colors: Record<string, string>, coreTokens: CoreThemeTokens): void {
colors["el-color-primary"] = coreTokens.brandPrimary;
colors["layout-bg-primary"] = coreTokens.layoutBgPrimary;
colors["layout-bg-secondary"] = coreTokens.layoutBgSecondary;
colors["layout-bg-tertiary"] = coreTokens.layoutBgTertiary;
colors["layout-sidebar-bg"] = coreTokens.layoutSidebarBg;
colors["layout-sidebar-text"] = coreTokens.layoutSidebarText;
colors["layout-sidebar-active-text"] = coreTokens.layoutSidebarActiveText;
colors["layout-sidebar-active-bg"] = coreTokens.layoutSidebarActiveBg;
colors["layout-sidebar-active-hover-bg"] = coreTokens.layoutSidebarActiveHoverBg;
colors["layout-sidebar-hover"] = coreTokens.layoutSidebarHover;
colors["layout-header-bg"] = coreTokens.layoutHeaderBg;
colors["layout-header-text"] = coreTokens.layoutHeaderText;
colors["layout-header-border"] = coreTokens.layoutHeaderBorder;
colors["layout-tags-bg"] = coreTokens.layoutTagsBg;
colors["layout-tags-border"] = coreTokens.layoutTagsBorder;
colors["layout-tags-item-bg"] = coreTokens.layoutTagsItemBg;
colors["layout-tags-item-text"] = coreTokens.layoutTagsItemText;
colors["layout-tags-item-active-bg"] = coreTokens.layoutTagsItemActiveBg;
colors["layout-tags-item-active-text"] = coreTokens.layoutTagsItemActiveText;
colors["layout-content-bg"] = coreTokens.layoutContentBg;
colors["layout-content-surface-bg"] = coreTokens.layoutContentSurfaceBg;
colors["layout-menu-bg"] = coreTokens.layoutSidebarBg;
colors["layout-menu-text"] = coreTokens.layoutSidebarText;
colors["layout-menu-active-text"] = coreTokens.layoutSidebarActiveText;
colors["layout-menu-active-bg"] = coreTokens.layoutMenuActiveBg;
colors["layout-menu-hover"] = coreTokens.layoutSidebarHover;
colors["layout-logo-bg"] = coreTokens.layoutSidebarBg;
colors["layout-logo-text"] = coreTokens.layoutSidebarActiveText;
colors["layout-text-primary"] = colors["layout-text-primary"] ?? "#0f172a";
colors["layout-text-secondary"] = colors["layout-text-secondary"] ?? "#475569";
colors["layout-text-tertiary"] = colors["layout-text-tertiary"] ?? "#64748b";
colors["layout-text-disabled"] = colors["layout-text-disabled"] ?? "#94a3b8";
}
export function generateThemeColors(primary: string, theme: ThemeMode) {
// 处理带alpha通道的颜色值,去除alpha部分
const cleanPrimary = primary.length === 9 ? primary.slice(0, 7) : primary;
const cleanPrimary = normalizeHexColor(primary);
const colors: Record<string, string> = {
primary: cleanPrimary,
...SEMANTIC_COLORS,
};
const isLight = isLightColor(cleanPrimary);
const rgb = hexToRgb(cleanPrimary);
const isDarkMode = theme === ThemeMode.DARK;
@@ -359,6 +422,7 @@ export function generateThemeColors(primary: string, theme: ThemeMode) {
? generateDarkModeVariants(cleanPrimary)
: generateColorVariants(cleanPrimary);
// 生成颜色变体,使用 Element Plus 标准命名
primaryVariants.light.forEach((color, i) => {
colors[`primary-light-${i + 1}`] = color;
});
@@ -372,100 +436,29 @@ export function generateThemeColors(primary: string, theme: ThemeMode) {
colors[`${name}-dark-2`] = variants.dark;
});
// 品牌颜色 RGB 值,用于科技感效果
colors["brand-primary-rgb"] = `${rgb[0]}, ${rgb[1]}, ${rgb[2]}`;
Object.entries(SEMANTIC_COLORS).forEach(([name, color]) => {
const c = hexToRgb(color);
colors[`brand-${name}-rgb`] = `${c[0]}, ${c[1]}, ${c[2]}`;
});
// 生成主题方案
const scheme = isDarkMode
? generateDarkThemeScheme(cleanPrimary, isLight)
: generateLightThemeScheme(cleanPrimary, isLight);
? generateDarkThemeScheme(cleanPrimary)
: generateLightThemeScheme(cleanPrimary);
colors["custom-bg-primary"] = scheme.bg.primary;
colors["custom-bg-secondary"] = scheme.bg.secondary;
colors["custom-bg-tertiary"] = scheme.bg.tertiary;
// 文本语义变量
colors["layout-text-primary"] = scheme.text.primary;
colors["layout-text-secondary"] = scheme.text.secondary;
colors["layout-text-tertiary"] = scheme.text.tertiary;
colors["layout-text-disabled"] = scheme.text.disabled;
colors["el-bg-color"] = scheme.bg.primary;
colors["el-bg-color-page"] = scheme.bg.primary;
colors["el-bg-color-overlay"] = scheme.bg.overlay ?? scheme.bg.secondary;
// 角色色和核心 token(主题控制主入口)
assignThemeRoleColors(colors, scheme);
const coreTokens = createCoreThemeTokens(scheme, cleanPrimary);
if (isDarkMode) {
colors["el-text-color-primary"] = "#f8fafc";
colors["el-text-color-regular"] = "#e2e8f0";
colors["el-text-color-secondary"] = "#94a3b8";
colors["el-text-color-placeholder"] = "#64748b";
colors["el-disabled-bg-color"] = "#0f172a";
colors["el-disabled-text-color"] = "#475569";
colors["el-disabled-border-color"] = "#1e293b";
colors["el-overlay-color"] = "rgba(0, 0, 0, 0.7)";
colors["el-overlay-color-light"] = "rgba(0, 0, 0, 0.5)";
colors["el-overlay-color-lighter"] = "rgba(0, 0, 0, 0.3)";
colors["el-scrollbar-bg-color"] = "#0f172a";
colors["el-scrollbar-thumb-color"] = "#334155";
colors["el-scrollbar-thumb-hover-color"] = "#475569";
} else {
colors["el-text-color-primary"] = "#0f172a";
colors["el-text-color-regular"] = "#334155";
colors["el-text-color-secondary"] = "#64748b";
colors["el-text-color-placeholder"] = "#94a3b8";
colors["el-disabled-bg-color"] = "#f1f5f9";
colors["el-disabled-text-color"] = "#94a3b8";
colors["el-disabled-border-color"] = "#e2e8f0";
colors["el-overlay-color"] = "rgba(15, 23, 42, 0.5)";
colors["el-overlay-color-light"] = "rgba(15, 23, 42, 0.3)";
colors["el-overlay-color-lighter"] = "rgba(15, 23, 42, 0.15)";
colors["el-scrollbar-bg-color"] = "#f1f5f9";
colors["el-scrollbar-thumb-color"] = "#cbd5e1";
colors["el-scrollbar-thumb-hover-color"] = "#94a3b8";
}
colors["el-border-color"] = scheme.border.base;
colors["el-border-color-light"] = scheme.border.light;
colors["el-border-color-lighter"] = scheme.border.lighter;
colors["el-border-color-extra-light"] = scheme.border.extraLight;
colors["el-border-color-dark"] = scheme.border.dark;
colors["el-border-color-darker"] = scheme.border.darker;
colors["el-fill-color"] = scheme.fill.base;
colors["el-fill-color-light"] = scheme.fill.light;
colors["el-fill-color-lighter"] = scheme.fill.lighter;
colors["el-fill-color-extra-light"] = scheme.fill.extraLight;
colors["el-fill-color-dark"] = scheme.fill.dark;
colors["el-fill-color-darker"] = scheme.fill.darker;
colors["el-box-shadow"] = scheme.shadow.base;
colors["el-box-shadow-light"] = scheme.shadow.light;
colors["el-box-shadow-lighter"] = scheme.shadow.lighter;
colors["el-box-shadow-dark"] = scheme.shadow.dark;
colors["el-table-bg-color"] = scheme.table.bg;
colors["el-table-tr-bg-color"] = scheme.table.trBg;
colors["el-table-header-bg-color"] = scheme.table.headerBg;
colors["el-table-row-hover-bg-color"] = scheme.table.hoverBg;
colors["el-table-border-color"] = scheme.table.border;
// 抽屉背景色 - 使用主题填充色
colors["el-drawer-bg-color"] = scheme.fill.base;
// 侧边栏和菜单相关变量
colors["sidebar-background"] = scheme.sidebar.background;
colors["sidebar-text-color"] = scheme.sidebar.text;
colors["sidebar-active-text-color"] = scheme.sidebar.activeText;
colors["sidebar-active-bg"] = scheme.sidebar.activeBg;
colors["sidebar-active-hover-bg"] = scheme.sidebar.activeHoverBg;
colors["sidebar-hover"] = scheme.sidebar.hover;
// Logo 相关变量
colors["sidebar-logo-background"] = scheme.sidebar.background;
colors["sidebar-logo-text-color"] = scheme.sidebar.activeText;
// 布局菜单变量 - 确保与布局文件兼容
colors["menu-background"] = scheme.menu.background;
colors["menu-text"] = scheme.menu.text;
colors["menu-active-text"] = scheme.menu.activeText;
colors["menu-active-bg"] = scheme.menu.activeBg;
colors["menu-hover"] = scheme.menu.hover;
assignCoreLayoutTokens(colors, coreTokens);
return colors;
}
@@ -473,16 +466,37 @@ export function generateThemeColors(primary: string, theme: ThemeMode) {
export function applyTheme(colors: Record<string, string>) {
const el = document.documentElement;
const setVar = (key: string, value: string) => {
el.style.setProperty(`--${key}`, value);
};
const pickColor = (keys: string[]) => keys.map((k) => colors[k]).find(Boolean);
const isDirectCssVar = (key: string) =>
key.startsWith("el-") || key.startsWith("brand-") || key.startsWith("layout-");
// 仅允许核心调色板变量走 --el-color-*,避免无关变量污染 :root
const isAllowedElColorToken = (key: string) =>
/^(primary|success|warning|danger|info)$/.test(key) ||
/^(primary|success|warning|danger|info)-light-[1-9]$/.test(key) ||
/^(primary|success|warning|danger|info)-dark-2$/.test(key);
Object.entries(colors).forEach(([key, value]) => {
if (key.startsWith("el-")) {
el.style.setProperty(`--${key}`, value);
} else if (key.startsWith("sidebar-") || key.startsWith("menu-")) {
el.style.setProperty(`--${key}`, value);
} else {
el.style.setProperty(`--el-color-${key}`, value);
if (isDirectCssVar(key)) {
setVar(key, value);
return;
}
if (isAllowedElColorToken(key)) {
// Element Plus 色板变量(--el-color-primary-light-1 等)
setVar(`el-color-${key}`, value);
}
});
// 确保布局相关的背景变量也被设置
setVar("bg-primary", pickColor(["layout-bg-primary", "el-bg-color"]) ?? "");
setVar("bg-secondary", pickColor(["layout-bg-secondary", "el-bg-color-overlay"]) ?? "");
setVar("bg-tertiary", pickColor(["layout-bg-tertiary", "el-bg-color-overlay"]) ?? "");
// 触发主题更新动画
requestAnimationFrame(() => {
el.style.setProperty("--theme-update-trigger", Date.now().toString());
});
@@ -1,5 +1,5 @@
<template>
<div class="chat-input">
<div class="chat-input" :class="{ 'chat-input--disabled': disabled }">
<div class="input-wrapper">
<div v-if="uploadedFiles.length > 0" class="uploaded-files">
<div v-for="file in uploadedFiles" :key="file.id" class="file-item">
@@ -201,19 +201,26 @@ defineExpose({
flex-direction: column;
gap: 12px;
padding: 20px;
background: var(--el-bg-color-page);
background: var(--el-bg-color-overlay);
border: 1px solid var(--el-border-color-light);
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
box-shadow: var(--el-box-shadow-light);
transition:
border-color 0.2s ease,
box-shadow 0.2s ease,
transform 0.2s ease;
&:hover {
border-color: var(--el-color-primary);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
box-shadow: var(--el-box-shadow-light);
box-shadow: var(--el-box-shadow);
transform: translateY(-2px);
}
&:focus-within {
border-color: var(--el-color-primary);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--el-color-primary) 35%, transparent);
}
.message-input {
flex: 1;
min-width: 0;
@@ -258,11 +265,11 @@ defineExpose({
.send-button {
flex-shrink: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(24, 144, 255, 0.3);
box-shadow: 0 2px 8px color-mix(in srgb, var(--el-color-primary) 35%, transparent);
transition: all 0.2s ease;
&:hover {
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
box-shadow: 0 4px 14px color-mix(in srgb, var(--el-color-primary) 45%, transparent);
transform: translateY(-1px);
}
@@ -278,10 +285,26 @@ defineExpose({
margin-top: 12px;
font-size: 12px;
font-weight: 400;
color: var(--el-text-color-tertiary);
color: var(--el-text-color-secondary);
text-align: center;
letter-spacing: 0.5px;
}
}
&.chat-input--disabled .input-wrapper .input-container {
opacity: 0.72;
filter: grayscale(0.06);
&:hover {
border-color: var(--el-border-color-light);
box-shadow: var(--el-box-shadow-light);
transform: none;
}
&:focus-within {
border-color: var(--el-border-color-light);
box-shadow: var(--el-box-shadow-light);
}
}
}
</style>
@@ -73,7 +73,8 @@ defineExpose({
.chat-messages {
height: 100%;
overflow-y: auto;
background: var(--el-bg-color);
/* 与 index chat-main 同底,避免 disabled 灰 + page 色打架 */
background: transparent;
.messages-list {
max-width: 800px;
@@ -7,9 +7,6 @@
</button>
</div>
<div class="navbar-right">
<el-button v-if="hasMessages" text :icon="Delete" @click="handleClearChat">
清空对话
</el-button>
<el-button text :icon="Setting" @click="handleToggleConnection">
{{ isConnected ? "断开连接" : "重新连接" }}
</el-button>
@@ -25,6 +22,9 @@
</el-icon>
<span class="status-text">{{ connectionStatusText }}</span>
</el-tag>
<el-button v-if="hasMessages" text :icon="Delete" @click="handleClearChat">
清空对话
</el-button>
</div>
</div>
</template>
@@ -108,6 +108,11 @@ const toggleSidebar = () => {
background: var(--el-fill-color-light);
}
&:focus-visible {
outline: 2px solid color-mix(in srgb, var(--el-color-primary) 45%, transparent);
outline-offset: 2px;
}
.collapse-icon {
width: 20px;
height: 20px;
@@ -118,14 +123,30 @@ const toggleSidebar = () => {
.navbar-right {
display: flex;
flex-wrap: nowrap;
gap: 12px;
align-items: center;
/* EP 相邻按钮自带 margin-left,叠在 flex gap 上会导致间距忽大忽小 */
:deep(.el-button) {
margin: 0;
}
.connection-status {
display: flex;
gap: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 32px;
padding: 0 12px;
margin: 0;
font-size: 14px;
line-height: 1;
:deep(.el-tag__content) {
display: inline-flex;
gap: 6px;
align-items: center;
}
.status-icon {
&.connected {
@@ -75,6 +75,7 @@ import MarkdownIt from "markdown-it";
import markdownItHighlightjs from "markdown-it-highlightjs";
import hljs from "highlight.js";
import DOMPurify from "dompurify";
/* 亮色用 atom-one;暗色由文末全局块覆盖 .hljs 底与字色,避免整段代码发亮底 */
import "highlight.js/styles/atom-one-light.css";
import { useUserStoreHook } from "@/store";
import type { ChatMessage } from "@/views/module_ai/chat/types";
@@ -347,7 +348,9 @@ const formatFileSize = (bytes: number): string => {
line-height: 1.6;
color: var(--el-text-color-primary);
word-wrap: break-word;
transition: all 0.3s ease;
transition:
max-height 0.25s ease,
opacity 0.2s ease;
&.collapsed {
position: relative;
@@ -361,7 +364,10 @@ const formatFileSize = (bytes: number): string => {
left: 0;
height: 60px;
content: "";
background: linear-gradient(transparent, var(--el-bg-color));
background: linear-gradient(
transparent,
var(--chat-area-bg, var(--layout-content-canvas, var(--el-bg-color-page)))
);
}
}
@@ -484,6 +490,29 @@ const formatFileSize = (bytes: number): string => {
.message-actions {
justify-content: flex-end;
}
.message-body .message-text {
padding: 10px 14px;
background: color-mix(in srgb, var(--el-color-primary) 10%, var(--el-bg-color-overlay));
border: 1px solid
color-mix(in srgb, var(--el-color-primary) 18%, var(--el-border-color-light));
border-radius: 12px;
&.collapsed::after {
background: linear-gradient(
transparent,
color-mix(in srgb, var(--el-color-primary) 10%, var(--el-bg-color-overlay))
);
}
:deep(pre) {
border: 1px solid var(--el-border-color-lighter);
}
:deep(code:not(pre code)) {
border: 1px solid var(--el-border-color-lighter);
}
}
}
}
@@ -507,3 +536,36 @@ const formatFileSize = (bytes: number): string => {
}
}
</style>
<!-- 暗色下覆盖 highlight.js 亮色主题,避免代码块发白底 -->
<style lang="scss">
html.dark .chat-messages .message-text {
pre.hljs,
code.hljs {
color: var(--el-text-color-regular) !important;
background: var(--el-fill-color) !important;
}
.hljs-comment,
.hljs-quote {
color: var(--el-text-color-secondary) !important;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-built_in {
color: var(--el-color-primary) !important;
}
.hljs-string,
.hljs-title,
.hljs-attr {
color: var(--el-color-success) !important;
}
.hljs-number,
.hljs-literal {
color: var(--el-color-warning) !important;
}
}
</style>
@@ -388,7 +388,7 @@ defineExpose({
}
&:hover {
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
box-shadow: 0 4px 12px color-mix(in srgb, var(--el-color-primary) 30%, transparent);
transform: translateY(-1px);
}
@@ -486,9 +486,17 @@ defineExpose({
&.active {
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
border-color: var(--el-color-primary-light-7);
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.1);
background: color-mix(
in srgb,
var(--el-color-primary) 9%,
var(--chat-area-bg, var(--el-bg-color-overlay))
);
border-color: color-mix(
in srgb,
var(--el-color-primary) 18%,
var(--el-border-color-light)
);
box-shadow: none;
}
.session-icon {
@@ -8,21 +8,18 @@
<p class="welcome-subtitle">我是您的专属AI助手,可以帮您回答问题、处理任务和进行智能对话</p>
<div class="example-prompts">
<div class="prompt-card" @click="handlePromptClick('请介绍一下FastApiAdmin系统')">
<h4>系统介绍</h4>
<p>请介绍一下FastApiAdmin系统</p>
</div>
<div class="prompt-card" @click="handlePromptClick('如何在系统中创建新的模块?')">
<h4>开发指导</h4>
<p>如何在系统中创建新的模块?</p>
</div>
<div class="prompt-card" @click="handlePromptClick('系统的权限管理是如何工作的?')">
<h4>权限管理</h4>
<p>FA系统的权限管理是如何工作的?</p>
</div>
<div class="prompt-card" @click="handlePromptClick('如何优化FA系统的性能?')">
<h4>性能优化</h4>
<p>如何优化系统的性能?</p>
<div
v-for="card in promptCards"
:key="card.prompt"
class="prompt-card"
role="button"
tabindex="0"
@click="handlePromptClick(card.prompt)"
@keydown.enter.prevent="handlePromptClick(card.prompt)"
@keydown.space.prevent="handlePromptClick(card.prompt)"
>
<h4>{{ card.title }}</h4>
<p>{{ card.body }}</p>
</div>
</div>
</div>
@@ -38,6 +35,17 @@ interface Emits {
const emit = defineEmits<Emits>();
const promptCards = [
{ title: "系统介绍", body: "请介绍一下FastApiAdmin系统", prompt: "请介绍一下FastApiAdmin系统" },
{ title: "开发指导", body: "如何在系统中创建新的模块?", prompt: "如何在系统中创建新的模块?" },
{
title: "权限管理",
body: "FA系统的权限管理是如何工作的?",
prompt: "系统的权限管理是如何工作的?",
},
{ title: "性能优化", body: "如何优化系统的性能?", prompt: "如何优化FA系统的性能?" },
];
const handlePromptClick = (prompt: string) => {
emit("prompt-click", prompt);
};
@@ -85,10 +93,13 @@ const handlePromptClick = (prompt: string) => {
padding: 20px;
text-align: left;
cursor: pointer;
background: var(--el-bg-color-page);
background: var(--el-bg-color-overlay);
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
transition: all 0.2s ease;
transition:
border-color 0.2s ease,
box-shadow 0.2s ease,
transform 0.2s ease;
&:hover {
border-color: var(--el-color-primary);
@@ -96,6 +107,12 @@ const handlePromptClick = (prompt: string) => {
transform: translateY(-2px);
}
&:focus-visible {
outline: 2px solid color-mix(in srgb, var(--el-color-primary) 40%, transparent);
outline-offset: 2px;
border-color: var(--el-color-primary);
}
h4 {
margin: 0 0 8px;
font-size: 14px;
+15 -5
View File
@@ -301,12 +301,20 @@ onUnmounted(disconnectWebSocket);
<style lang="scss" scoped>
.main-chat {
/* L2 内容面:与 AppMain 的 L0 画布(留白/间隙)区分开,避免与 gap 同色 */
--chat-area-bg: var(--layout-content-surface-bg, var(--el-bg-color-overlay));
height: 100%;
overflow: hidden;
background: var(--chat-area-bg);
border: 1px solid var(--el-border-color-light);
border-radius: 8px;
box-shadow: var(--el-box-shadow-light);
/* 与右侧同一表面色;与内容区的分界交给 Sidebar 的竖线即可 */
.sidebar-container {
width: 200px;
background: var(--el-bg-color);
background: transparent;
transition: width 0.3s ease;
&.collapsed {
@@ -319,26 +327,28 @@ onUnmounted(disconnectWebSocket);
flex-direction: column;
height: 100%;
overflow: hidden;
background: var(--chat-area-bg);
}
.chat-header {
height: auto;
padding: 0;
background: var(--el-bg-color);
background: var(--chat-area-bg);
border-bottom: 1px solid var(--el-border-color-lighter);
}
.chat-main {
flex: 1;
padding: 0;
overflow: hidden;
background: var(--chat-area-bg);
}
.chat-footer {
height: auto;
min-height: 80px;
padding: 0;
background: var(--el-bg-color);
border-top: 1px solid var(--el-border-color-light);
background: var(--chat-area-bg);
border-top: 1px solid var(--el-border-color-lighter);
}
}
</style>
@@ -138,7 +138,6 @@
ref="tableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -215,7 +215,6 @@
ref="tableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -67,7 +67,6 @@
<el-button
v-hasPerm="['module_generator:gencode:create']"
type="primary"
plain
icon="Plus"
@click="createTableVisible = true"
>
@@ -78,7 +77,6 @@
<el-button
v-hasPerm="['module_generator:gencode:import']"
type="success"
plain
icon="Upload"
@click="handleImportClick"
>
@@ -89,7 +87,6 @@
<el-button
v-hasPerm="['module_generator:gencode:delete']"
type="danger"
plain
icon="Delete"
:disabled="ids.length === 0"
@click="handleDelete()"
@@ -101,7 +98,6 @@
<el-button
v-hasPerm="['module_generator:gencode:code']"
type="warning"
plain
icon="Download"
:disabled="!canGenerate"
@click="handleGenTable('0')"
@@ -145,7 +141,6 @@
ref="dataTableRef"
v-loading="loading"
:data="tableList"
highlight-current-row
class="data-table__content"
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
+1
View File
@@ -145,6 +145,7 @@
height="calc(100vh - 290px)"
max-height="calc(100vh - 290px)"
border
stripe
>
<template #empty>
<el-empty :image-size="80" description="暂无数据" />
@@ -107,7 +107,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -230,7 +230,7 @@
</el-tooltip>
</div>
</template>
<el-table :data="server.disks" border>
<el-table :data="server.disks" border stripe>
<template #empty>
<el-empty :image-size="80" description="暂无数据" />
</template>
@@ -143,7 +143,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 430px)"
max-height="calc(100vh - 430px)"
border
@@ -173,7 +173,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -142,7 +142,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -192,7 +192,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -163,7 +163,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -177,7 +177,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -169,7 +169,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -188,7 +188,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 445px)"
max-height="calc(100vh - 445px)"
border
@@ -327,7 +327,6 @@
v-loading="logLoading"
:data="executionLogData"
class="data-table__content"
highlight-current-row
border
stripe
height="calc(100vh - 240px)"
@@ -87,7 +87,6 @@
ref="dataTableRef"
v-loading="loading"
:data="pageTableData"
highlight-current-row
height="calc(100vh - 440px)"
max-height="calc(100vh - 440px)"
border
@@ -107,7 +107,6 @@
ref="tableRef"
v-loading="loading"
:data="dataSource"
highlight-current-row
class="data-table__content"
height="450"
max-height="450"