/* 主题系统:账户独立主题(dark/light/auto)。 * * - 后端 GET /api/theme 返回 {mode, effective};auto 已解析为 dark/light * (按用户经纬度+时区计算日出日落)。 * - 应用方式:html 元素加 .theme-light class(CSS 变量覆盖浅色)。 * - 切换动画:切换瞬间给 html 加 .theme-anim,触发全局 CSS transition * 平滑渐变,平时无全局 transition(不影响交互反馈)。 * - auto 模式每 5 分钟轮询一次(日落日出边界切换);显示端还会在收到 * SSE theme_changed 时立即刷新。 */ (function (global) { 'use strict'; var current = 'dark'; var pollTimer = null; function apply(effective) { if (!effective) return; if (effective === current) return; current = effective; var root = document.documentElement; root.classList.add('theme-anim'); root.classList.toggle('theme-light', effective === 'light'); // 过渡结束后移除动画 class,恢复常规交互反馈 setTimeout(function () { root.classList.remove('theme-anim'); }, 600); } function fetchTheme(cb) { // noRedirect: 登录页/未登录时 401 静默失败(保持默认主题),绝不跳转造成循环 Auth.request('GET', '/api/theme', null, function (d, status) { if (status === 200 && d.effective) apply(d.effective); if (cb) cb(d); }, { noRedirect: true }); } function initTheme() { fetchTheme(); if (!pollTimer) { // auto 模式日落/日出切换:每 5 分钟刷新一次 pollTimer = setInterval(fetchTheme, 5 * 60 * 1000); } } global.ThemeBridge = { apply: apply, fetch: fetchTheme, init: initTheme, getCurrent: function () { return current; } }; // 页面加载即应用主题 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initTheme); } else { initTheme(); } })(window);