首页 / 开发文档 / 单标签与全局变量(全表)

单标签与全局变量(全表)

全部 16 个单标签的属性和用途,以及模板里可以直接用的全局变量。

单标签**不压作用域**:它取一个值直接输出,写在页面的任何位置都行 —— 循环里、循环外、属性值里、甚至同一行属性中间(见「套标签」那一篇)。

全部单标签

标签属性作用
{naicha:site name="…"} name(站点设置里的键,默认 title) 取一条站点设置。常用键:title slogan keywords description icp copyright contact_email domain
{naicha:brand}— 品牌字标,输出两段(.brand-a / .brand-b)。在最后一个「小写→大写」的驼峰边界断开(NaichaCMS → Naicha+CMS);中文站名没有这种边界就整段当第一段,不会拆坏
{naicha:icp}— 备案号,输出成指向工信部的链接。没填时输出空串 —— 配合 .footer-bottom span:empty{display:none} 不会在页脚留空隙。海外站不需要备案,所以它必须是"空就不显示"
{naicha:param name="…"} name、default 取参数:先看控制器传的 param,再看 URL 上的同名查询参数,都没有就用 default。{naicha:param name="cid"} 是最常用的一个(当前栏目 id)
{naicha:url } type = list|detail|page|tag|admin,配 cid/id/alias/name/path;或直接给 path 生成站内地址。会自动带上当前语言的前缀 —— 所以英文站里的 /list/x 会变成 /en/list/x,不要自己拼前缀
{naicha:page} total、num、page、param 分页条。不传 total 就复用最近一次 {naicha:list} 的统计;只有一页时输出空串。param 是页码参数名(默认 page,用户列表用的是 pg)
{naicha:include file="…"}file 套用另一个模板文件(相对当前主题)。文件名只取 basename,写路径穿越无效
{naicha:search}— 当前搜索关键词(等同于 {keyword}),回填到搜索框的 value 里
{naicha:token}— CSRF 令牌,放进表单的隐藏域。页面里有它就不会被整页缓存(缓存下来发给别人会导致提交失败)
{naicha:now}format(默认 Y-m-d) 当前时间(服务器时区)
{naicha:asset file="…"} file 静态资源地址,自动带 ?v= 版本号(取自文件修改时间)。改了文件 URL 就变,浏览器必然重新拉取 —— 比手工维护版本号可靠
{naicha:block name="…"} name 页面片段(后台「站点素材 → 页面片段」里维护的那段文字)。取不到时输出空串,删掉片段不会把站点弄崩
{naicha:filters} cid、show(如 sort,year,tag) 筛选条。它自己读当前地址判断"选了哪些",并生成保留其它参数的链接
{naicha:comments} num、form(0 = 只列不显示表单) 评论区。站点关了评论、或拿不到内容 ID 时输出空串(不报错、不留空壳)
{naicha:commentcount}同 comments 只输出评论条数,适合放在标题旁边
{naicha:captcha}— 验证码控件(一行输入框 + 算式)。站点没开验证码时输出空串,模板不用自己判断

全局变量

这些不是标签,是控制器交给模板的值。取法就是 {名字}, 可以带修饰符(见「标签语法总览」那篇)。

变量含义
{seo.title} {seo.keywords} {seo.desc}当前页的 SEO 三件套(页头里那三个 meta)
{canonical}规范地址。多语言站按语言区分 —— 所有语言都指同一个 canonical 等于白翻
{currentPath}当前路径。导航高亮就是拿它和导航项的 url 比
{title}页面主标题(部分页面提供,如商品列表的「全部商品」)
{keyword}搜索关键词
{lang.html} {lang.dir} {lang.name} {lang.code} {lang.prefix} {lang.on}当前语言。写在 <html lang="…" dir="…"> 上
{lang.hreflang html="1"}输出各语言的 hreflang 标注。必须带 html="1",否则标签会被转义成文本显示在页面上
{lang.list}语言列表,配 {naicha:loop from="lang.list" as="L"} 用(语言切换器)
{listTotal} {listPages} {listItems}最近一次 {naicha:list} 的总数 / 页数 / 本次条数。写「共 N 条」「没有结果」靠它们
{shopTotal} {shopItems}最近一次 {naicha:shop} 的同名统计(命名刻意与 list 一致)
{crumbs}面包屑数组,配 {naicha:loop from="crumbs" as="c"},行内可用 {c.name} {c.url}
{category.…}当前栏目(栏目页与详情页提供),如 {category.alias} {category.name}
{hasShop} {shopItem.…} {shopUnlock.…} {loginUrl}详情页的商品与付费解锁。hasShop 是 '1'/'0' 开关(不要让模板去判断数组——引擎对数组会先 count)
{viewSwitch.on} {viewSwitch.url} {viewSwitch.label}手机版 / 电脑版切换入口
{adminBase}后台地址前缀(后台模板里拼链接用)

