在构建网站时,保持页面风格统一、结构清晰是提升用户体验和提高开发效率的关键。AnQiCMS 提供了强大且灵活的模板引擎,它借鉴了 Django 模板的优秀思想,特别是 extends 标签的使用,让这一目标变得触手可及。
统一风格的基石:基础布局模板
想象一下,您的网站就像一栋精心设计的建筑,每个房间都有独特的装饰,但它们的承重墙、屋顶和地基都是统一的。在 AnQiCMS 的模板体系中,这个“地基”就是我们的基础布局模板,通常命名为 base.html。
这份 base.html 文件承载了网站最核心、最普适的结构,比如 HTML 文档类型声明、<head> 区域(包含字符集、SEO 元信息、通用的 CSS 样式和 JavaScript 库链接)、网站头部(Logo、主导航)、页脚(版权信息、联系方式)以及其他所有页面都会共享的公共元素。
在 base.html 中,我们通过 {% block 标签名 %}{% endblock %} 这样的结构来定义可供子模板覆盖的区域。例如,一个典型的 base.html 可能会包含以下几个 block:
{% block title %}:用于定义页面标题。{% block head_extra %}:用于在<head>区域添加当前页面特有的 CSS 或 JS。{% block header %}:用于网站头部内容。{% block content %}:这是最核心的区域,承载每个页面的独特内容。{% block footer %}:用于网站页脚。
通过这种方式,base.html 就像一个骨架,为所有页面搭建了统一的外观和行为框架。
示例 base.html 结构:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
{# 使用 AnQiCMS 的 TDK 标签获取页面标题、关键词和描述 #}
<title>{% tdk with name="Title" siteName=true %}</title>
<meta name="keywords" content="{% tdk with name="Keywords" %}">
<meta name="description" content="{% tdk with name="Description" %}">
{# 规范链接,有利于SEO #}
{%- tdk canonical with name="CanonicalUrl" %}
{%- if canonical %}
<link rel="canonical" href="{{canonical}}" />
{%- endif %}
{# 使用 AnQiCMS 的 system 标签获取模板静态文件地址,确保正确引用CSS #}
<link rel="stylesheet" href="{% system with name="TemplateUrl" %}/css/main.css">
{% block head_extra %}{% endblock %} {# 预留给子模板添加额外样式或脚本 #}
</head>
<body>
<header class="site-header">
<div class="container">
<a href="{% system with name="BaseUrl" %}" class="logo">
<img src="{% system with name="SiteLogo" %}" alt="{% system with name="SiteName" %}">
</a>
<nav class="main-nav">
{# 使用 AnQiCMS 的 navList 标签动态生成主导航 #}
{% navList navs %}
<ul>
{% for item in navs %}
<li class="{% if item.IsCurrent %}active{% endif %}">
<a href="{{ item.Link }}">{{item.Title}}</a>
{% if item.NavList %}
<ul class="sub-nav">
{% for subItem in item.NavList %}
<li><a href="{{ subItem.Link }}">{{subItem.Title}}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endnavList %}
</nav>
</div>
</header>
<main class="site-main">
<div class="container">
{% block content %}{% endblock %} {# 页面主体内容区域 #}
</div>
</main>
<footer class="site-footer">
<div class="container">
<p>{% system with name="SiteCopyright" %}</p>
<p>{% system with name="SiteIcp" %}</p>
{# 友情链接也可以在这里通过 linkList 标签动态调用 #}
{% linkList friendLinks %}
{% if friendLinks %}
<div class="friend-links">
{% for item in friendLinks %}
<a href="{{item.Link}}" {% if item.Nofollow == 1 %} rel="nofollow"{% endif %} target="_blank">{{item.Title}}</a>
{% endfor %}
</div>
{% endif %}
{% endlinkList %}
</div>
</footer>
{# 通用JavaScript文件引用 #}
<script src="{% system with name="TemplateUrl" %}/js/jquery.min.js"></script>
<script src="{% system with name="TemplateUrl" %}/js/common.js"></script>
{% block body_extra %}{% endblock %} {# 预留给子模板添加页面底部脚本 #}
</body>
</html>
请注意,在上述 base.html 中,我们利用了 AnQiCMS 提供的各种标签,如 system 用于获取网站名称、Logo 和模板路径,tdk 用于 SEO 信息,navList 和 linkList 用于动态生成导航和友情链接。这样,即使是基础布局,也充分发挥了 CMS 的动态管理能力。
继承与内容定制:子模板的灵活运用
有了 base.html 这个统一的骨架,其他所有页面模板,比如首页 index.html、文章详情页 archive/detail.html 就不需要重复编写那些公共的 HTML 结构了。它们只需要在文件的第一行使用 {% extends 'base.html' %} 标签来声明继承关系。
然后,这些子模板就可以选择性地覆盖 base.html 中定义的 block 区域。未被覆盖的 block 区域将保持 base.html 中的默认内容。
示例 index.html (首页模板) 如何继承 base.html:
{% extends 'base.html' %} {# 声明继承 base.html,必须是文件第一行 #}
{% block title %} {# 覆盖 base.html 中的 title block #}
<title>{% system with name="SiteName" %} - 专注于提供高效的企业级CMS解决方案</title>
{% endblock %}
{% block head_extra %} {# 在 head_extra block 中添加首页特有的 CSS #}
<link rel="stylesheet" href="{% system with name="TemplateUrl" %}/css/home.css">
{% endblock %}
{% block content %} {# 覆盖 base.html 中的 content block,添加首页特有内容 #}
<section class="hero-banner">
<h1>欢迎使用 AnQiCMS</h1>
<p>高效、可定制、易扩展的内容管理系统</p>
<a href="/about-us.html" class="btn btn-primary">了解更多</a>
</section>
<section class="latest-articles">
<h2>最新文章</h2>
{# 使用 AnQiCMS 的 archiveList 标签获取最新文章列表 #}
{% archiveList archives with type="list" moduleId="1" order="id desc" limit="5" %}
<ul>
{% for item in archives %}
<li>
<h3><a href="{{item.Link}}">{{item.Title}}</a></h3>
<p>{{item.Description|truncatechars:100}}</p>
<span>发布于:{{stampToDate(item.CreatedTime, "2006-01-02")}}</span>
</li>
{% empty %}
<li>暂无文章</li>
{% endfor %}
</ul>
{% endarchiveList %}
</section>
{% endblock %}
{% block body_extra %} {# 添加首页底部所需的JS #}
<script src="{% system with name="TemplateUrl" %}/js/home-animations.js"></script>
{% endblock %}
可以看到,index.html 专注于填充自身独有的内容,而无需关心 <head>、header、footer 这些公共部分的结构,极大地提高了模板的编写效率和可读性。
示例 archive/detail.html (文章详情页模板) 如何继承 base.html:
{% extends 'base.html' %}
{% block title %} {# 覆盖标题,显示文章标题 #}
<title>{% archiveDetail with name="Title" %} - {% system with name="SiteName" %}</title>
{% endblock %}
{% block content %} {# 覆盖内容,显示文章详情 #}
{% archiveDetail article with name="all" %} {# 获取当前文章的所有字段 #}
<article class="article-detail">
<h1>{{article.Title}}</h1>
<div class="article-meta">
<span>分类:<a href="{% categoryDetail with name='Link' id=article.CategoryId %}">{% categoryDetail with name='Title' id=article.CategoryId %}</a></span>
<span>发布日期:{{stampToDate(article.CreatedTime, "2006-01-02 15:04")}}</span>
<span>浏览量:{{article.Views}}</span>
</div>
<div class="article-body">
{{article.Content|safe}} {# 文章内容通常需要使用 safe 过滤器以防止HTML转义 #}
</div>
<div class="article-tags">
{# 获取文章的标签 #}
{% tagList tags with itemId=article.Id limit="10" %}
{% for item in tags %}
<a href="{{item.Link}}">{{item.Title}}</a>
{% endfor %}
{% endtagList %}
</div>
<div class="article-navigation">
{% prevArchive prev %}
<p>上一篇:{% if prev %}<a href="{{prev.Link}}">{{prev.Title}}</a>{% else %}没有了{% endif %}</p>
{% endprevArchive %}
{% nextArchive next %}
<p>下一篇:{% if next %}<a href="{{next.Link}}">{{next.Title}}</a>{% else %}没有了{% endif %}</p>
{% endnextArchive %}
</div>
</article>
<section class="related-articles">
<h2>相关文章</h2>
{# 获取相关文章列表 #}
{% archiveList relatedArchives with type="related" limit="5" %}
<ul>
{% for item in relatedArchives %}
<li><a href="{{item.Link}}">{{item.Title}}</a></li>
{% endfor %}
</ul>
{% endarchiveList %}
</section>
{% endblock %}
在文章详情页中,我们使用了 archiveDetail 来获取当前文章的详细信息,tagList 来显示文章标签,prevArchive 和 nextArchive 来提供上一篇/下一篇文章的导航,以及 archiveList 来展示相关文章。
优点显而易见
通过 extends 标签和 block 机制,AnQiCMS 模板制作带来了诸多好处:
- 风格统一,品牌一致性强:所有页面共享相同的头部、尾部和基本布局,确保了网站的整体视觉风格和品牌形象的一致性。
- 开发效率大幅提升:开发者无需重复编写每个页面的公共部分,只需专注于特定页面的内容和布局。这大大减少了冗余代码,缩短了开发周期。
- 后期维护更简单:当网站设计需要调整(例如更改导航结构或页脚内容)时,只需修改
base.html一处,所有继承它的页面都会自动更新,极大地降低了维护成本。 - 模板结构清晰,易于理解:父子模板的关系明确,每个模板各司其职,使得模板代码更易于阅读和管理。
- 高度灵活和可扩展:通过
block机制,子模板可以按需覆盖或扩展父模板的任何区域,为个性化定制提供了无限可能。
AnQiCMS 这种模板继承的设计模式,不仅让网站保持了高度的视觉统一,更从根本上提升了网站模板的开发和管理效率,让内容运营者和开发者都能专注于他们擅长的领域。
常见问题 (FAQ)
1. 如果我需要为网站的不同区域(例如产品中心和新闻中心)使用不同的基础布局,应该如何操作?
您可以在 base.html 之上,创建更多的“中间层”基础模板。例如,您可以有一个通用的 base.html 定义了最全局的 HTML 结构和公共资源。然后,您可以创建 product_base.html 和 news_base.html,它们都 extends 'base.html',并分别在其中定义产品中心和新闻中心特有的侧边栏、子导航或特定区块。最后,产品详情页和新闻详情页再分别 extends 'product_base.html' 和 news_base.html',这样就能实现多层级的模板继承,满足复杂网站的布局需求。
2. 在子模板中,除了覆盖 block,我还能保留父模板 block 的内容,并在此基础上添加新内容吗?
当然可以。在子模板中覆盖父模板的 block 时,您可以使用 {{ block.super }} 变量来引用父模板中对应 block 的原始内容。这样,您就可以在不完全覆盖父模板内容的前提下,在 block 的上方、下方