目 录CONTENT

文章目录

Halo主题开发

16Reverie
2025-03-24 / 0 评论 / 1 点赞 / 74 阅读 / 0 字

能力有限,目前主题开发只是在大佬们开发的主题上折腾,加上一些自己想要的功能(Memos,时间轴以及随手拍等)。

自定义模板

一般来说,新增功能页面通过新增自定义模板的方式来进行会比较方便,最好就是有功能相近的页面进行参考。例如我的Memos页面就是搬了瞬间的前端代码,修改了获取渲染数据的逻辑代码,来达成展示Memos数据的目标。

说是模板,但无非也是一个HTML页面,实现非模板的功能页面当然也可以。

目前 Halo 支持为 文章独立页面分类归档 设置自定义模板。

新增自定义模板配置

修改主题文件目录下的 theme.yaml文件,定义新模板,方式如下:

theme.yamlspec 节点下添加如下配置:

customTemplates:
  {type}:
    - name: {name}
      description: {description}
      screenshot: {screenshot}
      file: {file}.html

字段说明:

  • type:模板类型,目前支持 post page category

  • name:模板名称

  • description:模板描述

  • screenshot:模板预览图

  • file:模板文件名,需要在 /templates/ 目录下创建

示例:

  customTemplates:
    page:
      - name: 留言板
        description: 留言板文章
        screenshot:
        file: page_leaving.html
      - name: Memos
        description: Memos
        screenshot:
        file: memos_display.html

配置加载后,就能在Halo后台新建相应类型的内容时,在设置处,选择自定义模板 。此时打开内容则会打开自定义模板配置时的html文件。具体的功能便可以以此 html 文件为起点进行设计开发。

image-npos.png

setting.yaml

定义主题配置里的配置项。在spec.forms 节点下添加新配置定义

spec:
  forms:
    - group: basic
      label: 基本设置
      formSchema:
        - $formkit: select
          name: 
          if:
          label: 
          value: 'user'
          help: ''   
  • group配置分组,即配置的导航栏菜单选项

  • label :group对于显示的名称

  • formkit :配置方式

    • select :下拉选择框

    • radio :复选框

    • text :文本输入

    • number :数值输入

    • attachment :文件路径选择

    • group :一组配置,含多个子配置

  • name :配置变量名

  • if :判断条件,满足条件表单结构才会显示

  • label :配置展示名

  • value :默认值

  • help :配置提示信息

  • options :选择项配置,配置方式为select或radio时需要给出选择项

  • children :子配置,当配置方式为group时需要添加子配置

  • default : 默认值

  • validation :验证规则

    • required:表示字段是必填项,不能为空。

    • minLength:length:要求字段值的长度至少为 length

    • maxLength:length:要求字段值的长度最大为 length

    • numeric:验证字段值是否为数字

    • matches:fieldName:验证当前字段的值是否和另一个name为 fieldName 的字段的值相匹配

    • alpha:只允许字母

    • alpha_num:只允许字母和数

    • url:验证是否为有效的 URL。

    • min:value:数值字段的最小值验证。

    • max:value:数值字段的最大值验证。

    • pattern:regex:使用正则表达式进行验证。

  • validationMessage :验证提示信息

Tip:theme.yaml 以及 setting.yaml 是持久化存储在数据库中的,不会在修改之后主动更新,需要重新加载主题才能生效

模板变量

以单页为例,在单页模板开发中,可以用到变量singlePage ,singlePage是SinglePageVo类型,类型定义如下:(来源Halo文档)

{
  "metadata": {
    "name": "string",                                   // 唯一标识
    "creationTimestamp": "2022-11-20T14:29:44.601Z",    // 创建时间
                        ......
  },
  "spec": {
    "title": "string",                                  // 标题
    "cover": "string",                                  // 封面图
                        ......
  "stats": {
    "visit": 0,                                         // 访问数量
    "upvote": 0,                                        // 点赞数量
    "comment": 0                                        // 评论数量
  },
  "owner": "#ContributorVo",                            // 创建者
  "content": "#ContentVo"                               // 内容
}

在进行单页的模板开发时就可以用到这个变量。我所用到的场景是时间轴/里程碑页面的数据传递。通过编辑页面内容,在页面内容里保存数据,而页面代码通过${singlePage.content.content} 获取到内容字符串,再进行后续的数据处理。相当于把页面的数据存储在了编辑器的内容里。

变量类型具体信息参考Halo官方文档

元数据

元数据,应该是指在每个内容的设置页面里的配置数据。

元素据的定义在主题根目录下的annotation-setting.yaml文件。

代码中使用参考:

<!--是否开启侧边栏-->
<th:block th:if="${#annotations.getOrDefault(singlePage, 'enable_aside', 'true') == 'true'}">
    <th:block th:replace="~{modules/common/aside :: aside}" />
</th:block>
 <!--end-->

htmlType

主题的多数html文件头部都有着类似代码:

<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
  th:replace="~{modules/layout :: html(title = ${theme.config.journals.journals_title ?: '我的动态'}+'-'+${site.title},htmlType = journals,header = null,leftSidebar = true,content = ~{::content}, head = null, footer = null)}"
>

其中指定了htmlType ,不同的htmlType,代码所加载的内容会不同,例如引入的js文件,链接的css文件等。

tail.html 为例,多数html文件最后都含有这句代码

<th:block th:replace="~{modules/macro/tail :: tail}" />

templates/modules/macro下找到tail.html 文件,此文件用于引入js文件,同时会根据htmlType的不同,引入不同的文件。

Finder API

为了满足在任意位置获取数据的需求,Halo官方提供了 Finder API

例如,需要获取指定单页的数据,可以使用singlePageFinder.getByName(pageName) ,API将返回所指定页面的SinglePageVo singlePage ,就是上面模板变量的例子。

如此就可以在任何地方获取到想要的数据。

其中参数pageName 为单页的唯一标识,其他类型内容的API也是一样,即上面例子中的metadata.name 。较为直接的获取单一内容唯一标识的方法是,点开内容的编辑页面,点开后的URL,接于name= 后的是唯一标识。

RESTful API

Halo 也提供了 RESTful 风格的 API。

Finder API有一定的使用限制,它是Halo自定义的方法,并不能在JS代码中灵活使用,需要使用注入之类的方法进行传递数据。

<script th:inline="javascript"> // 服务器端注入数据
    const Config = {
        Host: /*[[${theme.config.memos.memos_host}]]*/ '',
        Height: /*[[${theme.config.memos.memos_block_height}]]*/ 300,
        isShow: /*[[${theme.config.memos.enable_memos_link}]]*/ true
    };
</script>

以singlePageFinder.getByName(pageName) 为例,如果参数pageName是在JS代码执行过程中动态获得的,想通过getByName获取数据就不是那么容易了,此时就需要Halo提供的RESTful API。

同样的例子,Halo提供了SinglePage的API/apis/api.content.halo.run/v1alpha1/singlepages/{name} 来获取单页的数据。

async function getSinglePageDataByName(name) {
    try {
        const url = `/apis/api.content.halo.run/v1alpha1/singlepages/${name}`;
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const responseData = await response.json();
        ......
        
    } catch (error) {
        console.error('Error fetching post:', error);
        return null;
    }
}

其他API参考 Halo API 文档

参考

https://docs.halo.run/category/%E4%B8%BB%E9%A2%98%E5%BC%80%E5%8F%91https://api.halo.run/

1

评论区