imean-service-engine-htmx-plugin
v2.11.1
Published
HtmxAdminPlugin for IMean Service Engine
Downloads
2,963
Maintainers
Readme
HtmxAdminPlugin v2
基于 PageModel 和 Feature 的简化管理后台插件。
设计理念
核心原则
- 围绕模型设计:使用
PageModel作为核心抽象,统一处理有数据模型和无数据模型的场景 - 权限与 Feature 绑定:每个 Feature 声明自己的权限,支持灵活的权限控制
- 函数式数据操作:移除数据源机制,直接使用函数处理数据操作
- 简洁统一:单一类、统一模式,降低学习成本
快速开始
安装
npm install imean-service-engine-htmx-plugin基础使用
import { Factory } from "imean-service-engine";
import { HtmxAdminPlugin, PageModel, CustomFeature } from "imean-service-engine-htmx-plugin";
// 创建插件
const adminPlugin = new HtmxAdminPlugin({
title: "管理后台",
prefix: "/admin",
});
// 创建页面模型
class LoginPageModel extends PageModel {
constructor() {
super("login", undefined, {
title: "登录",
useAdminLayout: false,
});
this.features.custom("login", new CustomFeature({
name: "login",
permission: null,
routes: [
{ method: "get", path: "" },
{ method: "post", path: "" },
],
handler: async (context) => {
// 处理逻辑
},
}));
}
}
// 注册页面
adminPlugin.registerPage(new LoginPageModel());
// 创建引擎
const { Microservice } = Factory.create(adminPlugin);
const engine = new Microservice({ name: "admin-service" });
await engine.start(3000);使用模式
有数据模型的页面
import { PageModel, type ModelSchema } from "imean-service-engine-htmx-plugin";
import { z } from "zod";
// 定义 Schema
const ArticleSchema = z.object({
id: z.number().optional(),
title: z.string().min(1).describe("标题"),
content: z.string().min(10).describe("内容"),
});
const articleModelSchema: ModelSchema<Article> = {
schema: ArticleSchema,
};
// 数据操作函数
async function getArticleList(params: ListParams): Promise<ListResult<Article>> {
// 从数据库或其他数据源获取列表
}
async function getArticle(id: string | number): Promise<Article | null> {
// 获取单条数据
}
async function createArticle(data: Partial<Article>): Promise<Article> {
// 创建数据
}
async function updateArticle(id: string | number, data: Partial<Article>): Promise<Article | null> {
// 更新数据
}
async function deleteArticle(id: string | number): Promise<boolean> {
// 删除数据
}
// 定义 PageModel
class ArticlePageModel extends PageModel<Article> {
constructor() {
super("articles", articleModelSchema, {
title: "文章管理",
description: "管理文章内容",
});
// 注册 CRUD Feature
this.features.crud({
permissionPrefix: "articles",
getList: getArticleList,
getItem: getArticle,
createItem: createArticle,
updateItem: updateArticle,
deleteItem: deleteArticle,
// 可选:弹窗大小配置(sm, md, lg, xl, full)
dialogSizes: {
detail: "xl",
edit: "xl",
create: "xl",
},
// 可选:遮罩关闭配置(编辑/创建时建议设为 false,防止误操作)
closeOnBackdropClick: {
edit: false,
create: false,
detail: true,
},
// 可选:动态标题和描述配置(使用实际数据的标题作为页面标题)
getTitles: {
detail: (item) => item.title, // 详情页使用文章标题
edit: (item) => `编辑:${item.title}`, // 编辑页使用 "编辑:{文章标题}"
},
getDescriptions: {
detail: (item) => item.excerpt || `作者:${item.author}`, // 详情页使用文章摘要
},
});
}
}无数据模型的页面
class LoginPageModel extends PageModel {
constructor() {
super("login", undefined, {
title: "登录",
useAdminLayout: false,
});
this.features.custom("login", new CustomFeature({
name: "login",
permission: null,
routes: [
{ method: "get", path: "" },
{ method: "post", path: "" },
],
handler: async (context) => {
// 处理逻辑
},
}));
}
}只读场景
class ArticleReadonlyPageModel extends PageModel<Article> {
constructor() {
super("articles-readonly", articleModelSchema, {
title: "文章查看(只读)",
});
this.features.crud({
permissionPrefix: "articles",
getList: getArticleList,
getItem: getArticle,
createItem: createArticle, // 必须提供,但不会注册
updateItem: updateArticle,
features: {
list: true,
detail: true,
create: false, // 禁用创建
edit: false, // 禁用编辑
delete: false, // 禁用删除
},
});
}
}权限系统
权限格式
- 默认格式:
{modelName}.{featureName}- 例如:
articles.list、articles.create、articles.delete
- 例如:
- 自定义权限:可以在 Feature 配置中覆盖
权限绑定
- 每个 Feature 声明自己的
permission - 插件在路由注册时自动进行权限检查
- 支持
null表示开放访问
API 参考
PageModel
class PageModel<T extends { id: string | number } = any> {
constructor(
modelName: string,
schema?: ModelSchema<T>,
metadata?: PageMetadata
);
getMetadata(): PageMetadata;
hasModel(): boolean;
validate(data: unknown): ValidationResult;
getFormFields(): FormField[];
getListFields(): string[];
getDetailFields(): string[];
getFieldLabel(fieldName: string): string;
}FeatureRegistry
class FeatureRegistry<T> {
crud(options: CRUDOptions): void;
list(feature: Feature<T>): void;
detail(feature: Feature<T>): void;
create(feature: Feature<T>): void;
edit(feature: Feature<T>): void;
delete(feature: Feature<T>): void;
custom(name: string, feature: Feature<T>): void;
}HtmxAdminPlugin
class HtmxAdminPlugin {
registerPage<T>(page: PageModel<T>): this;
registerPages(...pages: PageModel<any>[]): this;
}迁移指南
从 v1 迁移到 v2:
- 移除数据源:将数据源方法改为函数
- 移除装饰器:使用构造函数注册
- 合并元数据:将
getTitle、getDescription等合并为构造函数参数 - 权限迁移:将页面级权限改为 Feature 级权限
设计文档
详细设计文档请参考 DESIGN.md。
旧版本
旧版本代码已移至 src-old 目录,文档移至 README-old.md。
