从零做一套主题
复制 default 改个名,把首页、栏目页、详情页、单页、搜索页五个模板写出来,最后换上去。全程可复制。
本页目录(28 分钟读完)
这一章只做一件事:照着抄,做出一套能用的自己的模板。
语法不懂 → 看 模板开发入门。 标签记不住 → 看 模板标签速查。 页面报错 → 看 排错手册。
怎么准备主题目录#
作用:先有一个自己的主题文件夹,改坏了也不影响线上。
步骤:
- 进入项目里的
view目录 - 复制
default整个文件夹 - 把副本改名成
mysite
cd view
cp -r default mysite效果:多出 view/mysite/,结构和 default 一样。
| 文件 | 是什么页面 |
|---|---|
layout.html | 全站骨架 |
common/header.html | 页头导航 |
common/footer.html | 页脚 |
common/pager.html | 分页条 |
index/index.html | 首页 |
index/node.html | 栏目页 |
index/article.html | 详情页 |
index/page.html | 单页 |
index/search.html | 搜索页 |
static/css/style.css | 样式 |
先切过去再改:
- 后台 → 系统设置 → 基础设置
- 「前台模板」选
mysite - 点「保存」
- 跑
php aucms cache:clear
改一点,刷新看一点。
改坏了就切回 default,站点不受影响。打不开? → 看 排错手册
怎么写布局#
作用:定下每个页面的 HTML 骨架。
步骤:
- 打开
view/mysite/layout.html - 整个文件换成下面这份
- 保存
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{if !empty($seo.title)}{$seo.title}{else}{$site.name}{/if}</title>
{if !empty($seo.keywords)}<meta name="keywords" content="{$seo.keywords}">{/if}
{if !empty($seo.description)}<meta name="description" content="{$seo.description}">{/if}
<link rel="stylesheet" href="{$asset_css}">
</head>
<body>
{include common/header}
<main class="wrap">
{content}
</main>
{include common/footer}
<script src="{$asset_js}" defer></script>
</body>
</html>效果:每个页面都套这个骨架,标题自动带 SEO。
样式地址必须写 {$asset_css},不要写死路径。
写死就没有版本号,改了样式用户看不到。打不开? → 看 排错手册
怎么写页头#
作用:显示站点名、导航、搜索框。
步骤:
- 打开
view/mysite/common/header.html - 整个文件换成下面这份
- 保存
<header class="hd">
<div class="wrap hd__in">
<a class="hd__brand" href="/">
{if !empty($site.logo)}
<img src="{$site.logo}" alt="{$site.name}" height="32">
{else}
{$site.name}
{/if}
</a>
<nav class="hd__nav">
<a href="/">首页</a>
{loop $primary_nav as $nav}
<a href="{$nav.url}"{if $nav.target == '_blank'} target="_blank" rel="noopener"{/if}>{$nav.name}</a>
{/loop}
</nav>
<form class="hd__search" action="/search" method="get">
<input type="search" name="q" value="{$keyword}" placeholder="搜索">
<button type="submit">搜</button>
</form>
</div>
</header>效果:页头按「导航组」列菜单(可含外链与二级)。在后台「导航管理」里配,见 导航组。
打不开? → 看 排错手册
怎么写页脚#
作用:显示版权、备案号、统计代码。
步骤:
- 打开
view/mysite/common/footer.html - 整个文件换成下面这份
- 保存
<footer class="ft">
<div class="wrap">
<p>{$site.copyright|default=''}</p>
<p class="ft__links">
{loop $nodes as $nav}
<a href="/list/{$nav.slug}">{$nav.name}</a>
{/loop}
</p>
{if !empty($site.icp)}
<p><a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener nofollow">{$site.icp}</a></p>
{/if}
</div>
{if !empty($site.statistics)}{$site.statistics|raw}{/if}
</footer>效果:后台填的版权、备案、统计代码自动出现。
统计代码必须加 |raw。
不加会显示成一串 <script> 文字,统计收不到数据。打不开? → 看 排错手册
怎么写首页#
作用:显示头条、最新列表、热门、标签。
步骤:
- 打开
view/mysite/index/index.html - 整个文件换成下面这份
- 保存
首页数据已经由系统取好,不用自己查库。
| 变量 | 是什么 |
|---|---|
$lead | 头条(最新一条) |
$feed | 其余内容 |
$hot | 热门 |
$tags | 标签 |
{layout layout}
<section class="hero">
<h1>{$site.title|default=$site.name}</h1>
<p>{$site.description|default=''}</p>
</section>
<div class="cols">
<div class="cols__main">
{if !empty($lead)}
<article class="lead">
{if !empty($lead.thumb)}
<a class="lead__img" href="{$lead.url}">
<img src="{$lead.thumb|thumb=720,420}" alt="{$lead.title}">
</a>
{/if}
<div class="lead__body">
<h2><a href="{$lead.url}">{$lead.title}</a></h2>
<p>{$lead.description|limit=140}</p>
<time>{$lead.created_at|date=Y-m-d}</time>
</div>
</article>
{/if}
<div class="feed">
{loop $feed as $item}
<article class="item">
<h3><a href="{$item.url}">{$item.title}</a></h3>
<p>{$item.description|limit=80}</p>
<div class="item__meta">
<time>{$item.created_at|date=Y-m-d}</time>
<span>{$item.views|default=0} 阅读</span>
</div>
</article>
{/loop}
</div>
{if empty($lead) && empty($feed)}
<div class="empty">
<p>站点还没有内容。</p>
<p>去后台发布第一篇吧。</p>
</div>
{/if}
</div>
<aside class="cols__side">
<h3 class="side__title">热门</h3>
<ol class="side__list">
{loop $hot as $item}
<li><a href="{$item.url}">{$item.title|limit=26}</a></li>
{/loop}
</ol>
{if !empty($tags)}
<h3 class="side__title">标签</h3>
<div class="tags">
{loop $tags as $t}
<a href="/tag/{$t.name}">#{$t.name}</a>
{/loop}
</div>
{/if}
</aside>
</div>效果:前台打开 /,能看到头条和列表。
打不开? → 看 排错手册
怎么写栏目页#
作用:显示一个栏目的内容列表。
步骤:
- 打开
view/mysite/index/node.html - 整个文件换成下面这份
- 保存
{layout layout}
<header class="page-hd">
<nav class="crumb">
<a href="/">首页</a> / <span>{$node.name}</span>
</nav>
<h1>{if !empty($node.title)}{$node.title}{else}{$node.name}{/if}</h1>
{if !empty($node.description)}<p>{$node.description}</p>{/if}
</header>
<div class="feed">
{loop $list as $item}
<article class="item">
{if !empty($item.thumb)}
<a class="item__img" href="{$item.url}">
<img src="{$item.thumb|thumb=240,160}" alt="{$item.title}" loading="lazy">
</a>
{/if}
<div class="item__body">
<h3><a href="{$item.url}">{$item.title}</a></h3>
<p>{$item.description|limit=100}</p>
<div class="item__meta">
<time>{$item.created_at|date=Y-m-d}</time>
<span>{$item.views|default=0} 阅读</span>
</div>
</div>
</article>
{/loop}
</div>
{if empty($list)}
<div class="empty">这个栏目还没有内容。</div>
{/if}
{include common/pager}效果:打开 /list/栏目别名,能看到该栏目的文章。
打不开? → 看 排错手册
怎么写详情页#
作用:显示一篇文章的正文。
步骤:
- 打开
view/mysite/index/article.html - 整个文件换成下面这份
- 保存
{layout layout}
<article class="post">
<nav class="crumb">
<a href="/">首页</a>
{if !empty($node)} / <a href="/list/{$node.slug}">{$node.name}</a>{/if}
</nav>
<h1 class="post__title">{$article.title}</h1>
<div class="post__meta">
<time>{$article.created_at|date=Y-m-d H:i}</time>
<span>{$article.views|default=0} 阅读</span>
</div>
{if !empty($article.thumb)}
<img class="post__cover" src="{$article.thumb|thumb=960,0}" alt="{$article.title}">
{/if}
<div class="post__body">{$article.content|raw}</div>
{if !empty($article.tags_links)}
<div class="tags">
{loop $article.tags_links as $tl}
<a href="{$tl.url}">#{$tl.name}</a>
{/loop}
</div>
{/if}
</article>
<div class="post-nav">
{if !empty($prev)}
<a href="{$prev.url}"><span>上一篇</span>{$prev.title|limit=24}</a>
{/if}
{if !empty($next)}
<a href="{$next.url}"><span>下一篇</span>{$next.title|limit=24}</a>
{/if}
</div>
{if !empty($related)}
<section class="related">
<h2>相关阅读</h2>
<ul>
{loop $related as $item}
<li><a href="{$item.url}">{$item.title|limit=32}</a></li>
{/loop}
</ul>
</section>
{/if}效果:从栏目页点标题,能看到正文、上下篇、相关阅读。
正文必须写 {$article.content|raw}。
不加 raw 会显示一堆 <p> 标签原文。打不开? → 看 排错手册
怎么写单页#
作用:显示「关于我们」这类固定页。
步骤:
- 打开
view/mysite/index/page.html - 整个文件换成下面这份
- 保存
{layout layout}
<article class="post">
<h1 class="post__title">{$article.title}</h1>
<div class="post__body">{$article.content|raw}</div>
</article>效果:打开 /page/别名,显示标题和正文。
打不开? → 看 排错手册
怎么写搜索页#
作用:显示搜索结果。
步骤:
- 打开
view/mysite/index/search.html - 整个文件换成下面这份
- 保存
{layout layout}
<header class="page-hd">
<h1>搜索{if !empty($keyword)}:{$keyword}{/if}</h1>
<form action="/search" method="get">
<input type="search" name="q" value="{$keyword}" placeholder="输入关键词">
<button type="submit">搜索</button>
</form>
</header>
{loop $list as $item}
<article class="item">
<h3><a href="{$item.url}">{$item.title}</a></h3>
<p>{$item.description|limit=100}</p>
</article>
{/loop}
{if empty($list)}
<div class="empty">没有找到相关内容,换个词试试。</div>
{/if}
{include common/pager}效果:打开 /search?q=关键词,能看到结果列表。
打不开? → 看 排错手册
怎么写样式#
作用:决定站点长什么样。
步骤:
- 打开
view/mysite/static/css/style.css - 先换成下面这份最小可用样式
- 保存
- 浏览器强制刷新(Ctrl+F5)
:root{
--ink:#1b2430; --mute:#6b7684; --line:#e6eaf0;
--bg:#fff; --soft:#f7f9fc; --brand:#2563eb;
--wrap:1140px; --radius:10px;
}
*{box-sizing:border-box}
body{margin:0;font:16px/1.75 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif;color:var(--ink);background:var(--bg)}
a{color:inherit;text-decoration:none}
a:hover{color:var(--brand)}
img{max-width:100%;display:block}
.wrap{width:100%;max-width:var(--wrap);margin:0 auto;padding:0 18px}
.hd{border-bottom:1px solid var(--line);position:sticky;top:0;background:#fff;z-index:20}
.hd__in{display:flex;align-items:center;gap:24px;height:62px}
.hd__brand{font-weight:700;font-size:18px;color:var(--brand)}
.hd__nav{display:flex;gap:18px;flex:1;font-size:15px}
.hd__search{display:flex;gap:6px}
.hd__search input{height:34px;padding:0 10px;border:1px solid var(--line);border-radius:8px;outline:none}
.hd__search input:focus{border-color:var(--brand)}
.cols{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:34px;padding:28px 0 60px}
.lead{display:grid;grid-template-columns:1.15fr 1fr;gap:22px;margin-bottom:28px;padding-bottom:26px;border-bottom:1px solid var(--line)}
.lead__img img{border-radius:var(--radius);aspect-ratio:16/9;object-fit:cover}
.lead__body h2{margin:0 0 10px;font-size:24px;line-height:1.35}
.lead__body p{color:var(--mute);margin:0 0 10px}
.item{display:flex;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)}
.item__body h3{margin:0 0 6px;font-size:17px;font-weight:600}
.item__body p{margin:0 0 8px;color:var(--mute);font-size:14.5px}
.item__meta{font-size:12.5px;color:#98a2b3;display:flex;gap:12px}
.item__img img{width:200px;height:130px;object-fit:cover;border-radius:8px}
.post{max-width:820px;margin:0 auto;padding:28px 0}
.post__title{font-size:30px;line-height:1.35;margin:12px 0 12px}
.post__meta{color:var(--mute);font-size:13px;display:flex;gap:14px;margin-bottom:22px}
.post__cover{border-radius:var(--radius);margin:0 0 22px}
.post__body{font-size:16.5px;line-height:1.9}
.post__body p{margin:16px 0}
.post__body h2{margin:36px 0 12px;font-size:21px}
.post__body img{border-radius:8px;margin:16px 0}
.post__body pre{background:#0f1724;color:#dbe4f0;padding:14px;border-radius:8px;overflow:auto}
.post__body table{width:100%;border-collapse:collapse}
.post__body th,.post__body td{border:1px solid var(--line);padding:8px 10px}
.tags{display:flex;flex-wrap:wrap;gap:8px;margin:20px 0}
.tags a{font-size:13px;background:var(--soft);padding:3px 10px;border-radius:999px;color:var(--mute)}
.tags a:hover{background:#eaf0ff;color:var(--brand)}
.cols__side .side__list{padding-left:18px;margin:0 0 28px;font-size:14.5px}
.cols__side .side__list li{margin:8px 0;color:var(--mute)}
.side__title{font-size:14px;letter-spacing:.06em;text-transform:uppercase;color:#98a2b3;margin:0 0 10px}
.crumb{font-size:13px;color:var(--mute);margin-bottom:10px}
.page-hd{padding:26px 0 10px;border-bottom:1px solid var(--line)}
.page-hd h1{font-size:26px;margin:6px 0 8px}
.empty{padding:50px 0;text-align:center;color:var(--mute)}
.ft{border-top:1px solid var(--line);padding:26px 0;text-align:center;color:var(--mute);font-size:13px}
.ft__links{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}
@media(max-width:880px){
.cols{grid-template-columns:1fr}
.lead{grid-template-columns:1fr}
.hd__nav{display:none}
.item__img img{width:120px;height:82px}
}效果:站点有导航、列表、正文排版,手机上不横向滚动。
改配色只改最上面几行:
| 变量 | 改什么 |
|---|---|
--brand | 主色(链接、按钮) |
--ink | 正文颜色 |
--bg | 背景色 |
--wrap | 内容宽度 |
打不开? → 看 排错手册
怎么换上去看效果#
作用:确认每个页面都正常。
步骤:
- 后台 → 系统设置 → 基础设置
- 「前台模板」选
mysite - 点「保存」
- 跑命令清缓存
php aucms cache:clear- 前台按这个顺序点一遍
| 顺序 | 打开哪里 | 该看到什么 |
|---|---|---|
| 1 | / | 首页有头条和列表 |
| 2 | /list/你的栏目 | 栏目列表 |
| 3 | 从栏目点进一篇文章 | 详情正文 |
| 4 | /page/about(如果建了) | 单页 |
| 5 | /search?q=随便 | 搜索结果 |
| 6 | /abc-def-xyz | 404 页 |
| 7 | 浏览器切到手机宽度 | 不横向滚动 |
每个页面问自己三句:有样式吗?有内容吗?控制台报错吗?
打不开? → 看 排错手册
怎么让不同栏目用不同模板#
作用:「产品」用网格、「新闻」用列表,不用做两套主题。
步骤:
- 新建
view/mysite/node/grid.html,写网格版列表 - 后台 → 内容 → 栏目管理
- 点开「产品中心」
- 「列表模板」填
grid - 点「保存」
效果:产品栏目用 grid.html,其它栏目仍用 index/node.html。
详情页同理:
- 新建
view/mysite/article/video.html - 栏目编辑里「详情模板」填
video
打不开? → 看 排错手册
上线前检查这五项#
| 检查项 | 怎么查 |
|---|---|
| 手机 375px 不横向滚 | 浏览器开发者工具切 iPhone |
| 图片都有 alt | 看模板里 alt= 有没有写 |
| 一页只有一个 h1 | 看模板里 <h1> 数量 |
| 统计代码在页脚 | 看 footer.html 有没有 ` |
| 备案号在页脚 | 国内站必须有,链到 beian.miit.gov.cn |
接下来看哪一章#
server_debug.php 复现一次,页面会直接告诉你异常文件与行号。