新增石米等淡色背景预设并设为默认,修复页面配置抽屉偶发不弹出问题,移除登录欢迎卡;同步 IDE 官网导航与背景样式,并扩展管理端工作台统计展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
182 lines
6.0 KiB
TypeScript
182 lines
6.0 KiB
TypeScript
/**
|
||
* 主题色工具:根据取色器选中的主色,计算 hover/light 并写入 :root CSS 变量
|
||
* 用于导航栏、侧边菜单、按钮等公共颜色统一切换
|
||
*/
|
||
|
||
const STORAGE_KEY = 'app-theme-color';
|
||
const THEME_MODE_KEY = 'app-theme-mode';
|
||
|
||
/** 旧默认色:切到新主色时自动迁移,避免 localStorage 卡住旧主题 */
|
||
const LEGACY_THEME_COLORS = new Set([
|
||
'#132c44',
|
||
'#132C44',
|
||
'#2d8c8c',
|
||
'#2D8C8C',
|
||
'#007979',
|
||
'#1d7281',
|
||
'#1D7281',
|
||
'#1e7fa0',
|
||
'#1E7FA0',
|
||
]);
|
||
|
||
export const DEFAULT_THEME_COLOR = '#054f74';
|
||
export type ThemeMode = 'light' | 'dark';
|
||
|
||
export function getStoredThemeColor(): string | null {
|
||
try {
|
||
const stored = localStorage.getItem(STORAGE_KEY);
|
||
if (!stored) return null;
|
||
if (LEGACY_THEME_COLORS.has(stored)) {
|
||
localStorage.setItem(STORAGE_KEY, DEFAULT_THEME_COLOR);
|
||
return DEFAULT_THEME_COLOR;
|
||
}
|
||
return stored;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
export function setStoredThemeColor(hex: string): void {
|
||
try {
|
||
localStorage.setItem(STORAGE_KEY, hex);
|
||
} catch {
|
||
// ignore
|
||
}
|
||
}
|
||
|
||
export function getStoredThemeMode(): ThemeMode | null {
|
||
try {
|
||
const stored = localStorage.getItem(THEME_MODE_KEY);
|
||
if (stored === 'light' || stored === 'dark') return stored;
|
||
return null;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
export function setStoredThemeMode(mode: ThemeMode): void {
|
||
try {
|
||
localStorage.setItem(THEME_MODE_KEY, mode);
|
||
} catch {
|
||
// ignore
|
||
}
|
||
}
|
||
|
||
/** 将 hex 转为 r,g,b (0-255) */
|
||
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
||
const m = hex.replace(/^#/, '').match(/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
|
||
if (!m) return null;
|
||
return {
|
||
r: parseInt(m[1], 16),
|
||
g: parseInt(m[2], 16),
|
||
b: parseInt(m[3], 16),
|
||
};
|
||
}
|
||
|
||
/** 将 r,g,b 转为 #rrggbb */
|
||
function rgbToHex(r: number, g: number, b: number): string {
|
||
return (
|
||
'#' +
|
||
[r, g, b]
|
||
.map((x) => Math.round(Math.max(0, Math.min(255, x))).toString(16).padStart(2, '0'))
|
||
.join('')
|
||
);
|
||
}
|
||
|
||
/** 在 hex 颜色基础上按比例变暗(用于 hover) */
|
||
function darkenHex(hex: string, ratio: number): string {
|
||
const rgb = hexToRgb(hex);
|
||
if (!rgb) return hex;
|
||
return rgbToHex(rgb.r * (1 - ratio), rgb.g * (1 - ratio), rgb.b * (1 - ratio));
|
||
}
|
||
|
||
/** 生成浅色背景(用于 menu hover 等) */
|
||
function lightenHex(hex: string, mixWhite = 0.92): string {
|
||
const rgb = hexToRgb(hex);
|
||
if (!rgb) return hex;
|
||
const r = Math.round(rgb.r * (1 - mixWhite) + 255 * mixWhite);
|
||
const g = Math.round(rgb.g * (1 - mixWhite) + 255 * mixWhite);
|
||
const b = Math.round(rgb.b * (1 - mixWhite) + 255 * mixWhite);
|
||
return rgbToHex(r, g, b);
|
||
}
|
||
|
||
/**
|
||
* 将主题色应用到页面:设置 :root 上的 CSS 变量
|
||
* @param primaryHex 主色,如 #1d7281
|
||
*/
|
||
export function applyThemeColor(primaryHex: string): void {
|
||
const hex = primaryHex.startsWith('#') ? primaryHex : `#${primaryHex}`;
|
||
const root = document.documentElement;
|
||
const rgb = hexToRgb(hex);
|
||
const isDark = document.body.getAttribute('arco-theme') === 'dark';
|
||
|
||
const primary = hex;
|
||
const primaryHover = darkenHex(hex, 0.05);
|
||
const primaryLight = lightenHex(hex, 0.92);
|
||
|
||
root.style.setProperty('--app-primary-color', primary);
|
||
root.style.setProperty('--app-primary-color-hover', primaryHover);
|
||
root.style.setProperty('--app-primary-color-light', primaryLight);
|
||
root.style.setProperty('--color-primary-light-1', primaryLight);
|
||
|
||
// 顶栏:日间用主色;夜间用深色壳(由 applyThemeMode / 下方分支写入)
|
||
if (isDark) {
|
||
applyNavbarDarkTokens();
|
||
} else {
|
||
root.style.setProperty('--app-navbar-bg', primary);
|
||
root.style.setProperty('--app-navbar-item-active-bg', lightenHex(hex, 0.16));
|
||
root.style.setProperty('--app-sidebar-bg', primary);
|
||
}
|
||
|
||
// 同步 Arco Design primary 色阶,保证按钮/链接等组件跟主题一致
|
||
if (rgb) {
|
||
const rgbStr = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
|
||
const hover = hexToRgb(primaryHover);
|
||
const light = hexToRgb(lightenHex(hex, 0.15));
|
||
root.style.setProperty('--primary-6', rgbStr);
|
||
root.style.setProperty('--primary-7', hover ? `${hover.r}, ${hover.g}, ${hover.b}` : rgbStr);
|
||
root.style.setProperty('--primary-5', light ? `${light.r}, ${light.g}, ${light.b}` : rgbStr);
|
||
root.style.setProperty('--arcoblue-6', rgbStr);
|
||
}
|
||
}
|
||
|
||
/** 夜间顶栏 / 侧栏表面色(与内容区一致的深色壳,选中胶囊带主色) */
|
||
function applyNavbarDarkTokens(): void {
|
||
const root = document.documentElement;
|
||
root.style.setProperty('--app-navbar-bg', 'var(--color-bg-2)');
|
||
root.style.setProperty(
|
||
'--app-navbar-item-active-bg',
|
||
'color-mix(in srgb, var(--app-primary-color) 34%, var(--color-bg-3))',
|
||
);
|
||
root.style.setProperty('--app-sidebar-bg', 'var(--color-bg-2)');
|
||
}
|
||
|
||
/** 将日间/夜间模式应用到 DOM */
|
||
export function applyThemeMode(mode: ThemeMode): void {
|
||
const root = document.documentElement;
|
||
if (mode === 'dark') {
|
||
document.body.setAttribute('arco-theme', 'dark');
|
||
root.setAttribute('data-theme', 'dark');
|
||
// 同步写到 :root,避免仅依赖 body 变量时列表壳仍读到 global.less 的浅色默认值
|
||
root.style.setProperty('--app-page-bg', 'var(--color-bg-1)');
|
||
root.style.setProperty('--app-card-bg', 'var(--color-bg-2)');
|
||
root.style.setProperty('--app-card-border', 'var(--color-border-2)');
|
||
root.style.setProperty('--app-elevated-bg', 'var(--color-bg-3)');
|
||
applyNavbarDarkTokens();
|
||
} else {
|
||
document.body.removeAttribute('arco-theme');
|
||
root.setAttribute('data-theme', 'light');
|
||
root.style.removeProperty('--app-page-bg');
|
||
root.style.removeProperty('--app-card-bg');
|
||
root.style.removeProperty('--app-card-border');
|
||
root.style.removeProperty('--app-elevated-bg');
|
||
// 恢复日间主色顶栏
|
||
const primary =
|
||
getComputedStyle(root).getPropertyValue('--app-primary-color').trim() ||
|
||
DEFAULT_THEME_COLOR;
|
||
root.style.setProperty('--app-navbar-bg', primary);
|
||
root.style.setProperty('--app-navbar-item-active-bg', lightenHex(primary, 0.16));
|
||
root.style.setProperty('--app-sidebar-bg', primary);
|
||
}
|
||
}
|