Todo List
本示例用于展示如何从插件模板创建一个插件并写一个 Todo List:
首先通过模板仓库创建一个插件,例如叫 halo-plugin-todolist
配置你的插件
-
修改
build.gradle
中的group
为你自己的,如:group = 'run.halo.tutorial'
-
修改
settings.gradle
中的rootProject.name
rootProject.name = 'halo-plugin-todolist'
-
修改插件的描述文件
plugin.yaml
,它位于src/main/resources/plugin.yaml
。示例:apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
name: todolist
spec:
enabled: true
requires: ">=2.0.0"
author:
name: halo-dev
website: https://www.halo.run
logo: https://www.halo.run/logo
homepage: https://github.com/halo-dev/plugin-starter#readme
repo: https://github.com/halo-dev/plugin-starter
issues: https://github.com/halo-dev/plugin-starter/issues
displayName: "插件 Todo List"
description: "插件开发的 hello world,用于学习如何开发一个简单的 Halo 插件"
license:
- name: "GPL-3.0"
url: "https://github.com/halo-dev/plugin-starter/blob/main/LICENSE"
参考链接:
此时我们已经准备好了可以开发一个 TodoList 插件的一切,下面让我们正式进入 TodoList 插件开发教程。
运行插件
为了看到效果,首先我们需要让插件能最简单的运行起来。
-
在
src/main/java
下创建包,如run.halo.tutorial
,在创建一个类TodoListPlugin
,它继承自BasePlugin
类内容如下:package run.halo.tutorial;
import run.halo.app.plugin.PluginContext;
import org.springframework.stereotype.Component;
import run.halo.app.plugin.BasePlugin;
@Component
public class TodoListPlugin extends BasePlugin {
public TodoListPlugin(PluginContext pluginContext) {
super(pluginContext);
}
}src/main/java
下的文件结构如下:.
└── run
└── halo
└── tutorial
└── TodoListPlugin.java -
然后在项目目录执行命令
./gradlew build
-
使用
IntelliJ IDEA
打开 Halo,参考 Halo 开发环境运行 及 插件入门 配置插件的运行模式和路径:halo:
plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-todolist -
启动 Halo,然后访问
http://localhost:8090/console
在插件列表将能看到插件已经被正确启用:
创建一个自定义模型
我们希望 TodoList 能够被持久化以避免重启后数据丢失,因此需要创建一个自定义模型来进行数据持久化。
首先创建一个 class
名为 Todo
并写入如下内容:
package run.halo.tutorial;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import run.halo.app.extension.AbstractExtension;
import run.halo.app.extension.GVK;
@Data
@EqualsAndHashCode(callSuper = true)
@GVK(kind = "Todo", group = "todo.plugin.halo.run",
version = "v1alpha1", singular = "todo", plural = "todos")
public class Todo extends AbstractExtension {
@Schema(requiredMode = REQUIRED)
private TodoSpec spec;
@Data
public static class TodoSpec {
@Schema(requiredMode = REQUIRED, minLength = 1)
private String title;
@Schema(defaultValue = "false")
private Boolean done;
}
}
然后在 TodoListPlugin
的 start
生命周期方法中注册此自定义模型到 Halo 中。
// ...
+ import run.halo.app.extension.SchemeManager;
@Component
public class TodoListPlugin extends BasePlugin {
+ private final SchemeManager schemeManager;
- public TodoListPlugin(PluginContext pluginContext) {
+ public TodoListPlugin(PluginContext pluginContext, SchemeManager schemeManager) {
super(pluginContext);
+ this.schemeManager = schemeManager;
}
@Override
public void start() {
+ // 插件启动时注册自定义模型
+ schemeManager.register(Todo.class);
System.out.println("Hello world 插件启动了!");
}
@Override
public void stop() {
+ // 插件停用时取消注册自定义模型
+ Scheme todoScheme = schemeManager.get(Todo.class);
+ schemeManager.unregister(todoScheme);
System.out.println("Hello world 被停止!");
}
// ....
}
然后 build 项目,重启 Halo,访问 http://localhost:8090/swagger-ui.html
,
可以找到如下 Todo APIs:
由于所有以 /api
和 /apis
开头的 APIs 都需要认证才能访问,因此先在 Swagger UI 界面顶部点击 Authorize
认证,然后尝试访问
GET /apis/todo.plugin.halo.run/v1alpha1/todos
可以看到如下结果:
{
"page": 0,
"size": 0,
"total": 0,
"items": [],
"first": true,
"last": true,
"hasNext": false,
"hasPrevious": false,
"totalPages": 1
}
至此我们完成了一个自定义模型的创建和使用插件生命周期方法实现了自定义模型的注册和删除,下一步我们将编写用户界面,使用这些 APIs 完成 TodoList 功能。
编写用户界面
目标
我们希望实现如下的用户界面:
- 在左侧菜单添加一个名为
Todo List
的菜单项,它属于一个工具
的组。 - 内容页为一个简单的 Todo List,它实现以下功能:
- 添加
Todo item
- 将一个
Todo item
标记为完成,也可以取消完成状态 - 列表有三个
Tab
可供切 换,用于过滤数据展示
- 添加
实现
使用模板仓库创建的项目中与 src
目录同级有一个 ui
目录,它即为用户界面的源码目录。
打开 ui/src/index.ts
文件,修改如下:
export default definePlugin({
components: {},
routes: [
{
parentName: "Root",
route: {
- path: "/example",
+ path: "/todos", // TodoList 的路由 path
- name: "Example",
+ name: "TodoList",// 菜单标识名
component: HomeView,
meta: {
- title: "示例页面",
+ title: "Todo List",//菜单页的浏览器 tab 标题
searchable: true,
menu: {
- name: "示例页面",
+ name: "Todo List",// TODO 菜单显示名称
- group: "示例分组",
= group: "工具",// 所在组名
icon: markRaw(IconPlug),
priority: 0,
},
},
},
},
],
extensionPoints: {},
});
完成此步骤后 Console 左侧菜单多了一个名 工具
的组,其下有 Todo List
,浏览器标签页名称也是 Todo List
。
接来下我们需要在右侧内容区域实现 目标 中图示的 Todo 样式,为了快速上手,我们使用 todomvc 提供的 Vue 标准实现。
编辑 ui/src/views/HomeView.vue
文件,清空它的内容,并拷贝 examples/#todomvc 的所有代码粘贴到此文件中,并执行以下步骤:
-
cd ui
切换到ui
目录。 -
pnpm install todomvc-app-css
。 -
修改
ui/src/views/HomeView.vue
最底部的style
标签。- <style>
+ <style scoped>
- @import "https://unpkg.com/todomvc-app-css@2.4.1/index.css";
+ @import "todomvc-app-css/index.css";
</style> -
重新 Build 后刷新页面,便能看到目标图所示效果。
通过以上步骤就实现了一个 Todo List 的用户界面功能,但 Todo
数据只是被临时存放到了 LocalStorage
中,下一步我们将通过自定义模型生成的 APIs 来让用户界面与服务端交互。