import { getCache, setCache } from '@/utils/cache' import { getThemeApi } from '@/api/page/theme.js' /** * 主题运行时:缓存 css_vars + tokens + layout * 页面通过 mixin / Theme* 组件消费,禁止按 template.code 特判 */ const STORAGE_KEY = 'theme_tokens' const STORAGE_VARS_KEY = 'theme_css_vars' const STORAGE_LAYOUT_KEY = 'theme_layout' const STORAGE_META_KEY = 'theme_meta' const STORAGE_SCHEMES_KEY = 'theme_card_schemes' const STORAGE_FEATURES_KEY = 'theme_features' export const FALLBACK_VARS = { '--color-primary': '#B08D57', '--color-primary-soft': '#d9b97f', '--color-accent': '#8C6A3F', '--color-bg': '#FAF7F2', '--color-bg-soft': '#f5f2ec', '--color-surface': '#ffffff', '--color-surface-soft': '#fafafa', '--color-text': '#1C1917', '--color-text-soft': '#666666', '--color-text-muted': '#999999', '--color-border': '#E7DFD3', '--color-price': '#B08D57', '--radius-md': '16rpx', '--radius-lg': '20rpx', '--shadow-md': '0 8rpx 24rpx rgba(0,0,0,0.06)', '--motion-base': '320ms', '--motion-easing': 'cubic-bezier(0.22,1,0.36,1)', } export const FALLBACK_LAYOUT = { home: { hero: 'carousel', category: 'grid', product: 'grid' }, product: { gallery: 'swiper', price: 'inline', action: 'fixed' }, list: { style: 'card' }, mine: { header: 'gradient', menu: 'list' }, package: { list: 'card' }, effect: { transition: 'fade', skeleton: 'shimmer', card: 'elevated', icon: 'line', }, card_scheme: 'champagne-foil', chrome: { tabbar: 'plain', navbar: 'solid', tabbar_anim: 'slide' }, page_presets: {}, pages: {}, } /** 拉取并落地主题 */ export async function loadAndApplyTheme() { let theme = null try { // eslint-disable-next-line no-console console.log('[theme] GET theme …') theme = await getThemeApi() // eslint-disable-next-line no-console console.log('[theme] ok', theme && theme.code, theme && theme.fallback) } catch (e) { // eslint-disable-next-line no-console console.warn('[theme] request failed', e) } if (!theme || !theme.css_vars) { theme = { code: '', name: '', css_vars: getCache(STORAGE_VARS_KEY) || FALLBACK_VARS, tokens: getCache(STORAGE_KEY) || {}, layout: getCache(STORAGE_LAYOUT_KEY) || FALLBACK_LAYOUT, card_schemes: getCache(STORAGE_SCHEMES_KEY) || {}, features: getThemeFeatures(), fallback: true, } } applyTheme(theme) return theme } export function applyTheme(theme) { if (!theme) return const vars = { ...FALLBACK_VARS, ...(theme.css_vars || {}) } const layout = mergeLayout(theme.layout) setCache(STORAGE_VARS_KEY, vars) setCache(STORAGE_LAYOUT_KEY, layout) if (theme.tokens) { setCache(STORAGE_KEY, theme.tokens) } setCache(STORAGE_META_KEY, { code: theme.code || '', name: theme.name || '', style_tag: theme.style_tag || '', fallback: !!theme.fallback, }) setCache(STORAGE_SCHEMES_KEY, theme.card_schemes || {}) setCache(STORAGE_FEATURES_KEY, { package_enabled: !!(theme.features && theme.features.package_enabled), subscribe_pay_tpl: (theme.features && theme.features.subscribe_pay_tpl) || '', subscribe_ship_tpl: (theme.features && theme.features.subscribe_ship_tpl) || '', }) applyVarsToDom(vars) } function mergeLayout(layout) { const src = layout && typeof layout === 'object' ? layout : {} const chrome = src.chrome && typeof src.chrome === 'object' ? src.chrome : {} return { home: { ...FALLBACK_LAYOUT.home, ...(src.home || {}) }, product: { ...FALLBACK_LAYOUT.product, ...(src.product || {}) }, list: { ...FALLBACK_LAYOUT.list, ...(src.list || {}) }, mine: { ...FALLBACK_LAYOUT.mine, ...(src.mine || {}) }, package: { ...FALLBACK_LAYOUT.package, ...(src.package || {}) }, effect: { ...FALLBACK_LAYOUT.effect, ...(src.effect || {}) }, card_scheme: src.card_scheme || FALLBACK_LAYOUT.card_scheme, chrome: { tabbar: chrome.tabbar || 'plain', navbar: chrome.navbar || 'solid', tabbar_anim: chrome.tabbar_anim || 'slide', tabbar_color: chrome.tabbar_color || '', tabbar_color_on: chrome.tabbar_color_on || '', }, page_presets: src.page_presets && typeof src.page_presets === 'object' ? src.page_presets : {}, pages: src.pages && typeof src.pages === 'object' ? src.pages : {}, } } export function applyVarsToDom(vars) { if (!vars) return // #ifdef H5 try { const root = document.documentElement Object.entries(vars).forEach(([k, v]) => { root.style.setProperty(k, String(v)) }) } catch (e) { // ignore } // #endif } export function getActiveVars() { return getCache(STORAGE_VARS_KEY) || FALLBACK_VARS } export function getActiveLayout() { return mergeLayout(getCache(STORAGE_LAYOUT_KEY) || FALLBACK_LAYOUT) } export function getActiveTokens() { return getCache(STORAGE_KEY) || {} } export function getThemeMeta() { return getCache(STORAGE_META_KEY) || { code: '', name: '', style_tag: '', fallback: true } } /** 完整主题快照(页面调试 / 组件用) */ export function getActiveSchemes() { return getCache(STORAGE_SCHEMES_KEY) || {} } /** 应用级开关,跟主题一起缓存,底栏/首页热门套餐都读这里 */ export function getThemeFeatures() { const cached = getCache(STORAGE_FEATURES_KEY) || {} return { package_enabled: !!cached.package_enabled, subscribe_pay_tpl: cached.subscribe_pay_tpl || '', subscribe_ship_tpl: cached.subscribe_ship_tpl || '', } } export function getTheme() { return { ...getThemeMeta(), css_vars: getActiveVars(), tokens: getActiveTokens(), layout: getActiveLayout(), card_schemes: getActiveSchemes(), features: getThemeFeatures(), } } export function varsToStyleString(vars) { const obj = vars || getActiveVars() return Object.entries(obj) .map(([k, v]) => `${k}: ${v}`) .join('; ') }