/** * 管理端主题状态(yh-theme,定稿 07-seam-pure.html,方案《管理端整体主题迁移方案》§6.5)。 * 档位全部挂在 :data-theme × side-deep|side-light × grad-on|grad-off × side-plain, * 皮肤规则在 assets/styles/yh-theme.css(生成物)。与门户皮肤包(templates/themes)正交。 */ export const THEME_NAMES = ["blue", "ocean", "ink", "violet", "teal"] as const; export type ThemeName = (typeof THEME_NAMES)[number]; /** 定稿默认档:blue + side-deep + grad-on + 侧栏跟随(D1 拍板 deep) */ export interface ThemeState { theme: ThemeName; /** 侧栏深色(false=浅色档) */ sideDeep: boolean; /** 顶栏渐变(联动侧栏竖向渐变) */ gradOn: boolean; /** 侧栏颜色跟随顶栏品牌色(false=中性灰 side-plain) */ sideFollow: boolean; } export const THEME_STORAGE_KEY = "yh-theme"; export const DEFAULT_THEME_STATE: ThemeState = { theme: "blue", sideDeep: true, gradOn: true, sideFollow: true, }; /** 宽松解析:任一字段非法/缺失即回退默认档(脏数据不阻塞启动) */ export function parseThemeState(raw: string | null): ThemeState { if (!raw) return { ...DEFAULT_THEME_STATE }; try { const o = JSON.parse(raw); if (!o || typeof o !== "object") return { ...DEFAULT_THEME_STATE }; return { theme: THEME_NAMES.includes(o.theme) ? o.theme : DEFAULT_THEME_STATE.theme, sideDeep: typeof o.sideDeep === "boolean" ? o.sideDeep : DEFAULT_THEME_STATE.sideDeep, gradOn: typeof o.gradOn === "boolean" ? o.gradOn : DEFAULT_THEME_STATE.gradOn, sideFollow: typeof o.sideFollow === "boolean" ? o.sideFollow : DEFAULT_THEME_STATE.sideFollow, }; } catch { return { ...DEFAULT_THEME_STATE }; } } interface StorageLike { getItem(k: string): string | null; setItem(k: string, v: string): void; } export function loadThemeState(storage: StorageLike | undefined = globalThis.localStorage): ThemeState { if (!storage) return { ...DEFAULT_THEME_STATE }; return parseThemeState(storage.getItem(THEME_STORAGE_KEY)); } export function saveThemeState( state: ThemeState, storage: StorageLike | undefined = globalThis.localStorage ): void { try { storage?.setItem(THEME_STORAGE_KEY, JSON.stringify(state)); } catch { /* 隐私模式/配额失败静默:仅丢持久化,不丢本次会话生效 */ } } /** 把状态落到根元素档位类名(幂等:先清后挂),返回传入的 state 便于链式 */ export function applyThemeState( state: ThemeState, root: HTMLElement = document.documentElement ): ThemeState { root.dataset.theme = state.theme; root.classList.toggle("side-deep", state.sideDeep); root.classList.toggle("side-light", !state.sideDeep); root.classList.toggle("grad-on", state.gradOn); root.classList.toggle("grad-off", !state.gradOn); root.classList.toggle("side-plain", !state.sideFollow); return state; } /** 启动引导:main.ts 于挂载前调用,消除首帧闪色 */ export function initTheme(): ThemeState { return applyThemeState(loadThemeState()); }