节点扩展字段驱动栏目类型差异化展示
模块概述
本方案利用节点扩展字段,实现在同一父级栏目下,各子栏目根据"栏目类型"扩展字段值,动态切换不同的文章列表展示样式(图文列表、图片列表、轮播列表等),无需为每种展示类型单独创建页面模板。
核心解决问题
传统做法中,同一频道页下所有子栏目只能使用同一种列表样式,无法满足"不同栏目需要不同展示形式"的需求。本方案通过节点扩展字段 lmlx(栏目类型)作为渲染开关,让每个子栏目自行决定文章列表的展示风格。
功能需求
- 扩展字段驱动:通过节点扩展字段
lmlx(栏目类型)的值,控制子栏目的文章列表展示类型。 - 多种列表样式:
lmlx = "3"→ 图文列表(三列式,带标题+摘要+封面图)lmlx = "4"→ 图片列表(五列式 Swiper 轮播)- 其他值或未设置 → 图片列表(三列式,默认样式)
- Banner图支持:通过节点扩展字段
picphone控制栏目 Banner 图的显示。 - 统一页面框架:所有子栏目共用同一页面模板,仅列表渲染部分根据扩展字段动态切换。
适用场景
- 专题页面:多个子栏目需要不同展示风格(图文混排、纯图轮播、普通列表)。
- 频道首页:各子栏目根据不同内容特性(新闻、图集、视频)采用不同列表形式。
- 需要灵活控制子栏目展示风格,减少模板数量的场景。
核心价值
- 一套模板适配多种展示需求,减少重复开发
- 栏目管理员可自主切换展示类型,无需开发介入
- 与现有文章列表组件无缝集成,CSS 样式兼容
核心功能分类
基于节点扩展字段的动态列表渲染
场景描述
此场景适用于需要为不同子栏目提供不同文章列表展示风格的专题页或频道首页。通过在节点扩展字段中配置"栏目类型"值,页面渲染时自动匹配对应的列表组件。
扩展字段配置
首先在目标节点上添加扩展字段:
| 字段标识 | 字段名称 | 字段类型 | 选项值 | 说明 |
|---|---|---|---|---|
| lmlx | 栏目类型 | 单选下拉 | 3=图文列表 / 4=图片轮播 | 控制子栏目文章列表的展示类型 |
| picphone | Banner图 | 图片上传 | — | 栏目顶部 Banner 图片 |
视图代码
@inject NodeService NodeService
@inject NodeInterfaceExtend NodeInterfaceExtend
@inject NodeExtendFieldService NodeExtendFieldService
@inject StringExtensionsHelper StringExtensionsHelper
@Power.VisualizationView(new { Area = "ContentManage", Controller = "Node" })
@model Node
@{
Layout = $"~/Views/{this.Context.GetCurrentSite().Identifier}/Layout/公共布局页.cshtml";
// 获取当前节点的所有扩展字段定义
var extendField = NodeExtendFieldService.GetAll();
// 读取当前节点 Banner 图(扩展字段 picphone)
var bannerUrl = Model.ExtendObject?.picphone;
}
依赖注入说明
| 服务 | 用途 |
|---|---|
NodeService | 获取当前节点的子节点列表 |
NodeInterfaceExtend | 调用节点相关接口扩展(如 NodeHits) |
NodeExtendFieldService | 获取节点扩展字段定义集合,用于 InitExtendContent |
StringExtensionsHelper | 工具类,用于路径/URL转换 |
Banner 渲染
@if(!string.IsNullOrEmpty(bannerUrl)){
<div class="nodeBanner">
<img alt="@Model.NodeName" src="@StringExtensionsHelper.ToUrl(bannerUrl)" />
</div>
}
子栏目循环 + 动态列表渲染
<div class="pageBoxLoop pageBoxLoop1">
@{
var nodeService = NodeService;
// 获取子节点列表,最多取 20 个
var childNodes = nodeService.GetChildNodeList(Model).Take(20);
foreach (Node childNode in childNodes.Where(n => n.ShowOnParentNodeList))
{
// 关键步骤:初始化子节点的扩展内容,使 ExtendObject 可用
childNode.ExtendContentObject.InitExtendContent(extendField, childNode.ExtendContent);
// 读取子节点的"栏目类型"扩展字段值
var nodeType = childNode.ExtendObject?.lmlx.ToString();
<div class="box">
<div class="hd">
<h3>@Power.Url.NodeLink(childNode.Identifier)</h3>
@Power.Url.NodeLink(childNode.Identifier, "查看更多>>", new { @class = "more" })
</div>
<div class="bd">
@if(nodeType == "3"){
@* 图文列表 - 三列式 *@
<div class="picIntroList-box">
<ul class="picIntroList scalePic">
@Power.ArticleList("文章图文列表", new {
Node=childNode.Identifier,
Count=4,
Illustrated=true,
TitleLength=80,
DisplayDateTime="yyyy-MM-dd",
PicFollowTitle=false,
ContentLength=100,
ImageWidth=412,
ImageHeight=275,
RefNode=true
})
</ul>
</div>
}
else if(nodeType == "4"){
@* 图片列表 - 五列式(Swiper 轮播) *@
<div class="picTitleSwiperList-box">
<ul class="picTitleSwiperList scalePic swiper-wrapper">
@Power.ArticleList("文章图片列表", new {
Node=childNode.Identifier,
Count=10,
Illustrated=true,
TitleLength=60,
ImageWidth=264,
ImageHeight=190,
RefNode=true
})
</ul>
<div class="pic-button pic-button-prev"></div>
<div class="pic-button pic-button-next"></div>
</div>
}
else{
@* 默认 - 图片列表 - 三列式 *@
<div class="picTitleList-box">
<ul class="picTitleList scalePic">
@Power.ArticleList("文章图片列表", new {
Node=childNode.Identifier,
Count=4,
Illustrated=true,
TitleLength=60,
ImageWidth=440,
ImageHeight=294,
RefNode=true
})
</ul>
</div>
}
</div>
</div>
}
}
</div>
三种列表类型对照
| lmlx 值 | 列表组件 | HTML 容器 | 布局 | 特殊交互 |
|---|---|---|---|---|
"3" | 文章图文列表 | ul.picIntroList | 三列图文 | — |
"4" | 文章图片列表 | ul.picTitleSwiperList.swiper-wrapper | 五列轮播 | Swiper 自动轮播+导航箭头 |
| 其他/空 | 文章图片列表 | ul.picTitleList | 三列图片 | — |
Swiper 初始化脚本(lmlx="4" 类型)
$(".picTitleSwiperList-box .swiper-wrapper li").addClass("swiper-slide");
var swiper = new Swiper(".picTitleSwiperList-box", {
slidesPerView: 2,
spaceBetween: 12,
loop: true,
autoplay: {
delay: 6000,
disableOnInteraction: false,
pauseOnMouseEnter: true,
},
navigation: {
nextEl: '.pic-button-next',
prevEl: '.pic-button-prev'
},
breakpoints: {
1024: { slidesPerView: 3, spaceBetween: 12 },
1280: { slidesPerView: 5, spaceBetween: 20 }
}
});
核心 API 说明
| API | 说明 |
|---|---|
NodeExtendFieldService.GetAll() | 获取当前节点的所有扩展字段定义(字段标识、名称、类型等),返回扩展字段集合 |
childNode.ExtendContentObject.InitExtendContent(extendField, extendContent) | 将扩展字段定义和扩展内容值绑定到节点的 ExtendContentObject,使 ExtendObject 动态属性可用 |
childNode.ExtendObject?.lmlx | 通过动态属性访问扩展字段值(? 为 null 条件运算符,避免扩展字段未配置时报错) |
Model.ExtendObject?.picphone | 读取当前节点的 Banner 图扩展字段 |
注意事项
- 扩展字段初始化时机:必须在访问
ExtendObject之前调用childNode.ExtendContentObject.InitExtendContent(extendField, childNode.ExtendContent),否则ExtendObject动态属性不可用。 - null 安全:使用
?.运算符访问扩展字段值,当节点未配置该扩展字段时不会抛出异常,返回null走默认分支。 - ToString() 调用:
lmlx为动态类型,判断前需调用.ToString()转为字符串再比较。 - RefNode 参数:调用
Power.ArticleList时必须传入RefNode=true,表示引用子节点作为数据源。 - Swiper 初始化:五列轮播类型需在
scripts区域单独初始化 Swiper,并处理响应式断点。 - 子节点过滤:通过
childNodes.Where(n => n.ShowOnParentNodeList)仅渲染"显示在父节点列表"的子栏目,尊重栏目的显示开关设置。
实现要点
扩展字段定义获取 调用
NodeExtendFieldService.GetAll()一次性获取当前节点所有扩展字段定义,传给每个子节点的InitExtendContent方法,确保动态属性可正确解析。子节点扩展内容初始化 遍历子节点时,必须对每个子节点执行:
childNode.ExtendContentObject.InitExtendContent(extendField, childNode.ExtendContent);此操作将扩展字段的元数据与子节点的扩展内容值关联,生成
ExtendObject动态对象。栏目类型分支渲染 通过
if / else if / else三分支结构,根据lmlx字段值渲染不同列表组件。新增展示类型时只需增加else if分支,扩展性好。列表组件参数对齐 三种列表类型使用不同的
Power.ArticleList组件(文章图文列表vs文章图片列表),参数需与各自容器 CSS 相匹配(如ImageWidth/ImageHeight需对应对应容器的设计尺寸)。Banner 图渲染 通过
Model.ExtendObject?.picphone读取当前节点扩展字段中的 Banner 图,若未配置则整个nodeBanner区域不渲染。
示例效果

节点扩展字段驱动栏目类型差异化展示效果示意。
