第三十章 详情页模板

Ju7CMS 用户手册 · 发布于 2026-10-27 · 更新于 2026-10-27

详情页渲染一篇文章;按骨架讲面包屑、章节树、正文自动处理与上一章/下一章,并给出三个容易踩的坑。

这一章解决什么

详情页是「一篇文章」的页面,地址形如 /show/?id=314。手册里的每一章都是详情页,全部由 theme/576so/model/show/Ju7CMS1.html 渲染。改这个文件,全书 46 篇一起变;只想改某一章的版面,应该改那一章的正文,而不是改模板。

页面地址形态谁决定内容
手册章节/show/?id=314content 表里 id=314 的那一行
普通文章/show/?id=123同上,栏目决定用哪个模板
单页栏目/list/?classid=16栏目的 content,走列表链路,不是本章

栏目绑定哪一个详情页模板,存在 category.cate_show 列(后台:设置 → 栏目管理 → 编辑 → 内容模板)。手册七个栏目都指向 model/show/Ju7CMS1.html。

一次请求里发生什么

  1. 按 id 从 content 表取一行,只认 islock=1;查不到或有草稿标记就直接 404。
  2. 把这一行的每一列都变成同名模板变量:title、content、createdate、lastupdate、keywords、descriptions、pic、tags,以及字段管理里加的自定义列(手册用的 Ju7_Other)。
  3. 按 classid 取栏目行,补出 classname、catedesc、cateurl、sonid、followid、modeid 等变量。
  4. 处理正文:多页分隔符、正文图补 alt/loading/宽高、内链首处替换、图片防盗链等,都在这一步完成。
  5. 整份模板编译执行,输出 HTML。

正文里用编辑器插入的分页符会被拆成多页,第 2 页起地址会增加 &page=2。分页逻辑只认正文里的分隔标记,不占模板变量。

模板骨架

Ju7CMS1 详情页共 65 行,结构与列表页同源,差异在正文区和翻页区。

文档头与页头

<title>{seotitle}</title><meta name="keywords" content="{seokey}" /><meta name="description" content="{seodesc}" />{andvar:include("../../seo/head.html")}<body class="layout-center docs-chapter">{andvar:include("../../head[langid].html")}

详情页的 title 不带品牌后缀,因为文章的 SEO 标题通常已经写全;列表页才统一补「-聚企互联」。docs-chapter 这个类名只做一件事:手机版把左侧章节树收起来,免得多占一屏。

面包屑

<a href="{webroot}">{c_name}首页</a><a href="{andvar.getcateurl(langid,classid,ismobile)}">{catename}</a><span>{title}</span>

andvar.getcateurl 是公共方法,按当前运行模式拼出栏目地址:动态模式给 /list/?classid=21,静态模式给实际的 .html 路径。不要在模板里手写 /list/?classid=,否则切换运行模式后链接会全部失效。

两栏与章节树

<div class="docs-layout"> <aside class="docs-side" aria-label="手册章节目录"> {andvar:rs top="0" field="autoid,cateid,followid,catename,catelang,htmldir,isnav,ismark,ismenu,ordnum" table="category" where="followid=10 and catelang=[langid]" order="ordnum,autoid" var="cateid:cateid"} <p class="docs-side__book">{$rs[catename]}</p> {andvar:rsm field="id,classid,isurl,url,languages,title,descriptions,ordnum" table="content" where="classid=[cateid] and islock=1" order="ordnum,id"} <a class="docs-side__item {andvar.is_current(id,$rsm[id],"active")}" href="{$rsm[link]}">{$rsm[title]}</a> {/andvar:rsm} {/andvar:rs} </aside>

章节页的左侧目录固定显示整本书(followid=10),当前章用 andvar.is_current 加 active 类高亮:三个参数依次是「当前 id、列表里的 id、命中时输出的类名」。这个方法的第三个参数可省,省了默认输出 active。

正文区

<article class="docs-main"> <h1 class="docs-main__title">{title}</h1> <p class="docs-main__meta">发布于 {andvar.getdate(createdate,"-",1)} · 更新于 {andvar.getdate(lastupdate,"-",1)}</p> <div class="docs-body field-name-body"> {Ju7_Other} {if seodesc<>""}<p class="docs-body__lede">{seodesc}</p>{/if} {content} </div>
写法意思
{Ju7_Other}自定义字段「Ju7_Other」的原样输出。手册用它放章节开头的一句话导语,正文仍在编辑器里
{content}文章正文,HTML 原样输出,不做二次转义
{seodesc}SEO 描述。这里当导语用,所以为空时整段不输出
{andvar.getdate(…)}日期格式化,第三个参数 1 表示只取年月日

{content} 里如果写了模板花括号,会被当作模板表达式执行。手册正文里要显示 {andvar:rs …} 这类示例时,必须写成实体 &#123;、&#125;,否则示例会被编译掉,页面上直接少一段。

上一章 / 下一章

{andvar:rs field="id,classid,isurl,url,languages,title" top="1" table="content" where="classid in(select cateid from [category] where followid='[followid]') and islock=1 and ordnum<[ordnum]" order="ordnum desc,id desc"} <a href="{$rs[link]}">上一章:{$rs[title]}</a>{/andvar:rs}{andvar:rs field="id,classid,isurl,url,languages,title" top="1" table="content" where="classid in(select cateid from [category] where followid='[followid]') and islock=1 and ordnum>[ordnum]" order="ordnum asc,id asc"} <a href="{$rs[link]}">下一章:{$rs[title]}</a>{/andvar:rs}

翻页按 ordnum(章节序号)而不是 id:手册的 46 篇文章分布在六个分组里,按 id 翻只能在本组内前后走,跨组就断。子查询 select cateid from [category] where followid='[followid]' 取「当前栏目的兄弟栏目集合」,[followid] 在章节页等于 10,于是六个分组全部纳入范围。

如果某个栏目没有兄弟栏目,子查询只返回它自己,翻页在本栏目内工作,不会报错。想改成「只在同组内翻」,把子查询换成 classid=[classid],或直接比较 classid。

正文会自动做哪些处理

处理触发条件结果
补图片 alt<img> 没有 alt用当前页 seotitle 填,多图时追加序号
懒加载正文里的 <img>补 loading="lazy" 与 decoding="async"
站内链接后台「文章内链」有匹配词正文里第一次出现该词时替换成链接,只替换一处
多页正文编辑器里插入分页符拆成 &page=N 的多页,自动生成上一页/下一页
手机图字段带 _m 后缀且有值手机端优先用 _m 列的值覆盖同名字段

这些处理都在 lib/show.asp 里,模板只负责取变量。所以即使模板换成另一套,正文的 alt、懒加载与内链行为仍然生效。

三个容易踩的坑

  1. {if} 里比较什么。seodesc<>"" 是字符串比较;islock=1 是数字比较。把布尔值 ismobile 直接写进 SQL 会得到 True,Access 不认识。
  2. 正文里的花括号。见上面的警告。代码示例一律用实体,或者放进 <pre><code> 里也照样会被编译——实体转义是唯一可靠的办法。
  3. 缓存。改模板后要等编译缓存失效;改文章则只影响整页缓存,到缓存管理清一次即可。若是「编辑页存了但前台没变」,先分清楚是模板缓存、整页缓存还是浏览器缓存,见第三十三章。

接下来怎么读

  1. 页头页脚是怎么被引用的:第三十一章,公共片段。
  2. 模板语法还没看熟:第二十八章。
  3. 想换一套主题:第三十五章,主题切换与手机版。