每个单标签的用法演示

下面每一段都是可以直接粘进模板的最小片段。 方括号里写的是"这段放在哪":[页头] 页头模板、[列表] 列表模板、 [详情] 详情模板、[任意] 哪都能放。

{naicha:site name="…"}

<!-- [页头] 站点名 + 描述,回落到站点设置 -->
<title>{naicha:site name="title"}</title>
<meta name="description" content="{naicha:site name="description"}">

<!-- [页脚] 联系方式(这三个键在「站点设置 → 站点信息」里填)-->
<a href="mailto:{naicha:site name="contact_email"}">{naicha:site name="contact_email"}</a>
<span>{naicha:site name="contact_wechat"}</span>

<!-- [任意] 统计代码:站点设置里存的是一整段 HTML,必须带 html="1" -->
{naicha:site name="stat_code" html="1"}

别用它输出正文。它只读「站点设置」里那一张表,取不到就是空串 —— 想让"某个键没填时整块不显示",外面套一层 {naicha:if var="…" notempty="1"}。

{naicha:brand}

<!-- [页头] 品牌字标:输出两段,后一段可以换个颜色 -->
<a class="logo" href="/">{naicha:brand}</a>

<!-- 配套 CSS:NaichaCMS 会被拆成 Naicha + CMS -->
<style>
.logo .brand-a{color:#1e293b}
.logo .brand-b{color:#0078d4}   /* CMS 用品牌色 */
</style>

拆分规则是在最后一个「小写→大写」的驼峰边界断开; 中文站名(如「奶茶科技」)没有这种边界,就整段当第一段、第二段为空 —— 不会把站名拆坏。

{naicha:icp}

<!-- [页脚] 备案号。没填时输出空串,配合下面这条 CSS 不会留空隙 -->
<div class="footer-bottom">
  {naicha:copyright}
  {naicha:icp}
</div>

<style>.footer-bottom span:empty{display:none}</style>

它自己会包成指向 beian.miit.gov.cn 的链接。 海外站不需要备案,所以它必须是"空就不显示",而不是在模板里判断有没有填。

{naicha:param name="…"}

<!-- [列表] 当前页码:交给列表标签与分页条 -->
{naicha:list cid="{naicha:param name='cid'}" num="10" page="{naicha:param name='page'}"}
  …
{/naicha:list}

<!-- [搜索页] 回填关键词;不存在时用默认值 -->
<input type="search" name="kw" value="{naicha:param name="kw"}" placeholder="搜点什么">
<input type="hidden" name="from" value="{naicha:param name="from" default="nav"}">

取值顺序:控制器传的 param → URL 查询参数 → default。 ⚠️ 属性里的引号:外层用双引号时里面那层要写单引号({naicha:param name='cid'}), 写成一样的一对会把标签截断。

{naicha:url …}

<!-- [任意] 栏目地址(别名优先,别名空则用 id)-->
<a href="{naicha:url type="list" cid="3"}">解决方案</a>

<!-- [任意] 内容详情地址 -->
<a href="{naicha:url type="detail" id="12" alias="hello-world"}">读全文</a>

<!-- [任意] 单页 / 标签聚合 / 后台 -->
<a href="{naicha:url type="page" alias="about"}">关于我们</a>
<a href="{naicha:url type="tag" name="模板"}">#模板</a>
<a href="{naicha:url type="admin" path="contents"}">内容管理</a>

<!-- [任意] 只想拼一个站内相对地址 -->
<a href="{naicha:url path="/list/news"}">新闻</a>

⚠️ 它会自动带上当前语言的前缀:英文站里 /list/news 会变成 /en/list/news。 所以不要自己拼 /en/,两处都加就会变成 /en/en/…。

{naicha:page …}

<!-- [列表] 分页条。紧跟在列表标签后面即可,它会复用那次列表的总数 -->
{naicha:list cid="{naicha:param name='cid'}" num="10" page="{naicha:param name='page'}"}
  <a href="{list.url}">{list.title}</a>
{/naicha:list}
{naicha:page}

<!-- [后台列表] 明确给数字,页码参数换成 pg(用户列表用的是 pg)-->
{naicha:page total="{total}" num="20" page="{page}" param="pg"}

<!-- [列表] 只有一页时它输出空串,所以外面不用判断 -->
<div class="pager-wrap">{naicha:page}</div>

不传 total 就复用最近一次 {naicha:list} / {naicha:shop} 的统计。链接会保留当前地址上的其它查询参数(筛选、搜索词), 所以翻页不会把筛选条件丢掉。

{naicha:include file="…"}

<!-- [任意] 套用同主题下的另一个文件 -->
{naicha:include file="header.html"}
…页面内容…
{naicha:include file="footer.html"}

<!-- [详情] 把"骨架"拆成片段,九套版式共用同一份 -->
{naicha:include file="detail_head.html"}      <!-- 标题区 -->
{naicha:include file="detail_action.html"}    <!-- 购买/下载/门槛 -->
{naicha:include file="detail_foot.html"}      <!-- 上下篇 + 相关推荐 -->

文件名只取 basename,所以写 ../../config/config.php 也只会去主题目录里找 config.php —— 路径穿越无效。子主题里没有这个文件时会自动回落到父主题。

{naicha:search} / {keyword}

<!-- [搜索页] 搜索框 + 关键词高亮(两个标签等价,search 更好读)-->
<form action="/search" method="get">
  <input type="search" name="kw" value="{naicha:search}">
  <button>搜索</button>
</form>

<!-- [搜索页] 结果列表:标题里的关键词会被包成 <mark class="nc-hl"> -->
{naicha:list keyword="{naicha:search}" num="10"}
  <a href="{list.url}">{list.title highlight="{naicha:search}"}</a>
{/naicha:list}

<!-- [搜索页] 空结果时给一句人话 -->
{naicha:if var="listTotal" eq="0"}没有找到与「{naicha:search}」相关的内容。{/naicha:if}

highlight 是先转义、再插标签的(顺序反了就是 XSS 入口), 所以它能安全地用在用户可控的关键词上。

{naicha:token}

<!-- [任意表单] 每个 POST 表单里都要有,否则会被 CSRF 拦下 -->
<form method="post">
  <input type="hidden" name="_token" value="{naicha:token}">
  <textarea name="content"></textarea>
  <button type="submit">提交</button>
</form>

⚠️ 页面里出现它,整页缓存就会自动跳过这一页(缓存下来发给别人会让提交失败)。 所以别把它放进"人人相同、且希望被缓存"的页面里 —— 那类页面本来也不该有表单。

{naicha:now …}

<!-- [页脚] 年份:用 U 拿时间戳,配合别的标签算版权年份 -->
<span>© {naicha:now format="Y"} {naicha:site name="title"}</span>

<!-- [任意] 完整时间 / 只到天 -->
<time>{naicha:now format="Y-m-d H:i"}</time>
<time>{naicha:now}</time>   <!-- 默认 Y-m-d -->

时间格式就是 PHP 的 date() 那一套。 时区取站点设置;没设就是服务器时区(NaichaMail 那类应用会自己设 Asia/Shanghai,前台不必重复设)。

{naicha:asset file="…"}

<!-- [页头] 样式与脚本,自动带 ?v= 版本号 -->
<link rel="stylesheet" href="{naicha:asset file="static/css/site.css"}">
<script src="{naicha:asset file="static/js/site.js"}" defer></script>

<!-- [详情] 只有这一页需要的播放器脚本 -->
<script src="{naicha:asset file="static/js/player.js"}"></script>

<!-- [任意] 图片同理(图标、logo 这类静态图)-->
<img src="{naicha:asset file="static/img/arrow.svg"}" alt="">

版本号取自文件修改时间(filemtime 的后 7 位)—— 所以改了文件 URL 就会变,不用手工维护版本号。 ⚠️ 代价要知道:重新解压/覆盖部署会把 mtime 刷成新值,于是所有访客的缓存集体失效、重下一遍。 这不是 bug,但如果你在意那一次流量,就在部署时保留原 mtime(tar -m / unzip -X)。 (后台自己的样式表用的是内容哈希 md5_file,两处机制不同,别互相套用。)

{naicha:block name="…"}

<!-- [首页] 一段可以在后台改的文案(后台「内容与外观 → 页面片段」)-->
<section class="hero">
  <h1>{naicha:block name="home_hero_title"}</h1>
  <p>{naicha:block name="home_hero_sub"}</p>
</section>

<!-- [任意] 片段里存 HTML 时带 html="1" -->
<div class="notice">{naicha:block name="site_notice" html="1"}</div>

<!-- [任意] 取不到片段就整块不显示(删掉片段不会把站点弄崩)-->
{naicha:if var="site_notice" notempty="1"}
  <div class="notice">{naicha:block name="site_notice" html="1"}</div>
{/naicha:if}

片段的意义是改文案不动 HTML 结构: 站长想改首页标题,不该需要去编辑模板文件。

{naicha:detailbody} 与 {naicha:toc}

<!-- [详情] 正文 + 侧栏章节目录(两件配套用)-->
<div class="dt-wrap">
  <article class="article">
    {naicha:detailbody}        <!-- 正文 HTML(已给 h2/h3 加好锚点)-->
  </article>
  <aside class="dt-side">
    {naicha:toc title="本页目录"}
  </aside>
</div>

detailbody 与 {detail.content html="1"} 输出的是同一份正文, 差别是它会给正文里的 h2/h3 自动补上 id 锚点(已经手写过 id 的尊重原值,不覆盖)。 toc 就是根据那些锚点生成目录,所以正文必须用 detailbody, 否则目录里点进去会跳不动。toc 的 title 属性是目录标题,留空则用「本页目录」。 正文里一个 h2/h3 都没有时,toc 输出空串。

{naicha:filters}

<!-- [列表] 筛选条:站点设置里开启筛选功能后用它 -->
{naicha:filters show="sort,year,tag"}

<!-- 它自己读当前地址判断"选了哪些",并生成保留其它参数的链接。
     想让它真的生效,列表标签要开 filters: -->
{naicha:list cid="{naicha:param name='cid'}" num="10" filters="1"}
  …
{/naicha:list}

⚠️ filters="1" 只加在"响应地址栏筛选"的那个主列表上。 侧栏的「最新 5 篇」这类局部列表不要开 —— 否则用户点一个筛选,侧栏内容也跟着变,看着莫名其妙。 show 里没写的维度不显示(如 show="year" 就只有年份)。

{naicha:comments …}

<!-- [详情] 评论区(列表 + 表单)-->
<section class="comments">
  <h2>评论 <span>{naicha:commentcount}</span></h2>
  {naicha:comments}
</section>

<!-- [详情] 只要列表、不要表单(比如表单放在页面别处)-->
{naicha:comments num="20" form="0"}

站点关了评论、或当前拿不到内容 ID 时,它输出空串(不报错、也不留一个空壳)。 所以模板里不用写"如果开了评论就…"。

{naicha:commentcount}

<!-- [详情] 标题旁边那一个数字 -->
<h1>{detail.title}</h1>
<div class="meta">浏览 {detail.hits} · 评论 {naicha:commentcount}</div>

<!-- [列表] 想在列表里显示每篇的评论数:只能走 list 的字段,
     这个标签是"当前内容"的评论数,循环里不是你要的数 -->

属性和 comments 一样(num / form),但它只输出数字。

{naicha:captcha}

<!-- [表单] 验证码控件:一行输入框 + 一道算式 -->
<form method="post">
  <input type="hidden" name="_token" value="{naicha:token}">
  <input type="text" name="name" placeholder="称呼">
  {naicha:captcha}
  <button type="submit">提交</button>
</form>

站点没开验证码时输出空串,所以模板不用判断开关 —— 开了就有、没开就没有。后台不做这类判断:模板里有没有这个标签是一回事, 服务端要不要校验是另一回事(服务端只认设置开关)。

取不到的值一律是空串,不报错。变量写错了、字段不存在、循环外取循环内的字段, 页面都照常渲染,只是那块是空的 —— 方便,但也意味着拼错不会有人告诉你。 「套标签」那一篇有一张失败模式表,写模板卡住时先看那张。
Powered by NaichaCMS
没找到答案? 可以在 联系我们 留言说明使用场景,我们会补充进文档。