!60 完善登录页主题切换功能
Merge pull request !60 from Coast/fix/theme-follow-system
This commit is contained in:
8797
pnpm-lock.yaml
generated
8797
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,8 @@ const watermarkContent = appConfig.name;
|
|||||||
|
|
||||||
// 明亮/暗黑主题水印字体颜色适配
|
// 明亮/暗黑主题水印字体颜色适配
|
||||||
const fontColor = computed(() => {
|
const fontColor = computed(() => {
|
||||||
return settingsStore.theme === ThemeMode.DARK ? "rgba(255, 255, 255, .15)" : "rgba(0, 0, 0, .15)";
|
return settingsStore.resolvedTheme === ThemeMode.DARK
|
||||||
|
? "rgba(255, 255, 255, .15)"
|
||||||
|
: "rgba(0, 0, 0, .15)";
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
39
src/assets/images/login-bg-dark.svg
Normal file
39
src/assets/images/login-bg-dark.svg
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1400 800">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bgDark" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#0b1324" />
|
||||||
|
<stop offset="58%" stop-color="#162135" />
|
||||||
|
<stop offset="100%" stop-color="#1e2c44" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<radialGradient id="glowPrimary" cx="18%" cy="14%" r="62%">
|
||||||
|
<stop offset="0%" stop-color="rgba(98,142,255,0.42)" />
|
||||||
|
<stop offset="46%" stop-color="rgba(98,142,255,0.18)" />
|
||||||
|
<stop offset="100%" stop-color="rgba(98,142,255,0)" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<radialGradient id="glowSecondary" cx="82%" cy="70%" r="56%">
|
||||||
|
<stop offset="0%" stop-color="rgba(59,108,224,0.28)" />
|
||||||
|
<stop offset="48%" stop-color="rgba(59,108,224,0.14)" />
|
||||||
|
<stop offset="100%" stop-color="rgba(59,108,224,0)" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<linearGradient id="meshDark" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="rgba(255,255,255,0.1)" />
|
||||||
|
<stop offset="35%" stop-color="rgba(255,255,255,0.04)" />
|
||||||
|
<stop offset="100%" stop-color="rgba(255,255,255,0)" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="url(#bgDark)" />
|
||||||
|
<rect width="100%" height="100%" fill="url(#glowPrimary)" />
|
||||||
|
<rect width="100%" height="100%" fill="url(#glowSecondary)" />
|
||||||
|
<rect width="100%" height="100%" fill="url(#meshDark)" />
|
||||||
|
|
||||||
|
<g opacity="0.32">
|
||||||
|
<rect x="-40" y="520" width="520" height="220" rx="180" fill="rgba(255,255,255,0.12)" />
|
||||||
|
<rect x="760" y="90" width="520" height="210" rx="180" fill="rgba(255,255,255,0.1)" />
|
||||||
|
<rect x="420" y="620" width="560" height="190" rx="180" fill="rgba(255,255,255,0.08)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -1,45 +1,24 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1400 800">
|
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1400 800">
|
||||||
<defs>
|
<defs>
|
||||||
<style>
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
#bg-layer { fill: url(#bgDark); }
|
|
||||||
#soft-glow { fill: url(#glowDark); }
|
|
||||||
.accent-arc { stroke: rgba(118, 156, 255, 0.35); }
|
|
||||||
.accent-dot { fill: rgba(154, 188, 255, 0.45); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<linearGradient id="bgLight" x1="0%" y1="0%" x2="100%" y2="100%">
|
<linearGradient id="bgLight" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
<stop offset="0%" stop-color="#f3f7ff" />
|
<stop offset="0%" stop-color="#f3f7ff" />
|
||||||
<stop offset="60%" stop-color="#e3edff" />
|
<stop offset="60%" stop-color="#e3edff" />
|
||||||
<stop offset="100%" stop-color="#d6e7ff" />
|
<stop offset="100%" stop-color="#d6e7ff" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
|
|
||||||
<linearGradient id="bgDark" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#0b1324" />
|
|
||||||
<stop offset="60%" stop-color="#162135" />
|
|
||||||
<stop offset="100%" stop-color="#1e2c44" />
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<radialGradient id="glowLight" cx="20%" cy="15%" r="60%">
|
<radialGradient id="glowLight" cx="20%" cy="15%" r="60%">
|
||||||
<stop offset="0%" stop-color="rgba(64,128,255,0.35)" />
|
<stop offset="0%" stop-color="rgba(64,128,255,0.35)" />
|
||||||
<stop offset="40%" stop-color="rgba(64,128,255,0.18)" />
|
<stop offset="40%" stop-color="rgba(64,128,255,0.18)" />
|
||||||
<stop offset="100%" stop-color="rgba(64,128,255,0)" />
|
<stop offset="100%" stop-color="rgba(64,128,255,0)" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
|
|
||||||
<radialGradient id="glowDark" cx="20%" cy="15%" r="60%">
|
|
||||||
<stop offset="0%" stop-color="rgba(98,142,255,0.4)" />
|
|
||||||
<stop offset="50%" stop-color="rgba(98,142,255,0.18)" />
|
|
||||||
<stop offset="100%" stop-color="rgba(98,142,255,0)" />
|
|
||||||
</radialGradient>
|
|
||||||
|
|
||||||
<radialGradient id="glowSecondary" cx="80%" cy="70%" r="55%">
|
<radialGradient id="glowSecondary" cx="80%" cy="70%" r="55%">
|
||||||
<stop offset="0%" stop-color="rgba(22,93,255,0.3)" />
|
<stop offset="0%" stop-color="rgba(22,93,255,0.3)" />
|
||||||
<stop offset="50%" stop-color="rgba(22,93,255,0.12)" />
|
<stop offset="50%" stop-color="rgba(22,93,255,0.12)" />
|
||||||
<stop offset="100%" stop-color="rgba(22,93,255,0)" />
|
<stop offset="100%" stop-color="rgba(22,93,255,0)" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
|
|
||||||
<linearGradient id="meshLight" x1="0%" y1="0%" x2="100%" y2="100%">
|
<linearGradient id="meshLight" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
<stop offset="0%" stop-color="rgba(255,255,255,0.6)" />
|
<stop offset="0%" stop-color="rgba(255,255,255,0.6)" />
|
||||||
<stop offset="35%" stop-color="rgba(255,255,255,0.2)" />
|
<stop offset="35%" stop-color="rgba(255,255,255,0.2)" />
|
||||||
@@ -60,4 +39,4 @@
|
|||||||
</g>
|
</g>
|
||||||
|
|
||||||
<!-- 去掉点状噪声,仅保留大区域柔光 -->
|
<!-- 去掉点状噪声,仅保留大区域柔光 -->
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -6,15 +6,6 @@
|
|||||||
--orange: rgba(230, 162, 60, 0.06);
|
--orange: rgba(230, 162, 60, 0.06);
|
||||||
--green: rgba(144, 238, 144, 0.06);
|
--green: rgba(144, 238, 144, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--blue: rgba(64, 158, 255, 0.04);
|
|
||||||
--grey: rgba(144, 147, 153, 0.03);
|
|
||||||
--orange: rgba(230, 162, 60, 0.04);
|
|
||||||
--green: rgba(144, 238, 144, 0.04);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- 左侧波浪 -->
|
<!-- 左侧波浪 -->
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -19,7 +19,7 @@ const layout = computed(() => settingsStore.layout);
|
|||||||
|
|
||||||
const hamburgerClass = computed(() => {
|
const hamburgerClass = computed(() => {
|
||||||
// 如果暗黑主题
|
// 如果暗黑主题
|
||||||
if (settingsStore.theme === ThemeMode.DARK) {
|
if (settingsStore.resolvedTheme === ThemeMode.DARK) {
|
||||||
return "hamburger--white";
|
return "hamburger--white";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dropdown trigger="click" @command="handleDarkChange">
|
<el-dropdown trigger="click" @command="handleDarkChange">
|
||||||
<el-icon :size="20">
|
<el-icon :size="20">
|
||||||
<component :is="settingsStore.theme === ThemeMode.DARK ? Moon : Sunny" />
|
<component :is="currentThemeIcon" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
@@ -34,6 +34,14 @@ const theneList = [
|
|||||||
{ label: t("login.auto"), value: ThemeMode.AUTO, component: Monitor },
|
{ label: t("login.auto"), value: ThemeMode.AUTO, component: Monitor },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const currentThemeIcon = computed(() => {
|
||||||
|
if (settingsStore.theme === ThemeMode.AUTO) {
|
||||||
|
return Monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingsStore.resolvedTheme === ThemeMode.DARK ? Moon : Sunny;
|
||||||
|
});
|
||||||
|
|
||||||
const handleDarkChange = (theme: ThemeMode) => {
|
const handleDarkChange = (theme: ThemeMode) => {
|
||||||
settingsStore.theme = theme;
|
settingsStore.theme = theme;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ import { useLayout } from "./useLayout";
|
|||||||
import { useAppStore, usePermissionStore, useSettingsStore } from "@/stores";
|
import { useAppStore, usePermissionStore, useSettingsStore } from "@/stores";
|
||||||
import { isExternal } from "@/utils/index";
|
import { isExternal } from "@/utils/index";
|
||||||
import { translateRouteTitle } from "@/lang/utils";
|
import { translateRouteTitle } from "@/lang/utils";
|
||||||
import { SidebarColor } from "@/enums/settings";
|
import { SidebarColor, ThemeMode } from "@/enums/settings";
|
||||||
import { ElIcon } from "element-plus";
|
import { ElIcon } from "element-plus";
|
||||||
import BaseLayout from "./BaseLayout.vue";
|
import BaseLayout from "./BaseLayout.vue";
|
||||||
import LayoutLogo from "./components/LayoutLogo.vue";
|
import LayoutLogo from "./components/LayoutLogo.vue";
|
||||||
@@ -127,7 +127,8 @@ const isLogoCollapsed = computed(() => width.value < 768);
|
|||||||
// 是否使用深色菜单配色(暗色主题或经典蓝侧边栏)
|
// 是否使用深色菜单配色(暗色主题或经典蓝侧边栏)
|
||||||
const useMenuColors = computed(
|
const useMenuColors = computed(
|
||||||
() =>
|
() =>
|
||||||
settingsStore.theme === "dark" || settingsStore.sidebarColorScheme === SidebarColor.CLASSIC_BLUE
|
settingsStore.resolvedTheme === ThemeMode.DARK ||
|
||||||
|
settingsStore.sidebarColorScheme === SidebarColor.CLASSIC_BLUE
|
||||||
);
|
);
|
||||||
|
|
||||||
// 顶部菜单项(处理单子菜单显示优化)
|
// 顶部菜单项(处理单子菜单显示优化)
|
||||||
|
|||||||
@@ -11,13 +11,17 @@
|
|||||||
<el-divider>{{ t("settings.theme") }}</el-divider>
|
<el-divider>{{ t("settings.theme") }}</el-divider>
|
||||||
|
|
||||||
<div class="flex-center">
|
<div class="flex-center">
|
||||||
<el-switch
|
<el-radio-group v-model="themeMode" class="theme-mode-group">
|
||||||
v-model="isDark"
|
<el-radio-button :value="ThemeMode.LIGHT">
|
||||||
active-icon="Moon"
|
{{ t("login.light") }}
|
||||||
inactive-icon="Sunny"
|
</el-radio-button>
|
||||||
class="theme-switch"
|
<el-radio-button :value="ThemeMode.DARK">
|
||||||
@change="handleThemeChange"
|
{{ t("login.dark") }}
|
||||||
/>
|
</el-radio-button>
|
||||||
|
<el-radio-button :value="ThemeMode.AUTO">
|
||||||
|
{{ t("login.auto") }}
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -71,7 +75,10 @@
|
|||||||
<el-switch v-model="settingsStore.colorWeak" />
|
<el-switch v-model="settingsStore.colorWeak" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!isDark" class="config-item flex-x-between">
|
<div
|
||||||
|
v-if="settingsStore.resolvedTheme !== ThemeMode.DARK"
|
||||||
|
class="config-item flex-x-between"
|
||||||
|
>
|
||||||
<span class="text-xs">{{ t("settings.sidebarColorScheme") }}</span>
|
<span class="text-xs">{{ t("settings.sidebarColorScheme") }}</span>
|
||||||
<el-radio-group v-model="sidebarColor" @change="changeSidebarColor">
|
<el-radio-group v-model="sidebarColor" @change="changeSidebarColor">
|
||||||
<el-radio :value="SidebarColor.CLASSIC_BLUE">
|
<el-radio :value="SidebarColor.CLASSIC_BLUE">
|
||||||
@@ -199,8 +206,13 @@ const colorPresets = [...themeColorPresets];
|
|||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
const isDark = ref<boolean>(settingsStore.theme === ThemeMode.DARK);
|
|
||||||
const sidebarColor = ref(settingsStore.sidebarColorScheme);
|
const sidebarColor = ref(settingsStore.sidebarColorScheme);
|
||||||
|
const themeMode = computed({
|
||||||
|
get: () => settingsStore.theme,
|
||||||
|
set: (value: ThemeMode) => {
|
||||||
|
settingsStore.theme = value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const selectedThemeColor = computed({
|
const selectedThemeColor = computed({
|
||||||
get: () => settingsStore.themeColor,
|
get: () => settingsStore.themeColor,
|
||||||
@@ -214,15 +226,6 @@ const drawerVisible = computed({
|
|||||||
set: (value) => (settingsStore.settingsVisible = value),
|
set: (value) => (settingsStore.settingsVisible = value),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理主题切换
|
|
||||||
*
|
|
||||||
* @param isDark 是否启用暗黑模式
|
|
||||||
*/
|
|
||||||
const handleThemeChange = (isDark: string | number | boolean) => {
|
|
||||||
settingsStore.theme = isDark ? ThemeMode.DARK : ThemeMode.LIGHT;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更改侧边栏颜色
|
* 更改侧边栏颜色
|
||||||
*
|
*
|
||||||
@@ -277,8 +280,6 @@ const handleResetSettings = async () => {
|
|||||||
try {
|
try {
|
||||||
settingsStore.resetSettings();
|
settingsStore.resetSettings();
|
||||||
|
|
||||||
// 同步更新本地状态"
|
|
||||||
isDark.value = settingsStore.theme === ThemeMode.DARK;
|
|
||||||
sidebarColor.value = settingsStore.sidebarColorScheme;
|
sidebarColor.value = settingsStore.sidebarColorScheme;
|
||||||
|
|
||||||
ElMessage.success(t("settings.resetSuccess"));
|
ElMessage.success(t("settings.resetSuccess"));
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { useRoute } from "vue-router";
|
|||||||
import path from "path-browserify";
|
import path from "path-browserify";
|
||||||
import type { MenuInstance } from "element-plus";
|
import type { MenuInstance } from "element-plus";
|
||||||
import type { RouteRecordRaw } from "vue-router";
|
import type { RouteRecordRaw } from "vue-router";
|
||||||
import { SidebarColor } from "@/enums/settings";
|
import { SidebarColor, ThemeMode } from "@/enums/settings";
|
||||||
import { useSettingsStore, useAppStore } from "@/stores";
|
import { useSettingsStore, useAppStore } from "@/stores";
|
||||||
import { isExternal } from "@/utils/index";
|
import { isExternal } from "@/utils/index";
|
||||||
import LayoutSidebarItem from "./LayoutSidebarItem.vue";
|
import LayoutSidebarItem from "./LayoutSidebarItem.vue";
|
||||||
@@ -61,7 +61,7 @@ const currentRoute = useRoute();
|
|||||||
const expandedMenuIndexes = ref<string[]>([]);
|
const expandedMenuIndexes = ref<string[]>([]);
|
||||||
|
|
||||||
// 获取主题
|
// 获取主题
|
||||||
const theme = computed(() => settingsStore.theme);
|
const theme = computed(() => settingsStore.resolvedTheme);
|
||||||
|
|
||||||
// 获取浅色主题下的侧边栏配色方案
|
// 获取浅色主题下的侧边栏配色方案
|
||||||
const sidebarColorScheme = computed(() => settingsStore.sidebarColorScheme);
|
const sidebarColorScheme = computed(() => settingsStore.sidebarColorScheme);
|
||||||
@@ -69,7 +69,7 @@ const sidebarColorScheme = computed(() => settingsStore.sidebarColorScheme);
|
|||||||
// 菜单主题属性
|
// 菜单主题属性
|
||||||
const menuThemeProps = computed(() => {
|
const menuThemeProps = computed(() => {
|
||||||
const isDarkOrClassicBlue =
|
const isDarkOrClassicBlue =
|
||||||
theme.value === "dark" || sidebarColorScheme.value === SidebarColor.CLASSIC_BLUE;
|
theme.value === ThemeMode.DARK || sidebarColorScheme.value === SidebarColor.CLASSIC_BLUE;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backgroundColor: isDarkOrClassicBlue ? variables["menu-background"] : undefined,
|
backgroundColor: isDarkOrClassicBlue ? variables["menu-background"] : undefined,
|
||||||
|
|||||||
@@ -125,15 +125,15 @@ function handleProfileClick() {
|
|||||||
|
|
||||||
// 根据主题和侧边栏配色方案选择样式类
|
// 根据主题和侧边栏配色方案选择样式类
|
||||||
const navbarActionsClass = computed(() => {
|
const navbarActionsClass = computed(() => {
|
||||||
const { theme, sidebarColorScheme, layout } = settingStore;
|
const { resolvedTheme, sidebarColorScheme, layout } = settingStore;
|
||||||
|
|
||||||
// 暗黑主题下,所有布局都使用白色文字
|
// 暗黑主题下,所有布局都使用白色文字
|
||||||
if (theme === ThemeMode.DARK) {
|
if (resolvedTheme === ThemeMode.DARK) {
|
||||||
return "navbar-actions--white-text";
|
return "navbar-actions--white-text";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 明亮主题下
|
// 明亮主题下
|
||||||
if (theme === ThemeMode.LIGHT) {
|
if (resolvedTheme === ThemeMode.LIGHT) {
|
||||||
// 顶部布局和混合布局的顶部区域:
|
// 顶部布局和混合布局的顶部区域:
|
||||||
// - 如果侧边栏是经典蓝色,使用白色文字
|
// - 如果侧边栏是经典蓝色,使用白色文字
|
||||||
// - 如果侧边栏是极简白色,使用深色文字
|
// - 如果侧边栏是极简白色,使用深色文字
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { SidebarColor, ThemeMode } from "@/enums";
|
import { SidebarColor, ThemeMode } from "@/enums";
|
||||||
import type { LayoutMode } from "@/enums";
|
import type { LayoutMode } from "@/enums";
|
||||||
import { applyTheme, generateThemeColors, toggleDarkMode, toggleSidebarColor } from "@/utils/theme";
|
import {
|
||||||
|
applyTheme,
|
||||||
|
generateThemeColors,
|
||||||
|
resolveThemeMode,
|
||||||
|
toggleDarkMode,
|
||||||
|
toggleSidebarColor,
|
||||||
|
watchSystemTheme,
|
||||||
|
} from "@/utils/theme";
|
||||||
import { STORAGE_KEYS } from "@/constants";
|
import { STORAGE_KEYS } from "@/constants";
|
||||||
import { defaults } from "@/settings";
|
import { defaults } from "@/settings";
|
||||||
|
|
||||||
@@ -25,14 +32,34 @@ export const useSettingsStore = defineStore("setting", () => {
|
|||||||
// 主题
|
// 主题
|
||||||
const theme = useStorage<ThemeMode>(STORAGE_KEYS.THEME, defaults.theme);
|
const theme = useStorage<ThemeMode>(STORAGE_KEYS.THEME, defaults.theme);
|
||||||
const themeColor = useStorage(STORAGE_KEYS.THEME_COLOR, defaults.themeColor);
|
const themeColor = useStorage(STORAGE_KEYS.THEME_COLOR, defaults.themeColor);
|
||||||
|
const resolvedTheme = ref<ThemeMode>(resolveThemeMode(theme.value));
|
||||||
|
|
||||||
// 特殊模式
|
// 特殊模式
|
||||||
const grayMode = useStorage(STORAGE_KEYS.GRAY_MODE, false);
|
const grayMode = useStorage(STORAGE_KEYS.GRAY_MODE, false);
|
||||||
const colorWeak = useStorage(STORAGE_KEYS.COLOR_WEAK, false);
|
const colorWeak = useStorage(STORAGE_KEYS.COLOR_WEAK, false);
|
||||||
|
|
||||||
// 主题变化监听
|
// 主题变化监听
|
||||||
|
let stopWatchingSystemTheme: (() => void) | undefined;
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[theme, themeColor],
|
theme,
|
||||||
|
(value) => {
|
||||||
|
stopWatchingSystemTheme?.();
|
||||||
|
resolvedTheme.value = resolveThemeMode(value);
|
||||||
|
|
||||||
|
if (value === ThemeMode.AUTO) {
|
||||||
|
stopWatchingSystemTheme = watchSystemTheme((systemTheme) => {
|
||||||
|
resolvedTheme.value = systemTheme;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stopWatchingSystemTheme = undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[resolvedTheme, themeColor],
|
||||||
([t, c]: [ThemeMode, string]) => {
|
([t, c]: [ThemeMode, string]) => {
|
||||||
toggleDarkMode(t === ThemeMode.DARK);
|
toggleDarkMode(t === ThemeMode.DARK);
|
||||||
applyTheme(generateThemeColors(c, t));
|
applyTheme(generateThemeColors(c, t));
|
||||||
@@ -87,6 +114,7 @@ export const useSettingsStore = defineStore("setting", () => {
|
|||||||
layout,
|
layout,
|
||||||
themeColor,
|
themeColor,
|
||||||
theme,
|
theme,
|
||||||
|
resolvedTheme,
|
||||||
resetSettings,
|
resetSettings,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { ThemeMode } from "@/enums";
|
import { ThemeMode } from "@/enums";
|
||||||
|
|
||||||
|
const SYSTEM_DARK_MEDIA = "(prefers-color-scheme: dark)";
|
||||||
|
|
||||||
// 辅助函数:将十六进制颜色转换为 RGB
|
// 辅助函数:将十六进制颜色转换为 RGB
|
||||||
function hexToRgb(hex: string): [number, number, number] {
|
function hexToRgb(hex: string): [number, number, number] {
|
||||||
const bigint = parseInt(hex.slice(1), 16);
|
const bigint = parseInt(hex.slice(1), 16);
|
||||||
@@ -52,6 +54,7 @@ export const getLightColor = (color: string, level: number): string => {
|
|||||||
* @param theme 主题类型
|
* @param theme 主题类型
|
||||||
*/
|
*/
|
||||||
export function generateThemeColors(primary: string, theme: ThemeMode) {
|
export function generateThemeColors(primary: string, theme: ThemeMode) {
|
||||||
|
const resolvedTheme = resolveThemeMode(theme);
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
primary,
|
primary,
|
||||||
};
|
};
|
||||||
@@ -59,18 +62,39 @@ export function generateThemeColors(primary: string, theme: ThemeMode) {
|
|||||||
// 生成浅色变体
|
// 生成浅色变体
|
||||||
for (let i = 1; i <= 9; i++) {
|
for (let i = 1; i <= 9; i++) {
|
||||||
colors[`primary-light-${i}`] =
|
colors[`primary-light-${i}`] =
|
||||||
theme === ThemeMode.LIGHT
|
resolvedTheme === ThemeMode.LIGHT
|
||||||
? `${getLightColor(primary, i / 10)}`
|
? `${getLightColor(primary, i / 10)}`
|
||||||
: `${getDarkColor(primary, i / 10)}`;
|
: `${getDarkColor(primary, i / 10)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成深色变体
|
// 生成深色变体
|
||||||
colors["primary-dark-2"] =
|
colors["primary-dark-2"] =
|
||||||
theme === ThemeMode.LIGHT ? `${getLightColor(primary, 0.2)}` : `${getDarkColor(primary, 0.3)}`;
|
resolvedTheme === ThemeMode.LIGHT
|
||||||
|
? `${getLightColor(primary, 0.2)}`
|
||||||
|
: `${getDarkColor(primary, 0.3)}`;
|
||||||
|
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSystemTheme() {
|
||||||
|
return window.matchMedia(SYSTEM_DARK_MEDIA).matches ? ThemeMode.DARK : ThemeMode.LIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveThemeMode(theme: ThemeMode) {
|
||||||
|
return theme === ThemeMode.AUTO ? getSystemTheme() : theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function watchSystemTheme(callback: (theme: ThemeMode) => void) {
|
||||||
|
const mediaQuery = window.matchMedia(SYSTEM_DARK_MEDIA);
|
||||||
|
const handler = () => callback(mediaQuery.matches ? ThemeMode.DARK : ThemeMode.LIGHT);
|
||||||
|
|
||||||
|
mediaQuery.addEventListener("change", handler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mediaQuery.removeEventListener("change", handler);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function applyTheme(colors: Record<string, string>) {
|
export function applyTheme(colors: Record<string, string>) {
|
||||||
const el = document.documentElement;
|
const el = document.documentElement;
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,55 @@ const formComponents = {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.auth-view {
|
.auth-view {
|
||||||
|
--auth-page-bg: #f5f7ff;
|
||||||
|
--auth-overlay: linear-gradient(120deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0));
|
||||||
|
--auth-toolbar-bg: rgba(255, 255, 255, 0.85);
|
||||||
|
--auth-toolbar-border: rgba(22, 93, 255, 0.15);
|
||||||
|
--auth-toolbar-shadow: 0 10px 30px rgba(22, 93, 255, 0.12);
|
||||||
|
--auth-toolbar-hover-shadow: 0 16px 40px rgba(22, 93, 255, 0.18);
|
||||||
|
--auth-toolbar-inner-hover-bg: var(--el-fill-color);
|
||||||
|
--auth-feature-text: var(--el-text-color-primary);
|
||||||
|
--auth-badge-text: rgba(22, 93, 255, 0.95);
|
||||||
|
--auth-badge-bg: rgba(22, 93, 255, 0.1);
|
||||||
|
--auth-dot-bg: #165dff;
|
||||||
|
--auth-dot-shadow: 0 0 12px rgba(22, 93, 255, 0.7);
|
||||||
|
--auth-subtitle-text: var(--el-text-color-regular);
|
||||||
|
--auth-highlight-text: var(--el-text-color-primary);
|
||||||
|
--auth-highlight-bg: rgba(255, 255, 255, 0.9);
|
||||||
|
--auth-highlight-border: rgba(64, 128, 255, 0.08);
|
||||||
|
--auth-highlight-accent: rgba(22, 93, 255, 0.8);
|
||||||
|
--auth-panel-bg: rgba(255, 255, 255, 0.95);
|
||||||
|
--auth-panel-border: rgba(22, 93, 255, 0.1);
|
||||||
|
--auth-panel-shadow:
|
||||||
|
0 16px 48px rgba(22, 93, 255, 0.12), 0 4px 16px rgba(22, 93, 255, 0.08),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.5) inset;
|
||||||
|
--auth-brand-border: rgba(22, 93, 255, 0.06);
|
||||||
|
--auth-panel-title: var(--el-text-color-primary);
|
||||||
|
--auth-version-label: var(--el-text-color-secondary);
|
||||||
|
--auth-version-tag-bg: rgba(22, 93, 255, 0.1);
|
||||||
|
--auth-version-tag-border: rgba(22, 93, 255, 0.12);
|
||||||
|
--auth-version-tag-text: rgba(22, 93, 255, 0.9);
|
||||||
|
--auth-logo-bg: radial-gradient(circle at 30% 20%, #ffffff, #e6efff);
|
||||||
|
--auth-logo-shadow: 0 8px 20px rgba(22, 93, 255, 0.16), 0 0 0 1px rgba(255, 255, 255, 0.8) inset;
|
||||||
|
--auth-footer-border: rgba(22, 93, 255, 0.06);
|
||||||
|
--auth-link: rgba(22, 93, 255, 0.85);
|
||||||
|
--auth-link-hover: rgba(22, 93, 255, 1);
|
||||||
|
--auth-inline-text: var(--el-text-color-regular);
|
||||||
|
--auth-input-bg: rgba(255, 255, 255, 0.92);
|
||||||
|
--auth-input-border: var(--el-border-color);
|
||||||
|
--auth-input-border-hover: var(--el-border-color-hover);
|
||||||
|
--auth-input-border-focus: var(--el-color-primary);
|
||||||
|
--auth-input-text: var(--el-text-color-primary);
|
||||||
|
--auth-input-placeholder: var(--el-text-color-placeholder);
|
||||||
|
--auth-input-icon: var(--el-text-color-secondary);
|
||||||
|
--auth-checkbox-text: var(--el-text-color-regular);
|
||||||
|
--auth-divider: var(--el-border-color-light);
|
||||||
|
--auth-social-text: var(--el-text-color-secondary);
|
||||||
|
--auth-social-hover-bg: var(--el-fill-color);
|
||||||
|
--auth-captcha-shadow: 0 0 0 1px var(--el-border-color) inset;
|
||||||
|
--auth-button-shadow: 0 12px 24px rgba(22, 93, 255, 0.12);
|
||||||
|
--auth-button-success-shadow: 0 12px 24px rgba(35, 166, 111, 0.2);
|
||||||
|
--auth-button-warning-shadow: 0 12px 24px rgba(230, 162, 60, 0.2);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -110,7 +159,7 @@ const formComponents = {
|
|||||||
padding: clamp(1rem, 3vw, 2rem);
|
padding: clamp(1rem, 3vw, 2rem);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background-color: #f5f7ff;
|
background-color: var(--auth-page-bg);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -126,7 +175,62 @@ const formComponents = {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
content: "";
|
content: "";
|
||||||
background: linear-gradient(120deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0));
|
background: var(--auth-overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .auth-view {
|
||||||
|
--auth-page-bg: #08101f;
|
||||||
|
--auth-overlay: linear-gradient(120deg, rgba(7, 12, 24, 0.4), rgba(7, 12, 24, 0));
|
||||||
|
--auth-toolbar-bg: rgba(24, 28, 43, 0.9);
|
||||||
|
--auth-toolbar-border: rgba(64, 128, 255, 0.35);
|
||||||
|
--auth-toolbar-shadow: 0 10px 30px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(90, 140, 255, 0.25) inset;
|
||||||
|
--auth-toolbar-hover-shadow:
|
||||||
|
0 16px 40px rgba(0, 0, 0, 0.72), 0 0 0 1px rgba(96, 148, 255, 0.28) inset;
|
||||||
|
--auth-feature-text: rgba(240, 245, 255, 0.92);
|
||||||
|
--auth-badge-text: rgba(160, 190, 255, 0.95);
|
||||||
|
--auth-badge-bg: rgba(64, 128, 255, 0.12);
|
||||||
|
--auth-dot-bg: #7aa2ff;
|
||||||
|
--auth-subtitle-text: rgba(220, 230, 255, 0.75);
|
||||||
|
--auth-highlight-text: rgba(230, 236, 255, 0.85);
|
||||||
|
--auth-highlight-bg: rgba(18, 22, 36, 0.7);
|
||||||
|
--auth-highlight-border: rgba(98, 149, 255, 0.18);
|
||||||
|
--auth-highlight-accent: rgba(122, 162, 255, 0.9);
|
||||||
|
--auth-panel-bg: rgba(26, 32, 48, 0.9);
|
||||||
|
--auth-panel-border: rgba(86, 140, 255, 0.28);
|
||||||
|
--auth-panel-shadow:
|
||||||
|
0 20px 60px rgba(0, 0, 0, 0.58), 0 4px 16px rgba(0, 0, 0, 0.36),
|
||||||
|
0 0 0 1px rgba(110, 150, 255, 0.16) inset;
|
||||||
|
--auth-brand-border: rgba(64, 128, 255, 0.12);
|
||||||
|
--auth-panel-title: rgba(245, 248, 255, 0.95);
|
||||||
|
--auth-version-label: rgba(186, 202, 236, 0.78);
|
||||||
|
--auth-version-tag-bg: rgba(86, 140, 255, 0.16);
|
||||||
|
--auth-version-tag-border: rgba(104, 156, 255, 0.2);
|
||||||
|
--auth-version-tag-text: rgba(214, 226, 255, 0.96);
|
||||||
|
--auth-logo-bg: radial-gradient(circle at 30% 20%, #1f2438, #141827);
|
||||||
|
--auth-logo-shadow: 0 8px 20px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(90, 140, 255, 0.3) inset;
|
||||||
|
--auth-footer-border: rgba(64, 128, 255, 0.12);
|
||||||
|
--auth-link: rgba(140, 170, 255, 0.88);
|
||||||
|
--auth-link-hover: rgba(160, 190, 255, 1);
|
||||||
|
--auth-inline-text: rgba(208, 220, 244, 0.76);
|
||||||
|
--auth-input-bg: rgba(14, 19, 33, 0.92);
|
||||||
|
--auth-input-border: rgba(104, 132, 194, 0.28);
|
||||||
|
--auth-input-border-hover: rgba(122, 154, 224, 0.4);
|
||||||
|
--auth-input-border-focus: rgba(122, 162, 255, 0.92);
|
||||||
|
--auth-input-text: rgba(240, 245, 255, 0.96);
|
||||||
|
--auth-input-placeholder: rgba(168, 185, 221, 0.56);
|
||||||
|
--auth-input-icon: rgba(168, 186, 227, 0.74);
|
||||||
|
--auth-checkbox-text: rgba(208, 220, 244, 0.78);
|
||||||
|
--auth-divider: rgba(98, 128, 191, 0.24);
|
||||||
|
--auth-social-text: rgba(176, 192, 224, 0.72);
|
||||||
|
--auth-social-hover-bg: rgba(36, 49, 79, 0.78);
|
||||||
|
--auth-captcha-shadow: 0 0 0 1px rgba(104, 132, 194, 0.28) inset;
|
||||||
|
--auth-button-shadow: 0 14px 26px rgba(51, 100, 214, 0.22);
|
||||||
|
--auth-button-success-shadow: 0 14px 26px rgba(34, 149, 108, 0.24);
|
||||||
|
--auth-button-warning-shadow: 0 14px 26px rgba(211, 142, 38, 0.24);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-image: url("@/assets/images/login-bg-dark.svg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,16 +239,16 @@ const formComponents = {
|
|||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
background-color: rgba(255, 255, 255, 0.85);
|
background-color: var(--auth-toolbar-bg);
|
||||||
border: 1px solid rgba(22, 93, 255, 0.15);
|
border: 1px solid var(--auth-toolbar-border);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
box-shadow: 0 10px 30px rgba(22, 93, 255, 0.12);
|
box-shadow: var(--auth-toolbar-shadow);
|
||||||
transition:
|
transition:
|
||||||
transform 0.3s ease,
|
transform 0.3s ease,
|
||||||
box-shadow 0.3s ease;
|
box-shadow 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 16px 40px rgba(22, 93, 255, 0.18);
|
box-shadow: var(--auth-toolbar-hover-shadow);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +262,7 @@ const formComponents = {
|
|||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--el-fill-color);
|
background-color: var(--auth-toolbar-inner-hover-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,20 +274,6 @@ const formComponents = {
|
|||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
background-color: rgba(24, 28, 43, 0.8);
|
|
||||||
border-color: rgba(64, 128, 255, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 应用内暗黑主题下顶部设置面板的深色样式 */
|
|
||||||
.dark .auth-view__toolbar {
|
|
||||||
background-color: rgba(24, 28, 43, 0.9);
|
|
||||||
border-color: rgba(64, 128, 255, 0.35);
|
|
||||||
box-shadow:
|
|
||||||
0 10px 30px rgba(0, 0, 0, 0.7),
|
|
||||||
0 0 0 1px rgba(90, 140, 255, 0.25) inset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-view__wrapper {
|
.auth-view__wrapper {
|
||||||
@@ -200,14 +290,10 @@ const formComponents = {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: clamp(1.5rem, 3vw, 3rem);
|
padding: clamp(1.5rem, 3vw, 3rem);
|
||||||
color: var(--el-text-color-primary);
|
color: var(--auth-feature-text);
|
||||||
animation: featureFade 0.8s ease-out;
|
animation: featureFade 0.8s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .auth-feature {
|
|
||||||
color: rgba(240, 245, 255, 0.92);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.auth-view__wrapper {
|
.auth-view__wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -234,28 +320,19 @@ const formComponents = {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
padding: 0.3rem 0.9rem;
|
padding: 0.3rem 0.9rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: rgba(22, 93, 255, 0.95);
|
color: var(--auth-badge-text);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
background: rgba(22, 93, 255, 0.1);
|
background: var(--auth-badge-bg);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
color: rgba(160, 190, 255, 0.95);
|
|
||||||
background: rgba(64, 128, 255, 0.12);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-feature__dot {
|
.auth-feature__dot {
|
||||||
width: 0.5rem;
|
width: 0.5rem;
|
||||||
height: 0.5rem;
|
height: 0.5rem;
|
||||||
background: #165dff;
|
background: var(--auth-dot-bg);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: 0 0 12px rgba(22, 93, 255, 0.7);
|
box-shadow: var(--auth-dot-shadow);
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
background: #7aa2ff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-feature__title {
|
.auth-feature__title {
|
||||||
@@ -269,11 +346,7 @@ const formComponents = {
|
|||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--auth-subtitle-text);
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
color: rgba(220, 230, 255, 0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-feature__highlights {
|
.auth-feature__highlights {
|
||||||
@@ -289,28 +362,16 @@ const formComponents = {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--auth-highlight-text);
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: var(--auth-highlight-bg);
|
||||||
border: 1px solid rgba(64, 128, 255, 0.08);
|
border: 1px solid var(--auth-highlight-border);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
backdrop-filter: blur(6px);
|
backdrop-filter: blur(6px);
|
||||||
|
|
||||||
span {
|
span {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: rgba(22, 93, 255, 0.8);
|
color: var(--auth-highlight-accent);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
li {
|
|
||||||
color: rgba(230, 236, 255, 0.85);
|
|
||||||
background: rgba(18, 22, 36, 0.7);
|
|
||||||
border-color: rgba(98, 149, 255, 0.18);
|
|
||||||
|
|
||||||
span {
|
|
||||||
color: rgba(122, 162, 255, 0.9);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,34 +387,12 @@ const formComponents = {
|
|||||||
min-height: 560px;
|
min-height: 560px;
|
||||||
padding: clamp(1.5rem, 3vw, 2rem);
|
padding: clamp(1.5rem, 3vw, 2rem);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: var(--auth-panel-bg);
|
||||||
border: 1px solid rgba(22, 93, 255, 0.1);
|
border: 1px solid var(--auth-panel-border);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
box-shadow:
|
box-shadow: var(--auth-panel-shadow);
|
||||||
0 16px 48px rgba(22, 93, 255, 0.12),
|
|
||||||
0 4px 16px rgba(22, 93, 255, 0.08),
|
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.5) inset;
|
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
animation: panelLift 0.7s ease;
|
animation: panelLift 0.7s ease;
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
background: rgba(18, 20, 32, 0.88);
|
|
||||||
border-color: rgba(64, 128, 255, 0.25);
|
|
||||||
box-shadow:
|
|
||||||
0 20px 60px rgba(0, 0, 0, 0.6),
|
|
||||||
0 4px 16px rgba(0, 0, 0, 0.4),
|
|
||||||
0 0 0 1px rgba(90, 140, 255, 0.12) inset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 应用内暗黑主题(例如 html/body 上挂 .dark 类)下的登录表单样式 */
|
|
||||||
.dark .auth-panel {
|
|
||||||
background: rgba(26, 32, 48, 0.9);
|
|
||||||
border-color: rgba(86, 140, 255, 0.28);
|
|
||||||
box-shadow:
|
|
||||||
0 20px 60px rgba(0, 0, 0, 0.58),
|
|
||||||
0 4px 16px rgba(0, 0, 0, 0.36),
|
|
||||||
0 0 0 1px rgba(110, 150, 255, 0.16) inset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-panel__brand {
|
.auth-panel__brand {
|
||||||
@@ -363,11 +402,7 @@ const formComponents = {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-bottom: 0.875rem;
|
padding-bottom: 0.875rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
border-bottom: 1px solid rgba(22, 93, 255, 0.06);
|
border-bottom: 1px solid var(--auth-brand-border);
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
border-color: rgba(64, 128, 255, 0.12);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-panel__logo-wrap {
|
.auth-panel__logo-wrap {
|
||||||
@@ -376,18 +411,9 @@ const formComponents = {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 52px;
|
width: 52px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
background: radial-gradient(circle at 30% 20%, #ffffff, #e6efff);
|
background: var(--auth-logo-bg);
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
box-shadow:
|
box-shadow: var(--auth-logo-shadow);
|
||||||
0 8px 20px rgba(22, 93, 255, 0.16),
|
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.8) inset;
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
background: radial-gradient(circle at 30% 20%, #1f2438, #141827);
|
|
||||||
box-shadow:
|
|
||||||
0 8px 20px rgba(0, 0, 0, 0.7),
|
|
||||||
0 0 0 1px rgba(90, 140, 255, 0.3) inset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-panel__logo {
|
.auth-panel__logo {
|
||||||
@@ -416,7 +442,7 @@ const formComponents = {
|
|||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--auth-panel-title);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +451,16 @@ const formComponents = {
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
|
|
||||||
|
:deep(.el-text) {
|
||||||
|
color: var(--auth-version-label);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag) {
|
||||||
|
color: var(--auth-version-tag-text);
|
||||||
|
background-color: var(--auth-version-tag-bg);
|
||||||
|
border-color: var(--auth-version-tag-border);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-panel__form {
|
.auth-panel__form {
|
||||||
@@ -438,22 +474,91 @@ const formComponents = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-input__wrapper) {
|
:deep(.el-input__wrapper) {
|
||||||
box-shadow: 0 0 0 1px var(--el-border-color) inset;
|
background-color: var(--auth-input-bg);
|
||||||
|
box-shadow: 0 0 0 1px var(--auth-input-border) inset;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 0 0 1px var(--el-border-color-hover) inset;
|
box-shadow: 0 0 0 1px var(--auth-input-border-hover) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-focus {
|
&.is-focus {
|
||||||
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
|
box-shadow: 0 0 0 1px var(--auth-input-border-focus) inset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: var(--auth-input-text);
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--auth-input-placeholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__prefix),
|
||||||
|
:deep(.el-input__suffix),
|
||||||
|
:deep(.el-input__prefix-inner),
|
||||||
|
:deep(.el-input__suffix-inner) {
|
||||||
|
color: var(--auth-input-icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-text) {
|
||||||
|
color: var(--auth-inline-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-link) {
|
||||||
|
color: var(--auth-link);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--auth-link-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-checkbox) {
|
||||||
|
color: var(--auth-checkbox-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-checkbox__label) {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button) {
|
||||||
|
box-shadow: var(--auth-button-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button--success) {
|
||||||
|
box-shadow: var(--auth-button-success-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button--warning) {
|
||||||
|
box-shadow: var(--auth-button-warning-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-card) {
|
:deep(.el-card) {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(img[alt="captchaCode"]),
|
||||||
|
:deep(img[alt="code"]) {
|
||||||
|
box-shadow: var(--auth-captcha-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.third-party-login .divider-line) {
|
||||||
|
background: linear-gradient(to right, transparent, var(--auth-divider), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.third-party-login .divider-text) {
|
||||||
|
color: var(--auth-inline-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.third-party-login .social-login) {
|
||||||
|
color: var(--auth-social-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.third-party-login .social-login__item:hover) {
|
||||||
|
background-color: var(--auth-social-hover-bg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-panel__footer {
|
.auth-panel__footer {
|
||||||
@@ -461,28 +566,16 @@ const formComponents = {
|
|||||||
margin-top: 0.125rem;
|
margin-top: 0.125rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-top: 1px solid rgba(22, 93, 255, 0.06);
|
border-top: 1px solid var(--auth-footer-border);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
color: rgba(22, 93, 255, 0.85);
|
color: var(--auth-link);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(22, 93, 255, 1);
|
color: var(--auth-link-hover);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
border-color: rgba(64, 128, 255, 0.12);
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: rgba(140, 170, 255, 0.88);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: rgba(160, 190, 255, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user