内容列表:{naicha:list}
列表页的主角。全部参数、排序键、循环里能用的字段,以及一份可以直接抄的完整列表页。
适用范围:列表页;首页、详情页侧栏、任意需要「取一批内容」的地方。
基本写法
{naicha:list cid="3" num="10" page="{naicha:param name='page'}" order="publish desc"}
<a href="{list.url}">{list.title}</a>
{/naicha:list}
全部参数
cid当前栏目;栏目 idname—;栏目别名(推荐,比写死 id 好)。⚠️ 只有 cid 和 name 都没写时才继承当前栏目num10;每页条数(上限 100)page当前页;页码,通常写 {naicha:param name='page'}ordersort desc;排序键,见下keyword搜索词;关键词搜索model—;只取某个内容模型的内容(如 media)top—;只看置顶(1)tag / tags / tagmode—;按标签筛;tags="a,b" 多标签,tagmode="and"(默认)/ orfield op fvalue—;按自定义字段筛(op 是比较方式)withsub / sub—;连子栏目一起取media—;顺带取图集(列表页每行多一个 {list.gallery})filters—;filters="1" 表示这个列表接受 URL 上的筛选(排序/时间/标签)。侧栏那种局部列表不要开kwscope站点设置;搜索范围(标题摘要 / 含正文)排序键(写错的会静默回落到 sort desc)
sort desc sort asc publish desc publish asc
hits desc id desc id asc relevance(只在带 keyword 时有意义)
⚠️ 写错不报错,页面照常渲染,只是顺序不对。看着「正常」而顺序怪,先来这一行对一下。
循环体里能用的字段(一条不漏)
下面这些都写 {list.字段名}。列表查询取的是内容表整行,所以表里有的列模板里都能取,
包括平时用不到的。
基本
{list.id}内容 id。拼自定义链接、调 {naicha:rel} / {naicha:gallery} 都要它{list.cid}所属栏目 id{list.model}内容模型名(article / media / 自定义的){list.title}标题{list.alias}别名(拼固定地址用;没填就是空串){list.url}详情页地址(已带语言前缀,别再自己拼){list.summary}摘要;没填过时引擎会自动从正文截前 120 字{list.content}正文 HTML。⚠️ 输出必须写 html="1",否则会被转义成源码显示出来时间与状态
{list.publish_time}发布时间。配 format="Y-m-d" 用{list.is_new}1/0,7 天内发布的。判断要写 eq="1"{list.hits}浏览量{list.top}是否置顶(1/0){list.sort}后台填的排序值{list.status}状态(published 已发布 / 其它为待审等){list.author_id}作者的用户 id(想显示名字得自己按 id 映射){list.lang}属于哪个语言(空串 = 不限语言){list.created_at} {list.updated_at}入库时间 / 最后修改时间(与发布时间不是一回事)图片
{list.thumb}封面图的相对路径。⚠️ 别直接塞进 src,见下面的提示{list.thumb_url}封面图可直接用的地址(引擎算好的)。没图时是空串{list.has_thumb}1/0,有没有封面图 —— 套一层 {naicha:if} 省得渲染一个空 <img>{list.gallery}图集数组,只有写了 media="1" 才有;元素里有 path / url / alt{list.gallery_count}图集里有几张(做「共 N 图」角标){list.has_gallery}1/0,有没有图集⚠️ {list.thumb} 存的是相对路径(uploads/2026xx/a.png)。
在 /list/cid-3 这类多层地址下直接写进 src,浏览器会当成 /list/uploads/…,图全裂。
一律用 {list.thumb_url} —— 它还顺带处理了多语言前缀。
自定义字段与门槛
{list.扩展字段名}直接写字段名就行。引擎在正式列里找不到同名时,会自动去扩展字段里找 —— 所以 {list.media_type} 与 {list.ext.media_type} 完全等价,前者更好写{list.need_login}1 = 这篇仅登录可见{list.need_level}大于 0 = 需要该等级及以上{list.detail_template}这篇指定用哪套详情版式(空 = 跟栏目或主题默认){list.files}附件(下载型内容用){list.reject_reason}被驳回的原因(后台审核流程用,前台一般是空串)
取不到的值一律是空串,不报错。
所以「页面上少了一块」八成是名字写错了,而不是引擎坏了 ——
先照上面的表对一遍拼写,再去看「嵌套与失败模式」那一篇。
一份可以直接抄的列表页
<!-- [templates/default/list.html] -->
{naicha:include file="header.html"}
<div class="page-head">
<h1>{sort.name}</h1>
<p>{sort.intro}</p>
</div>
<div class="container">
<!-- filters="1" 让这个列表响应地址栏上的筛选(排序 / 年份 / 标签)-->
{naicha:filters show="sort,year,tag"}
<div class="grid c3">
{naicha:list cid="{naicha:param name='cid'}" num="12"
page="{naicha:param name='page'}" filters="1" order="publish desc"}
<a class="card" href="{list.url}">
{naicha:if var="list.has_thumb" eq="1"}
<img src="{list.thumb_url}" alt="{list.title}">
{/naicha:if}
<h3>{list.title}</h3>
<p>{list.summary excerpt="60"}</p>
<span>{list.publish_time format="Y-m-d"} · 浏览 {list.hits}</span>
{naicha:if var="list.is_new" eq="1"}<span class="badge">最新</span>{/naicha:if}
</a>
{/naicha:list}
</div>
<!-- 空结果要说一句人话,而不是留一片空白 -->
{naicha:if var="listTotal" eq="0"}
<div class="empty">
<p>这个栏目还没有内容。{naicha:if var="keyword" notempty="1"}换个关键词试试。{/naicha:if}</p>
</div>
{/naicha:if}
{naicha:page}
</div>
{naicha:include file="footer.html"}
同一栏目里放不同版式
<!-- [列表] 视频栏目用播放角标,其它用普通卡片 -->
{naicha:list cid="{naicha:param name='cid'}" num="12" media="1"}
{naicha:if var="list.media_type" eq="video"}
<span class="badge play">▶ {list.duration}</span>
{naicha:elseif var="list.media_type" eq="audio"}
<span class="badge audio">♪ {list.duration}</span>
{naicha:else}
<span class="badge">图文</span>
{/naicha:if}
{/naicha:list}
{list.media_type} 是内容模型的扩展字段 ——
引擎在同名列找不到时会自动去 ext 里找,所以不用写 {list.ext.media_type}。
点赞 / 反对(后补的字段)
{list.likes}点赞数{list.oppose}反对数{list.likeslink}点赞地址,直接当 href 用即可(点一次算一票,重复点不叠加){list.opposelink}反对地址<!-- 点赞 / 反对。链接直接点就行,不用写 JS -->
<a class="like" href="{list.likeslink}">赞 <b>{list.likes}</b></a>
<a class="oppose" href="{list.opposelink}">踩 <b>{list.oppose}</b></a>
<!-- 想点完不跳页,加一个 fetch 即可(接口对 ajax=1 返回 JSON)-->
<script>
document.querySelectorAll('.like,.oppose').forEach(function (a) {
a.addEventListener('click', function (ev) {
ev.preventDefault();
fetch(a.href + '&ajax=1').then(function (r) { return r.json(); }).then(function (d) {
if (d.ok) { a.querySelector('b').textContent = d.likes || d.oppose; }
});
});
});
</script>
行所属栏目与作者(后补的字段)
{list.sortname} {list.sorturl}这一行属于哪个栏目 —— 混排列表(多个栏目一起列)要用{list.author}作者名(取昵称,没填取用户名;没设作者时是空串)