适用版本:V2.0.0 及以上 | 适合人群:已部署StarPay并准备定制首页的开发者
StarPay 是一套可自行部署的聚合支付系统。除了系统自带的几套首页模板外,你完全可以 从零开发属于自己的首页模板 ——模板即一个普通文件夹,放进 public/web/home/ 目录、后台一键启用即可生效,无需改动任何 PHP 源码。本文将从机制原理讲起,手把手带你完成一个完整的自定义模板。
一、模板机制是怎么工作的?
首页的渲染链路非常简单,一共三步:
- 扫描 :系统会扫描 public/web/home/ 下所有「合法」的模板目录,展示在后台的「首页模板」列表中(每个模板的截图、名称、描述、版本都会展示出来)。
- 选择 :管理员在后台选中某个模板,系统会把模板目录名保存到配置项 home_temp 中。
- 渲染 :用户访问首页时,控制器读取 home_temp ,定位到 public/web/home/{模板目录}/index.html ,用 ThinkPHP 模板引擎渲染后输出给浏览器。
所以对你(模板开发者)来说, 唯一要做的就是:创建一个模板目录,写好里面的文件 。系统会自动识别并允许用户在后台启用。
二、目录结构与必备文件
一个完整的首页模板目录结构如下:
public/web/home/
└── mytheme/ # 模板目录名(建议小写英文,不要有空格和特殊符号)
├── index.html # 首页模板文件(必填,ThinkPHP 模板语法)
├── style.css # 样式文件(必填,且文件头部必须带主题元信息)
├── screenshot.png # 后台列表展示的模板预览图(建议 800x600 左右)
├── js/ # 可选:你的脚本目录
│ └── main.js
└── images/ # 可选:模板用到的本地图片
└── banner.png
三个要点:
- 目录名就是模板的唯一标识 ,后台用它来定位模板,也用它作为配置值保存。
- index.html 是渲染入口,使用 ThinkPHP 模板语法 (不是纯静态 HTML)。
- style.css 是模板的“身份证”,系统只认带元信息的样式文件。
三、style.css 头部元信息(模板的“身份证”)
后台在扫描模板时,会读取 style.css 文件头部的注释 ,从中解析出三个字段: ThemeName (名称)、 Description (描述)、 Version (版本)。 三个字段缺一不可 ,否则该目录不会被识别为有效模板。
参考 aurora 模板的写法:
/*
ThemeName=MyTheme-我的主题
Description=这是我的第一个自定义首页模板
Version=V1.0.0
*/
解析规则说明:
| 字段 | 含义 | 说明 |
| ThemeName | 模板名称 | 后台列表里展示的名称,可中文 |
| Description | 模板描述 | 后台列表里的简介文字 |
| Version | 版本号 | 建议形如 V1.0.0 |
注意:元信息必须写在 CSS 文件 最开头的 /* */ 注释块内 ,一行一个字段,格式为 字段名=值 。写完之后,即使样式内容为空,模板也能被识别。
四、模板里可以使用的变量
这是开发的核心部分。系统在渲染 index.html 时会注入两类数据: 系统配置 和 首页数据数组 。
4.1 系统配置:getConfig()[‘xxx’]
在模板中通过 {:getConfig()[‘键名’]} 语法即可输出任意系统配置。最常用的几个键:
| 键名 | 含义 | 示例写法 |
| sitename | 站点名称 | {:getConfig()['sitename']} |
| title | 站点副标题 | {:getConfig()['title']} |
| key | SEO关键词 | <meta name="keywords" content="{:getConfig()['key']}"> |
| desc | 站点描述 | <meta name="description" content="{:getConfig()['desc']}"> |
| logo | 站点 Logo 地址 | <img src="{:getConfig()['logo']}"> |
| favicon | 网站图标地址 | <link rel="icon" href="{:getConfig()['favicon']}"> |
| index_popup | 首页弹窗 HTML 内容 | 见下方示例 |
| icp | 备案号 | 页脚常见 |
这些配置全部由站长在后台「系统设置」里维护,你的模板会自动读取,无需写死。
4.2 首页数据数组:$index_array
控制器在渲染前会组装一个 $index_array 数组,里面包含导航、登录状态、公告三类数据。
① 顶部导航: $index_array[‘nav’]
数据来自「导航管理」表(状态为启用、按排序升序)。每条导航包含字段:
| 字段 | 含义 |
| name | 导航名称 |
| url | 导航链接地址 |
| is_target | 是否新窗口打开(1=新窗口) |
在模板中循环输出:
{volist name="index_array.nav" id="vo"}
<a {if condition="$vo.is_target == 1"} target="_blank" {/if} href="{$vo.url}">{$vo.name}</a>
{/volist}
② 登录状态: $index_array[‘is_login’]
值为 1 表示用户已登录, 0 表示未登录。可以用它来做「用户中心 / 登录注册」按钮的切换:
<?php if ($index_array['is_login'] == 1): ?>
<a href="/User/Index">用户中心</a>
<?php else: ?>
<a href="/User/Login">登录 / 注册</a>
<?php endif; ?>
③ 公告列表: $index_array[‘news1’] / news2 / news3
分别对应后台「公告管理」中类型为 1 / 2 / 3 的公告,每类最多 5 条。每条公告包含 title (标题)、 color (标题颜色)、 content (内容)、 create_time (发布时间)等字段。示例:
温馨提示:以上数据均以“导航、登录按钮”为最常见用法。公告栏属于可选项,模板里不展示公告也完全没问题。
{volist name="index_array.news1" id="vo"}
<a href="javascript:;" style="color:{$vo.color}">{$vo.title}</a>
{/volist}
五、静态资源引用规则(重要!)
模板里引用自己目录下的 CSS/JS/图片时, 路径必须以 /web/home/{$resource}/ 开头 ,其中 {$resource} 就是当前模板的目录名(系统自动注入的变量)。
以 aurora 模板为例:
<link rel="stylesheet" href="/web/home/{$resource}/style.css">
<script src="/web/home/{$resource}/js/main.js"></script>
<img src="/web/home/{$resource}/images/banner.png">
为什么不能写死目录名? 两个原因:
- 模板目录名由用户上传时决定,可能会被改动(比如从 mytheme 改名成 my-theme-v2 );
- 将来模板被打包分发到其他站点时,目录名可能与开发时不一致。
使用 {$resource} 动态拼接,无论目录叫什么,资源都能正确加载。
六、实战:从零开发一个极简模板
下面我们动手创建一个名为 mytheme 的完整模板,包含导航栏、登录按钮、Hero 区、页脚和公告栏。
第 1 步:创建目录与样式文件
在 public/web/home/ 下创建目录 mytheme/ ,然后创建 style.css :
/*
ThemeName=MyTheme-极简首页
Description=极简风格的首页模板,适合快速上线
Version=V1.0.0
*/
:root {
--primary: #2563eb;
--bg: #f6f8fc;
--text: #0f172a;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
background: var(--bg);
color: var(--text);
}
.container { width: min(1200px, 92vw); margin: 0 auto; }
.site-header {
background: #fff;
border-bottom: 1px solid #e5e9f2;
padding: 14px 0;
}
.nav { display: flex; align-items: center; justify-content: space-between; }
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.brand-logo { height: 36px; }
.brand-text { font-size: 20px; font-weight: 700; }
.nav-links { display: flex; align-items: center; gap: 20px; }
.nav-links a { text-decoration: none; color: #475569; font-size: 14px; }
.nav-links a:hover { color: var(--primary); }
.btn { display: inline-block; padding: 10px 22px; border-radius: 8px; text-decoration: none; }
.btn-primary { background: var(--primary); color: #fff; }
.hero { padding: 80px 0; text-align: center; }
.hero h1 { font-size: 42px; margin-bottom: 16px; }
.hero p { color: #64748b; font-size: 16px; }
.news { margin: 40px 0; }
.news-card { background: #fff; border-radius: 12px; padding: 20px 24px; box-shadow: 0 2px 8px rgba(15, 23, 42, .06); }
.news-title { font-size: 18px; font-weight: 700; margin-bottom: 12px; }
.news-list li { list-style: none; padding: 8px 0; border-bottom: 1px dashed #eef1f6; }
.news-list a { color: #334155; text-decoration: none; font-size: 14px; }
.site-footer { background: #0f172a; color: #94a3b8; text-align: center; padding: 24px 0; font-size: 13px; margin-top: 60px; }
</style>
第 2 步:编写 index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{:getConfig()['sitename']} - {:getConfig()['title']}</title>
<meta name="keywords" content="{:getConfig()['key']}">
<meta name="description" content="{:getConfig()['desc']}">
<link rel="icon" type="image/x-icon" href="{:getConfig()['favicon']}">
<link rel="stylesheet" href="/web/home/{$resource}/style.css">
</head>
<body>
<header class="site-header">
<div class="container nav">
<a href="/" class="brand">
<img class="brand-logo" src="{:getConfig()['logo']}" alt="{:getConfig()['sitename']}">
<span class="brand-text">{:getConfig()['sitename']}</span>
</a>
<nav class="nav-links">
{volist name="index_array.nav" id="vo"}
<a {if condition="$vo.is_target == 1"} target="_blank" {/if} href="{$vo.url}">{$vo.name}</a>
{/volist}
<?php if ($index_array['is_login'] == 1): ?>
<a class="btn btn-primary" href="/User/Index">用户中心</a>
<?php else: ?>
<a class="btn btn-primary" href="/User/Login">登录 / 注册</a>
<?php endif; ?>
</nav>
</div>
</header>
<main>
<section class="hero">
<div class="container">
<h1>{:getConfig()['sitename']}</h1>
<p>{:getConfig()['desc']}</p>
<p style="margin-top:24px;">
<a class="btn btn-primary" href="/User/Login">立即接入</a>
</p>
</div>
</section>
<section class="news">
<div class="container news-card">
<div class="news-title">系统公告</div>
<ul class="news-list">
{volist name="index_array.news1" id="vo"}
<li><a href="javascript:;" style="color:{$vo.color}">{$vo.title}</a></li>
{/volist}
</ul>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container">© {:getConfig()['sitename']} 保留所有权利{notempty name="getConfig()['icp']"} | {:getConfig()['icp']}{/notempty}</div>
</footer>
</body>
</html>
第 3 步:放一张预览图
在 mytheme/ 目录下放一张 screenshot.png 截图(可以先浏览器预览后截图,尺寸建议 800×600 以上),这样后台列表里就能看到模板的预览效果。
第 4 步:在后台启用
- 进入后台 → 首页模板 ;
- 列表中应能看到「MyTheme-极简首页」以及你上传的截图;
- 点击「设置」即可一键启用,访问前台首页立即生效。
七、补充说明:首页弹窗(可选功能)
系统提供「首页公告弹窗」能力,后台配置项 index_popup 里填写的 HTML 会在首页弹出展示。如果你想让模板支持这个功能,可以参考 aurora 模板的做法:用 {:getConfig()[‘index_popup’]} 渲染弹窗内容,配合 sessionStorage 实现“同一天只弹一次”。
核心片段:
<?php if (!empty(getConfig()['index_popup'])): ?>
<div id="home-index-popup" style="display:none; ...">
<div><?php echo getConfig()['index_popup']; ?></div>
</div>
<?php endif; ?>
完整逻辑建议直接参考自带的 aurora 模板源码( public/web/home/aurora/index.html 底部),它已经处理好了空内容判断、会话去重、关闭按钮等细节,是最佳范例。
八、常见问题 FAQ
Q1:为什么后台看不到我创建的模板? 依次排查:① 目录必须放在 public/web/home/ 下;② 目录内必须有 style.css ;③ style.css 头部必须同时包含 ThemeName 、 Description 、 Version 三个字段;④ 如果开了 PHP OPcache/模板缓存,可稍等片刻或清一下缓存再看。
Q2:模板目录名能是中文吗? 不建议。目录名会写入数据库配置、参与 URL 拼装,建议只用小写英文字母、数字和下划线。
Q3: index.html 里能用完整的 PHP 吗? 只能用模板引擎支持的语法( {:函数()} 、 {$变量} 、 {volist} 、 {if} 等)以及少量内联 标签。凡是涉及数据查询的业务逻辑,都应交给系统控制器完成,模板里只负责展示系统注入好的变量。
Q4:删不掉我自己上传的模板怎么办? 后台「首页模板」列表里,系统内置的 default 、 old 、 aurora 三个模板受保护、不允许删除;其余模板可在后台直接删除,也可以手动删除对应的目录。
Q5:我的模板能分享/出售给别人吗? 可以。一个模板就是 public/web/home/{目录名}/ 下的一堆文件,把整个目录打包成 zip 发给别人,对方解压放到自己的 public/web/home/ 目录即可。如果接入应用商店体系, home-theme 类型的安装包也会被自动安装到 public/web/home/ 目录,实现一键分发。
九、小结
开发 StarPay 首页模板的本质,就是 写一个放在 public/web/home/{模板名}/ 下的目录 :
| 文件 | 作用 | 是否必填 |
| index.html | 首页页面模板(ThinkPHP 模板语法) | ✅ 必填 |
| style.css | 样式文件,头部带 ThemeName/Description/Version 元信息 | ✅ 必填 |
| screenshot.png | 后台列表预览图 | ✅ 建议 |
| js/ 、 images/ 等 | 模板配套资源,用 /web/home/{$resource}/ 引用 | 可选 |
掌握了目录结构、元信息、注入变量、资源引用四个知识点,你就能开发出任何风格的首页模板。动手试试吧,期待看到你的作品!







暂无评论内容