域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。现在,我们已启用新域名 AXUM.EU.ORG
  • 方案AXUM.RS 域名 = 3000
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名,或者该域名到期,本站将使用免费域名 AXUM.EU.ORG 继续提供服务。

网站首页

后台管理完成后,我们开始进入前台功能的开发。本章我们将完成博客首页的开发。

母模板

templates/frontend/base.html

是时候对前台母模板进行数据填充和块的定义了:

其中:

<nav class="nav d-flex justify-content-between">
  {% for cat in cats %}
  <a class="p-2 link-secondary" href="/category/{{cat.id}}">{{cat.name}}</a>
  {%endfor%}
</nav>

用于将分类列表填充为头部导航,而

<ol class="list-unstyled mb-0">
  {% for arch in archives %}
  <li><a href="/archive/{{arch.dateline}}">{{arch.dateline}}</a></li>
  {%endfor%}
</ol>

用于填充按月份为单位的存档。

其它块的定义之前章节已做过说明,此处略过。

首页模板

templates/frontend/index.html

{%extends "./base.html"%}
{%block category_name%}最新博文{%endblock%}
{% block content %}
{% for item in list.data %}
<article class="blog-post">
    <h2 class="blog-post-title">
        <a href="/topic/{{item.id}}">{{item.title}}</a>
    </h2>
    <p class="blog-post-meta">
        <a href="https://axum.rs" target="_blank">AXUM.RS</a> 发表于 {{item.dateline()}} [<a href="/category/{{item.category_id}}">{{item.category_name}}</a>]
    </p>

    {{ item.summary }}
</article>
{%endfor%}
{% endblock %}
{% block paginate %}
{%include "../pagination.html"%}
{%endblock%}

其中 {% for item in list.data %}...{%endfor%}用于填充文章列表。这个list是一个 Paginate分页对象。

视图类

// src/view/frontend/index.rs
#[derive(Template)]
#[template(path="frontend/index.html")]
pub struct Index {
    pub list: Paginate<Vec<TopicList>>,
    pub page : u32,
    pub cats: Vec<Category>,
    pub archives: Vec<TopicArchive>,
}

handler

// src/handler/frontend/index.rs
pub async fn index(
    Extension(state):Extension<Arc<AppState>>,
    Query(args):Query<Args>
)->Result<HtmlView> {
    let page = args.page();
    let handler_name = "frontend/index/index";
    let client = get_client(&state).await.map_err(log_error(handler_name))?;
    let list = topic::list(&client, page).await.map_err(log_error(handler_name))?;
    let cats = category::list(&client).await.map_err(log_error(handler_name))?;
    let archives = topic::archive_list(&client).await.map_err(log_error(handler_name))?;
    let tmpl = Index{
        list,
        page,
        cats,
        archives,
    };
    render(tmpl).map_err(log_error(handler_name))
}
  • Args:前台页面所需要的参数,请参见下文的“Args”部分。
  • topic::archive_list():根据已发表的文章获取按月存档的日期列表。详情请见下文的“数据库操作”部分。

数据库操作

// src/db/topic.rs
pub async fn archive_list(client: &Client) -> Result<Vec<TopicArchive>> {
    let sql = "SELECT
       to_char(DATE_TRUNC('month',dateline), 'YYYY年MM月')
         AS  dateline
FROM topics
GROUP BY to_char(DATE_TRUNC('month',dateline), 'YYYY年MM月')";
    super::query(client, sql, &[]).await
}
  • archive_list():调用Postgresql相关函数,根据已发表的文章获取按月存档的日期列表。单条记录的格式如2022年03月

Args

  • page:可选的分页页码。

本章代码位于07/网站首页分支。

要查看完整内容,请先登录