成套片段(照抄即用)
列表页、详情页、导航高亮、分页、搜索、评论、会员门槛、商品卡……每段都标了放在哪。
前面几篇是逐个标签的说明,这一篇是成套的片段: 每一段都能直接粘进模板跑起来,标题后面标了它该放在哪个文件里。 写主题时通常先照抄一段,再按自己的设计改 HTML 结构 —— 标签部分不用动。
1. 一个完整的列表页
<!-- [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"}
2. 导航高亮(当前栏目变亮)
<!-- [页头] 用列表标签取栏目,拿 url 和当前路径比 -->
{naicha:list cid="0" num="8"}{/naicha:list} <!-- 不适用;导航请用下面的 navs -->
<!-- 正确做法:后台自定义导航 + 当前路径比较 -->
{naicha:navs type="top" num="10"}
<a href="{navitem.url}" target="{navitem.target}"
class="{naicha:if var="navitem.url" eq="{currentPath}"}on{/naicha:if}">{navitem.name}</a>
{/naicha:navs}
3. 面包屑 + 上一篇 / 下一篇
<!-- [详情] 面包屑:控制器给了 crumbs 数组 -->
<nav class="crumb">
<a href="/">首页</a>
{naicha:loop from="crumbs" as="c"}
<span>/</span><a href="{c.url}">{c.name}</a>
{/naicha:loop}
</nav>
<!-- [详情] 上下篇:九套详情版式共用 detail_foot.html,抄这一行就够 -->
{naicha:include file="detail_foot.html"}
4. 搜索框 + 结果高亮
<!-- [搜索页] -->
<form class="search" action="/search" method="get">
<input type="search" name="kw" value="{naicha:search}" placeholder="搜索内容">
<button type="submit">搜索</button>
</form>
{naicha:if var="listTotal" gt="0"}
<p class="muted">共 {listTotal} 条结果</p>
{naicha:list keyword="{naicha:search}" num="10" page="{naicha:param name='page'}" order="relevance"}
<a href="{list.url}">{list.title highlight="{naicha:search}"}</a>
<p>{list.summary highlight="{naicha:search}" excerpt="80"}</p>
{/naicha:list}
{naicha:page}
{naicha:else}
<p>没有找到与「{naicha:search}」相关的内容。</p>
{/naicha:if}
5. 详情页骨架(标题 → 行动区 → 正文 → 文件 → 评论)
<!-- [templates/你的主题/detail.html] 九套官方版式都是这一套骨架 -->
{naicha:include file="header.html"}
{naicha:detail}
<div class="container">
{naicha:include file="detail_head.html"} <!-- 封面/标签/标题/元信息/摘要 -->
<div class="dt-wrap">
<div>
<article class="article">
<div class="body">{detail.content html="1"}</div>
</article>
{naicha:include file="detail_files.html"} <!-- 文件信息 / 版本记录 -->
{naicha:include file="detail_foot.html"} <!-- 上下篇 + 相关推荐 -->
<div class="dt-com">{naicha:comments}</div>
</div>
<aside class="dt-side">
{naicha:include file="detail_action.html"} <!-- 购买 / 下载 / 门槛,判据在 PHP -->
</aside>
</div>
</div>
{/naicha:detail}
{naicha:include file="footer.html"}
行动区为什么是一行 include:"这篇该显示购买、下载、还是门槛"这件事 只有在 PHP 里判得对(它要看是不是商品、买没买过、等级够不够)。 九套版式各自 if 一遍的结果,就是「有些详情页有购买按钮、有些没有」—— 所以判据只有一份,模板只负责把它放在哪。
6. 会员门槛 / 登录引导
<!-- [详情] 正文要会员才能看时,服务端已经把正文摘掉了;这里只需给个出口 -->
{naicha:include file="member_gate.html"}
<!-- [任意] 未登录时给登录入口;判断必须用 eq="1" -->
{naicha:user}
{naicha:if var="user.logged" eq="1"}
<a href="/user">{user.nickname}</a>
<a href="/user/logout">退出</a>
{naicha:else}
<a href="/user/login">登录</a>
<a href="/user/register">注册</a>
{/naicha:if}
{/naicha:user}
⚠️ 判断登录态只能写 eq="1"。
写成 notempty="1" 的话,未登录时 user.logged 是字符串 '0' ——
非空,判断永远为真,于是访客也会看到"我的账户"。
7. 商品列表与价格
<!-- [商城列表页] -->
{naicha:shop num="12" order="sort desc"}
<a class="card" href="{shop.url}">
<img src="{shop.thumb_url}" alt="{shop.title}">
<h3>{shop.title}</h3>
<div class="price">
<b>{shop.price}</b>
{naicha:if var="shop.market_price" notempty="1"}<del>{shop.market_price}</del>{/naicha:if}
</div>
<span>{shop.stock_text}</span>
</a>
{/naicha:shop}
{naicha:page total="{shopTotal}" num="12"}
字段较多(含会员价、库存文案、已售数),完整清单见「商城与商家标签」那一篇。
8. 分类型的内容——同一栏目里放不同版式
<!-- [列表] 视频栏目用播放角标,其它用普通卡片 -->
{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}。
上面这段用到了模板引擎 1.7.0 起支持的 {naicha:elseif}(见「套标签」那一篇)。
9. 手机版 / 电脑版切换入口
<!-- [页脚] 必须给这个入口:UA 判断不可能 100% 准(平板、折叠屏、微信内置浏览器),
判错了用户得能自己纠正 -->
{naicha:if var="viewSwitch.on" eq="1"}
<a href="{viewSwitch.url}">切换到{viewSwitch.label}</a>
{/naicha:if}
10. 多语言切换器
<!-- [页头] 语言列表由控制器注入,用 loop 展开 -->
{naicha:if var="lang.on" eq="1"}
<div class="lang-switch">
{naicha:loop from="lang.list" as="L"}
<a href="{L.url}" hreflang="{L.code}" class="{naicha:if var="L.current" eq="1"}on{/naicha:if}">{L.name}</a>
{/naicha:loop}
</div>
{/naicha:if}
<!-- [页头] 别忘 hreflang 标注;html="1" 不能省,否则会显示成源码 -->
{lang.hreflang html="1"}
11. 标签聚合页
<!-- [标签页] 取当前内容的标签,或某个栏目的常用标签 -->
{naicha:tags content_id="{naicha:param name='id'}" num="20"}
<a class="chip" href="/tag/{tag.name urlencode="1"}">#{tag.name} <i>{tag.count}</i></a>
{/naicha:tags}
进 /tag/ 地址时必须 urlencode="1":
中文标签不编码会拼出一个非法地址,看着像标签页 404。
12. 图集网格
<!-- [详情] 图集存在独立表里,可以排序、带 alt -->
{naicha:gallery content_id="{naicha:param name='id'}" num="30"}
<figure>
<img src="{photo.url}" alt="{photo.alt}" loading="lazy">
<figcaption>第 {photo.index} 张</figcaption>
</figure>
{/naicha:gallery}
抄完之后要检查的三件事:
- 列表里有没有
{naicha:page}(没有的话第二页永远进不去); - 判断登录/开关是不是写成了
eq="1"(notempty在'0'上永远为真); - 正文输出有没有带
html="1"(不带就把 HTML 当文本显示出来了)。
