MacCMS 模板二次开发实战 +「Skill 策略」
想给自建的 MacCMS 站点换个样子——改配色、加一块推荐榜、把列表改成瀑布流、顺手适配一下手机——其实都是在动「模板」。本文结合 MacCMS V10(magicblack/maccms10)真实源码,讲清模板到底长什么样、那些 {maccms:...} 标签怎么来的、改完怎么生效;再给出一套把这些经验沉淀成「Skill 策略」的方法,让 AI 助手按规矩帮你改模板。
读之前先了解整体架构
本文只聚焦「模板二开」。MacCMS 的整体架构(ThinkPHP 5.0.24、三入口、请求生命周期、API 两套)在源码结构分析里讲过,本文不重复,涉及底层时直接引用那篇。
结论来自实际读源码
下面所有路径、标签名、配置项都来自实际阅读 d:\maccms10 源码(只读,未改动任何源码文件)。不同发行版的目录可能略有增减,以你本地源码为准。
一、模板目录长什么样
MacCMS 的前台模板都放在源码根目录的 template/ 下。源码自带一套名为 default 的模板集:
template/
└── default/ # 一套完整模板集(可以有多套,如 template/1、template/2)
├── info.ini # 模板信息文件
├── config.defaults.json.example # 模板主题配置示例
├── asset/ # 该模板自带的静态资源
│ ├── css/ js/ img/ fonts/ language/ ...
├── images/ # 模板图片
└── html/ # 模板文件主目录(真正要改的在这里)
├── index/ # 首页
├── vod/ # 视频模块(详情/播放/分类/搜索…)
├── art/ # 文章模块
├── user/ # 用户中心
├── public/ # 全站公共片段(头、尾、引入文件、分页…)
├── module/ widget/ # 可复用的页面区块
├── topic/ actor/ role/ manga/ website/ plot/ ... # 其它内容模块
├── comment/ gbook/ # 评论、留言本
├── map/ rss/ # sitemap、RSS
└── ads/ # 广告位片段规律很清晰:一套模板 = 一个 template/模板名/ 目录,里面 html/ 按「业务模块」分子目录,asset/(或 css/js/images)放这套模板自己的样式脚本。想做一套全新皮肤,常见做法是复制 default 为 template/mytheme,在副本里改,不动原版。
html/ 下几个最常打交道的目录:
| 目录 | 放什么 |
|---|---|
index/ | 首页 index.html |
vod/ | 视频相关页:detail.html(详情)、play.html(播放)、type.html(分类)、show.html(筛选)、search.html(搜索)等 |
art/ | 文章相关页,结构与 vod 对应 |
user/ | 用户中心各页(登录、注册、收藏、充值…) |
public/ | 全站公共片段:include.html(引入 js/css)、head.html(头部)、foot.html(尾部)、paging.html(分页)等 |
module/、widget/ | 可被各页 include 的可复用区块(如首页榜单、标签墙) |
ads/ | 广告位片段 |
自带标签速查表就在源码里
源码 说明文档/标签说明.txt 是一份完整的模板文件清单 + 标签字段表(视频/文章/演员/专题等每个模块能用哪些字段、列表标签支持哪些筛选参数)。改模板时把它当字典查最省事——但标签的实际行为以源码为准(见下一节)。
二、模板标签引擎:{maccms:…} 是怎么来的
MacCMS 的模板不是纯 HTML,而是在 ThinkPHP5 原生模板引擎之上扩展了一套自有标签。两层关系要分清:
底层:ThinkPHP5 的 Think 模板引擎
application/config.php 的 template 配置段里写死了引擎类型和标记:
'template' => [
'type' => 'Think', // 用 TP5 自带的 Think 模板引擎
'view_path' => './template/',
'view_suffix' => 'html',
'tpl_begin' => '{', // 普通标签 {$var}
'tpl_end' => '}',
'taglib_begin' => '{', // 标签库标签 {maccms:xxx}
'taglib_end' => '}',
'taglib_pre_load' => 'app\common\taglib\Maccms', // 预加载 MacCMS 标签库
],所以模板里能直接用 TP5 原生语法:
- 输出变量:
{$maccms.site_name}、{$obj.vod_name} - 引入片段:
{include file="public/head"} - 条件判断:
{if condition="$obj.vod_jumpurl neq ''"}...{/if} - 循环:
{volist name="list" id="vo"}...{/volist} - 调用函数:
{:mac_url_vod_detail($obj)}、{:mac_url_img($vo.vod_pic)}
上层:MacCMS 自己的标签库 TagLib
关键在那行 taglib_pre_load——它把 app\common\taglib\Maccms 预加载进了模板引擎。这个类(源码 application/common/taglib/Maccms.php)继承自 TP5 的 think\template\TagLib,用 $tags 数组声明了所有自定义标签和它们支持的属性:
class Maccms extends Taglib {
protected $tags = [
'type' => ['attr' => 'order,by,start,num,id,ids,...'],
'vod' => ['attr' => 'order,by,start,num,type,class,tag,area,year,level,...'],
'art' => ['attr' => '...'],
'actor' => ['attr' => '...'],
'topic' => ['attr' => '...'],
'link' => ['attr' => '...'],
'foreach' => ['attr' => 'name,id,key'],
// 还有 area / lang / year / class / version / comment / gbook / manga / website ...
];
// 每个标签对应一个 tagXxx() 方法,把 {maccms:vod ...} 编译成底层 PHP + volist 循环
public function tagVod($tag, $content) { ... }
}工作原理:模板里写 {maccms:vod num="6" order="desc" by="hits_month"}...{/maccms:vod},引擎在编译阶段调用 tagVod(),它内部把标签翻译成「查数据 + {volist} 循环」的底层代码。所以**{maccms:xxx} 本质是 TP5 TagLib 的语法糖**,循环体里再用 {$vo.字段名} 取每条记录的字段。
一个真实的首页热门推荐片段(取自 template/default/html/index/index.html):
<ul class="img-list">
{maccms:vod num="6" level="1,2,3,4,5,6,7,8,9" order="desc" by="hits_month"}
{notempty name="vo"}{include file="widget/vod_box"}{else}{include file="public/notempty"}{/notempty}
{/maccms:vod}
</ul>这段的意思:取月点击量最高的 6 条视频,循环里把每条交给 widget/vod_box 片段渲染;没有数据就渲染占位片段。
列表标签的常用筛选属性
{maccms:vod} / {maccms:art} 这类列表标签支持一大把筛选属性:num(条数)、order(asc/desc)、by(time/hits/hits_month/level/rnd…)、type(分类)、class(扩展分类)、area/lang/year(地区/语言/年代)、level(推荐值)、ids(指定 id)、not(排除 id)、paging="yes"(翻页)等。完整清单看源码 说明文档/标签说明.txt。
三、静态资源:asset 与 static_new 怎么分工
模板里引用静态资源,统一用全局变量拼路径,而不是写死域名:
{$maccms.path}:网站根目录(带末尾斜杠),指向站点根。{$maccms.path_tpl}:当前模板目录,等价于template/当前模板名。
实际源码里两类资源并存:
<!-- 模板自带资源:跟着这套皮肤走 -->
<link rel="stylesheet" href="{$maccms.path}template/default/asset/css/index.css?v=xxx">
<img src="{$maccms.path_tpl}/images/la/home-icon10.png">
<!-- 全站共享资源:所有模板通用,如播放器库 -->
<script src="{$maccms.path}static_new/player/dplayer/flv.min.js"></script>分工大致是:
| 位置 | 作用 |
|---|---|
template/模板名/asset(或 css/js/images) | 这套模板专属的样式、脚本、图片,换皮肤就跟着换 |
static / static_new | 全站共享的基础资源(播放器 DPlayer、layui、字体、公共 js 等),不随模板切换 |
加版本号防缓存
源码里大量 ?v=202604169 这样的版本参数,就是改完 css/js 后强制浏览器重新拉取。你自己改样式后,顺手把版本号加一,省得读者看到旧样式。
四、后台怎么配置与编辑模板
选用哪套模板
当前用哪套模板、html 目录叫什么,存在 MacCMS 配置里(源码 application/extra/maccms.php 的 site 段):
'site' => [
'template_dir' => 'default', // PC 端用的模板集目录名
'html_dir' => 'html', // 模板文件子目录名
'mob_status' => '0', // 是否启用独立手机模板
'mob_template_dir' => 'default', // 手机端模板集
'mob_html_dir' => 'html',
'ads_dir' => 'ads', // 广告位目录
// ...
],这些值由后台「网站参数配置」里的模板选项写入,不建议手改配置文件。切换模板,本质就是把 template_dir 指到另一个 template/xxx 目录。mob_status=1 时手机端会走 mob_template_dir 那套独立模板。
在线编辑模板文件
后台自带一个模板文件管理器(源码 application/admin/controller/Template.php):它读的就是「当前模板」目录(config('maccms.site') 里的 template_dir + html_dir),能在浏览器里直接浏览、编辑 html/ 下的模板文件和 ads/ 广告位。
这个入口只允许改模板目录
Template::index() 对路径做了限制:请求的 path 必须以 .@template 开头,并且会剔除 /、\ 等字符、限制层级,防止越权读写模板目录以外的文件。也就是说后台模板编辑器设计上只让你碰 template/ 下的东西,这是好事——别想着用它去改框架文件。
五、实操:改一个页面
目标:把首页「热门推荐」默认取 6 条改成 10 条,并换个排序。
定位文件。首页在
template/default/html/index/index.html(default换成你实际在用的template_dir)。找到标签。搜到那段
{maccms:vod num="6" ... by="hits_month"}。改属性。把
num="6"改成num="10",想按最新更新排就把by="hits_month"换成by="time":html{maccms:vod num="10" level="1,2,3,4,5,6,7,8,9" order="desc" by="time"} {notempty name="vo"}{include file="widget/vod_box"}{else}{include file="public/notempty"}{/notempty} {/maccms:vod}清缓存看效果。MacCMS 会缓存编译后的模板和页面。改完如果前台没变化,去后台清一次缓存,或删掉
runtime/下的缓存文件(runtime目录可写,见源码结构分析),再刷新前台。
两条红线
- 别动
thinkphp/核心:模板二开只该碰template/和它的asset。改框架文件会在升级时被覆盖,还可能破坏安全补丁(见安全加固)。 - 别破坏标签语法:
{maccms:vod}必须成对闭合{/maccms:vod},{if}/{volist}/{include}同理。少一个闭合标签,整页编译报错白屏。
常见「改一个页面」的落点:
- 换配色/字体 → 改
template/模板名/asset/css/*.css,加版本号。 - 加一块推荐榜 → 在目标页复制一段
{maccms:vod ...}{/maccms:vod},改by/type/num。 - 改列表卡片样式 → 多半在
widget/vod_box.html这类可复用片段里。 - 适配移动端 → 要么在现有模板写响应式 css,要么开
mob_status用独立手机模板。
六、Skill 策略:把模板二开交给 AI 来做
上面这些改动有个共同点:重复、套路化、边界清晰。换配色、加版块、改卡片、适配移动端——每次的步骤几乎一样。这正适合沉淀成一套「Skill 策略」,让 AI 助手照着执行。
这里说的「Skill」是方法论
本节的「Skill 策略」指的是你写给 AI 的一套结构化指令/检查清单/提示词模板,不是某个具体软件的插件。核心思想:把「怎么改 MacCMS 模板」这件事从你脑子里的经验,变成一份可复用、可交给 AI 执行的文档。
什么是「Skill 策略」
一句话:把一类重复的模板改造任务,抽象成结构化的指令集 + 检查清单。好处是——
- 你不用每次从头交代背景,AI 一上来就知道 MacCMS 的目录和标签。
- 改动有固定步骤和验证环节,不容易漏(比如忘了清缓存)。
- 把「不能碰什么」写死,AI 不会手滑去改框架核心。
一套合格的模板二开 Skill 策略应包含的要素
| 要素 | 内容 |
|---|---|
| 目标描述 | 这次要达成什么效果(换色/加版块/改列表/移动端适配),尽量给出可验收的标准 |
| 模板定位方法 | 当前模板在 template/{template_dir}/html/{模块}/;公共片段在 public/、module/、widget/ |
| 标签知识 | {maccms:vod/art/...} 列表标签 + {$obj.字段};筛选属性查 说明文档/标签说明.txt |
| 改动步骤 | 定位文件 → 改标签/样式 → 加版本号 |
| 验证方法 | 清 runtime 缓存(或后台清缓存)→ 前台刷新核对效果 |
| 不可触碰边界 | 不改 thinkphp/ 核心、不改 vendor/;标签必须成对闭合;不写死域名用 {$maccms.path} |
可直接套用的策略模板
策略模板 A:通用「改一个前台页面」清单
# Skill:MacCMS 模板改一个页面
## 背景(固定)
- 站点:MacCMS V10(ThinkPHP5),模板引擎 Think + 自定义 TagLib。
- 当前模板目录:template/{template_dir}/html/,{template_dir} 默认 default。
- 资源路径用 {$maccms.path}(站点根)、{$maccms.path_tpl}(当前模板目录)。
## 输入(每次填)
- 页面:<首页 / 视频详情 / 分类页 / ...>
- 想要的改动:<一句话目标>
## 执行步骤
1. 按模块定位 html 文件(首页=index/index.html,视频详情=vod/detail.html…)。
2. 只改该文件内的标签/HTML/样式;复用区块优先改 widget/ 或 module/ 下的片段。
3. 用到数据列表时,用 {maccms:vod ...}{/maccms:vod} 并保证闭合;字段用 {$vo.字段名}。
4. 改 css/js 后在引用处把 ?v= 版本号 +1。
## 验证
- 清 runtime 缓存(或后台清缓存)→ 打开对应前台页面核对。
- 页面不白屏、标签全部成对闭合、无 PHP 报错。
## 禁止
- 不修改 thinkphp/、vendor/ 任何文件。
- 不写死协议/域名;不在模板里放密钥。
- 不破坏 {maccms:...}/{if}/{volist}/{include} 的成对语法。策略模板 B:「加一块推荐版块」最小提示词
在 template/{template_dir}/html/index/index.html 的 <目标位置> 新增一块「<版块名>」:
- 用 {maccms:vod num="10" type="<分类id或all>" order="desc" by="<time|hits_month|level>"} 取数据;
- 循环体复用现有卡片片段(如 {include file="widget/vod_box"}),没有则按同页其它卡片的 class 结构写;
- 标签必须 {/maccms:vod} 闭合;
- 新增/改动 css 放到 template/{template_dir}/asset/css/ 并在引用处加版本号;
- 完成后提示我清缓存并给出需要核对的前台 URL。
不要改 thinkphp/ 和 vendor/,不要写死域名。安全与可维护(策略里务必写死)
把 AI 当成一个会照做的新手,边界必须显式写进策略:
- 输出转义防 XSS:模板里凡是回显用户可控内容(评论、留言、搜索词),用转义输出,别直接
{$var|raw}原样打印。 - 不在模板写死密钥:接口 key、第三方令牌放配置,不要硬编码进 html。
- 改动走版本管理:改模板前先
git托管这套template/模板名/,每次改动留提交记录,出问题能回滚。 - 先备份再大改:换皮肤复制
default为新目录改副本,别在原版上直接动刀。