AnQiCMS模板中如何定义基础布局并通过`extends`标签统一显示风格?

在构建网站时,保持页面风格统一、结构清晰是提升用户体验和提高开发效率的关键。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 信息,navListlinkList 用于动态生成导航和友情链接。这样,即使是基础布局,也充分发挥了 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>headerfooter 这些公共部分的结构,极大地提高了模板的编写效率和可读性。

示例 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 来显示文章标签,prevArchivenextArchive 来提供上一篇/下一篇文章的导航,以及 archiveList 来展示相关文章。

优点显而易见

通过 extends 标签和 block 机制,AnQiCMS 模板制作带来了诸多好处:

  • 风格统一,品牌一致性强:所有页面共享相同的头部、尾部和基本布局,确保了网站的整体视觉风格和品牌形象的一致性。
  • 开发效率大幅提升:开发者无需重复编写每个页面的公共部分,只需专注于特定页面的内容和布局。这大大减少了冗余代码,缩短了开发周期。
  • 后期维护更简单:当网站设计需要调整(例如更改导航结构或页脚内容)时,只需修改 base.html 一处,所有继承它的页面都会自动更新,极大地降低了维护成本。
  • 模板结构清晰,易于理解:父子模板的关系明确,每个模板各司其职,使得模板代码更易于阅读和管理。
  • 高度灵活和可扩展:通过 block 机制,子模板可以按需覆盖或扩展父模板的任何区域,为个性化定制提供了无限可能。

AnQiCMS 这种模板继承的设计模式,不仅让网站保持了高度的视觉统一,更从根本上提升了网站模板的开发和管理效率,让内容运营者和开发者都能专注于他们擅长的领域。


常见问题 (FAQ)

1. 如果我需要为网站的不同区域(例如产品中心和新闻中心)使用不同的基础布局,应该如何操作?

您可以在 base.html 之上,创建更多的“中间层”基础模板。例如,您可以有一个通用的 base.html 定义了最全局的 HTML 结构和公共资源。然后,您可以创建 product_base.htmlnews_base.html,它们都 extends 'base.html',并分别在其中定义产品中心和新闻中心特有的侧边栏、子导航或特定区块。最后,产品详情页和新闻详情页再分别 extends 'product_base.html'news_base.html',这样就能实现多层级的模板继承,满足复杂网站的布局需求。

2. 在子模板中,除了覆盖 block,我还能保留父模板 block 的内容,并在此基础上添加新内容吗?

当然可以。在子模板中覆盖父模板的 block 时,您可以使用 {{ block.super }} 变量来引用父模板中对应 block 的原始内容。这样,您就可以在不完全覆盖父模板内容的前提下,在 block 的上方、下方