const BackgroundManager = (function () { const CACHE_KEY = 'background_cache'; const LAST_UPDATE_KEY = 'background_last_update'; const CACHE_EXPIRE_DAYS = 30; const CONFIG_ENDPOINT = '/api/calendar/sys-config'; let configCache = null; const UNSPLASH_ACCESS_KEY = 'osWW4Fx0IbpfJs2LbCl_Jpv5ej-6ZaphyIX9sgdPdxI'; const PICSUM_BASE_URL = 'https://picsum.photos'; const CHINESE_THEME_KEYWORDS = [ 'chinese landscape', 'chinese painting', 'bamboo', 'lotus', 'plum blossom', 'orchid', 'chrysanthemum', 'mountain water', 'ink wash', 'traditional chinese', 'landscape painting', 'watercolor', 'minimalist', 'serene', 'nature' ]; async function loadConfig() { if (configCache) return configCache; try { const res = await fetch(CONFIG_ENDPOINT, { cache: 'no-store' }); if (!res.ok) { configCache = {}; return configCache; } const data = await res.json().catch(() => ({})); const cfg = data && data.config && typeof data.config === 'object' ? data.config : {}; configCache = cfg; return cfg; } catch (_) { configCache = {}; return configCache; } } async function getLocalPoolBackground(cfg) { try { const theme = String((cfg && (cfg.background_theme || cfg.backgroundTheme)) || '默认背景'); const params = new URLSearchParams(); params.set('theme', theme); params.set('prefer_local', '1'); const res = await fetch('/api/home/bg?' + params.toString(), { cache: 'no-store' }); if (!res.ok) return ''; const data = await res.json().catch(() => ({})); const url = data && typeof data.url === 'string' ? data.url : ''; return url || ''; } catch (_) { return ''; } } async function getAllPoolBackground() { const themes = [ '夜空星轨', '蓝天白云', '治愈森林', '运动风', '科技霓虹', '商务简约', '暖色日落', '冷色极夜' ]; const theme = themes[Math.floor(Math.random() * themes.length)]; try { const params = new URLSearchParams(); params.set('theme', theme); params.set('prefer_local', '1'); const res = await fetch('/api/home/bg?' + params.toString(), { cache: 'no-store' }); if (!res.ok) return ''; const data = await res.json().catch(() => ({})); const url = data && typeof data.url === 'string' ? data.url : ''; return url || ''; } catch (_) { return ''; } } async function getDailyBackground() { const cfg = await loadConfig(); const today = new Date().toDateString(); const cached = getCachedBackground(); if (cached && cached.date === today && cached.url) { return cached.url; } const enableDynamic = cfg && Object.prototype.hasOwnProperty.call(cfg, 'ENABLE_DYNAMIC_BACKGROUND') ? cfg.ENABLE_DYNAMIC_BACKGROUND !== false : true; const modeRaw = cfg && cfg.background_mode ? String(cfg.background_mode) : ''; const mode = modeRaw.toUpperCase(); const useAllPool = mode === 'ALL_POOL'; if (!enableDynamic) { const url = getDefaultBackground(); cacheBackground(url, today); return url; } if (useAllPool) { const allUrl = await getAllPoolBackground(); if (allUrl) { cacheBackground(allUrl, today); return allUrl; } const fallback = getDefaultBackground(); cacheBackground(fallback, today); return fallback; } if (cfg && (cfg.background_theme || cfg.backgroundTheme)) { const localUrl = await getLocalPoolBackground(cfg); if (localUrl) { cacheBackground(localUrl, today); return localUrl; } const fallback = getDefaultBackground(); cacheBackground(fallback, today); return fallback; } try { const unsplashUrl = await tryUnsplash(); if (unsplashUrl) { cacheBackground(unsplashUrl, today); return unsplashUrl; } } catch (error) { console.warn('Unsplash API failed:', error); } if (cached && cached.url) return cached.url; try { const picsumUrl = await tryPicsum(); if (picsumUrl) { cacheBackground(picsumUrl, today); return picsumUrl; } } catch (error) { console.warn('Picsum API failed:', error); } const fallback = getDefaultBackground(); cacheBackground(fallback, today); return fallback; } async function tryUnsplash() { if (!UNSPLASH_ACCESS_KEY || UNSPLASH_ACCESS_KEY === 'your_unsplash_access_key_here') { return null; } const randomKeyword = CHINESE_THEME_KEYWORDS[Math.floor(Math.random() * CHINESE_THEME_KEYWORDS.length)]; const url = `https://api.unsplash.com/photos/random?query=${encodeURIComponent(randomKeyword)}&orientation=landscape&client_id=${UNSPLASH_ACCESS_KEY}`; try { const response = await fetch(url, { method: 'GET', headers: { 'Accept': 'application/json', 'Accept-Version': 'v1' } }); if (!response.ok) { console.warn('Unsplash API error:', response.status, response.statusText); return null; } const data = await response.json(); return data.urls.regular; } catch (error) { console.warn('Unsplash fetch failed:', error); return null; } } async function tryPicsum() { const width = window.innerWidth; const height = window.innerHeight; const imageId = Math.floor(Math.random() * 1000); return `${PICSUM_BASE_URL}/${width}/${height}?blur=2&image=${imageId}`; } function getDefaultBackground() { return 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; } function getCachedBackground() { try { const cached = localStorage.getItem(CACHE_KEY); const lastUpdate = localStorage.getItem(LAST_UPDATE_KEY); if (!cached || !lastUpdate) return null; // 检查缓存是否过期(30天) const updateDate = new Date(lastUpdate); const expireDate = new Date(updateDate); expireDate.setDate(expireDate.getDate() + CACHE_EXPIRE_DAYS); if (new Date() > expireDate) { localStorage.removeItem(CACHE_KEY); localStorage.removeItem(LAST_UPDATE_KEY); return null; } return JSON.parse(cached); } catch (error) { console.warn('Failed to read cache:', error); return null; } } function cacheBackground(url, date) { try { const cacheData = { url, date }; localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData)); localStorage.setItem(LAST_UPDATE_KEY, new Date().toISOString()); } catch (error) { console.warn('Failed to cache background:', error); } } function applyBackground(url) { const body = document.body; let bgContainer = document.getElementById('background-container'); if (!bgContainer) { bgContainer = document.createElement('div'); bgContainer.id = 'background-container'; const isImage = typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')); bgContainer.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; background: ${isImage ? `url(${url}) center/cover no-repeat` : url}; opacity: 0.15; filter: blur(8px); `; document.body.appendChild(bgContainer); } else { const isImage = typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')); bgContainer.style.background = isImage ? `url(${url}) center/cover no-repeat` : url; } body.style.background = 'transparent'; const importantElements = document.querySelectorAll('.card, .calendar-grid, .navbar'); importantElements.forEach(el => { el.style.backgroundColor = 'rgba(255, 255, 255, 0.95)'; el.style.backdropFilter = 'blur(10px)'; }); } async function init() { try { const backgroundUrl = await getDailyBackground(); applyBackground(backgroundUrl); } catch (error) { console.error('Failed to set background:', error); applyBackground(getDefaultBackground()); } } return { init, refresh: init }; })(); // 页面加载时初始化 window.addEventListener('load', () => { BackgroundManager.init(); }); // 导出供其他模块使用 if (typeof module !== 'undefined' && module.exports) { module.exports = BackgroundManager; }