准备工作:理解DedeCMS的栏目结构
在开始之前,我们先明确一下DedeCMS的栏目逻辑:

(图片来源网络,侵删)
- 顶级栏目 (Top-level channel):没有父栏目的栏目。
- 二级栏目 (Secondary channel):拥有父栏目的栏目,其父栏目是顶级栏目。
- 文章列表:存放在栏目下的文章。
我们的目标是:先获取一个顶级栏目,然后找到它的所有二级栏目,最后再循环调用每个二级栏目下的文章列表。
使用 {dede:channelartlist} 标签(最推荐、最灵活)
这是处理“顶级栏目包含多个二级栏目”场景的标准且最强大的方法,它会循环顶级栏目,并在每次循环中,嵌套调用该顶级栏目下的二级栏目和文章。
标签结构解析
{dede:channelartlist}: 循环顶级栏目。typeid='top': 指定只调用顶级栏目,你也可以写具体的顶级栏目ID,如typeid='1,2,3'。row='10': 调用多少个顶级栏目。
{dede:field name='typename'/}: 获取当前顶级栏目的名称。{dede:channel}: 在{dede:channelartlist}内部使用,用于循环调用当前顶级栏目下的二级栏目。reid='~typeid~': 这是关键!~typeid~是一个变量,代表当前顶级栏目的ID。reid表示调用父ID为~typeid~的所有栏目,也就是二级栏目。row='10': 调用多少个二级栏目。type='son': 与reid作用类似,表示调用子栏目。
{dede:field name='typename'/}: 获取当前二级栏目的名称。{dede:arclist}: 在{dede:channel}内部使用,用于调用当前二级栏目下的文章。typeid='~id~': 关键!~id~是一个变量,代表当前二级栏目的ID。typeid指定只调用这个栏目ID下的文章。row='5': 调用多少篇文章。titlelen='30': 标题字符数限制。
完整代码示例
假设我们要在首页调用所有顶级栏目及其下的二级栏目和文章。
{dede:channelartlist typeid='top' row='8'}
<!-- 顶级栏目名称 -->
<h2>{dede:field name='typename'/}</h2>
<div class="main">
<!-- 循环当前顶级栏目下的二级栏目 -->
{dede:channel type='son' row='8'}
<div class="sub-column">
<!-- 二级栏目名称 -->
<h3><a href="[field:typelink/]">[field:typename/]</a></h3>
<!-- 调用当前二级栏目下的文章列表 -->
<ul>
{dede:arclist row='5' titlelen='30' typeid='~id~'}
<li>
<a href="[field:arcurl/]" title="[field:title/]">[field:title/]</a>
<span class="date">[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:arclist}
</ul>
</div>
{/dede:channel}
</div>
{/dede:channelartlist}
使用 {dede:channel} 和 {dede:arclist} 的组合(适用于固定结构)
如果你的页面结构是固定的,比如你明确知道要调用ID为 1 的顶级栏目下的所有二级栏目,那么可以使用这种方法,代码结构更清晰。

(图片来源网络,侵删)
标签结构解析
-
第一步:获取顶级栏目的所有二级栏目ID
- 使用
{dede:channel}并指定顶级栏目的typeid和reid属性,来获取所有二级栏目的ID,通常我们会把这些ID存入一个变量。 {dede:sql sql="SELECT id FROMdede_arctypeWHERE reid=1"}dede_arctype是DedeCMS存放栏目的表名。reid=1中的1是你要调用的顶级栏目的ID。- 这会输出所有二级栏目的ID,如:
5 6 7 8。
- 使用
-
第二步:调用这些二级栏目下的文章
- 使用
{dede:arclist}并将上一步获取的ID列表赋值给typeid属性。
- 使用
完整代码示例
假设顶级栏目ID为 1。
<h2>公司新闻</h2> <!-- 假设顶级栏目1的名称是“公司新闻” -->
<!-- 调用ID为1的顶级栏目下的所有二级栏目(如ID为5,6,7,8)下的文章 -->
{dede:arclist typeid='5,6,7,8' row='10' titlelen='50'}
<li>
<span class="title">[field:title/]</span>
<span class="time">[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
<a href="[field:arcurl/]">[详细]</a>
</li>
{/dede:arclist}
优点:代码简单。
缺点:不灵活,如果二级栏目有增减,你需要手动修改 typeid='5,6,7,8' 这部分代码。
使用 {dede:sql} 直接查询数据库(高级用法)
当你需要非常复杂的查询逻辑,或者默认标签无法满足需求时,可以直接使用SQL语句从数据库中获取数据。
标签结构解析
{dede:sql sql="..."}: 执行自定义的SQL查询语句。
完整代码示例
这个例子会查询顶级栏目ID为 1 的所有二级栏目,并为每个栏目调用最新的5篇文章。
{dede:sql sql="
SELECT
c.id, c.typename, c.typeurl,
(SELECT a.title FROM dede_archives a WHERE a.typeid = c.id ORDER BY a.pubdate DESC LIMIT 5) as articles
FROM
dede_arctype c
WHERE
c.reid = 1
"}
<h3>[field:typename/]</h3>
<ul>
<!-- 注意:这里的文章循环需要更复杂的处理,因为SQL查询返回的是一个集合 -->
<!-- 更简单的方式是分开查询 -->
</ul>
{/dede:sql}
更实用的SQL方法(结合PHP):
直接在模板里处理复杂的SQL关联查询比较麻烦,通常的做法是在模板里先获取二级栏目ID,然后再循环调用文章,与方法二类似。
<!-- 1. 先获取所有二级栏目的ID列表 -->
{dede:sql sql="SELECT GROUP_CONCAT(id) as ids FROM `dede_arctype` WHERE reid=1"}
{dede:get sql="SELECT id, typename FROM dede_arctype WHERE id IN (~ids~)"}
<h3>[field:typename/]</h3>
<!-- 2. 再根据每个ID调用文章 -->
{dede:arclist typeid='[field:id/]' row='3'}
<li><a href="[field:arcurl/]">[field:title/]</a></li>
{/dede:arclist}
{/dede:get}
{/dede:sql}
这种方法比较绕,一般不推荐初学者使用。
总结与建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
{dede:channelartlist} |
最标准、最灵活、代码结构清晰,支持动态顶级栏目。 | 标签嵌套,对新手来说理解稍复杂。 | 绝大多数场景,特别是首页、列表页需要动态调用时。 |
{dede:channel} + {dede:arclist} |
代码简单直观,容易理解。 | 不灵活,顶级栏目ID或子栏目ID变动时需手动修改代码。 | 页面结构固定,只调用特定的几个顶级栏目时。 |
{dede:sql} |
功能最强大,可以实现任何复杂的查询逻辑。 | 代码可读性差,对数据库结构要求高,有安全风险(需注意防注入)。 | 需要获取标签无法提供的特殊数据时。 |
对于99%的需求,强烈推荐使用方法一 {dede:channelartlist},它既符合DedeCMS的设计思想,也足够灵活,能应对各种变化。
