使用 CSS 变量实现节日换肤
通过 CSS 自定义属性集中管理主题色与背景图,再用 WebFuture 广告版位的投放时间控制节日主题。页面请求当前版位对应的主题标识,将其写入根元素的 data-pe-theme 属性,即可切换整站样式。本文以默认绿色、节日红色为例。
使用说明
本文属应用类案例。其中的 module.css 变量定义、「广告版位-换肤」组件与布局页脚本均需单独编写,不在产品包内置范围。文中的图片路径(如 ../../base/img/bg-02.png)与版位 ID 2083 取自案例站点,必须替换为当前站点的实际资源与 ID。组件建议按产品包惯例放在 Views/_Common/Shared/,与 广告版位-图片列表.cshtml 同目录。改造范围与产品包现有实现的差异见文末说明。
实现思路
- 在
module.css中定义默认变量,以及[data-pe-theme="red"]对应的覆盖值。 - 将页面中需要换肤的固定颜色、背景图改为
var(...)引用。 - 在后台创建「节日换肤」广告版位,通过广告上下线时间安排主题;广告
Description填写与 CSS 对应的主题标识,如red。 - 用组件读取版位中的广告描述,布局页通过 Ajax 获取标识并设置到
<html>元素。
广告版位只负责决定当前主题,具体视觉效果仍由站点 CSS 和图片资源决定。
1. 定义主题变量
在页面较早加载的 module.css 中定义默认主题和节日主题。以下图片路径相对于 module.css 所在目录,应用时应按实际资源目录调整。
/* 默认主题:绿色 */
:root {
--pe-theme-color: #185C42;
--pe-theme-color-rgb: 24, 92, 66;
--pe-theme-color-light: #B7CAC4;
--pe-theme-bg-icons-ind-box: url(../../base/img/icons-ind-box.png);
--pe-theme-bg-02: url(../../base/img/bg-02.png);
--pe-theme-bg-03: url(../../base/img/bg-03.png);
--pe-theme-bg-04: url(../../base/img/bg-04.png);
--pe-theme-bg-content: url(../img/bg-content.png);
}
/* 节日主题:红色 */
[data-pe-theme="red"] {
--pe-theme-color: #A80014;
--pe-theme-color-rgb: 168, 0, 20;
--pe-theme-color-light: #e9c9cc;
--pe-theme-bg-icons-ind-box: url(../../base/img/theme-red/icons-ind-box.png);
--pe-theme-bg-02: url(../../base/img/theme-red/bg-02.png);
--pe-theme-bg-03: url(../../base/img/theme-red/bg-03.png);
--pe-theme-bg-04: url(../../base/img/theme-red/bg-04.png);
--pe-theme-bg-content: url(../img/theme-red/bg-content.png);
}
将原样式中的固定值替换为变量。例如:
#kyxx .list-menu li a::before {
color: var(--pe-theme-color);
background: var(--pe-theme-bg-02) no-repeat center center;
}
需要半透明主题色时,可使用 rgba(var(--pe-theme-color-rgb), 0.5)。新增主题时,增加对应的属性选择器,并覆盖所有受主题影响的变量;未覆盖的变量会沿用 :root 的默认值。
2. 配置广告版位
在后台创建名为「节日换肤」的广告版位,并为各节日创建广告:
| 配置项 | 用途 |
|---|---|
| 上线、下线时间 | 控制节日主题生效区间 |
Description | 填写 CSS 主题标识,例如 red |
| Banner 图片 | 按站点设计上传相应素材 |
| 所属版位 | 绑定到「节日换肤」版位 |
记下版位 ID,供布局页请求组件时使用。下文的 2083 是原案例中的示例 ID,应替换为当前站点的实际值。避免同一版位同时存在多个生效广告;以下组件只读取集合中的第一个广告。
广告列表中可查看各节日广告的上线、下线时间及所属版位:

广告详情中,将「广告描述」填写为对应的主题标识;下图以 red 为例:

