feat: add remote theme override support
实现了全量的主题远端覆写功能,包含: 1. 新增主题合并工具类,支持多桶安全合并与原型链过滤 2. 新增主题远端覆写运行时,支持多租户、多端、三态缓存 3. 改造Vuex主题加载逻辑,实现本地预设+远端覆写的合并应用 4. 改造App.vue首屏主题加载流程,支持并行拉取与缓存预热 5. 新增主题更新、租户切换的事件监听与自动刷新逻辑
This commit is contained in:
@@ -6,6 +6,8 @@ import Http from '../common/js/http.js'
|
||||
import { themeConfig } from '../common/js/config-external.js'
|
||||
import configExternal from '../common/js/config-external.js'
|
||||
import util from '../common/js/util.js'
|
||||
import ThemeOverride from '../common/js/theme-override.js'
|
||||
import { sanitizeFlatMap, mergeThemeBuckets } from '../common/js/merge-theme.js'
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
@@ -266,9 +268,9 @@ const store = new Vuex.Store({
|
||||
success: async res => {
|
||||
var data = res.data;
|
||||
if (data) {
|
||||
const themeData = await configExternal.loadTheme(data.style_theme?.name);
|
||||
this.commit('setThemeStyle', themeData);
|
||||
this.dispatch('themeColorSet');
|
||||
// 走新 action: 加载预设 + 远端覆写合并 + 应用
|
||||
// (不直接 commit setThemeStyle + themeColorSet,避免漏远端)
|
||||
await this.dispatch('applyThemeWithRemote', data.style_theme?.name || themeConfig.defaultTheme);
|
||||
|
||||
// 底部导航
|
||||
this.commit('setTabBarList', data.diy_bottom_nav);
|
||||
@@ -443,6 +445,54 @@ const store = new Vuex.Store({
|
||||
} catch (e) {
|
||||
console.error('设置主题颜色失败', e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 加载预设 + 拉远端覆写 + 深合并 + 走原 setThemeStyle/themeColorSet 链路
|
||||
* 关键: 应用层一行不动 — Vuex.themeStyle / themeColor / 页面 wrapper / CSS var
|
||||
* 全部保持兼容,所有改动只发生在"加载"环节
|
||||
*
|
||||
* @param {string} presetName style_color.js 中的 preset 名(default/red/green/...)
|
||||
* @returns {Promise<{ ok: boolean, source: string, applied: object }>}
|
||||
*/
|
||||
async applyThemeWithRemote({ state, commit, dispatch }, presetName) {
|
||||
const finalPresetName = presetName || themeConfig.defaultTheme;
|
||||
try {
|
||||
// 1) 加载本地预设(style_color.js)
|
||||
const base = await configExternal.loadTheme(finalPresetName);
|
||||
if (!base) {
|
||||
console.warn('[theme] loadTheme 返回空:', finalPresetName);
|
||||
return { ok: false, source: 'empty', applied: null };
|
||||
}
|
||||
|
||||
// 2) 拉远端 brand 桶(失败静默,使用本地缓存或空)
|
||||
const brandOverrides = ThemeOverride.getByTheme('brand') || {};
|
||||
|
||||
// 3) 浅合并(主题 token 是扁平叶子,不需要深合并)
|
||||
// preset 先, brand 后 — 远端覆盖本地
|
||||
const merged = mergeThemeBuckets(
|
||||
sanitizeFlatMap(base),
|
||||
sanitizeFlatMap(brandOverrides)
|
||||
);
|
||||
|
||||
// 4) 走原链路(完全不动下游) — Vuex.themeStyle = 合并后对象
|
||||
commit('setThemeStyle', merged);
|
||||
dispatch('themeColorSet');
|
||||
return { ok: true, source: 'merged', applied: merged };
|
||||
} catch (e) {
|
||||
console.error('[theme] applyThemeWithRemote 失败,回退到纯预设:', e);
|
||||
// 兜底: 至少把预设应用了,避免页面无主题
|
||||
try {
|
||||
const base = await configExternal.loadTheme(finalPresetName);
|
||||
if (base) {
|
||||
commit('setThemeStyle', base);
|
||||
dispatch('themeColorSet');
|
||||
return { ok: false, source: 'preset-only-fallback', applied: base };
|
||||
}
|
||||
} catch (e2) {
|
||||
console.error('[theme] 纯预设回退也失败:', e2);
|
||||
}
|
||||
return { ok: false, source: 'fail', applied: null };
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user