imean-service-engine-htmx-plugin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2373 -0
- package/dist/index.d.mts +1484 -0
- package/dist/index.d.ts +1484 -0
- package/dist/index.js +3099 -0
- package/dist/index.mjs +3087 -0
- package/docs/README.md +87 -0
- package/docs/architecture.md +564 -0
- package/docs/quick-reference.md +559 -0
- package/package.json +47 -0
package/docs/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# HtmxAdminPlugin 文档索引
|
|
2
|
+
|
|
3
|
+
## 文档列表
|
|
4
|
+
|
|
5
|
+
### 📖 [完整文档](../README.md)
|
|
6
|
+
|
|
7
|
+
完整的使用文档,包含:
|
|
8
|
+
- 概述和核心特性
|
|
9
|
+
- 快速开始指南
|
|
10
|
+
- 模块类型详解
|
|
11
|
+
- 数据源说明
|
|
12
|
+
- 组件系统
|
|
13
|
+
- 路由系统
|
|
14
|
+
- 响应机制
|
|
15
|
+
- 错误处理
|
|
16
|
+
- 高级功能
|
|
17
|
+
- API 参考
|
|
18
|
+
- 最佳实践
|
|
19
|
+
- 示例项目
|
|
20
|
+
|
|
21
|
+
**适合**: 初次使用或需要全面了解插件的开发者
|
|
22
|
+
|
|
23
|
+
### 🏗️ [架构设计文档](./architecture.md)
|
|
24
|
+
|
|
25
|
+
详细的架构设计文档,包含:
|
|
26
|
+
- 架构概述
|
|
27
|
+
- 核心设计原则
|
|
28
|
+
- 统一响应架构
|
|
29
|
+
- 模块系统
|
|
30
|
+
- 路由系统
|
|
31
|
+
- 组件系统
|
|
32
|
+
- 数据流
|
|
33
|
+
- 响应处理流程
|
|
34
|
+
- 关键实现细节
|
|
35
|
+
- 扩展点
|
|
36
|
+
- 性能考虑
|
|
37
|
+
- 安全性考虑
|
|
38
|
+
|
|
39
|
+
**适合**: 需要深入理解插件架构或进行二次开发的开发者
|
|
40
|
+
|
|
41
|
+
### ⚡ [快速参考指南](./quick-reference.md)
|
|
42
|
+
|
|
43
|
+
快速参考指南,包含:
|
|
44
|
+
- 插件配置速查
|
|
45
|
+
- 模块定义模板
|
|
46
|
+
- 数据源示例
|
|
47
|
+
- 常用方法速查
|
|
48
|
+
- 组件速查表
|
|
49
|
+
- 响应头速查
|
|
50
|
+
- 常见场景示例
|
|
51
|
+
- 类型定义速查
|
|
52
|
+
- 路由速查
|
|
53
|
+
- 调试技巧
|
|
54
|
+
- 常见问题
|
|
55
|
+
|
|
56
|
+
**适合**: 已经熟悉插件,需要快速查找信息的开发者
|
|
57
|
+
|
|
58
|
+
## 文档使用建议
|
|
59
|
+
|
|
60
|
+
### 新手入门
|
|
61
|
+
|
|
62
|
+
1. 阅读 [完整文档](../README.md) 的"快速开始"部分
|
|
63
|
+
2. 查看示例项目了解实际用法
|
|
64
|
+
3. 参考 [快速参考指南](./quick-reference.md) 的常见场景
|
|
65
|
+
|
|
66
|
+
### 深入理解
|
|
67
|
+
|
|
68
|
+
1. 阅读 [架构设计文档](./architecture.md) 了解设计理念
|
|
69
|
+
2. 阅读 [完整文档](../README.md) 的"高级功能"部分
|
|
70
|
+
3. 查看源代码了解实现细节
|
|
71
|
+
|
|
72
|
+
### 日常开发
|
|
73
|
+
|
|
74
|
+
1. 使用 [快速参考指南](./quick-reference.md) 查找常用功能
|
|
75
|
+
2. 参考 [完整文档](../README.md) 的 API 参考
|
|
76
|
+
3. 查看示例项目了解最佳实践
|
|
77
|
+
|
|
78
|
+
## 相关资源
|
|
79
|
+
|
|
80
|
+
- **示例项目**: `examples/` 目录
|
|
81
|
+
- **源代码**: `src/plugins/htmx-admin/` 目录
|
|
82
|
+
- **类型定义**: `src/plugins/htmx-admin/types.ts`
|
|
83
|
+
|
|
84
|
+
## 反馈和建议
|
|
85
|
+
|
|
86
|
+
如有问题或建议,欢迎提交 Issue 或 Pull Request。
|
|
87
|
+
|
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
# HtmxAdminPlugin 架构设计文档
|
|
2
|
+
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
- [架构概述](#架构概述)
|
|
6
|
+
- [核心设计原则](#核心设计原则)
|
|
7
|
+
- [统一响应架构](#统一响应架构)
|
|
8
|
+
- [模块系统](#模块系统)
|
|
9
|
+
- [路由系统](#路由系统)
|
|
10
|
+
- [组件系统](#组件系统)
|
|
11
|
+
- [数据流](#数据流)
|
|
12
|
+
- [响应处理流程](#响应处理流程)
|
|
13
|
+
|
|
14
|
+
## 架构概述
|
|
15
|
+
|
|
16
|
+
HtmxAdminPlugin 采用后端驱动的统一响应架构,通过 `RouteHandler` 统一处理所有请求。所有模块都通过 `__handle()` 方法作为统一入口,简化了请求处理流程。所有响应都通过后端响应头明确指定目标容器,前端无需指定 `hx-target` 或 `hx-swap`。
|
|
17
|
+
|
|
18
|
+
### 架构层次
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌─────────────────────────────────────────┐
|
|
22
|
+
│ HtmxAdminPlugin │
|
|
23
|
+
│ (插件入口,模块注册和路由管理) │
|
|
24
|
+
└─────────────────────────────────────────┘
|
|
25
|
+
│
|
|
26
|
+
┌───────────┼───────────┐
|
|
27
|
+
│ │ │
|
|
28
|
+
┌───────▼──────┐ ┌─▼──────┐ ┌─▼──────────┐
|
|
29
|
+
│ 模块系统 │ │ 路由系统│ │ 组件系统 │
|
|
30
|
+
│ (Base Classes)│ │(Routes) │ │(Components)│
|
|
31
|
+
└───────┬──────┘ └─┬──────┘ └─┬──────────┘
|
|
32
|
+
│ │ │
|
|
33
|
+
└───────────┼───────────┘
|
|
34
|
+
│
|
|
35
|
+
┌───────────▼───────────┐
|
|
36
|
+
│ 响应处理系统 │
|
|
37
|
+
│ (OOB Response Builder) │
|
|
38
|
+
└───────────────────────┘
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 核心设计原则
|
|
42
|
+
|
|
43
|
+
### 1. 后端驱动
|
|
44
|
+
|
|
45
|
+
**原则**: 所有响应行为由后端控制,前端只负责触发请求。
|
|
46
|
+
|
|
47
|
+
**实现**:
|
|
48
|
+
- 所有响应都通过 `HX-Retarget` 响应头明确指定目标容器
|
|
49
|
+
- `HX-Push-Url` 由后端控制,决定是否更新 URL
|
|
50
|
+
- 前端请求不指定 `hx-target` 或 `hx-swap`
|
|
51
|
+
|
|
52
|
+
**优势**:
|
|
53
|
+
- 简化前端代码
|
|
54
|
+
- 统一响应处理逻辑
|
|
55
|
+
- 易于维护和调试
|
|
56
|
+
|
|
57
|
+
### 2. 统一响应架构
|
|
58
|
+
|
|
59
|
+
**原则**: 所有响应使用统一的 OOB 交换机制。
|
|
60
|
+
|
|
61
|
+
**实现**:
|
|
62
|
+
- 主要内容作为非 OOB 内容返回
|
|
63
|
+
- 导航更新等通过 OOB 元素更新
|
|
64
|
+
- 使用 `OOBResponseBuilder` 统一构建响应
|
|
65
|
+
|
|
66
|
+
**优势**:
|
|
67
|
+
- 一致的响应格式
|
|
68
|
+
- 易于扩展
|
|
69
|
+
- 减少代码重复
|
|
70
|
+
|
|
71
|
+
### 3. 类型安全
|
|
72
|
+
|
|
73
|
+
**原则**: 完整的 TypeScript 类型定义。
|
|
74
|
+
|
|
75
|
+
**实现**:
|
|
76
|
+
- 所有接口和类型都有明确的定义
|
|
77
|
+
- 数据源接口类型化
|
|
78
|
+
- 组件 Props 类型化
|
|
79
|
+
|
|
80
|
+
**优势**:
|
|
81
|
+
- 编译时错误检查
|
|
82
|
+
- 更好的 IDE 支持
|
|
83
|
+
- 减少运行时错误
|
|
84
|
+
|
|
85
|
+
### 4. 可扩展性
|
|
86
|
+
|
|
87
|
+
**原则**: 通过继承和重写实现扩展。
|
|
88
|
+
|
|
89
|
+
**实现**:
|
|
90
|
+
- 基类提供默认实现
|
|
91
|
+
- 子类可以重写任何方法
|
|
92
|
+
- 支持完全自定义渲染
|
|
93
|
+
|
|
94
|
+
**优势**:
|
|
95
|
+
- 灵活的定制能力
|
|
96
|
+
- 保持代码简洁
|
|
97
|
+
- 易于理解和使用
|
|
98
|
+
|
|
99
|
+
## 统一响应架构
|
|
100
|
+
|
|
101
|
+
### 响应目标检测
|
|
102
|
+
|
|
103
|
+
响应目标检测在 `HtmxAdminContext` 构造函数中实现:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// 在 HtmxAdminContext 构造函数中
|
|
107
|
+
const url = new URL(ctx.req.url);
|
|
108
|
+
this.isFragment = ctx.req.header("HX-Request") === "true";
|
|
109
|
+
this.isDialog =
|
|
110
|
+
url.searchParams.get("dialog") === "true" ||
|
|
111
|
+
ctx.req.header("HX-Target") === "#dialog-container" ||
|
|
112
|
+
(ctx.req.header("Referer") || "").includes("dialog=true");
|
|
113
|
+
|
|
114
|
+
// 确定响应目标
|
|
115
|
+
this.target = this.isDialog ? "#dialog-container" : "#main-content";
|
|
116
|
+
this.swap = this.isDialog ? "innerHTML" : "innerHTML";
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 响应头设置
|
|
120
|
+
|
|
121
|
+
所有响应都会自动添加 `HX-Retarget` 响应头(在 `RouteHandler.renderFragment()` 中):
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const target = adminContext.isDialog
|
|
125
|
+
? "#dialog-container"
|
|
126
|
+
: "#admin-layout";
|
|
127
|
+
const swap = adminContext.isDialog ? "innerHTML" : "outerHTML";
|
|
128
|
+
|
|
129
|
+
const headers = {
|
|
130
|
+
"HX-Retarget": target,
|
|
131
|
+
"HX-Reswap": swap,
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 响应构建
|
|
136
|
+
|
|
137
|
+
响应构建在 `RouteHandler` 中实现:
|
|
138
|
+
|
|
139
|
+
**完整页面响应**:
|
|
140
|
+
```typescript
|
|
141
|
+
return ctx.html(
|
|
142
|
+
<BaseLayout title={adminContext.title} description={adminContext.description}>
|
|
143
|
+
<AdminLayout adminContext={adminContext}>
|
|
144
|
+
{adminContext.content}
|
|
145
|
+
</AdminLayout>
|
|
146
|
+
</BaseLayout>
|
|
147
|
+
);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**片段响应**:
|
|
151
|
+
```typescript
|
|
152
|
+
const fragments = [
|
|
153
|
+
// 通知(通过 OOB 更新)
|
|
154
|
+
adminContext.notifications.map((notification) => (
|
|
155
|
+
<ErrorAlert type={notification.type} title={notification.title} message={notification.message} />
|
|
156
|
+
)),
|
|
157
|
+
<title>{adminContext.title}</title>,
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
return ctx.html(
|
|
161
|
+
<>
|
|
162
|
+
<AdminLayout adminContext={adminContext}>
|
|
163
|
+
{adminContext.content}
|
|
164
|
+
</AdminLayout>
|
|
165
|
+
{fragments}
|
|
166
|
+
</>,
|
|
167
|
+
200,
|
|
168
|
+
{ "HX-Retarget": target, "HX-Reswap": swap }
|
|
169
|
+
);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## 模块系统
|
|
173
|
+
|
|
174
|
+
### 模块类型
|
|
175
|
+
|
|
176
|
+
插件支持四种模块类型,所有模块类型都继承自 `PageModule` 基类:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
PageModule (基类)
|
|
180
|
+
├── ListPageModule (列表页)
|
|
181
|
+
├── DetailPageModule (详情页)
|
|
182
|
+
├── FormPageModule (表单页)
|
|
183
|
+
└── PageModule (自定义页面,直接继承)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### 架构优势
|
|
187
|
+
|
|
188
|
+
- **统一基类**: 所有页面模块都继承自 `PageModule`,具有相同的处理过程和结构
|
|
189
|
+
- **默认行为**: 每种类型提供默认的渲染逻辑,满足大部分场景
|
|
190
|
+
- **灵活扩展**: 可以重写 `render()` 方法完全自定义渲染,即使使用 `list`、`detail`、`form` 类型
|
|
191
|
+
- **代码复用**: 通用属性(`basePath`、`__moduleName`、`__moduleMetadata`、`getBreadcrumbs`)在基类中统一管理
|
|
192
|
+
|
|
193
|
+
### 模块注册流程
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
1. 插件初始化 (onInit)
|
|
197
|
+
└─> 存储插件配置
|
|
198
|
+
└─> 获取 Hono 实例
|
|
199
|
+
|
|
200
|
+
2. 模块加载 (onModuleLoad)
|
|
201
|
+
└─> 检查模块类型约束(继承关系)
|
|
202
|
+
└─> 构建模块类型映射(moduleTypeMap)
|
|
203
|
+
└─> 为每个模块类型创建 RouteHandler 实例
|
|
204
|
+
└─> 注册路由(list, detail, form, custom)
|
|
205
|
+
└─> 注册首页重定向
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 模块元数据
|
|
209
|
+
|
|
210
|
+
每个模块都有元数据,记录该模块的完整信息:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
interface ModuleTypeInfo {
|
|
214
|
+
title: string; // 模块标题
|
|
215
|
+
description: string; // 模块描述
|
|
216
|
+
basePath: string; // 模块基础路径
|
|
217
|
+
hasList: boolean; // 是否有列表页
|
|
218
|
+
hasDetail: boolean; // 是否有详情页
|
|
219
|
+
hasForm: boolean; // 是否有表单页
|
|
220
|
+
hasCustom: boolean; // 是否有自定义页
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
模块可以通过 `this.context.moduleMetadata` 访问元数据:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
getActions(item: T) {
|
|
228
|
+
const actions = [];
|
|
229
|
+
|
|
230
|
+
if (this.context.moduleMetadata.hasDetail) {
|
|
231
|
+
actions.push({ label: "查看", href: this.paths.detail(item.id) });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (this.context.moduleMetadata.hasForm) {
|
|
235
|
+
actions.push({ label: "编辑", href: this.paths.edit(item.id) });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return actions;
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 路由系统
|
|
243
|
+
|
|
244
|
+
### 路由注册
|
|
245
|
+
|
|
246
|
+
插件根据模块类型自动注册路由:
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
List 模块:
|
|
250
|
+
GET /{basePath}/list → 列表页
|
|
251
|
+
DELETE /{basePath}/:id → 删除操作
|
|
252
|
+
|
|
253
|
+
Detail 模块:
|
|
254
|
+
GET /{basePath}/detail/:id → 详情页
|
|
255
|
+
DELETE /{basePath}/detail/:id → 删除操作
|
|
256
|
+
|
|
257
|
+
Form 模块:
|
|
258
|
+
GET /{basePath}/new → 新建表单
|
|
259
|
+
POST /{basePath} → 创建操作
|
|
260
|
+
GET /{basePath}/edit/:id → 编辑表单
|
|
261
|
+
PUT /{basePath}/:id → 更新操作
|
|
262
|
+
|
|
263
|
+
Custom 模块:
|
|
264
|
+
GET /{basePath} → 自定义页面
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 路由处理流程
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
1. RouteHandler.handle(ctx)
|
|
271
|
+
└─> 获取用户信息(getUserInfo)
|
|
272
|
+
└─> 创建 HtmxAdminContext
|
|
273
|
+
└─> 创建模块实例(new moduleClass())
|
|
274
|
+
└─> 初始化模块实例(moduleInstance.__init(context))
|
|
275
|
+
|
|
276
|
+
2. 处理请求
|
|
277
|
+
└─> 调用 moduleInstance.__handle()
|
|
278
|
+
└─> 默认调用 moduleInstance.render()
|
|
279
|
+
└─> FormPageModule 重写此方法处理不同 HTTP method
|
|
280
|
+
|
|
281
|
+
3. 设置页面元数据
|
|
282
|
+
└─> adminContext.title = moduleInstance.getTitle()
|
|
283
|
+
└─> adminContext.description = moduleInstance.getDescription()
|
|
284
|
+
└─> adminContext.breadcrumbs = moduleInstance.getBreadcrumbs()
|
|
285
|
+
|
|
286
|
+
4. 构建响应
|
|
287
|
+
└─> 如果是完整页面请求 → renderFullPage()
|
|
288
|
+
└─> 如果是片段请求 → renderFragment()
|
|
289
|
+
└─> 自动添加 HX-Retarget 响应头
|
|
290
|
+
└─> 设置 HX-Push-Url(如果 context.pushUrl 存在)
|
|
291
|
+
|
|
292
|
+
5. 返回响应
|
|
293
|
+
└─> ctx.html(responseContent, status, headers)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## 组件系统
|
|
297
|
+
|
|
298
|
+
### 组件分类
|
|
299
|
+
|
|
300
|
+
#### 内部组件(不导出)
|
|
301
|
+
|
|
302
|
+
- `Layout`: 主布局组件
|
|
303
|
+
- `NavItem`: 导航项组件
|
|
304
|
+
- `Header`: 页面头部组件
|
|
305
|
+
- `Breadcrumb`: 面包屑组件
|
|
306
|
+
- `LoadingBar`: 加载指示器
|
|
307
|
+
- `Table`: 表格组件
|
|
308
|
+
|
|
309
|
+
#### 外部组件(通过 Components 命名空间导出)
|
|
310
|
+
|
|
311
|
+
- **页面组件**: `Detail`, `Form`, `PageHeader`, `EmptyState`, `Dialog`, `ErrorAlert`
|
|
312
|
+
- **布局组件**: `Card`, `Button`, `ActionButton`, `Pagination`, `FilterCard`
|
|
313
|
+
- **数据展示**: `StatCard`, `ActivityCard`, `SystemStatusCard`, `Badge`
|
|
314
|
+
- **表单组件**: `FormField`, `Input`, `Textarea`, `Select`, `DateInput`
|
|
315
|
+
|
|
316
|
+
### 组件设计原则
|
|
317
|
+
|
|
318
|
+
1. **单一职责**: 每个组件只负责一个功能
|
|
319
|
+
2. **可组合**: 组件可以组合使用
|
|
320
|
+
3. **类型安全**: 所有组件都有完整的类型定义
|
|
321
|
+
4. **样式统一**: 使用 Tailwind CSS 统一样式
|
|
322
|
+
|
|
323
|
+
## 数据流
|
|
324
|
+
|
|
325
|
+
### 列表页数据流
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
用户请求 → 路由处理 → 解析参数 → 调用模块方法 → 数据源查询 → 渲染组件 → 返回响应
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 表单提交数据流
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
用户提交 → 路由处理 → 解析表单数据 → 验证数据 → 调用模块方法 → 数据源操作 → 返回响应
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### 错误处理数据流
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
异常发生 → 捕获异常 → createErrorResponse → 构建错误响应 → 设置 HX-Retarget → 返回错误响应
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## 响应处理流程
|
|
344
|
+
|
|
345
|
+
### 正常响应流程
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
1. RouteHandler.handle(ctx)
|
|
349
|
+
└─> 创建 HtmxAdminContext
|
|
350
|
+
└─> 创建并初始化模块实例
|
|
351
|
+
└─> 调用 moduleInstance.__handle()
|
|
352
|
+
└─> 默认调用 moduleInstance.render()
|
|
353
|
+
|
|
354
|
+
2. 设置页面元数据
|
|
355
|
+
└─> adminContext.title = moduleInstance.getTitle()
|
|
356
|
+
└─> adminContext.description = moduleInstance.getDescription()
|
|
357
|
+
└─> adminContext.breadcrumbs = moduleInstance.getBreadcrumbs()
|
|
358
|
+
|
|
359
|
+
3. 构建响应
|
|
360
|
+
└─> 如果是完整页面 → renderFullPage()
|
|
361
|
+
└─> 如果是片段请求 → renderFragment()
|
|
362
|
+
└─> 自动设置 HX-Retarget 响应头
|
|
363
|
+
|
|
364
|
+
4. 返回响应
|
|
365
|
+
└─> ctx.html(responseContent, status, headers)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Dialog 模式响应流程
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
1. 检测 Dialog 模式(在 HtmxAdminContext 构造函数中)
|
|
372
|
+
└─> URL 参数 dialog=true
|
|
373
|
+
└─> 或 HX-Target = #dialog-container
|
|
374
|
+
└─> adminContext.isDialog = true
|
|
375
|
+
└─> adminContext.target = "#dialog-container"
|
|
376
|
+
|
|
377
|
+
2. 处理请求(与正常流程相同)
|
|
378
|
+
└─> 调用 moduleInstance.__handle()
|
|
379
|
+
|
|
380
|
+
3. 构建响应
|
|
381
|
+
└─> renderFragment() 检测到 isDialog
|
|
382
|
+
└─> HX-Retarget: #dialog-container
|
|
383
|
+
└─> 不设置 HX-Push-Url(保持当前 URL)
|
|
384
|
+
|
|
385
|
+
4. 返回响应
|
|
386
|
+
└─> ctx.html(responseContent, 200, headers)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 错误响应流程
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
1. 捕获异常(在 RouteHandler.handle() 中)
|
|
393
|
+
└─> try-catch 捕获业务错误
|
|
394
|
+
└─> handleError(adminContext, error)
|
|
395
|
+
|
|
396
|
+
2. 处理错误
|
|
397
|
+
└─> adminContext.content = 错误内容
|
|
398
|
+
└─> adminContext.sendNotification("error", "错误", errorMessage)
|
|
399
|
+
|
|
400
|
+
3. 构建响应
|
|
401
|
+
└─> 正常构建响应(错误内容 + 通知)
|
|
402
|
+
└─> 通知通过 OOB 更新到错误容器
|
|
403
|
+
|
|
404
|
+
4. 返回响应
|
|
405
|
+
└─> ctx.html(responseContent, status, headers)
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## 关键实现细节
|
|
409
|
+
|
|
410
|
+
### 1. 响应目标检测
|
|
411
|
+
|
|
412
|
+
响应目标检测逻辑考虑了多种情况:
|
|
413
|
+
|
|
414
|
+
- URL 参数 `dialog=true`
|
|
415
|
+
- 请求头 `HX-Target`
|
|
416
|
+
- Referer 中的 `dialog=true`
|
|
417
|
+
|
|
418
|
+
这样可以确保 Dialog 模式在各种场景下都能正确工作。
|
|
419
|
+
|
|
420
|
+
### 2. OOB 交换策略
|
|
421
|
+
|
|
422
|
+
OOB 交换策略:
|
|
423
|
+
|
|
424
|
+
- **主要内容**: 作为非 OOB 内容返回,通过 `HX-Retarget` 指定目标
|
|
425
|
+
- **导航更新**: 通过 OOB 元素更新,只更新状态改变的导航项
|
|
426
|
+
- **错误清空**: 成功响应时通过 OOB 清空错误容器
|
|
427
|
+
|
|
428
|
+
这样可以实现多区域同时更新,而不需要多个请求。
|
|
429
|
+
|
|
430
|
+
### 3. URL 状态管理
|
|
431
|
+
|
|
432
|
+
URL 状态管理策略:
|
|
433
|
+
|
|
434
|
+
- 筛选条件记录在 URL 查询参数中
|
|
435
|
+
- 分页信息记录在 URL 查询参数中
|
|
436
|
+
- 排序信息记录在 URL 查询参数中
|
|
437
|
+
- 刷新页面后状态自动恢复
|
|
438
|
+
|
|
439
|
+
这样可以实现书签和分享功能。
|
|
440
|
+
|
|
441
|
+
### 4. 错误处理机制
|
|
442
|
+
|
|
443
|
+
错误处理机制:
|
|
444
|
+
|
|
445
|
+
- 统一的错误响应格式
|
|
446
|
+
- 全局错误通知区域
|
|
447
|
+
- 支持多个错误消息同时显示
|
|
448
|
+
- 每个错误消息可以单独关闭
|
|
449
|
+
|
|
450
|
+
这样可以提供良好的用户体验。
|
|
451
|
+
|
|
452
|
+
## 扩展点
|
|
453
|
+
|
|
454
|
+
### 1. 自定义数据源
|
|
455
|
+
|
|
456
|
+
实现对应的数据源接口即可:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
class CustomDatasource<T> implements ListDatasource<T> {
|
|
460
|
+
async getList(params: ListParams): Promise<ListResult<T>> {
|
|
461
|
+
// 自定义实现
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### 2. 自定义组件
|
|
467
|
+
|
|
468
|
+
可以在自定义页面中使用任何组件:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
async render() {
|
|
472
|
+
return (
|
|
473
|
+
<div>
|
|
474
|
+
{/* 使用任何组件 */}
|
|
475
|
+
</div>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### 3. 自定义请求处理
|
|
481
|
+
|
|
482
|
+
可以重写 `__handle()` 方法处理不同的请求逻辑:
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
async __handle() {
|
|
486
|
+
const method = this.context.ctx.req.method;
|
|
487
|
+
if (method === "POST") {
|
|
488
|
+
// 处理 POST 请求
|
|
489
|
+
}
|
|
490
|
+
return await this.render();
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### 4. 自定义响应处理
|
|
495
|
+
|
|
496
|
+
可以通过 `HtmxAdminContext` 控制响应行为:
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// 设置推送 URL
|
|
500
|
+
this.context.setPushUrl("/custom/path");
|
|
501
|
+
|
|
502
|
+
// 发送通知
|
|
503
|
+
this.context.sendSuccess("操作成功", "数据已保存");
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## 性能考虑
|
|
507
|
+
|
|
508
|
+
### 1. 分页
|
|
509
|
+
|
|
510
|
+
- 列表页默认使用分页
|
|
511
|
+
- 数据源应该实现分页逻辑
|
|
512
|
+
- 避免加载过多数据
|
|
513
|
+
|
|
514
|
+
### 2. 缓存
|
|
515
|
+
|
|
516
|
+
- 可以在数据源层面实现缓存
|
|
517
|
+
- 使用适当的缓存策略
|
|
518
|
+
- 注意缓存失效
|
|
519
|
+
|
|
520
|
+
### 3. 异步处理
|
|
521
|
+
|
|
522
|
+
- 所有数据操作都是异步的
|
|
523
|
+
- 使用 Promise 处理异步操作
|
|
524
|
+
- 避免阻塞主线程
|
|
525
|
+
|
|
526
|
+
## 安全性考虑
|
|
527
|
+
|
|
528
|
+
### 1. 输入验证
|
|
529
|
+
|
|
530
|
+
- 在数据源层面验证输入
|
|
531
|
+
- 使用参数化查询
|
|
532
|
+
- 防止 SQL 注入
|
|
533
|
+
|
|
534
|
+
### 2. 权限控制
|
|
535
|
+
|
|
536
|
+
- 在数据源层面实现权限检查
|
|
537
|
+
- 验证用户身份
|
|
538
|
+
- 控制数据访问
|
|
539
|
+
|
|
540
|
+
### 3. 错误处理
|
|
541
|
+
|
|
542
|
+
- 不暴露敏感信息
|
|
543
|
+
- 提供有意义的错误消息
|
|
544
|
+
- 记录错误日志
|
|
545
|
+
|
|
546
|
+
## 总结
|
|
547
|
+
|
|
548
|
+
HtmxAdminPlugin 采用后端驱动的统一响应架构,通过 `RouteHandler` 统一处理所有请求。所有模块都通过 `__handle()` 方法作为统一入口,简化了请求处理流程。
|
|
549
|
+
|
|
550
|
+
核心设计原则:
|
|
551
|
+
- **后端驱动**: 所有响应行为由后端控制,前端只负责触发请求
|
|
552
|
+
- **统一处理**: 通过 `RouteHandler` 统一处理所有页面类型的请求
|
|
553
|
+
- **上下文封装**: 通过 `HtmxAdminContext` 封装请求状态和操作
|
|
554
|
+
- **类型安全**: 完整的 TypeScript 类型定义
|
|
555
|
+
- **可扩展性**: 通过继承和重写实现扩展
|
|
556
|
+
|
|
557
|
+
核心架构特点:
|
|
558
|
+
- **模块初始化**: 通过 `__init(context)` 注入上下文,而不是选项对象
|
|
559
|
+
- **统一入口**: `__handle()` 方法作为统一入口,`render()` 专注于渲染
|
|
560
|
+
- **路径助手**: `PathHelper` 简化路径生成
|
|
561
|
+
- **通知机制**: 统一的通知管理机制
|
|
562
|
+
|
|
563
|
+
通过这些原则和架构,插件提供了一个灵活、易用、可扩展的管理后台解决方案。
|
|
564
|
+
|