feat(theme): CMS 站点主题弱联动 + campus-green 色值对齐(T3)——新增 utils/cmsTheme 映射适配器(SKIN_MAP/PARAM_ALIASE/colorScheme 预设,未映射一律 null 回落);App.vue 皮肤三层优先级 stale-while-revalidate(缓存先画、后台刷新、失败静默);svc.ts 加 hallApi.site;campus-green 四色与 CMS campus-glow 同源对齐并升 1.1.0;theme-base.css 克制补丁 view-ui 实际使用的 Checkbox/Radio/Page/Modal.confirm/dyn-file-btn 交互态

This commit is contained in:
chenxing
2026-10-03 13:52:56 +08:00
parent f636075a5e
commit 41b79566c1
6 changed files with 136 additions and 15 deletions
+42 -4
View File
@@ -5,23 +5,31 @@
* 与后台前端(front/)刻意不同:不渲染平台模块菜单(app_module 是管理端契约),
* 只做两个入口——办事大厅、我的办件(详情/办理页从列表进入)。
*
* 皮肤:启动时按运行时配置(env-config 的 theme/themeParams)应用皮肤包;
* 未配置 → default;未注册 → default(整页回退,见 utils/theme.ts)。
* 皮肤三层优先级(T3 弱联动,2026-10-03):
* CMS 站点推荐(/api/hall/site 的 theme/themeParams,经 utils/cmsTheme 映射;
* stale-while-revalidate——localStorage 缓存先画、后台刷新,失败静默)
* > env-config 部署级皮肤 > default。
* 推荐值、不保证生效:任一层缺失/非法都回落下一层(utils/theme.ts)。
*/
import { onMounted, ref } from "vue";
import { baseApi } from "@/api/module/base";
import config from "@/assets/config/host.config";
import { applyTheme, resolveTheme, type ThemeManifest } from "@/utils/theme";
import { toHallSkin } from "@/utils/cmsTheme";
import { useSite } from "@/utils/site";
import { hallApi } from "@/api/module/svc";
const userName = ref("");
const theme = ref<ThemeManifest>(resolveTheme(null));
// CMS 指南页来路(T4 回跳闭环):仅 /#/apply 深链带合法 from 参数时非空
const { portalFrom } = useSite();
const { portalFrom, currentSiteCode } = useSite();
onMounted(async () => {
// 皮肤先应用(首屏观感),再拉用户信息——用户信息失败不影响办理页面使用
// ① 基线:env-config 部署级皮肤(现状语义不变,先应用保证首屏观感)
theme.value = applyTheme(config.theme, config.themeParams);
// ② 站点推荐:CMS 站点主题(弱联动)——缓存先画,后台刷新
applySiteThemeRecommend();
// 用户信息失败不影响办理页面使用
try {
const user: any = await baseApi.getUserInfo().send();
userName.value = user?.name || "";
@@ -32,6 +40,36 @@ onMounted(async () => {
}
}
});
/**
* 站点主题推荐应用(stale-while-revalidate):
* 未登录时该请求会 302 跳 CAS(与 items 同行为,页面本来就要登录);
* 任何失败静默保留现皮肤——推荐、不保证生效,绝不阻塞首屏。
*/
async function applySiteThemeRecommend() {
const code = currentSiteCode.value; // useSite() 模块级 ref,当前视图 onMounted 已 resolve
if (!code) return;
const cacheKey = `svchall.public.siteTheme.${code}`;
// ① stale:同步读缓存立即应用(二次访问无闪色)
try {
const cached = JSON.parse(localStorage.getItem(cacheKey) || "null");
const stale = toHallSkin(cached);
if (stale) theme.value = applyTheme(stale.name, stale.params);
} catch {
// 坏缓存当没有
}
// ② revalidate:后台刷新(失败静默保留现皮肤——推荐、不保证生效)
try {
const info: any = await hallApi.site(code).send();
const pick = toHallSkin({ theme: info?.theme, themeParams: info?.themeParams });
localStorage.setItem(cacheKey, JSON.stringify({
theme: info?.theme || null, themeParams: info?.themeParams || null,
}));
if (pick) theme.value = applyTheme(pick.name, pick.params);
} catch (e) {
console.warn("[public-hall] 站点主题获取失败,沿用当前皮肤", e);
}
}
</script>
<template>