这是一个非常常见的需求,因为 {dede:loop} 是一个自由循环标签,不像 {dede:arclist} 或 {dede:list} 那样自带分页功能,所以我们需要手动实现。

(图片来源网络,侵删)
实现分页的核心思路是:
- 获取总记录数:首先要知道总共有多少条数据,才能计算出总页数。
- 获取当前页码:通过 URL 参数(如
?page=2)来判断用户当前在看第几页。 - 计算分页数据:根据当前页码和每页显示的数量,使用
LIMIT子句从数据库中只查询当前页需要的数据。 - 生成分页导航条:根据总页数和当前页码,生成上一页、下一页、页码等链接。
下面我们提供两种主流的实现方法:
使用原生 PHP 代码(推荐,灵活高效)
这种方法的核心是利用 DedeCMS 提供的 GetOne() 和 Execute() 方法来直接操作数据库,完全由自己控制分页逻辑。
完整代码示例
{dede:config.pagesize value='10'/} <!-- 1. 设置每页显示条数,这里设为10条 -->
<?php
// 2. 获取当前页码,如果没有则默认为第1页
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? intval($_GET['page']) : 1;
// 3. 获取每页显示条数
$pagesize = (isset($cfg_pagesize) && is_numeric($cfg_pagesize)) ? $cfg_pagesize : 10;
// 4. 计算总记录数 (非常重要!)
// 这里假设你的 loop 查询是 "SELECT * FROM dede_archives WHERE typeid=1"
$sql_count = "SELECT COUNT(*) AS total FROM dede_archives WHERE typeid=1";
$row_count = $dsql->GetOne($sql_count);
$totalcount = $row_count['total'];
// 5. 计算总页数
$totalpage = ceil($totalcount / $pagesize);
// 6. 确保当前页码不超过总页数
if ($page > $totalpage) $page = $totalpage;
if ($page < 1) $page = 1;
// 7. 计算数据库查询的偏移量 (LIMIT offset, rows)
$offset = ($page - 1) * $pagesize;
// 8. 执行带分页的查询
// 注意:这里使用了 {$offset} 和 {$pagesize} 变量,DedeCMS 模板引擎会自动替换
$sql_query = "SELECT * FROM dede_archives WHERE typeid=1 ORDER BY id DESC LIMIT {$offset}, {$pagesize}";
$dsql->Execute('me',$sql_query);
// 9. 使用 {dede:loop} 循环输出当前页的数据
// 注意这里的源数据名称 'me' 必须和上面 Execute 的第二个参数一致
{dede:loop table='dede_archives' sort='id' if='typeid=1' row='{$pagesize}'}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<span>[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:loop}
// 10. 生成分页导航条
<div class="dede_pages">
<ul class="pagelist">
// 上一页
<li><a href="{dede:global name='phpurl'/}/your_list.php?page=<?php echo ($page-1 > 0 ? $page-1 : 1); ?>">上一页</a></li>
// 页码列表
<?php
for ($i = 1; $i <= $totalpage; $i++) {
if ($i == $page) {
// 当前页,高亮显示,不添加链接
echo "<li class='thisclass'><span>{$i}</span></li>";
} else {
// 其他页,生成链接
echo "<li><a href='{dede:global name='phpurl'/}/your_list.php?page={$i}'>{$i}</a></li>";
}
}
?>
// 下一页
<li><a href="{dede:global name='phpurl'/}/your_list.php?page=<?php echo ($page+1 <= $totalpage ? $page+1 : $totalpage); ?>">下一页</a></li>
// 跳转到指定页
<li><span class="pageinfo">共 <strong><?php echo $totalcount; ?></strong> 条记录</span></li>
<li><span class="pageinfo">/<?php echo $totalpage; ?> 页</span></li>
<li>
<form name="formsearch" action="{dede:global name='phpurl'/}/your_list.php">
<select name="page" onchange="document.formsearch.submit()">
<?php
for ($j = 1; $j <= $totalpage; $j++) {
$selected = ($j == $page) ? "selected='selected'" : "";
echo "<option value='{$j}' {$selected}>第 {$j} 页</option>";
}
?>
</select>
</form>
</li>
</ul>
</div>
代码详解
-
{dede:config.pagesize value='10'/}:
(图片来源网络,侵删)- 这是一个 DedeCMS 的配置标签,用于在模板中定义一个变量
$cfg_pagesize,这是一种良好的实践,方便在后台统一修改每页显示数,你也可以直接在 PHP 代码里$pagesize = 10;。
- 这是一个 DedeCMS 的配置标签,用于在模板中定义一个变量
-
获取当前页码
$page:isset($_GET['page'])检查 URL 中是否存在page参数。is_numeric()和intval()确保传入的是数字,防止 SQL 注入等安全问题。
-
计算总记录数:
SELECT COUNT(*) AS total FROM ...是获取数据表中记录总数的标准 SQL 语句。$dsql->GetOne()执行查询并返回第一行结果,我们从中取出total字段的值。
-
计算
LIMIT偏移量:LIMIT {$offset}, {$pagesize}的意思是:从第{$offset}条记录开始,查询{$pagesize}条。- 第2页(
$page=2),每页10条($pagesize=10),则offset = (2-1) * 10 = 10,查询语句就是LIMIT 10, 10,即第11到第20条记录。
-
{dede:loop}的使用:
(图片来源网络,侵删)- 这里我们使用了
loop标签的另一种写法:{dede:loop table='...' sort='...' if='...' row='...'}。 table: 指定要查询的数据表。sort: 排序字段。if: 查询条件。row: 每页显示的行数,我们这里用{$pagesize}变量动态传入。- 注意:虽然
row属性可以限制输出条数,但它不能实现真正的分页(即LIMIT),所以我们还是需要在 PHP 中执行LIMIT查询,loop标签在这里只是一个循环输出工具,数据已经在$dsql->Execute()中准备好。
- 这里我们使用了
-
生成分页导航:
- 这部分是纯 PHP 代码,在模板中直接输出 HTML。
- 循环生成页码链接,并对当前页进行特殊样式处理(
class='thisclass')。 - 上一页/下一页链接需要判断边界,防止页码超出范围。
- 跳转下拉框也是通过 PHP 动态生成
option选项。
使用 list 标签的 pagesize 属性(简单但有限制)
如果你只是需要一个简单的分页,并且不关心复杂的排序或查询条件,可以借用 {dede:list} 的分页功能。
完整代码示例
{dede:list pagesize='10' titlelen='40' orderby='pubdate'}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<span>[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:list}
<!-- 调用 {dede:list} 自带的分页标签 -->
<div class="dede_pages">
<ul class="pagelist">
{dede:pagelist listsize='4' listitem='info,index,end,pre,next,pageno'}
</ul>
</div>
代码详解
-
{dede:list}:- 这个标签本身就是为分页列表设计的。
pagesize='10': 直接设置每页显示10条记录。orderby='pubdate': 按发布时间排序。- 限制:
{dede:list}的数据源是固定的,它只能列出某个栏目(typeid)下的文章,你无法像方法一那样自由地写WHERE条件(typeid=1 AND flag='c')。
-
{dede:pagelist}:- 这是
{dede:list}的配套分页标签,它会自动生成上一页、下一页、页码等。 listsize='4': 显示当前页码前后各4个页码。listitem='...': 定义要显示的分页项,包括info(记录总数/页数)、index(首页)、end(尾页)、pre(上一页)、next(下一页)、pageno(页码)。
- 这是
何时选择方法二?
- 优点:代码极其简单,无需写任何 PHP 逻辑。
- 缺点:不够灵活,分页逻辑被
{dede:list}标签绑定,无法进行自定义查询。
总结与对比
| 特性 | 方法一 (原生 PHP) | 方法二 ({dede:list}) |
|---|---|---|
| 灵活性 | 极高,可写任意 SQL 查询条件 | 低,只能按栏目列表 |
| 性能 | 高,只查询当前页数据 | 高,底层也是 LIMIT |
| 代码复杂度 | 较高,需要编写 PHP 逻辑 | 极低,纯模板标签 |
| 适用场景 | 自定义列表、特殊条件查询、API数据展示 | 普通的文章列表、产品列表等标准栏目内容 |
- 如果你的需求是简单的文章列表分页,直接使用方法二
{dede:list},最快最方便。 - 如果你的需求比较复杂,比如需要跨栏目查询、按特定字段排序、或者数据来源不是
dede_archives表,那么必须使用方法一,虽然代码多一些,但能完全掌控分页逻辑。
