mirror of
https://github.com/halo-dev/docs.git
synced 2025-10-21 02:04:01 +00:00
docs: refine theme development documentation (#110)
完善 Halo 2.0 主题开发的文档。 /kind documentation Fixes https://github.com/halo-dev/docs/issues/109 TODO list: - [x] 准备工作 - 提供从搭建 Halo 开发环境到成功应用一个新的主题的过程。 - [x] 目录结构 - 详细解释一个主题的目录构成,包括每一个目录和文件的作用。 - [x] 配置文件 - 提供 `theme.yaml` 的各个配置的解释。 - [x] 设置选项 - 讲解如何在主题模板中使用主题定义的 `settings.yaml` 配置项。**注意:关于 settings.yaml 或许可以单独出一篇文档,因为插件也需要用到。主题和插件的文档只需要单独讲解如何使用** - [x] 静态资源 - 讲解静态资源的引用。 - [x] 核心路由 - 列出 Halo 核心提供的路由以及模板,其中需要包括 `文章/自定义页面/分类的自定义模板` 的讲解 - [x] 模板变量 - 讲解系统内提供的路由对应模板中包含的变量。 - [x] Finder APIs - 讲解数据获取 API 的使用。 - [x] 常用代码片段 - 提供一些在主题开发中常用的代码片段,比如如何定义一个 `layout.html`,如何结构化渲染一个菜单或者文章分类等。 ```release-note None ```
This commit is contained in:
271
docs/developer-guide/form-schema.md
Normal file
271
docs/developer-guide/form-schema.md
Normal file
@@ -0,0 +1,271 @@
|
||||
---
|
||||
title: 表单定义
|
||||
---
|
||||
|
||||
在 Halo 2.0,在 Console 端的所有表单我们都使用了 [FormKit](https://github.com/formkit/formkit) 的方案。FormKit 不仅支持使用 Vue 组件的形式来构建表单,同时支持使用 Schema 的形式来构建。因此,我们的 [Setting](https://github.com/halo-dev/halo/blob/87ccd61ae5cd35a38324c30502d4e9c0ced41c6a/src/main/java/run/halo/app/core/extension/Setting.java#L20) 资源中的表单定义,都是使用 FormKit Schema 来定义的,最常用的场景即主题和插件的设置表单定义。当然,如果要在 Halo 2.0 的插件中使用,也可以参考 FormKit 的文档使用 Vue 组件的形式使用,但不需要在插件中引入 FormKit。
|
||||
|
||||
此文档将不会介绍 FormKit 的具体使用教程,因为我们已经很好的集成了 FormKit,并且使用方式基本无异。此文章将介绍 Halo 2.0 中表单定义的一些规范,以及额外的一些输入组件。
|
||||
|
||||
FormKit 相关文档:
|
||||
|
||||
- Form Schema:
|
||||
- <https://formkit.com/essentials/generation>
|
||||
- <https://formkit.com/advanced/schema>
|
||||
- FormKit Inputs: <https://formkit.com/inputs>
|
||||
|
||||
:::tip
|
||||
目前不支持 FormKit Pro 中的输入组件,但 Halo 额外提供了部分输入组件,将在下面文档列出。
|
||||
:::
|
||||
|
||||
## Setting 资源定义方式
|
||||
|
||||
```yaml title="settings.yaml"
|
||||
apiVersion: v1alpha1
|
||||
kind: Setting
|
||||
metadata:
|
||||
name: foo-setting
|
||||
spec:
|
||||
forms:
|
||||
- group: group_1
|
||||
label: 分组 1
|
||||
formSchema:
|
||||
- $formkit: radio
|
||||
name: color_scheme
|
||||
label: 默认配色
|
||||
value: system
|
||||
options:
|
||||
- label: 跟随系统
|
||||
value: system
|
||||
- label: 深色
|
||||
value: dark
|
||||
- label: 浅色
|
||||
value: light
|
||||
|
||||
- group: group_2
|
||||
label: 分组 2
|
||||
formSchema:
|
||||
- $formkit: text
|
||||
name: username
|
||||
label: 用户名
|
||||
value: ""
|
||||
- $formkit: password
|
||||
name: password
|
||||
label: 密码
|
||||
value: ""
|
||||
```
|
||||
|
||||
:::tip
|
||||
需要注意的是,FormKit Schema 本身应该是 JSON 格式的,但目前我们定义一个表单所使用的是 YAML,可能在参考 FormKit 写法时需要手动转换一下。
|
||||
:::
|
||||
|
||||
字段说明:
|
||||
|
||||
1. `metadata.name`:设置资源的名称,建议以 `-setting` 结尾。
|
||||
2. `spec.forms`:表单定义,可以定义多个表单,每个表单都有一个 `group` 字段,用于区分不同的表单。
|
||||
3. `spec.forms[].label`:表单的标题。
|
||||
4. `spec.forms[].formSchema`:表单的定义,使用 FormKit Schema 来定义。虽然我们使用的 YAML,但与 FormKit Schema 完全一致。
|
||||
|
||||
## 组件类型
|
||||
|
||||
除了 FormKit 官方提供的常用输入组件之外,Halo 还额外提供了一些输入组件,这些输入组件可以在 Form Schema 中使用。
|
||||
|
||||
### Repeater
|
||||
|
||||
#### 描述
|
||||
|
||||
一组重复的输入组件,可以用于定义一组数据,最终得到的数据为一个对象的数组,可以方便地让使用者对其进行增加、移除、排序等操作。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: repeater
|
||||
name: socials
|
||||
label: 社交账号
|
||||
value: []
|
||||
children:
|
||||
- $formkit: text
|
||||
name: name
|
||||
label: 名称
|
||||
value: ""
|
||||
- $formkit: text
|
||||
name: url
|
||||
label: 地址
|
||||
value: ""
|
||||
```
|
||||
|
||||
:::tip
|
||||
使用 `repeater` 类型时,一定要设置默认值,如果不需要默认有任何元素,可以设置为 `[]`。
|
||||
:::
|
||||
|
||||
其中 `name` 和 `url` 即数组对象的属性,最终保存表单之后得到的值为以下形式:
|
||||
|
||||
```json
|
||||
{
|
||||
"socials": [
|
||||
{
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/halo-dev"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
UI 效果:
|
||||
|
||||
<img src="/img/formkit/formkit-repeater.png" width="50%" />
|
||||
|
||||
### Attachment
|
||||
|
||||
#### 描述
|
||||
|
||||
附件类型的输入框,支持直接调用附件库弹框选择附件。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: attachment
|
||||
name: logo
|
||||
label: Logo
|
||||
value: ""
|
||||
```
|
||||
|
||||
### Code
|
||||
|
||||
#### 描述
|
||||
|
||||
代码编辑器的输入组件,集成了 [Codemirror](https://codemirror.net/)。
|
||||
|
||||
#### 参数
|
||||
|
||||
- `language`:代码语言,目前支持 `yaml` `html` `javascript` `css` `json`。
|
||||
- `height`:代码编辑器的高度。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: code
|
||||
name: footer_code
|
||||
label: 页脚代码注入
|
||||
value: ""
|
||||
language: yaml
|
||||
```
|
||||
|
||||
### menuCheckbox
|
||||
|
||||
#### 描述
|
||||
|
||||
菜单复选框,用于选择系统内的导航菜单。其中选择的值为菜单资源 `metadata.name` 的集合。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: menuCheckbox
|
||||
name: menus
|
||||
label: 菜单
|
||||
value: []
|
||||
```
|
||||
|
||||
### menuRadio
|
||||
|
||||
#### 描述
|
||||
|
||||
菜单单选框,用于选择系统内的导航菜单。其中选择的值为菜单资源 `metadata.name`。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: menuRadio
|
||||
name: menu
|
||||
label: 菜单
|
||||
value: ""
|
||||
```
|
||||
|
||||
### postSelect
|
||||
|
||||
#### 描述
|
||||
|
||||
文章选择器,用于选择系统内的文章。其中选择的值为文章资源 `metadata.name`。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: postSelect
|
||||
name: post
|
||||
label: 文章
|
||||
value: ""
|
||||
```
|
||||
|
||||
### singlePageSelect
|
||||
|
||||
#### 描述
|
||||
|
||||
单页选择器,用于选择系统内的独立页面。其中选择的值为独立页面资源 `metadata.name`。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: singlePageSelect
|
||||
name: singlePage
|
||||
label: 单页
|
||||
value: ""
|
||||
```
|
||||
|
||||
### categorySelect
|
||||
|
||||
#### 描述
|
||||
|
||||
文章分类选择器,用于选择系统内的文章分类。其中选择的值为文章分类资源 `metadata.name`。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: categorySelect
|
||||
name: category
|
||||
label: 分类
|
||||
value: ""
|
||||
```
|
||||
|
||||
### categoryCheckbox
|
||||
|
||||
#### 描述
|
||||
|
||||
文章分类复选框,用于选择系统内的文章分类。其中选择的值为文章分类资源 `metadata.name` 的集合。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: categoryCheckbox
|
||||
name: categories
|
||||
label: 分类
|
||||
value: []
|
||||
```
|
||||
|
||||
### tagSelect
|
||||
|
||||
#### 描述
|
||||
|
||||
文章标签选择器,用于选择系统内的文章标签。其中选择的值为文章标签资源 `metadata.name`。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: tagSelect
|
||||
name: tag
|
||||
label: 标签
|
||||
value: ""
|
||||
```
|
||||
|
||||
### tagCheckbox
|
||||
|
||||
#### 描述
|
||||
|
||||
文章标签复选框,用于选择系统内的文章标签。其中选择的值为文章标签资源 `metadata.name` 的集合。
|
||||
|
||||
#### 示例
|
||||
|
||||
```yaml
|
||||
- $formkit: tagCheckbox
|
||||
name: tags
|
||||
label: 标签
|
||||
value: []
|
||||
```
|
47
docs/developer-guide/theme/code-snippets.md
Normal file
47
docs/developer-guide/theme/code-snippets.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: 常用代码片段
|
||||
description: 本文档介绍了常用的代码片段,以便于开发者快速上手。
|
||||
---
|
||||
|
||||
## 布局模板
|
||||
|
||||
通常情况下,我们需要一个公共模板来定义页面的布局。
|
||||
|
||||
```html title="templates/layout.html"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="https://www.thymeleaf.org" th:fragment="html (head,content)">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2" />
|
||||
<title th:text="${site.title}"></title>
|
||||
<link rel="stylesheet" th:href="@{/assets/dist/style.css}" />
|
||||
<script th:src="@{/assets/dist/main.iife.js}"></script>
|
||||
<th:block th:if="${head != null}">
|
||||
<th:block th:replace="${head}" />
|
||||
</th:block>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<th:block th:replace="${content}" />
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```html title="templates/index.html"
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{modules/layout :: html(head = null,content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<!-- 文章列表 -->
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</html>
|
||||
```
|
@@ -1,261 +0,0 @@
|
||||
---
|
||||
title: 配置文件
|
||||
description: 主题配置文件的说明
|
||||
---
|
||||
|
||||
> Halo 的主题模块使用 yaml 来对主题进行配置,`theme.yaml` 里面主要描述主题的名称,开发者的信息,开源地址等等。`settings.yaml` 包含了主题所有的配置选项,需要注意的是,这些选项仅仅是用于构建配置表单,并不起到对主题的配置作用。
|
||||
|
||||
## theme.yaml
|
||||
|
||||
```yaml
|
||||
id: 主题id,唯一,不能与其他主题一样。我们建议设置为 `作者名_主题名称`
|
||||
name: 主题名称
|
||||
author:
|
||||
name: 作者名称
|
||||
website: 作者网址
|
||||
description: 主题描述
|
||||
logo: 主题 Logo 地址
|
||||
website: 主题地址,可填写为 git 仓库地址
|
||||
repo: 主题 git 仓库地址,如有填写,后台可在线更新
|
||||
version: 版本号
|
||||
require: 最低支持的 Halo 版本,如:1.3.0,那么如果用户的版本为 1.3.0 以下,则无法安装
|
||||
|
||||
postMetaField: 文章自定义 meta 变量
|
||||
- meta_key
|
||||
|
||||
sheetMetaField:
|
||||
- meta_key 页面自定义 meta 变量
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```yaml
|
||||
id: caicai_anatole
|
||||
name: Anatole
|
||||
author:
|
||||
name: Caicai
|
||||
website: https://www.caicai.me
|
||||
description: A other Halo theme
|
||||
logo: https://avatars1.githubusercontent.com/u/1811819?s=460&v=4
|
||||
website: https://github.com/halo-dev/halo-theme-anatole
|
||||
repo: https://github.com/halo-dev/halo-theme-anatole
|
||||
version: 1.0.0
|
||||
require: 1.3.0
|
||||
postMetaField:
|
||||
- music_url # 假设在文章页面需要播放背景音乐,用户可以自己填写音乐地址。
|
||||
- download_url # 假设在文章页有一个下载按钮,那么用户也可以自己填写加载地址。
|
||||
|
||||
sheetMetaField:
|
||||
- music_url
|
||||
- download_url
|
||||
```
|
||||
|
||||
### 自定义 meta
|
||||
|
||||
这个为 1.2.0 引入的功能,用户可以在文章设置中设置自定义 meta,我们在 `theme.yaml` 中填写的 `postMetaField` 和 `sheetMetaField` 为预设项,当用户激活当前主题之后,在文章设置中即可看到预先设置好的项,然后填写对应的值即可。
|
||||
|
||||
关于这个 meta 变量如何调用的问题,会在后面的模板变量中阐述。
|
||||
|
||||
## settings.yaml
|
||||
|
||||
```yaml
|
||||
# Tab 节点
|
||||
group1:
|
||||
label: 第一个 Tab 名称
|
||||
# 表单项
|
||||
items:
|
||||
# 省略
|
||||
group2:
|
||||
label: 第二个 Tab 名称
|
||||
# 表单项
|
||||
items:
|
||||
# 省略
|
||||
```
|
||||
|
||||
## settings.yaml#items
|
||||
|
||||
> settings.yaml 的 items 下即为所有表单元素,所支持的表单元素如下
|
||||
|
||||
```yaml
|
||||
items:
|
||||
|
||||
# 普通文本框
|
||||
item1:
|
||||
name: item1 // 设置项的 name 值,在页面可通过 ${settings.item1!} 获取值。
|
||||
label: item1 // 表单项的 label
|
||||
type: text // 表单项类型:普通文本框
|
||||
placeholder: '' // 表单项的 placeholder,一般给用户提示
|
||||
default: '' // 表单项的默认值
|
||||
description: '' // 描述,一般用于说明该设置的具体用途
|
||||
|
||||
# 颜色选择框
|
||||
item1:
|
||||
name: item1 // 设置项的 name 值,在页面可通过 ${settings.item1!} 获取值。
|
||||
label: item1 // 表单项的 label
|
||||
type: color // 表单项类型:颜色选择框
|
||||
placeholder: '' // 表单项的 placeholder,一般给用户提示
|
||||
default: '' // 表单项的默认值
|
||||
description: '' // 描述,一般用于说明该设置的具体用途
|
||||
|
||||
# 附件选择框
|
||||
item1:
|
||||
name: item1 // 设置项的 name 值,在页面可通过 ${settings.item1!} 获取值。
|
||||
label: item1 // 表单项的 label
|
||||
type: attachment // 表单项类型:颜色选择框
|
||||
placeholder: '' // 表单项的 placeholder,一般给用户提示
|
||||
default: '' // 表单项的默认值
|
||||
description: '' // 描述,一般用于说明该设置的具体用途
|
||||
|
||||
# 多行文本框
|
||||
item2: // 设置项的 name 值,在页面可通过 ${settings.item2!} 获取值。
|
||||
name: item2
|
||||
label: item2 // 同上
|
||||
type: textarea // 表单项类型:多行文本框
|
||||
placeholder: '' // 同上
|
||||
default: '' // 同上
|
||||
description: '' // 描述,一般用于说明该设置的具体用途
|
||||
|
||||
# 单选框
|
||||
item3:
|
||||
name: item3 // 同上
|
||||
label: item3_label // 同上
|
||||
type: radio // 表单项类型:单选框
|
||||
data-type: bool // 数据类型:bool,string,long,double
|
||||
default: value1 // 同上
|
||||
description: '' // 描述,一般用于说明该设置的具体用途
|
||||
options: // 选项
|
||||
- value: value1 // 值
|
||||
label: label1 // 说明
|
||||
- value: value2
|
||||
label: label2
|
||||
|
||||
# 下拉框
|
||||
item4:
|
||||
name: item4 // 同上
|
||||
label: item4 // 同上
|
||||
type: select // 表单项类型:下拉框
|
||||
data-type: bool // 数据类型:bool,string,long,double
|
||||
default: value1 // 同上
|
||||
description: '' // 描述,一般用于说明该设置的具体用途
|
||||
options: // 选项
|
||||
- value: value1 // 值
|
||||
label: label1 // 说明
|
||||
- value: value2
|
||||
label: label2
|
||||
```
|
||||
|
||||
### 一个例子
|
||||
|
||||
假设我们的配置文件如下:
|
||||
|
||||
```yaml
|
||||
general:
|
||||
label: 基础设置
|
||||
items:
|
||||
index_title:
|
||||
name: index_title
|
||||
label: 首页标题
|
||||
type: text
|
||||
description: '注意:将覆盖博客标题'
|
||||
background_cover:
|
||||
name: background_cover
|
||||
label: 首页背景图
|
||||
type: attachment
|
||||
default: '/casper/assets/images/blog-cover.jpg'
|
||||
description: '设置首页的背景图,你可以点击右边的选择按钮选择图片。'
|
||||
background_color:
|
||||
name: background_color
|
||||
label: 首页背景颜色
|
||||
type: color
|
||||
default: '#fff'
|
||||
music_enabled:
|
||||
name: music_enabled
|
||||
label: 背景音乐
|
||||
type: radio
|
||||
data-type: bool
|
||||
default: false
|
||||
description: '是否开启背景音乐,默认为 false'
|
||||
options:
|
||||
- value: true
|
||||
label: 开启
|
||||
- value: false
|
||||
label: 关闭
|
||||
code_pretty:
|
||||
name: code_pretty
|
||||
label: 文章代码高亮主题
|
||||
type: select
|
||||
default: Default
|
||||
options:
|
||||
- value: Default
|
||||
label: Default
|
||||
- value: Coy
|
||||
label: Coy
|
||||
- value: Dark
|
||||
label: Dark
|
||||
- value: Okaidia
|
||||
label: Okaidia
|
||||
- value: Solarized Light
|
||||
label: Solarized Light
|
||||
- value: Tomorrow Night
|
||||
label: Tomorrow Night
|
||||
- value: Twilight
|
||||
label: Twilight
|
||||
```
|
||||
|
||||
页面取值:
|
||||
|
||||
```html
|
||||
// 获取首页标题
|
||||
|
||||
<#if settings.index_title?? && settings.index_title != ''>
|
||||
<h1>${settings.index_title!}</h1>
|
||||
</#if>
|
||||
```
|
||||
|
||||
```html
|
||||
// 获取背景图片
|
||||
|
||||
<#if settings.background_cover?? && settings.background_cover != ''>
|
||||
<img src="${settings.background_cover!}">
|
||||
</#if>
|
||||
```
|
||||
|
||||
```html
|
||||
// 获取背景颜色
|
||||
|
||||
<style>
|
||||
body{
|
||||
<#if settings.background_color?? && settings.background_color != ''>
|
||||
background-color: ${settings.background_color!}
|
||||
<#else>
|
||||
background-color: #fff
|
||||
</#if>
|
||||
}
|
||||
</style>
|
||||
|
||||
或者
|
||||
|
||||
<style>
|
||||
body{
|
||||
background-color: ${settings.background_color!'#fff'}
|
||||
}
|
||||
</style>
|
||||
|
||||
```
|
||||
|
||||
```html
|
||||
// 判断是否开启背景音乐
|
||||
|
||||
<#if settings.music_enabled!false>
|
||||
do something...
|
||||
</#if>
|
||||
```
|
||||
|
||||
```html
|
||||
// 获取代码高亮主题
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="${theme_base!}/assets/prism/css/prism-${settings.code_pretty!'Default'}.css" />
|
||||
<script type="text/javascript" src="${theme_base!}/assets/prism/js/prism.js"></script>
|
||||
```
|
||||
|
||||
更多实例可参考:<https://github.com/halo-dev/halo-theme-material/blob/master/settings.yaml>。
|
83
docs/developer-guide/theme/config.md
Normal file
83
docs/developer-guide/theme/config.md
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: 配置文件
|
||||
description: 关于主题配置文件的文档。
|
||||
---
|
||||
|
||||
目前 Halo 2.0 的主题必须在根目录包含 `theme.yaml`,用于配置主题的基本信息,如主题名称、版本、作者等。
|
||||
|
||||
## 格式示例
|
||||
|
||||
```yaml title="theme.yaml"
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: theme-foo
|
||||
spec:
|
||||
displayName: 示例主题
|
||||
author:
|
||||
name: halo-dev
|
||||
website: https://halo.run
|
||||
description: 一个示例主题
|
||||
logo: https://halo.run/logo
|
||||
website: https://github.com/halo-sigs/theme-foo
|
||||
repo: https://github.com/halo-sigs/theme-foo.git
|
||||
settingName: "theme-foo-setting"
|
||||
configMapName: "theme-foo-configMap"
|
||||
customTemplates:
|
||||
post:
|
||||
- name: 文档
|
||||
description: 文档类型的文章
|
||||
screenshot:
|
||||
file: post_documentation.html
|
||||
category:
|
||||
- name: 知识库
|
||||
description: 知识库类型的分类
|
||||
screenshot:
|
||||
file: category_knowledge.html
|
||||
page:
|
||||
- name: 关于
|
||||
description: 关于页面
|
||||
screenshot:
|
||||
file: page_about.html
|
||||
version: 1.0.0
|
||||
require: 2.0.0
|
||||
```
|
||||
|
||||
## 字段详解
|
||||
|
||||
| 字段 | 描述 | 是否必填 |
|
||||
| ------------------------------- | ----------------------------------------------------------------------------- | -------- |
|
||||
| `metadata.name` | 主题的唯一标识 | 是 |
|
||||
| `spec.displayName` | 显示名称 | 是 |
|
||||
| `spec.author.name` | 作者名称 | 否 |
|
||||
| `spec.author.website` | 作者网站 | 否 |
|
||||
| `spec.description` | 主题描述 | 否 |
|
||||
| `spec.logo` | 主题 Logo | 否 |
|
||||
| `spec.website` | 主题网站 | 否 |
|
||||
| `spec.repo` | 主题托管地址 | 否 |
|
||||
| `spec.settingName` | 设置表单定义的名称,需要同时创建对应的 `settings.yaml` 文件 | 否 |
|
||||
| `spec.configMapName` | 设置持久化配置的 ConfigMap 名称 | 否 |
|
||||
| `spec.customTemplates.post` | 文章的自定义模板配置,详细文档可查阅 [模板路由](./template-route-mapping#custom-templates) | 否 |
|
||||
| `spec.customTemplates.category` | 分类的自定义模板配置,详细文档可查阅 [模板路由](./template-route-mapping#custom-templates) | 否 |
|
||||
| `spec.customTemplates.page` | 独立页面的自定义模板配置,详细文档可查阅 [模板路由](./template-route-mapping#custom-templates) | 否 |
|
||||
| `spec.version` | 主题版本 | 是 |
|
||||
| `spec.require` | 所需 Halo 的运行版本 | 是 |
|
||||
|
||||
## 从 1.x 迁移
|
||||
|
||||
为了方便主题开发者从 1.x 迁移,我们提供了工具用于迁移配置文件。
|
||||
|
||||
工具仓库地址:<https://github.com/halo-sigs/convert-theme-config-to-next>
|
||||
|
||||
```bash
|
||||
# 1.x 版本主题
|
||||
cd path/to/theme
|
||||
|
||||
npx @halo-dev/convert-theme-config-to-next theme
|
||||
```
|
||||
|
||||
执行完成之后即可看到主题目录下生成了 `theme.2.0.yaml` 文件,重命名为 `theme.yaml` 即可。
|
||||
|
||||
:::tip
|
||||
转换完成之后需要修改 `metadata.name`、`spec.settingName` 和 `spec.configMapName`。
|
||||
:::
|
10
docs/developer-guide/theme/finder-apis.md
Normal file
10
docs/developer-guide/theme/finder-apis.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Finder API
|
||||
description: 本文档介绍 Finder API 的使用方法。
|
||||
---
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
|
||||
目前在主题模板中获取数据可以使用对应路由提供的 [模板变量](./template-variables),但为了满足在任意位置获取数据的需求,我们提供了 Finder API。
|
||||
|
||||
<DocCardList />
|
246
docs/developer-guide/theme/finder-apis/category.md
Normal file
246
docs/developer-guide/theme/finder-apis/category.md
Normal file
@@ -0,0 +1,246 @@
|
||||
---
|
||||
title: 文章分类
|
||||
description: 文章分类 - CategoryFinder
|
||||
---
|
||||
|
||||
## getByName(name)
|
||||
|
||||
```js
|
||||
categoryFinder.getByName(name)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取文章分类。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `name:string` - 分类的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#CategoryVo](#categoryvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="category = ${categoryFinder.getByName('category-foo')}">
|
||||
<a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}"></a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## getByNames(names)
|
||||
|
||||
```js
|
||||
categoryFinder.getByNames(names)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据一组 `metadata.name` 获取文章分类。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `names:List<string>` - 分类的唯一标识 `metadata.name` 的集合。
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#CategoryVo](#categoryvo)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="categories = ${categoryFinder.getByNames(['category-foo', 'category-bar'])}">
|
||||
<a th:each="category : ${categories}" th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}"></a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## list(page,size)
|
||||
|
||||
```js
|
||||
categoryFinder.list(page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据分页参数获取分类列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<CategoryVo\>](#listresultcategoryvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="categories = ${categoryFinder.list(1,10)}">
|
||||
<li th:each="category : ${categories.items}">
|
||||
<a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## listAll()
|
||||
|
||||
```js
|
||||
categoryFinder.listAll()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取所有文章分类。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#CategoryVo](#categoryvo)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="categories = ${categoryFinder.listAll()}">
|
||||
<li th:each="category : ${categories}">
|
||||
<a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## listAsTree()
|
||||
|
||||
```js
|
||||
categoryFinder.listAsTree()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取所有文章分类的多层级结构。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#CategoryTreeVo](#categorytreevo)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="categories = ${categoryFinder.listAsTree()}">
|
||||
<ul>
|
||||
<li th:replace="~{modules/category-tree :: single(categories=${categories})}" />
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
```html title="/templates/category-tree.html"
|
||||
<ul th:fragment="next (categories)">
|
||||
<li th:fragment="single (categories)" th:each="category : ${categories}">
|
||||
<a th:href="@{${category.status.permalink}}">
|
||||
<span th:text="${category.spec.displayName}"> </span>
|
||||
</a>
|
||||
<th:block th:if="${not #lists.isEmpty(category.children)}">
|
||||
<th:block th:replace="~{modules/category-tree :: next (categories=${category.children})}"></th:block>
|
||||
</th:block>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### ListResult<CategoryVo\>
|
||||
|
||||
```json title="ListResult<CategoryVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#CategoryVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
|
||||
### CategoryTreeVo
|
||||
|
||||
```json title="CategoryTreeVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:18:49.230Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"children": "List<#CategoryTreeVo>",
|
||||
"parentName": "string",
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryTreeVo](#categorytreevo)
|
245
docs/developer-guide/theme/finder-apis/comment.md
Normal file
245
docs/developer-guide/theme/finder-apis/comment.md
Normal file
@@ -0,0 +1,245 @@
|
||||
---
|
||||
title: 评论
|
||||
description: 评论 - CommentFinder
|
||||
---
|
||||
|
||||
## getByName(name)
|
||||
|
||||
```js
|
||||
commentFinder.getByName(name)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取评论。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `name:string` - 评论的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#CommentVo](#commentvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="comment = ${commentFinder.getByName('comment-foo')}">
|
||||
<span th:text="${comment.spec.owner.displayName}"></span>
|
||||
<div th:text="${comment.spec.content}"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## list(ref,page,size)
|
||||
|
||||
```js
|
||||
commentFinder.list(ref,page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据评论的 `metadata.name` 和分页参数获取回复列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `ref:#Ref` - 评论的唯一标识 `metadata.name`。
|
||||
2. `page:int` - 分页页码,从 1 开始
|
||||
3. `size:int` - 分页条数
|
||||
|
||||
- [#Ref](#ref)
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<CommentVo\>](#listresultcommentvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="comments = ${commentFinder.list({ group: 'content.halo.run', version: 'v1alpha1', kind: 'Post', name: 'post-foo' },1,10)}">
|
||||
<li th:each="comment : ${comments.items}">
|
||||
<span th:text="${comment.spec.owner.displayName}"></span>
|
||||
<div th:text="${comment.spec.content}"></div>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## listReply(commentName,page,size)
|
||||
|
||||
```js
|
||||
commentFinder.listReply(commentName,page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据评论的 `metadata.name` 和分页参数获取回复列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `commentName:string` - 评论的唯一标识 `metadata.name`。
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<ReplyVo\>](#listresultreplyvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="replies = ${commentFinder.listReply('comment-foo',1,10)}">
|
||||
<li th:each="reply : ${replies.items}">
|
||||
<span th:text="${reply.spec.owner.displayName}"></span>
|
||||
<div th:text="${reply.spec.content}"></div>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CommentVo
|
||||
|
||||
```json title="CommentVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T12:16:19.788Z"
|
||||
},
|
||||
"spec": {
|
||||
"raw": "string",
|
||||
"content": "string",
|
||||
"owner": {
|
||||
"kind": "string",
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
},
|
||||
"userAgent": "string",
|
||||
"ipAddress": "string",
|
||||
"priority": 0,
|
||||
"top": false,
|
||||
"allowNotification": true,
|
||||
"approved": false,
|
||||
"hidden": false,
|
||||
"subjectRef": {
|
||||
"group": "string",
|
||||
"version": "string",
|
||||
"kind": "string",
|
||||
"name": "string"
|
||||
},
|
||||
"lastReadTime": "2022-11-20T12:16:19.788Z"
|
||||
},
|
||||
"status": {
|
||||
"lastReplyTime": "2022-11-20T12:16:19.788Z",
|
||||
"replyCount": 0,
|
||||
"unreadReplyCount": 0,
|
||||
"hasNewReply": true
|
||||
},
|
||||
"owner": {
|
||||
"kind": "string",
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"email": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ListResult<CommentVo\>
|
||||
|
||||
```json title="ListResult<CommentVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#CommentVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#CommentVo](#commentvo)
|
||||
|
||||
### ReplyVo
|
||||
|
||||
```json title="ReplyVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T12:25:32.357Z"
|
||||
},
|
||||
"spec": {
|
||||
"raw": "string",
|
||||
"content": "string",
|
||||
"owner": {
|
||||
"kind": "string",
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
},
|
||||
"userAgent": "string",
|
||||
"ipAddress": "string",
|
||||
"priority": 0,
|
||||
"top": false,
|
||||
"allowNotification": true,
|
||||
"approved": false,
|
||||
"hidden": false,
|
||||
"commentName": "string",
|
||||
"quoteReply": "string"
|
||||
},
|
||||
"owner": {
|
||||
"kind": "string",
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"email": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ListResult<ReplyVo\>
|
||||
|
||||
```json title="ListResult<ReplyVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#ReplyVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#ReplyVo](#replyvo)
|
||||
|
||||
### Ref
|
||||
|
||||
```json title="Ref"
|
||||
{
|
||||
"group": "string",
|
||||
"kind": "string",
|
||||
"version": "string",
|
||||
"name": "string"
|
||||
}
|
||||
```
|
69
docs/developer-guide/theme/finder-apis/contributor.md
Normal file
69
docs/developer-guide/theme/finder-apis/contributor.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: 作者
|
||||
description: 作者 - ContributorFinder
|
||||
---
|
||||
|
||||
## getContributor(name)
|
||||
|
||||
```js
|
||||
contributorFinder.getContributor(name)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取作者。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `name:string` - 作者的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#Contributor](#contributor)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="contributor = ${contributorFinder.getByName('contributor-foo')}">
|
||||
<h1 th:text="${contributor.displayName}"></h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
## getContributors(names)
|
||||
|
||||
```js
|
||||
contributorFinder.getContributors(names)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据一组 `metadata.name` 获取作者。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `names:List<string>` - 作者的唯一标识 `metadata.name` 的集合。
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#Contributor](#contributor)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="contributors = ${contributorFinder.getByNames(['contributor-foo, 'contributor-bar'])}">
|
||||
<span th:each="contributor : ${contributors}" th:text="${contributor.displayName}"></span>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### Contributor
|
||||
|
||||
```json title="Contributor"
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
```
|
145
docs/developer-guide/theme/finder-apis/menu.md
Normal file
145
docs/developer-guide/theme/finder-apis/menu.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: 导航菜单
|
||||
description: 导航菜单 - MenuFinder
|
||||
---
|
||||
|
||||
## getByName(name)
|
||||
|
||||
```js
|
||||
menuFinder.getByName(name)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取菜单。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `name:string` - 菜单的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#MenuVo](#menuvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="menu = ${menuFinder.getByName('menu-foo')}">
|
||||
<ul th:with="menuItems = ${menu.menuItems}">
|
||||
<li th:each="menuItem : ${menuItems}">
|
||||
<a th:href="@{${menuItem.status.href}}" th:text="${menuItem.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
## getPrimary()
|
||||
|
||||
```js
|
||||
menuFinder.getPrimary()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取主菜单。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
[#MenuVo](#menuvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="menu = ${menuFinder.getPrimary()}">
|
||||
<ul th:with="menuItems = ${menu.menuItems}">
|
||||
<li th:each="menuItem : ${menuItems}">
|
||||
<a th:href="@{${menuItem.status.href}}" th:text="${menuItem.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### MenuVo
|
||||
|
||||
```json title="MenuVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:44:58.984Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"menuItems": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"menuItems": "List<#MenuItemVo>"
|
||||
}
|
||||
```
|
||||
|
||||
### MenuItemVo
|
||||
|
||||
```json title="MenuItemVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:44:58.984Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"href": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
],
|
||||
"categoryRef": {
|
||||
"group": "string",
|
||||
"version": "string",
|
||||
"kind": "string",
|
||||
"name": "string"
|
||||
},
|
||||
"tagRef": {
|
||||
"group": "string",
|
||||
"version": "string",
|
||||
"kind": "string",
|
||||
"name": "string"
|
||||
},
|
||||
"postRef": {
|
||||
"group": "string",
|
||||
"version": "string",
|
||||
"kind": "string",
|
||||
"name": "string"
|
||||
},
|
||||
"singlePageRef": {
|
||||
"group": "string",
|
||||
"version": "string",
|
||||
"kind": "string",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"displayName": "string",
|
||||
"href": "string"
|
||||
},
|
||||
"children": "List<#MenuItemVo>",
|
||||
"parentName": "string",
|
||||
}
|
||||
```
|
33
docs/developer-guide/theme/finder-apis/plugin.md
Normal file
33
docs/developer-guide/theme/finder-apis/plugin.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: 插件
|
||||
description: 插件 - PluginFinder
|
||||
---
|
||||
|
||||
## available(pluginName)
|
||||
|
||||
```js
|
||||
pluginFinder.available(pluginName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
判断一个插件是否可用,会同时判断插件是否安装和启用。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `pluginName:string` - 插件的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
`boolean` - 插件是否可用
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<!-- https://github.com/halo-sigs/plugin-search-widget -->
|
||||
<li th:if="${pluginFinder.available('PluginSearchWidget')}">
|
||||
<a href="javascript:SearchWidget.open()" title="搜索">
|
||||
搜索
|
||||
</a>
|
||||
</li>
|
||||
```
|
637
docs/developer-guide/theme/finder-apis/post.md
Normal file
637
docs/developer-guide/theme/finder-apis/post.md
Normal file
@@ -0,0 +1,637 @@
|
||||
---
|
||||
title: 文章
|
||||
description: 文章 - PostFinder
|
||||
---
|
||||
|
||||
## getByName(postName)
|
||||
|
||||
```js
|
||||
postFinder.getByName(postName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取文章。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `postName:string` - 文章的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#PostVo](#postvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="post = ${postFinder.getByName('post-foo')}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## content(postName)
|
||||
|
||||
```js
|
||||
postFinder.content(postName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据文章的 `metadata.name` 单独获取文章内容。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `postName:string` - 文章的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ContentVo](#contentvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="content = ${postFinder.content('post-foo')}">
|
||||
<div th:utext="${content.content}"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## cursor(postName)
|
||||
|
||||
```js
|
||||
postFinder.cursor(postName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据文章的 `metadata.name` 获取相邻的文章(上一篇 / 下一篇)。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `postName:string` - 文章的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#NavigationPostVo](#navigationpostvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html title="/templates/post.html"
|
||||
<div
|
||||
th:with="postCursor = ${postFinder.cursor(post.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
th:if="${postCursor.hasPrevious()}"
|
||||
th:href="@{${postCursor.previous.status.permalink}}"
|
||||
>
|
||||
<span th:text="${postCursor.previous.spec.title}"></span>
|
||||
</a>
|
||||
<a
|
||||
th:if="${postCursor.hasNext()}"
|
||||
th:href="@{${postCursor.next.status.permalink}}"
|
||||
>
|
||||
<span th:text="${postCursor.next.spec.title}"></span>
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## listAll()
|
||||
|
||||
```js
|
||||
postFinder.listAll()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取所有文章。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#ListedPostVo](#listedpostvo)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="posts = ${postFinder.listAll()}">
|
||||
<li th:each="post : ${posts}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## list(page,size)
|
||||
|
||||
```js
|
||||
postFinder.list(page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据分页参数获取文章列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<ListedPostVo\>](#listresultlistedpostvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="posts = ${postFinder.list(1,10)}">
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## listByCategory(page,size,categoryName)
|
||||
|
||||
```js
|
||||
postFinder.listByCategory(page,size,categoryName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据分类标识和分页参数获取文章列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
3. `categoryName:string` - 文章分类唯一标识 `metadata.name`
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<ListedPostVo\>](#listresultlistedpostvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="posts = ${postFinder.listByCategory(1,10,'category-foo')}">
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## listByTag(page,size,tag)
|
||||
|
||||
```js
|
||||
postFinder.listByTag(page,size,tag)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据标签标识和分页参数获取文章列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
3. `tag:string` - 文章分类唯一标识 `metadata.name`
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<ListedPostVo\>](#listresultlistedpostvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="posts = ${postFinder.listByTag(1,10,'tag-foo')}">
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## archives(page,size)
|
||||
|
||||
```js
|
||||
postFinder.archives(page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据分页参数获取文章归档列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<PostArchiveVo\>](#listresultpostarchivevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<th:block th:with="archives = ${postFinder.archives(1,10)}">
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<h1 th:text="${archive.year}"></h1>
|
||||
<ul>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<li th:each="post : ${month.posts}">
|
||||
<a
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:text="${post.spec.title}">
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
</th:block>
|
||||
```
|
||||
|
||||
## archives(page,size,year)
|
||||
|
||||
```js
|
||||
postFinder.archives(page,size,year)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据年份和分页参数获取文章归档列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
2. `year:string` - 年份
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<PostArchiveVo\>](#listresultpostarchivevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<th:block th:with="archives = ${postFinder.archives(1,10,'2022')}">
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<h1 th:text="${archive.year}"></h1>
|
||||
<ul>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<li th:each="post : ${month.posts}">
|
||||
<a
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:text="${post.spec.title}">
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
</th:block>
|
||||
```
|
||||
|
||||
## archives(page,size,year,month)
|
||||
|
||||
```js
|
||||
postFinder.archives(page,size,year,month)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据年月和分页参数获取文章归档列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
2. `year:string` - 年份
|
||||
2. `month:string` - 月份
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<PostArchiveVo\>](#listresultpostarchivevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<th:block th:with="archives = ${postFinder.archives(1,10,'2022','11')}">
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<h1 th:text="${archive.year}"></h1>
|
||||
<ul>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<li th:each="post : ${month.posts}">
|
||||
<a
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:text="${post.spec.title}">
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
</th:block>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### PostVo
|
||||
|
||||
```json title="PostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T12:45:43.888Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T12:45:43.888Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
},
|
||||
"content": {
|
||||
"raw": "string",
|
||||
"content": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
||||
|
||||
### ContentVo
|
||||
|
||||
```json title="ContentVo"
|
||||
{
|
||||
"raw": "string",
|
||||
"content": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### NavigationPostVo
|
||||
|
||||
```json title="NavigationPostVo"
|
||||
{
|
||||
"previous": "#PostVo",
|
||||
"current": "#PostVo",
|
||||
"next": "#PostVo"
|
||||
}
|
||||
```
|
||||
|
||||
- [#PostVo](#postvo)
|
||||
|
||||
### ListedPostVo
|
||||
|
||||
```json title="ListedPostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.505Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T13:06:38.505Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
||||
|
||||
### ListResult<ListedPostVo\>
|
||||
|
||||
```json title="ListResult<ListedPostVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#ListedPostVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedPostVo](#listedpostvo)
|
||||
|
||||
### PostArchiveVo
|
||||
|
||||
```json title="PostArchiveVo"
|
||||
{
|
||||
"year": "string",
|
||||
"months": [
|
||||
{
|
||||
"month": "string",
|
||||
"posts": "#ListedPostVo"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedPostVo](#listedpostvo)
|
||||
|
||||
### ListResult<PostArchiveVo\>
|
||||
|
||||
```json title="ListResult<PostArchiveVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#PostArchiveVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#PostArchiveVo](#postarchivevo)
|
260
docs/developer-guide/theme/finder-apis/single-page.md
Normal file
260
docs/developer-guide/theme/finder-apis/single-page.md
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
title: 独立页面
|
||||
description: 独立页面 - SinglePageFinder
|
||||
---
|
||||
|
||||
## getByName(pageName)
|
||||
|
||||
```js
|
||||
singlePageFinder.getByName(pageName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取独立页面。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `pageName:string` - 独立页面的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#SinglePageVo](#singlepagevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="singlePage = ${singlePageFinder.getByName('page-foo')}">
|
||||
<a th:href="@{${singlePage.status.permalink}}" th:text="${singlePage.spec.title}"></a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## content(pageName)
|
||||
|
||||
```js
|
||||
singlePageFinder.content(pageName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据独立页面的 `metadata.name` 单独获取独立页面内容。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `pageName:string` - 独立页面的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ContentVo](#contentvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="content = ${singlePageFinder.content('page-foo')}">
|
||||
<div th:utext="${content.content}"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## list(page,size)
|
||||
|
||||
```js
|
||||
singlePageFinder.list(page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据分页参数获取独立页面列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<ListedSinglePageVo\>](#listresultlistedsinglepagevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="singlePages = ${singlePageFinder.list(1,10)}">
|
||||
<li th:each="singlePage : ${singlePages.items}">
|
||||
<a th:href="@{${singlePage.status.permalink}}" th:text="${singlePage.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### SinglePageVo
|
||||
|
||||
```json title="SinglePageVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:29:44.601Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T14:29:44.601Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"content": {
|
||||
"raw": "string",
|
||||
"content": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ListedSinglePageVo
|
||||
|
||||
```json title="ListedSinglePageVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:31:00.876Z"
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T14:31:00.876Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ListResult<ListedSinglePageVo\>
|
||||
|
||||
```json title="ListResult<ListedSinglePageVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#ListedSinglePageVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedSinglePageVo](#listedsinglepagevo)
|
||||
|
||||
### ContentVo
|
||||
|
||||
```json title="ContentVo"
|
||||
{
|
||||
"raw": "string",
|
||||
"content": "string"
|
||||
}
|
||||
```
|
45
docs/developer-guide/theme/finder-apis/site-stats.md
Normal file
45
docs/developer-guide/theme/finder-apis/site-stats.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: 站点统计
|
||||
description: 站点统计 - SiteStatsFinder
|
||||
---
|
||||
|
||||
## getStats()
|
||||
|
||||
```js
|
||||
siteStatsFinder.getStats()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取站点的统计信息。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
[#SiteStatsVo](#sitestatsvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="stats = ${siteStatsFinder.getStats()}">
|
||||
<li th:text="${stats.visit}"></li>
|
||||
<li th:text="${stats.post}"></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### SiteStatsVo
|
||||
|
||||
```json title="SiteStatsVo"
|
||||
{
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0,
|
||||
"post": 0,
|
||||
"category": 0
|
||||
}
|
||||
```
|
162
docs/developer-guide/theme/finder-apis/tag.md
Normal file
162
docs/developer-guide/theme/finder-apis/tag.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: 文章标签
|
||||
description: 文章标签 - TagFinder
|
||||
---
|
||||
|
||||
## getByName(name)
|
||||
|
||||
```js
|
||||
tagFinder.getByName(name)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据 `metadata.name` 获取标签。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `name:string` - 标签的唯一标识 `metadata.name`。
|
||||
|
||||
### 返回值
|
||||
|
||||
[#TagVo](#tagvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="tag = ${tagFinder.getByName('tag-foo')}">
|
||||
<a th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}"></a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## getByNames(names)
|
||||
|
||||
```js
|
||||
tagFinder.getByNames(names)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据一组 `metadata.name` 获取标签。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `names:List<string>` - 标签的唯一标识 `metadata.name` 的集合。
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#TagVo](#tagvo)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="tags = ${tagFinder.getByNames(['tag-foo', 'tag-bar'])}">
|
||||
<a th:each="tag : ${tags}" th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}"></a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## list(page,size)
|
||||
|
||||
```js
|
||||
tagFinder.list(page,size)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据分页参数获取标签列表。
|
||||
|
||||
### 参数
|
||||
|
||||
1. `page:int` - 分页页码,从 1 开始
|
||||
2. `size:int` - 分页条数
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ListResult<TagVo\>](#listresulttagvo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="tags = ${tagFinder.list(1,10)}">
|
||||
<li th:each="tag : ${tags.items}">
|
||||
<a th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## listAll()
|
||||
|
||||
```js
|
||||
tagFinder.listAll()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取所有文章标签。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
List<[#TagVo](#tagvo)>
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<ul th:with="tags = ${tagFinder.listAll()}">
|
||||
<li th:each="tag : ${tags}">
|
||||
<a th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### ListResult<TagVo\>
|
||||
|
||||
```json title="ListResult<TagVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#TagVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#TagVo](#tagvo)
|
119
docs/developer-guide/theme/finder-apis/theme.md
Normal file
119
docs/developer-guide/theme/finder-apis/theme.md
Normal file
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: 主题
|
||||
description: 主题 - ThemeFinder
|
||||
---
|
||||
|
||||
## activation()
|
||||
|
||||
```js
|
||||
themeFinder.activation()
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
获取当前激活的主题。
|
||||
|
||||
### 参数
|
||||
|
||||
无
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ThemeVo](#themevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="theme = ${themeFinder.activation()}">
|
||||
<h1 th:text="${theme.spec.displayName}"></h1>
|
||||
<p th:text="${theme.spec.version}"></p>
|
||||
</div>
|
||||
```
|
||||
|
||||
## getByName(themeName)
|
||||
|
||||
```js
|
||||
themeFinder.getByName(themeName)
|
||||
```
|
||||
|
||||
### 描述
|
||||
|
||||
根据主题的唯一标识 `metadata.name` 获取主题。
|
||||
|
||||
### 参数
|
||||
|
||||
- `themeName:string` - 主题名称
|
||||
|
||||
### 返回值
|
||||
|
||||
[#ThemeVo](#themevo)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<div th:with="theme = ${themeFinder.getByName('theme-foo')}">
|
||||
<h1 th:text="${theme.spec.displayName}"></h1>
|
||||
<p th:text="${theme.spec.version}"></p>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### ThemeVo
|
||||
|
||||
```json title="ThemeVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T15:27:15.036Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"author": {
|
||||
"name": "string",
|
||||
"website": "string"
|
||||
},
|
||||
"description": "string",
|
||||
"logo": "string",
|
||||
"website": "string",
|
||||
"repo": "string",
|
||||
"version": "string",
|
||||
"require": "string",
|
||||
"settingName": "string",
|
||||
"configMapName": "string",
|
||||
"customTemplates": {
|
||||
"post": [
|
||||
{
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"screenshot": "string",
|
||||
"file": "string"
|
||||
}
|
||||
],
|
||||
"category": [
|
||||
{
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"screenshot": "string",
|
||||
"file": "string"
|
||||
}
|
||||
],
|
||||
"page": [
|
||||
{
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"screenshot": "string",
|
||||
"file": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {}
|
||||
}
|
||||
```
|
@@ -1,261 +0,0 @@
|
||||
---
|
||||
title: 全局变量
|
||||
description: 系统提供的一些全局变量
|
||||
---
|
||||
|
||||
## 博客地址
|
||||
|
||||
```html
|
||||
${blog_url!}
|
||||
```
|
||||
|
||||
此变量与后台博客设置中的 `博客地址` 相对应。
|
||||
|
||||
## 网站根路径
|
||||
|
||||
```html
|
||||
${context!}
|
||||
```
|
||||
|
||||
需要注意的是,此变量和 `blog_url` 不同的是,这个变量有两种值,一种为相对路径形式,一种为绝对路径形式。
|
||||
|
||||
那么,当在后台博客设置中将 `全局绝对路径` 的选项打开时,`context` 变量值为 `${blog_url}/`,关闭时,`context` 的变量值为 `/`。
|
||||
|
||||
假设,我设置的 `博客地址` 为 `https://halo.run`,那么:
|
||||
|
||||
- 全局绝对路径为开启的状态下:<https://halo.run/>
|
||||
- 全局绝对路径为关闭的状态下:/
|
||||
|
||||
## 主题资源根路径
|
||||
|
||||
```html
|
||||
${theme_base!}
|
||||
```
|
||||
|
||||
假设你的主题在 `~/halo-dev/templates/themes/anatole` 这个目录,那么 `theme_base` 为 `https://yourdomain/themes/anatole`
|
||||
|
||||
举个例子,你当前开发的主题为 `anatole`,当你要获取主题下 `css/style.css` 这个文件的路径,那么:
|
||||
|
||||
```html
|
||||
${theme_base!}/css/style.css
|
||||
```
|
||||
|
||||
## 主题信息
|
||||
|
||||
主题名称:
|
||||
|
||||
```html
|
||||
${theme.name!}
|
||||
```
|
||||
|
||||
主题 git 仓库地址:
|
||||
|
||||
```html
|
||||
${theme.repo!}
|
||||
```
|
||||
|
||||
主题版本号:
|
||||
|
||||
```html
|
||||
${theme.version!}
|
||||
```
|
||||
|
||||
## 博客标题
|
||||
|
||||
```html
|
||||
${blog_title!}
|
||||
```
|
||||
|
||||
此变量与后台博客设置中的 `博客标题` 相对应。
|
||||
|
||||
## 博客 Logo
|
||||
|
||||
```html
|
||||
${blog_logo!}
|
||||
```
|
||||
|
||||
此变量与后台博客设置中的 `Logo` 相对应。
|
||||
|
||||
## Halo 版本
|
||||
|
||||
```html
|
||||
${version!}
|
||||
```
|
||||
|
||||
当前 Halo 的版本,如:1.3.0
|
||||
|
||||
## 博主信息
|
||||
|
||||
昵称:
|
||||
|
||||
```html
|
||||
${user.nickname!}
|
||||
```
|
||||
|
||||
邮箱地址:
|
||||
|
||||
```html
|
||||
${user.email!}
|
||||
```
|
||||
|
||||
描述:
|
||||
|
||||
```html
|
||||
${user.description!}
|
||||
```
|
||||
|
||||
头像地址:
|
||||
|
||||
```html
|
||||
${user.avatar!}
|
||||
```
|
||||
|
||||
上次登录时间:
|
||||
|
||||
```html
|
||||
${user.expireTime!}
|
||||
```
|
||||
|
||||
## SEO 关键词
|
||||
|
||||
```html
|
||||
${meta_keywords!}
|
||||
```
|
||||
|
||||
需要注意的是,虽然这个变量在任何页面都可以使用,但是其值可能在不同的页面是不一样的。会根据用户的设置,生成对应的值。
|
||||
|
||||
假设在文章页面:
|
||||
|
||||
- 如果用户为文章设置了标签,而没有设置 `自定义关键词`,系统会自动将标签设置为页面关键词。
|
||||
- 如果用户设置了 `自定义关键词`,那么则会取用户设置的值。
|
||||
|
||||
## SEO 描述
|
||||
|
||||
```html
|
||||
${meta_description!}
|
||||
```
|
||||
|
||||
需要注意的是,虽然这个变量在任何页面都可以使用,但是其值可能在不同的页面是不一样的。会根据用户的设置,生成对应的值。
|
||||
|
||||
## RSS 2.0 订阅地址
|
||||
|
||||
```html
|
||||
${rss_url!}
|
||||
```
|
||||
|
||||
如:`https://yourdomain/rss.xml`
|
||||
|
||||
## Atom 格式的订阅地址
|
||||
|
||||
```html
|
||||
${atom_url!}
|
||||
```
|
||||
|
||||
如:`https://yourdomain/atom.xml`
|
||||
|
||||
## XML 格式的网站地图地址
|
||||
|
||||
```html
|
||||
${sitemap_xml_url!}
|
||||
```
|
||||
|
||||
如:`https://yourdomain/sitemap.xml`
|
||||
|
||||
## HTML 格式的网站地图地址
|
||||
|
||||
```html
|
||||
${sitemap_html_url!}
|
||||
```
|
||||
|
||||
如:`https://yourdomain/sitemap.html`
|
||||
|
||||
## 友情链接页面地址
|
||||
|
||||
```html
|
||||
${links_url!}
|
||||
```
|
||||
|
||||
- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{links_prefix}`
|
||||
- **全局绝对路径为关闭的状态下**:`/{links_prefix}`
|
||||
|
||||
`{links_prefix}` 是用户可设定的值,用户可以在后台修改 `友情链接` 的前缀,默认为 `links`。
|
||||
|
||||
## 图库页面地址
|
||||
|
||||
```html
|
||||
${photos_url!}
|
||||
```
|
||||
|
||||
- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{photos_prefix}`
|
||||
- **全局绝对路径为关闭的状态下**:`/{photos_prefix}`
|
||||
|
||||
`{photos_prefix}` 是用户可设定的值,用户可以在后台修改 `图库页面` 的前缀,默认为 `photos`。
|
||||
|
||||
## 日志页面地址
|
||||
|
||||
```html
|
||||
${journals_url!}
|
||||
```
|
||||
|
||||
- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{journals_prefix}`
|
||||
- **全局绝对路径为关闭的状态下**:`/{journals_prefix}`
|
||||
|
||||
`{journals_prefix}` 是用户可设定的值,用户可以在后台修改 `日志页面` 的前缀,默认为 `journals`。
|
||||
|
||||
## 文章归档页面地址
|
||||
|
||||
```html
|
||||
${archives_url!}
|
||||
```
|
||||
|
||||
- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{archives_prefix}`
|
||||
- **全局绝对路径为关闭的状态下**:`/{archives_prefix}`
|
||||
|
||||
`{archives_prefix}` 是用户可设定的值,用户可以在后台修改 `归档` 的前缀,默认为 `archives`。
|
||||
|
||||
## 分类列表页面地址
|
||||
|
||||
```html
|
||||
${categories_url!}
|
||||
```
|
||||
|
||||
- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{categories_prefix}`
|
||||
- **全局绝对路径为关闭的状态下**:`/{categories_prefix}`
|
||||
|
||||
`{categories_prefix}` 是用户可设定的值,用户可以在后台修改 `分类` 的前缀,默认为 `categories`。
|
||||
|
||||
## 标签列表页面地址
|
||||
|
||||
```html
|
||||
${tags_url!}
|
||||
```
|
||||
|
||||
- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{tags_prefix}`
|
||||
- **全局绝对路径为关闭的状态下**:`/{tags_prefix}`
|
||||
|
||||
`{tags_prefix}` 是用户可设定的值,用户可以在后台修改 `标签` 的前缀,默认为 `tags`。
|
||||
|
||||
## 页面判断
|
||||
|
||||
判断当前页面是否是特定的页面。
|
||||
|
||||
- **is_index**:首页
|
||||
- **is_post**:文章页
|
||||
- **is_sheet**:自定义页面
|
||||
- **is_archives**:归档页面
|
||||
- **is_categories**:分类列表页面
|
||||
- **is_category**:单个分类页面
|
||||
- **is_tags**:标签列表页面
|
||||
- **is_tag**:单个标签页面
|
||||
- **is_search**:搜索结果页面
|
||||
- **is_journals**:日志页面
|
||||
- **is_photos**:图库页面
|
||||
- **is_links**:友情链接页面
|
||||
|
||||
用法:
|
||||
|
||||
```html
|
||||
<#if is_index??>
|
||||
当前页面是首页
|
||||
</#if>
|
||||
```
|
File diff suppressed because it is too large
Load Diff
@@ -11,47 +11,34 @@ Halo 在本地开发环境的运行可参考[开发环境运行](../core/run.md)
|
||||
|
||||
## 新建一个主题
|
||||
|
||||
Halo 的主题存放于工作目录的 `themes` 目录下,即 `~/halo-next/themes`,在该目录下新建一个文件夹,例如 `theme-foo`。当前一个最小可被系统加载的主题必须在主题根目录下包含 `theme.yaml` 配置文件。
|
||||
|
||||
以 [theme-default](https://github.com/halo-sigs/theme-default) 为例:
|
||||
Halo 的主题存放于工作目录的 `themes` 目录下,即 `~/halo2-dev/themes`,在该目录下新建一个文件夹,例如 `theme-foo`。当前一个最小可被系统加载的主题必须在主题根目录下包含 `theme.yaml` 配置文件。
|
||||
|
||||
```yaml title="theme.yaml"
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: theme-default
|
||||
name: theme-foo
|
||||
spec:
|
||||
displayName: Default
|
||||
displayName: 示例主题
|
||||
author:
|
||||
name: halo-dev
|
||||
website: https://halo.run
|
||||
description: Default theme for Halo 2.0
|
||||
description: 一个示例主题
|
||||
logo: https://halo.run/logo
|
||||
website: https://github.com/halo-sigs/theme-default
|
||||
repo: https://github.com/halo-sigs/theme-default.git
|
||||
settingName: "theme-default-setting"
|
||||
configMapName: "theme-default-configMap"
|
||||
website: https://github.com/halo-sigs/theme-foo
|
||||
repo: https://github.com/halo-sigs/theme-foo.git
|
||||
settingName: "theme-foo-setting"
|
||||
configMapName: "theme-foo-configMap"
|
||||
version: 1.0.0
|
||||
require: 2.0.0
|
||||
```
|
||||
|
||||
| 字段 | 描述 | 是否必填 |
|
||||
| --------------------- | ----------------------------------------------------------- | -------- |
|
||||
| `metadata.name` | 主题的唯一标识 | 是 |
|
||||
| `spec.displayName` | 显示名称 | 是 |
|
||||
| `spec.author.name` | 作者名称 | 否 |
|
||||
| `spec.author.website` | 作者网站 | 否 |
|
||||
| `spec.description` | 主题描述 | 否 |
|
||||
| `spec.logo` | 主题 Logo | 否 |
|
||||
| `spec.website` | 主题网站 | 否 |
|
||||
| `spec.repo` | 主题托管地址 | 否 |
|
||||
| `spec.settingName` | 设置表单定义的名称,需要同时创建对应的 `settings.yaml` 文件 | 否 |
|
||||
| `spec.configMapName` | 设置持久化的 ConfigMap 名称 | 否 |
|
||||
| `spec.version` | 主题版本 | 是 |
|
||||
| `spec.require` | 所需 Halo 的运行版本 | 是 |
|
||||
:::info 提示
|
||||
主题的配置文件详细文档请参考 [配置文件](./config.md)。
|
||||
:::
|
||||
|
||||
:::info 提示
|
||||
关于主题项目的目录结构请参考[主题目录结构](./structure.md)。
|
||||
主题项目的目录结构请参考 [主题目录结构](./structure.md)。
|
||||
:::
|
||||
|
||||
## 通过模板创建
|
||||
@@ -69,7 +56,7 @@ spec:
|
||||
|
||||
## 创建第一个页面模板
|
||||
|
||||
Halo 使用 [Thymeleaf](https://www.thymeleaf.org/) 作为后端模板引擎,后缀为 `.html`,与单纯编写 HTMl 一致。在 Halo 的主题中,主题的模板文件存放于 `templates` 目录下,例如 `~/halo-next/themes/theme-foo/templates`。为了此文档方便演示,我们先在 `templates` 创建一个首页的模板文件 `index.html`:
|
||||
Halo 使用 [Thymeleaf](https://www.thymeleaf.org/) 作为后端模板引擎,后缀为 `.html`,与单纯编写 HTML 一致。在 Halo 的主题中,主题的模板文件存放于 `templates` 目录下,例如 `~/halo2-dev/themes/theme-foo/templates`。为了此文档方便演示,我们先在 `templates` 创建一个首页的模板文件 `index.html`:
|
||||
|
||||
```html title="templates/index.html"
|
||||
<!DOCTYPE html>
|
||||
|
@@ -1,91 +0,0 @@
|
||||
---
|
||||
title: 公共宏模板
|
||||
description: 系统提供的一些宏模板
|
||||
---
|
||||
|
||||
> 为了减少重复代码,我们将某些常见的全局变量封装成了一个公共模板,我们只需要引入该模板,然后调用其中的宏模板即可。
|
||||
|
||||
## 公共 head 模板
|
||||
|
||||
> 需要注意的是,为了保证系统功能的完整性,我们强制要求在每个页面的 `<head>` 标签下必须包含此模板。
|
||||
|
||||
```html
|
||||
<@global.head />
|
||||
```
|
||||
|
||||
等同于:
|
||||
|
||||
```html
|
||||
<#if options.seo_spider_disabled!false>
|
||||
<meta name="robots" content="none">
|
||||
</#if>
|
||||
<meta name="generator" content="Halo ${version!}"/>
|
||||
<@global.favicon />
|
||||
<@global.custom_head />
|
||||
<@global.custom_content_head />
|
||||
```
|
||||
|
||||
## 公共底部
|
||||
|
||||
> 需要注意的是,为了保证系统功能的完整性,我们强制要求在每个页面的尾部必须包含此模板。
|
||||
|
||||
```html
|
||||
<@global.footer />
|
||||
```
|
||||
|
||||
等同于:
|
||||
|
||||
```html
|
||||
<@global.statistics />
|
||||
<@global.footer_info />
|
||||
````
|
||||
|
||||
## 相对时间
|
||||
|
||||
```html
|
||||
<@global.timeline datetime="时间" />
|
||||
|
||||
// 输出
|
||||
x 年前/x 个月前/x 天前/昨天/x 小时前/x 分钟前/x 秒前/刚刚
|
||||
```
|
||||
|
||||
## 评论模块
|
||||
|
||||
```html
|
||||
<@global.comment target= type="" />
|
||||
```
|
||||
|
||||
等同于:
|
||||
|
||||
```html
|
||||
<#if !post.disallowComment!false>
|
||||
<script src="//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
|
||||
<script src="${options.comment_internal_plugin_js!'//cdn.jsdelivr.net/npm/halo-comment@latest/dist/halo-comment.min.js'"></script>
|
||||
<halo-comment id="${post.id}" type="${type}"/>
|
||||
</#if>
|
||||
```
|
||||
|
||||
参数说明:
|
||||
|
||||
- target:post / sheet / journal 对象
|
||||
- type:评论类型,可为:post / sheet / journal
|
||||
|
||||
例子:
|
||||
|
||||
在文章页面(post.ftl or post_xxx.ftl):
|
||||
|
||||
```html
|
||||
<@global.comment target=post type="post" />
|
||||
```
|
||||
|
||||
在自定义页面(sheet.ftl or post_sheet.ftl):
|
||||
|
||||
```html
|
||||
<@global.comment target=sheet type="sheet" />
|
||||
```
|
||||
|
||||
在日志页面(journals.ftl):
|
||||
|
||||
```html
|
||||
<@global.comment target=journal type="journal" />
|
||||
```
|
129
docs/developer-guide/theme/settings.md
Normal file
129
docs/developer-guide/theme/settings.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
title: 设置选项
|
||||
description: 介绍主题如何定义以及使用设置选项。
|
||||
---
|
||||
|
||||
此文档将讲解如何在主题中定义和使用设置项,如 [表单定义](../form-schema) 中所说,目前 Halo 的 Console 端的所有表单都使用了 [FormKit](https://github.com/formkit/formkit) 的方案。
|
||||
|
||||
:::tip
|
||||
有关 FormKit 定义表单的更多信息,请参考 [表单定义](../form-schema),此文档仅针对主题中的设置项进行讲解。
|
||||
:::
|
||||
|
||||
## 定义表单
|
||||
|
||||
在主题中要使用设置项非常简单,只需要在主题根目录提供 `settings.yaml`,然后在 `theme.yaml` 中配置 `spec.settingName` 和 `spec.configMapName` 即可,在安装或者初始化主题的时候会自动识别并在 Console 端的主题设置中生成表单。
|
||||
|
||||
### 示例
|
||||
|
||||
```yaml title="theme-foo/theme.yaml" {14,15}
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: theme-foo
|
||||
spec:
|
||||
displayName: 示例主题
|
||||
author:
|
||||
name: halo-dev
|
||||
website: https://halo.run
|
||||
description: 一个示例主题
|
||||
logo: https://halo.run/logo
|
||||
website: https://github.com/halo-sigs/theme-foo
|
||||
repo: https://github.com/halo-sigs/theme-foo.git
|
||||
settingName: "theme-foo-setting"
|
||||
configMapName: "theme-foo-configMap"
|
||||
version: 1.0.0
|
||||
require: 2.0.0
|
||||
```
|
||||
|
||||
:::tip
|
||||
`settingName` 和 `configMapName` 必须同时配置,且可以自定义名称,但是 `settingName` 必须和 Setting 的 `metadata.name` 一致。
|
||||
:::
|
||||
|
||||
```yaml title="theme-foo/settings.yaml" {4}
|
||||
apiVersion: v1alpha1
|
||||
kind: Setting
|
||||
metadata:
|
||||
name: theme-foo-setting
|
||||
spec:
|
||||
forms:
|
||||
- group: style
|
||||
label: 样式
|
||||
formSchema:
|
||||
- $formkit: radio
|
||||
name: color_scheme
|
||||
label: 默认配色
|
||||
value: system
|
||||
options:
|
||||
- label: 跟随系统
|
||||
value: system
|
||||
- label: 深色
|
||||
value: dark
|
||||
- label: 浅色
|
||||
value: light
|
||||
- $formkit: color
|
||||
name: background_color
|
||||
label: 背景颜色
|
||||
value: "#f2f2f2"
|
||||
- group: layout
|
||||
label: 布局
|
||||
formSchema:
|
||||
- $formkit: radio
|
||||
name: nav
|
||||
label: 导航栏布局
|
||||
value: "single"
|
||||
options:
|
||||
- label: 单栏
|
||||
value: "single"
|
||||
- label: 双栏
|
||||
value: "double"
|
||||
```
|
||||
|
||||
:::tip
|
||||
Setting 资源的 `metadata.name` 必须和 `theme.yaml` 中的 `spec.settingName` 一致。
|
||||
:::
|
||||
|
||||
### 在主题模板中使用
|
||||
|
||||
在主题模板中,需要以 `theme.config.[group].[name]` 的形式进行调用。
|
||||
|
||||
其中:
|
||||
|
||||
1. `group`: 即 `spec.forms[].group`,如上面示例中的 `style` 和 `layout`。
|
||||
2. `name`: 即 `spec.forms[].formSchema[].name`,如上面示例中的 `color_scheme` 和 `nav`。
|
||||
|
||||
示例:
|
||||
|
||||
```html
|
||||
<body th:class="${theme.config.style.color_scheme}">
|
||||
<!-- do something -->
|
||||
</body>
|
||||
```
|
||||
|
||||
```html
|
||||
<ul th:if="${theme.config.layout.nav == 'single'}">
|
||||
<!-- do something -->
|
||||
</ul>
|
||||
|
||||
<div th:if="${theme.config.layout.nav == 'double'}">
|
||||
<!-- do something -->
|
||||
</div>
|
||||
```
|
||||
|
||||
## 从 1.x 迁移
|
||||
|
||||
为了方便主题开发者从 1.x 迁移,我们提供了工具用于迁移设置表单配置文件。
|
||||
|
||||
工具仓库地址:<https://github.com/halo-sigs/convert-theme-config-to-next>
|
||||
|
||||
```bash
|
||||
# 1.x 版本主题
|
||||
cd path/to/theme
|
||||
|
||||
npx @halo-dev/convert-theme-config-to-next settings
|
||||
```
|
||||
|
||||
执行完成之后即可看到主题目录下生成了 `settings.2.0.yaml` 文件,重命名为 `settings.yaml` 即可。
|
||||
|
||||
:::tip
|
||||
转换完成之后需要修改 `metadata.name` 字段。
|
||||
:::
|
55
docs/developer-guide/theme/static-resources.md
Normal file
55
docs/developer-guide/theme/static-resources.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: 静态资源
|
||||
description: 本文档介绍主题的静态资源的引用方法。
|
||||
---
|
||||
|
||||
通过 [目录结构](./structure.md) 的讲解我们可以知道,目前主题的静态资源统一托管在 `/templates/assets/` 目录下,下面讲解一下如何在模板中使用,大致会分为两种引入方式。
|
||||
|
||||
## 模板标签引用
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" th:href="@{/assets/dist/style.css}" />
|
||||
<script th:src="@{/assets/dist/main.iife.js}"></script>
|
||||
|
||||
<img th:src="@{/assets/images/logo.png}" />
|
||||
```
|
||||
|
||||
其中 `@{/assets/dist/style.css}` 表示引用 `/templates/assets/dist/style.css` 文件。最终会被渲染为:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="/themes/my-theme/assets/dist/style.css" />
|
||||
```
|
||||
|
||||
## API 引用
|
||||
|
||||
以上方式仅支持在 HTML 标签中使用,且必须使用 `@{}` 包裹才能渲染为正确的路径。如果需要在非 HTML 标签中得到正确的路径,我们提供了 `#theme.assets()` API。
|
||||
|
||||
:::info 注意
|
||||
需要注意的是,调用 `#theme.assets()` 的时候,资源地址不需要添加 `/assets/`。
|
||||
:::
|
||||
|
||||
比如我们需要在 JavaScript 中异步获取一些资源:
|
||||
|
||||
```html {3}
|
||||
<script th:inline="javascript">
|
||||
|
||||
loadScript('[(${#theme.assets("/dist/main.iife.js")})]');
|
||||
|
||||
// loadScript('/themes/my-theme/assets/dist/main.iife.js');
|
||||
|
||||
function loadScript(url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
:::info 提示
|
||||
关于在 JavaScript 中使用 Thymeleaf 语法可以参考 Thymeleaf 官方文档:[JavaScript inlining](https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#javascript-inlining)
|
||||
:::
|
@@ -5,7 +5,7 @@ description: 主题的目录结构介绍
|
||||
|
||||
Halo 2.0 的主题基本目录结构如下:
|
||||
|
||||
```bash title="~/halo-next/themes/my-theme"
|
||||
```bash title="~/halo2-dev/themes/my-theme"
|
||||
my-theme
|
||||
├── templates/
|
||||
│ ├── assets/
|
||||
@@ -27,7 +27,7 @@ my-theme
|
||||
|
||||
详细说明:
|
||||
|
||||
1. `/templates/` - 主题模板目录,存放主题模板文件,所有模板都需要放在这个目录。
|
||||
2. `/templates/assets/` - 主题静态资源目录,存放主题的静态资源文件,目前静态资源文件只能放在这个目录。
|
||||
3. `/theme.yaml` - 主题配置文件,配置主题的基本信息,如主题名称、版本、作者等。
|
||||
4. `/settings.yaml` - 主题设置定义文件,配置主题的设置项表单。
|
||||
1. `/templates/` - 主题模板目录,存放主题模板文件,所有模板都需要放在这个目录。关于模板的详细说明,请查阅 [模板路由](./template-route-mapping)。
|
||||
2. `/templates/assets/` - 主题静态资源目录,存放主题的静态资源文件,目前静态资源文件只能放在这个目录,引用方式请查阅 [静态资源](./static-resources)。
|
||||
3. `/theme.yaml` - 主题配置文件,配置主题的基本信息,如主题名称、版本、作者等。详细文档请查阅 [配置文件](./config)。
|
||||
4. `/settings.yaml` - 主题设置定义文件,配置主题的设置项表单。详细文档请查阅 [设置选项](./settings)。
|
||||
|
87
docs/developer-guide/theme/template-route-mapping.md
Normal file
87
docs/developer-guide/theme/template-route-mapping.md
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: 模板路由
|
||||
description: 本文档介绍路由与模板的映射关系,以及自定义模板。
|
||||
---
|
||||
|
||||
此文档讲解系统内部提供的路由与模板映射。
|
||||
|
||||
## 主要模板
|
||||
|
||||
### index.html
|
||||
|
||||
站点的首页模板,访问地址为 `/`。
|
||||
|
||||
### post.html
|
||||
|
||||
文章详情页面的模板,访问地址默认为 `/archives/:slug`。
|
||||
|
||||
### page.html
|
||||
|
||||
独立页面详情的模板,访问地址默认为 `/:slug`。
|
||||
|
||||
### archives.html
|
||||
|
||||
文章归档页面的模板,访问地址包括:
|
||||
|
||||
- `/archives`
|
||||
- `/archives/:year`
|
||||
- `/archives/:year/:month`
|
||||
|
||||
### tags.html
|
||||
|
||||
标签集合页面的模板,访问地址默认为 `/tags`。
|
||||
|
||||
### tag.html
|
||||
|
||||
标签归档页面的模板,访问地址默认为 `/tags/:slug`。
|
||||
|
||||
### categories.html
|
||||
|
||||
分类集合页面的模板,访问地址默认为 `/categories`。
|
||||
|
||||
### category.html
|
||||
|
||||
分类归档页面的模板,访问地址默认为 `/categories/:slug`。
|
||||
|
||||
## 自定义模板 {#custom-templates}
|
||||
|
||||
一般情况下,上文提到的模板已经能够满足大部分的需求,但如果需要针对某个特定的页面进行自定义,可以通过自定义模板来实现。目前系统支持为 **文章**、**独立页面**和**分类归档** 设置自定义模板:
|
||||
|
||||
在 `theme.yaml` 的 `spec` 节点下添加如下配置:
|
||||
|
||||
```yaml
|
||||
customTemplates:
|
||||
{type}:
|
||||
- name: {name}
|
||||
description: {description}
|
||||
screenshot: {screenshot}
|
||||
file: {file}.html
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```yaml
|
||||
customTemplates:
|
||||
post:
|
||||
- name: 文档
|
||||
description: 文档类型的文章
|
||||
screenshot:
|
||||
file: post_documentation.html
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `type`:模板类型,目前支持 `post` `page` `category`。
|
||||
- `name`:模板名称
|
||||
- `description`:模板描述
|
||||
- `screenshot`:模板预览图
|
||||
- `file`:模板文件名,需要在 `/templates/` 目录下创建
|
||||
|
||||
最终使用者即可在文章设置、独立页面设置、分类设置中选择自定义模板。
|
||||
|
||||
:::info 提示
|
||||
|
||||
1. 自定义模板与默认模板的功能相同,区别仅在于可以让使用者选择不同于默认模板风格的模板。
|
||||
2. 自定义模板的文件名需要以 `.html` 结尾,且需要在 `/templates/` 目录下创建。
|
||||
|
||||
:::
|
File diff suppressed because it is too large
Load Diff
7
docs/developer-guide/theme/template-variables.md
Normal file
7
docs/developer-guide/theme/template-variables.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: 模板变量
|
||||
---
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
|
||||
<DocCardList />
|
234
docs/developer-guide/theme/template-variables/archives.md
Normal file
234
docs/developer-guide/theme/template-variables/archives.md
Normal file
@@ -0,0 +1,234 @@
|
||||
---
|
||||
title: 文章归档
|
||||
description: archives.html - /archives
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/archives.html`
|
||||
- 访问路径
|
||||
- `/archives`
|
||||
- `/archives/:year`
|
||||
- `/archives/:year/:month`
|
||||
|
||||
## 变量
|
||||
|
||||
### archives
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#UrlContextListResult<PostArchiveVo\>](#urlcontextlistresultpostarchivevo)
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/archives.html"
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<h1 th:text="${archive.year}"></h1>
|
||||
<ul>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<li th:each="post : ${month.posts}">
|
||||
<a
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:text="${post.spec.title}">
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
<div th:if="${archives.hasPrevious() || archives.hasNext()}">
|
||||
<a
|
||||
th:href="@{${archives.prevUrl}}"
|
||||
>
|
||||
<span>上一页</span>
|
||||
</a>
|
||||
<span th:text="${archives.page} +' / '+ ${archives.total}"></span>
|
||||
<a
|
||||
th:href="@{${archives.nextUrl}}"
|
||||
>
|
||||
<span>下一页</span>
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### ListedPostVo
|
||||
|
||||
```json title="ListedPostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.505Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T13:06:38.505Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
||||
|
||||
### PostArchiveVo
|
||||
|
||||
```json title="PostArchiveVo"
|
||||
{
|
||||
"year": "string",
|
||||
"months": [
|
||||
{
|
||||
"month": "string",
|
||||
"posts": "#ListedPostVo"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedPostVo](#listedpostvo)
|
||||
|
||||
### UrlContextListResult<PostArchiveVo\>
|
||||
|
||||
```json title="UrlContextListResult<PostArchiveVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#PostArchiveVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0,
|
||||
"nextUrl": "string",
|
||||
"prevUrl": "string"
|
||||
}
|
||||
```
|
||||
|
||||
- [#PostArchiveVo](#postarchivevo)
|
84
docs/developer-guide/theme/template-variables/categories.md
Normal file
84
docs/developer-guide/theme/template-variables/categories.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: 文章分类集合
|
||||
description: categories.html - /categories
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/categories.html`
|
||||
- 访问路径:`/categories`
|
||||
|
||||
## 变量
|
||||
|
||||
### categories
|
||||
|
||||
#### 变量类型
|
||||
|
||||
List<[#CategoryTreeVo](#categorytreevo)>
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/categories.html"
|
||||
<ul>
|
||||
<li th:replace="~{modules/category-tree :: single(categories=${categories})}" />
|
||||
</ul>
|
||||
```
|
||||
|
||||
```html title="/templates/category-tree.html"
|
||||
<ul th:fragment="next (categories)">
|
||||
<li th:fragment="single (categories)" th:each="category : ${categories}">
|
||||
<a th:href="@{${category.status.permalink}}">
|
||||
<span th:text="${category.spec.displayName}"> </span>
|
||||
</a>
|
||||
<th:block th:if="${not #lists.isEmpty(category.children)}">
|
||||
<th:block th:replace="~{modules/category-tree :: next (categories=${category.children})}"></th:block>
|
||||
</th:block>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`categories`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryTreeVo
|
||||
|
||||
```json title="CategoryTreeVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:18:49.230Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"children": "List<#CategoryTreeVo>",
|
||||
"parentName": "string",
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryTreeVo](#categorytreevo)
|
225
docs/developer-guide/theme/template-variables/category.md
Normal file
225
docs/developer-guide/theme/template-variables/category.md
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: 分类归档
|
||||
description: category.html - /categories/:slug
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/category.html`
|
||||
- 访问路径:`/categories/:slug`
|
||||
|
||||
## 变量
|
||||
|
||||
### category
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#CategoryVo](#categoryvo)
|
||||
|
||||
### posts
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#UrlContextListResult<ListedPostVo\>](#urlcontextlistresultlistedpostvo)
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/category.html"
|
||||
<div>
|
||||
<h1 th:text="${category.spec.displayName}"></h1>
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a
|
||||
th:text="${post.spec.title}"
|
||||
th:href="${post.status.permalink}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a
|
||||
th:href="@{${posts.prevUrl}}"
|
||||
>
|
||||
<span>上一页</span>
|
||||
</a>
|
||||
<span th:text="${posts.page} +' / '+ ${posts.total}"></span>
|
||||
<a
|
||||
th:href="@{${posts.nextUrl}}"
|
||||
>
|
||||
<span>下一页</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`category`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### ListedPostVo
|
||||
|
||||
```json title="ListedPostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.505Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T13:06:38.505Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
||||
|
||||
### UrlContextListResult<ListedPostVo\>
|
||||
|
||||
```json title="UrlContextListResult<ListedPostVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#ListedPostVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0,
|
||||
"nextUrl": "string",
|
||||
"prevUrl": "string"
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedPostVo](#listedpostvo)
|
218
docs/developer-guide/theme/template-variables/index_.md
Normal file
218
docs/developer-guide/theme/template-variables/index_.md
Normal file
@@ -0,0 +1,218 @@
|
||||
---
|
||||
title: 首页
|
||||
description: index.html - /
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/index.html`
|
||||
- 访问路径:`/`
|
||||
|
||||
## 变量
|
||||
|
||||
### posts
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#UrlContextListResult<ListedPostVo\>](#urlcontextlistresultlistedpostvo)
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/index.html"
|
||||
<div>
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a
|
||||
th:text="${post.spec.title}"
|
||||
th:href="${post.status.permalink}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a
|
||||
th:href="@{${posts.prevUrl}}"
|
||||
>
|
||||
<span>上一页</span>
|
||||
</a>
|
||||
<span th:text="${posts.page} +' / '+ ${posts.total}"></span>
|
||||
<a
|
||||
th:href="@{${posts.nextUrl}}"
|
||||
>
|
||||
<span>下一页</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`index`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### ListedPostVo
|
||||
|
||||
```json title="ListedPostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.505Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T13:06:38.505Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
||||
|
||||
### UrlContextListResult<ListedPostVo\>
|
||||
|
||||
```json title="UrlContextListResult<ListedPostVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#ListedPostVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0,
|
||||
"nextUrl": "string",
|
||||
"prevUrl": "string"
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedPostVo](#listedpostvo)
|
109
docs/developer-guide/theme/template-variables/page.md
Normal file
109
docs/developer-guide/theme/template-variables/page.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: 独立页面
|
||||
description: page.html - /:slug
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/page.html`
|
||||
- 访问路径:`/:slug`
|
||||
|
||||
## 变量
|
||||
|
||||
### singlePage
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#SinglePageVo](#singlepagevo)
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/page.html"
|
||||
<article>
|
||||
<h1 th:text="${singlePage.spec.title}"></h1>
|
||||
<div th:utext="${singlePage.content.content}"> </div>
|
||||
</article>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`page`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### SinglePageVo
|
||||
|
||||
```json title="SinglePageVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T14:29:44.601Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T14:29:44.601Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"content": {
|
||||
"raw": "string",
|
||||
"content": "string"
|
||||
}
|
||||
}
|
||||
```
|
182
docs/developer-guide/theme/template-variables/post.md
Normal file
182
docs/developer-guide/theme/template-variables/post.md
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
title: 文章
|
||||
description: post.html - /archives/:slug
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/post.html`
|
||||
- 访问路径:`/archives/:slug`
|
||||
|
||||
## 变量
|
||||
|
||||
### post
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#PostVo](#postvo)
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/post.html"
|
||||
<article>
|
||||
<h1 th:text="${post.spec.title}"></h1>
|
||||
<div th:utext="${post.content.content}"> </div>
|
||||
</article>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`post`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### PostVo
|
||||
|
||||
```json title="PostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T12:45:43.888Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T12:45:43.888Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
},
|
||||
"content": {
|
||||
"raw": "string",
|
||||
"content": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
225
docs/developer-guide/theme/template-variables/tag.md
Normal file
225
docs/developer-guide/theme/template-variables/tag.md
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: 标签归档
|
||||
description: tag.html - /tags/:slug
|
||||
---
|
||||
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/tag.html`
|
||||
- 访问路径:`/tags/:slug`
|
||||
|
||||
## 变量
|
||||
|
||||
### tag
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#TagVo](#tagvo)
|
||||
|
||||
### posts
|
||||
|
||||
#### 变量类型
|
||||
|
||||
[#UrlContextListResult<ListedPostVo\>](#urlcontextlistresultlistedpostvo)
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/tag.html"
|
||||
<div>
|
||||
<h1 th:text="${tag.spec.displayName}"></h1>
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a
|
||||
th:text="${post.spec.title}"
|
||||
th:href="${post.status.permalink}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a
|
||||
th:href="@{${posts.prevUrl}}"
|
||||
>
|
||||
<span>上一页</span>
|
||||
</a>
|
||||
<span th:text="${posts.page} +' / '+ ${posts.total}"></span>
|
||||
<a
|
||||
th:href="@{${posts.nextUrl}}"
|
||||
>
|
||||
<span>下一页</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`tag`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### CategoryVo
|
||||
|
||||
```json title="CategoryVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"description": "string",
|
||||
"cover": "string",
|
||||
"template": "string",
|
||||
"priority": 0,
|
||||
"children": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"postCount": 0,
|
||||
"visiblePostCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### ListedPostVo
|
||||
|
||||
```json title="ListedPostVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.505Z",
|
||||
},
|
||||
"spec": {
|
||||
"title": "string",
|
||||
"slug": "string",
|
||||
"releaseSnapshot": "string",
|
||||
"headSnapshot": "string",
|
||||
"baseSnapshot": "string",
|
||||
"owner": "string",
|
||||
"template": "string",
|
||||
"cover": "string",
|
||||
"deleted": false,
|
||||
"publish": false,
|
||||
"publishTime": "2022-11-20T13:06:38.505Z",
|
||||
"pinned": false,
|
||||
"allowComment": true,
|
||||
"visible": "PUBLIC",
|
||||
"priority": 0,
|
||||
"excerpt": {
|
||||
"autoGenerate": true,
|
||||
"raw": "string"
|
||||
},
|
||||
"categories": [
|
||||
"string"
|
||||
],
|
||||
"tags": [
|
||||
"string"
|
||||
],
|
||||
"htmlMetas": [
|
||||
{
|
||||
"additionalProp1": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"excerpt": "string",
|
||||
"inProgress": true,
|
||||
"commentsCount": 0,
|
||||
"contributors": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"categories": "List<#CategoryVo>",
|
||||
"tags": "List<#TagVo>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
}
|
||||
],
|
||||
"owner": {
|
||||
"name": "string",
|
||||
"displayName": "string",
|
||||
"avatar": "string",
|
||||
"bio": "string"
|
||||
},
|
||||
"stats": {
|
||||
"visit": 0,
|
||||
"upvote": 0,
|
||||
"comment": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [#CategoryVo](#categoryvo)
|
||||
- [#TagVo](#tagvo)
|
||||
|
||||
### UrlContextListResult<ListedPostVo\>
|
||||
|
||||
```json title="UrlContextListResult<ListedPostVo>"
|
||||
{
|
||||
"page": 0,
|
||||
"size": 0,
|
||||
"total": 0,
|
||||
"items": "List<#ListedPostVo>",
|
||||
"first": true,
|
||||
"last": true,
|
||||
"hasNext": true,
|
||||
"hasPrevious": true,
|
||||
"totalPages": 0,
|
||||
"nextUrl": "string",
|
||||
"prevUrl": "string"
|
||||
}
|
||||
```
|
||||
|
||||
- [#ListedPostVo](#listedpostvo)
|
61
docs/developer-guide/theme/template-variables/tags.md
Normal file
61
docs/developer-guide/theme/template-variables/tags.md
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: 文章标签集合
|
||||
description: tags.html - /tags
|
||||
---
|
||||
## 路由信息
|
||||
|
||||
- 模板路径:`/templates/tags.html`
|
||||
- 访问路径:`/tags`
|
||||
|
||||
## 变量
|
||||
|
||||
### tags
|
||||
|
||||
#### 变量类型
|
||||
|
||||
List<[#TagVo](#tagvo)>
|
||||
|
||||
#### 示例
|
||||
|
||||
```html title="/templates/tags.html"
|
||||
<ul>
|
||||
<li th:each="tag : ${tags}" th:text="${tag.spec.displayName}" th:href="${tag.status.permalink}" />
|
||||
</ul>
|
||||
```
|
||||
|
||||
### _templateId
|
||||
|
||||
#### 变量值
|
||||
|
||||
`tags`
|
||||
|
||||
## 类型定义
|
||||
|
||||
### TagVo
|
||||
|
||||
```json title="TagVo"
|
||||
{
|
||||
"metadata": {
|
||||
"name": "string",
|
||||
"labels": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"additionalProp1": "string"
|
||||
},
|
||||
"creationTimestamp": "2022-11-20T13:06:38.512Z",
|
||||
},
|
||||
"spec": {
|
||||
"displayName": "string",
|
||||
"slug": "string",
|
||||
"color": "#F9fEB1",
|
||||
"cover": "string"
|
||||
},
|
||||
"status": {
|
||||
"permalink": "string",
|
||||
"visiblePostCount": 0,
|
||||
"postCount": 0
|
||||
},
|
||||
"postCount": 0
|
||||
}
|
||||
```
|
@@ -63,6 +63,11 @@ const config = {
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
docs: {
|
||||
sidebar: {
|
||||
autoCollapseCategories: true,
|
||||
},
|
||||
},
|
||||
navbar: {
|
||||
title: "Halo Documents",
|
||||
logo: {
|
||||
|
70
sidebars.js
70
sidebars.js
@@ -19,12 +19,18 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "入门",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
collapsed: false,
|
||||
items: [
|
||||
"getting-started/prepare",
|
||||
{
|
||||
type: "category",
|
||||
label: "安装指南",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
items: [
|
||||
// "getting-started/install/linux",
|
||||
"getting-started/install/docker",
|
||||
@@ -48,10 +54,16 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "开发者指南",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: "category",
|
||||
label: "系统开发",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
items: [
|
||||
// "developer-guide/core/structure",
|
||||
"developer-guide/core/prepare",
|
||||
@@ -62,21 +74,66 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "插件开发",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
items: ["developer-guide/plugin/prepare"],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "主题开发",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
items: [
|
||||
"developer-guide/theme/prepare",
|
||||
"developer-guide/theme/config",
|
||||
"developer-guide/theme/structure",
|
||||
// "developer-guide/theme/config-files",
|
||||
// "developer-guide/theme/global-variable",
|
||||
// "developer-guide/theme/public-template-tag",
|
||||
// "developer-guide/theme/page-variable",
|
||||
// "developer-guide/theme/template-tag",
|
||||
"developer-guide/theme/template-route-mapping",
|
||||
"developer-guide/theme/static-resources",
|
||||
"developer-guide/theme/settings",
|
||||
{
|
||||
type: "category",
|
||||
label: "模板变量",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "developer-guide/theme/template-variables",
|
||||
},
|
||||
items: [
|
||||
"developer-guide/theme/template-variables/index_",
|
||||
"developer-guide/theme/template-variables/post",
|
||||
"developer-guide/theme/template-variables/page",
|
||||
"developer-guide/theme/template-variables/archives",
|
||||
"developer-guide/theme/template-variables/tags",
|
||||
"developer-guide/theme/template-variables/tag",
|
||||
"developer-guide/theme/template-variables/categories",
|
||||
"developer-guide/theme/template-variables/category",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Finder API",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "developer-guide/theme/finder-apis",
|
||||
},
|
||||
items: [
|
||||
"developer-guide/theme/finder-apis/category",
|
||||
"developer-guide/theme/finder-apis/tag",
|
||||
"developer-guide/theme/finder-apis/post",
|
||||
"developer-guide/theme/finder-apis/single-page",
|
||||
"developer-guide/theme/finder-apis/comment",
|
||||
"developer-guide/theme/finder-apis/contributor",
|
||||
"developer-guide/theme/finder-apis/menu",
|
||||
"developer-guide/theme/finder-apis/site-stats",
|
||||
"developer-guide/theme/finder-apis/theme",
|
||||
"developer-guide/theme/finder-apis/plugin",
|
||||
],
|
||||
},
|
||||
"developer-guide/theme/code-snippets",
|
||||
],
|
||||
},
|
||||
"developer-guide/form-schema",
|
||||
// {
|
||||
// type: "link",
|
||||
// label: "REST API",
|
||||
@@ -87,6 +144,9 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "参与贡献",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
},
|
||||
items: ["contribution/issue", "contribution/pr"],
|
||||
},
|
||||
"about",
|
||||
|
BIN
static/img/formkit/formkit-repeater.png
Normal file
BIN
static/img/formkit/formkit-repeater.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Reference in New Issue
Block a user