3. 创建广告版位组件
新建 广告版位-换肤.cshtml,根据版位 ID 获取广告描述。组件参数名为 id;布局页请求中的 Id 与其对应。
组件存放位置按产品包惯例选择:需要各站点共用时放 Views/_Common/Shared/(产品包中的 广告版位-图片列表.cshtml、广告版位-固定.cshtml 均在此目录);仅单个站点使用时放 Views/{站点标识}/Advertising/,并在布局页写全相对路径。
该组件只输出主题标识文本,因此在可视化编辑器中的标签与图标应选用文本类,避免沿用「图片列表」的图标造成误判:
@inject AdvertisementSpaceService AdvertisementSpaceService
@Power.VisualizationPartialView(new
{
Description = "广告版位-换肤",
IsComponent = true,
ComponentType = "其他模块",
Category = "广告",
TagName = "span",
TagClassName = "power-theme-flag",
TypeName = "All",
Device = "PC,Pad,Phone",
Order = "2",
IcoFile = "/content/_common/componentsresource/img/icon/general-image-list.png",
Renderings = "/content/_common/componentsresource/img/renderings/general-image-list.png",
EditMode = "Code",
Parameters = new
{
id = new { DisplayName = "版位Id", Type = "Int32", ControlType = "Integer" }
}
})
@{
int advertisementPositionId = 0;
if (Param != null && Param.id != null)
{
advertisementPositionId = Param.id;
}
var advertisementPosition = AdvertisementSpaceService.GetAdvertisementFixedPosition(advertisementPositionId);
string theme = null;
}
@if (advertisementPosition != null)
{
if (advertisementPosition.AdvertisementAdvertisementSpaces.Count > 0)
{
foreach (var advertisementInformation in advertisementPosition.AdvertisementAdvertisementSpaces.Select(x => x.Advertisement))
{
theme = advertisementInformation.Description;
break;
}
}
}
@theme
组件应仅输出主题标识,不输出广告 HTML。这里使用 Razor 的 @theme 输出文本;主题标识应由后台维护为简单值(如 red)。实际接入时,请确认 GetAdvertisementFixedPosition 返回的广告集合符合站点的生效时间规则。
4. 在布局页应用主题
在 公共布局页.cshtml 的 header 区域请求组件,并把返回值赋给 <html>。本例沿用项目中的 jQuery Ajax 调用方式,因此脚本执行前须已加载 jQuery。
@section header
{
<script>
$.ajax({
url: '@Url.AjaxPartialUrl()',
type: 'post',
cache: false,
data: {
partialViewName: '广告版位-换肤',
parameters: '{"Id": 2083}', // 替换为实际版位 ID
cacheMinutes: 0
},
success: function (response) {
const theme = (response.html || '').trim();
if (theme === 'red') {
document.documentElement.setAttribute('data-pe-theme', theme);
}
}
});
</script>
}
示例只允许 red 主题。新增主题时,应同时增加 CSS 规则和此处的允许值。广告描述为空时不设置属性,页面使用 :root 默认主题。
Ajax 请求是异步的,即使脚本写在样式表之前,也不能保证响应先于页面绘制;加载过程中可能短暂显示默认主题。若项目要求首屏无切换闪烁,需要在服务端输出主题属性,或在首屏绘制前通过已缓存的主题标识同步设置属性。
该脚本写在布局页中,因此同一站点下所有引用该布局页的栏目都会生效。产品包的 PC 端与移动端视图相互独立(Views/、Views.Pad/、Views.Phone/),如需 Pad 或 Phone 端同样换肤,须在各端的组件目录与布局页中重复上述改造,否则仅 PC 端生效。
接入检查
- 确认
module.css的加载位置、背景图相对路径,以及所有需要换肤的样式均已改为变量。 - 核对广告版位 ID、广告生效时间和
Description的主题标识;同一时段只安排一个主题。 - 分别检查有生效广告和无生效广告时的页面效果;无生效广告应显示默认主题。
- 如使用多个节日主题,逐一核对 CSS 选择器与前端允许值是否一致。
与产品包现有实现的差异
以下为对照 SmartGovPortal 16.1.4 产品包出厂站点(WebSite--sgp16.1.4)核对的结果,用于评估改造范围。
产品包自带的是「飘红」整页方案
产品包通过 Views/main/Home/网站首页(飘红).cshtml(首页视图的整页副本)配合 @section header 内联样式覆盖 body 背景实现节日效果,配色资源为 content/main/base/img/body-red.jpg、body-red-new.jpg,并在 content/main/home/css/index.css 中引用 ../../base/img/body-red.jpg。
本文方案是单份视图 + 动态切换。改造时需确认是否保留原「飘红」首页视图,避免两套机制并存。
变量与图片资源需要新建
产品包 content/main/base/css/module.css 中没有任何 --pe-theme 变量,也不存在 base/img/theme-red/ 目录;文中示例引用的 icons-ind-box.png、bg-02.png、bg-03.png、bg-04.png、bg-content.png 在产品包中均不存在,需自备素材后按实际目录改写路径。
站群版位需分支处理
产品包 Views/_Common/Shared/广告版位-固定.cshtml 对「全站群通用版位」做了分支:
var advertisementPosition = isWholeStationGroup
? WholeStationGroupAdvertisementSpaceService.GetAdvertisementFixedPosition(advertisementPositionId)
: AdvertisementSpaceService.GetAdvertisementFixedPosition(advertisementPositionId);
若当前站点使用的版位属于全站群通用,应参照该写法改用站群服务,否则取不到广告描述。
已核对可直接复用的部分
Views/{站点标识}/Layout/公共布局页.cshtml 的路径与命名、AdvertisementSpaceService.GetAdvertisementFixedPosition(int)、@Power.VisualizationPartialView(new {...}) 的全部元数据字段、@Url.AjaxPartialUrl(),均与产品包现有组件一致,示例可直接沿用。
