imean-service-engine-htmx-plugin 2.6.0 → 2.7.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/dist/index.d.mts CHANGED
@@ -84,6 +84,39 @@ declare abstract class PageModel {
84
84
  * HtmxAdminPlugin 核心类型定义
85
85
  */
86
86
 
87
+ /**
88
+ * 操作按钮配置
89
+ */
90
+ interface ActionButton {
91
+ /** 按钮文本 */
92
+ label: string;
93
+ /** 链接地址(页面模式使用) */
94
+ href?: string;
95
+ /** HTTP 方法 */
96
+ method?: "get" | "post" | "put" | "delete";
97
+ /** 按钮样式 */
98
+ variant?: "primary" | "secondary" | "danger" | "ghost";
99
+ /** HTMX GET 请求(弹窗模式使用) */
100
+ hxGet?: string;
101
+ /** HTMX POST 请求 */
102
+ hxPost?: string;
103
+ /** HTMX PUT 请求 */
104
+ hxPut?: string;
105
+ /** HTMX DELETE 请求 */
106
+ hxDelete?: string;
107
+ /** 确认提示 */
108
+ confirm?: string;
109
+ /** 是否关闭弹窗(设置为 true 时,点击按钮会自动关闭弹窗,无需写代码) */
110
+ close?: boolean;
111
+ /** 是否提交表单(设置为 true 时,按钮会提交指定的表单,需要配合 formId 使用) */
112
+ submit?: boolean;
113
+ /** 表单 ID(当 submit 为 true 时,指定要提交的表单 ID) */
114
+ formId?: string;
115
+ /** 自定义类名 */
116
+ className?: string;
117
+ /** 链接目标(如 "_blank" 表示新窗口打开) */
118
+ target?: string;
119
+ }
87
120
  /**
88
121
  * 对话框大小类型
89
122
  */
@@ -390,20 +423,101 @@ interface FormField {
390
423
  /** HTML input step 属性(用于 number 类型,支持小数) */
391
424
  step?: string | number;
392
425
  }
426
+ /**
427
+ * 字段渲染器属性
428
+ * 统一接口,可在不同 Feature 中复用
429
+ */
430
+ interface FieldRendererProps<T = any, K extends keyof T = keyof T> {
431
+ /** 字段值 */
432
+ value: T[K];
433
+ /** 完整的初始数据项 */
434
+ item: Partial<T>;
435
+ /** 字段名 */
436
+ name: string;
437
+ /** 字段标签(没有会使用name的值作为标签) */
438
+ label: string;
439
+ }
393
440
  /**
394
441
  * 表单字段渲染器属性
395
442
  * 统一接口,可在不同 Feature 中复用
396
443
  */
397
- interface FormFieldRendererProps<T = any> {
444
+ interface FormFieldRendererProps<T = any, K extends keyof T = keyof T> extends FieldRendererProps<T, K> {
398
445
  /** 字段定义 */
399
446
  field: FormField;
400
- /** 当前值(已解析的对象/数组,不是 JSON 字符串) */
401
- value: any;
402
- /** 完整的初始数据项(用于编辑模式,包含所有字段的完整数据) */
403
- item?: Partial<T>;
404
- /** 字段名(用于更新隐藏字段) */
405
- name: string;
406
447
  }
448
+ /**
449
+ * 字段渲染器
450
+ * 用于渲染字段,可用于自定义渲染器
451
+ */
452
+ interface FieldRenderer<T extends FieldRendererProps = FieldRendererProps> {
453
+ (props: T): any;
454
+ }
455
+ /**
456
+ * 表单字段渲染器类型别名
457
+ * 用于统一表单字段渲染器的类型定义
458
+ * 支持字段名泛型,可以根据字段名推导出对应的值类型
459
+ */
460
+ type FormFieldRenderer<T = any, K extends keyof T = keyof T> = FieldRenderer<FormFieldRendererProps<T, K>>;
461
+ /**
462
+ * 表单字段渲染器映射类型
463
+ * 根据字段名自动推导对应的值类型,并严格约束 key 只能是 T 的字段名
464
+ *
465
+ * 使用方式:
466
+ * ```typescript
467
+ * formFieldRenderers: {
468
+ * banners: (props) => {
469
+ * // props.value 自动推导为 T["banners"] 类型
470
+ * return <BannerEditor {...props} value={props.value} />;
471
+ * }
472
+ * }
473
+ * ```
474
+ *
475
+ * TypeScript 会根据字段名自动推导对应的值类型,无需手动添加类型注解
476
+ * key 约束:只能使用 T 中存在的字段名,拼写错误会被 TypeScript 检查出来
477
+ * 例如:如果写成了 "tgas" 而不是 "tags",TypeScript 会报错
478
+ *
479
+ * 注意:为了支持类型推导,移除了索引签名,运行时访问字符串键时需要使用类型断言
480
+ */
481
+ /**
482
+ * 表单字段渲染器映射类型
483
+ * 根据字段名自动推导对应的值类型,并严格约束 key 只能是 T 的字段名
484
+ *
485
+ * 使用方式:
486
+ * ```typescript
487
+ * formFieldRenderers: {
488
+ * banners: (props) => {
489
+ * // props.value 自动推导为 T["banners"] 类型
490
+ * return <BannerEditor {...props} value={props.value} />;
491
+ * }
492
+ * }
493
+ * ```
494
+ *
495
+ * TypeScript 会根据字段名自动推导对应的值类型,无需手动添加类型注解
496
+ * key 约束:只能使用 T 中存在的字段名,拼写错误会被 TypeScript 检查出来
497
+ * 例如:如果写成了 "tgas" 而不是 "tags",TypeScript 会报错
498
+ *
499
+ * 注意:为了支持类型推导,移除了索引签名,运行时访问字符串键时需要使用类型断言
500
+ */
501
+ type FormFieldRendererMap<T = any> = {
502
+ [K in keyof T]?: FormFieldRenderer<T, K>;
503
+ };
504
+ /**
505
+ * 字段渲染器类型别名(用于详情页等非表单场景)
506
+ * 用于统一字段渲染器的类型定义
507
+ * 支持字段名泛型,可以根据字段名推导出对应的值类型
508
+ */
509
+ type FieldRendererType<T = any, K extends keyof T = keyof T> = FieldRenderer<FieldRendererProps<T, K>>;
510
+ /**
511
+ * 字段渲染器映射类型
512
+ * 根据字段名自动推导对应的值类型
513
+ * 使用 Record 类型保持兼容性,但在使用时可以通过类型断言获得字段级别的类型推导
514
+ *
515
+ * 注意:在使用时,TypeScript 会根据字段名自动推导对应的值类型
516
+ * 例如:fieldRenderers.banners 的类型会被推导为 FieldRendererType<T, "banners">
517
+ */
518
+ type FieldRendererTypeMap<T = any> = {
519
+ [K in keyof T]?: FieldRendererType<T, K>;
520
+ } & Record<string, FieldRendererType<T, any> | undefined> & Partial<Record<keyof T, FieldRendererType<T, keyof T>>>;
407
521
 
408
522
  /**
409
523
  * Feature 基类
@@ -468,6 +582,11 @@ declare abstract class BaseFeature implements Feature {
468
582
  render?(context: FeatureContext): Promise<any>;
469
583
  }
470
584
 
585
+ /**
586
+ * 表单 Feature 基类
587
+ * 提供创建和编辑 Feature 的公共逻辑
588
+ */
589
+
471
590
  /**
472
591
  * 表单操作类型
473
592
  */
@@ -484,7 +603,9 @@ declare abstract class BaseFormFeature<T extends {
484
603
  /** 当前请求的表单 ID(用于在 render 和 getActions 之间共享) */
485
604
  private currentFormId?;
486
605
  /** 自定义表单字段渲染器 */
487
- protected formFieldRenderers?: Record<string, (props: FormFieldRendererProps<T>) => any>;
606
+ protected formFieldRenderers?: {
607
+ [K in keyof T]?: FormFieldRenderer<T, K>;
608
+ };
488
609
  /**
489
610
  * 获取或生成表单 ID(确保在同一个请求中保持一致)
490
611
  */
@@ -591,6 +712,10 @@ declare class CustomFeature extends BaseFeature {
591
712
  render(context: FeatureContext): Promise<any>;
592
713
  }
593
714
 
715
+ /**
716
+ * 默认创建 Feature
717
+ */
718
+
594
719
  /**
595
720
  * 默认创建 Feature
596
721
  * 类型通过 schema 的 z.infer 推断
@@ -616,7 +741,7 @@ declare class DefaultCreateFeature<T extends {
616
741
  /** 字段分组配置(可选,用于将字段分组展示为 Tab) */
617
742
  groups?: FieldGroup[];
618
743
  /** 自定义表单字段渲染器 */
619
- formFieldRenderers?: Record<string, (props: FormFieldRendererProps<T>) => any>;
744
+ formFieldRenderers?: FormFieldRendererMap<T>;
620
745
  });
621
746
  getFormAction(): "create";
622
747
  getRoutes(): ({
@@ -689,7 +814,7 @@ declare class DefaultDetailFeature<T extends {
689
814
  /** 详情显示字段名(可选,不提供则显示 schema 中的所有字段) */
690
815
  detailFieldNames?: string[];
691
816
  /** 自定义字段渲染函数(可选,key 为字段名,value 为渲染函数) */
692
- fieldRenderers?: Record<string, (value: any, item: T) => any>;
817
+ fieldRenderers?: FieldRendererTypeMap<T>;
693
818
  /** 字段分组配置(可选,用于将字段分组展示为卡片) */
694
819
  groups?: FieldGroup[];
695
820
  });
@@ -703,6 +828,10 @@ declare class DefaultDetailFeature<T extends {
703
828
  getActions(context: FeatureContext): Promise<any>;
704
829
  }
705
830
 
831
+ /**
832
+ * 默认编辑 Feature
833
+ */
834
+
706
835
  /**
707
836
  * 默认编辑 Feature
708
837
  * 类型通过 schema 的 z.infer 推断
@@ -729,8 +858,11 @@ declare class DefaultEditFeature<T extends {
729
858
  formFieldNames?: string[];
730
859
  /** 字段分组配置(可选,用于将字段分组展示为 Tab) */
731
860
  groups?: FieldGroup[];
732
- /** 自定义表单字段渲染器 */
733
- formFieldRenderers?: Record<string, (props: FormFieldRendererProps<T>) => any>;
861
+ /** 自定义表单字段渲染器
862
+ * 使用 satisfies FormFieldRendererMap<T> 来检查 key 约束
863
+ * 例如:formFieldRenderers: { ... } satisfies FormFieldRendererMap<T>
864
+ */
865
+ formFieldRenderers?: FormFieldRendererMap<T>;
734
866
  });
735
867
  getFormAction(): "edit";
736
868
  getRoutes(): ({
@@ -935,17 +1067,213 @@ interface ErrorAlertProps {
935
1067
  */
936
1068
  declare function ErrorAlert(props: ErrorAlertProps): hono_jsx_jsx_dev_runtime.JSX.Element;
937
1069
 
1070
+ interface FilterFormProps {
1071
+ /** 筛选字段 */
1072
+ fields: FormField[];
1073
+ /** 列表路径(用于提交筛选表单) */
1074
+ listPath: string;
1075
+ /** 当前筛选参数 */
1076
+ currentFilters?: Record<string, any>;
1077
+ }
1078
+ /**
1079
+ * 筛选表单组件
1080
+ */
1081
+ declare function FilterForm(props: FilterFormProps): hono_jsx_jsx_dev_runtime.JSX.Element | null;
1082
+
1083
+ /**
1084
+ * 面包屑组件
1085
+ */
1086
+ interface BreadcrumbItem {
1087
+ label: string;
1088
+ href?: string;
1089
+ }
1090
+ interface BreadcrumbProps {
1091
+ items: BreadcrumbItem[];
1092
+ }
1093
+ /**
1094
+ * 面包屑组件
1095
+ */
1096
+ declare function Breadcrumb(props: BreadcrumbProps): hono_jsx_jsx_dev_runtime.JSX.Element | null;
1097
+
1098
+ interface HeaderProps {
1099
+ /** 面包屑项 */
1100
+ breadcrumbs?: BreadcrumbItem[];
1101
+ /** 用户信息 */
1102
+ userInfo?: UserInfo | null;
1103
+ /** 退出登录的URL */
1104
+ logoutUrl?: string;
1105
+ /** 页面标题(当没有面包屑时显示) */
1106
+ title?: string;
1107
+ }
1108
+ /**
1109
+ * 头部组件
1110
+ */
1111
+ declare function Header(props: HeaderProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1112
+
938
1113
  /**
939
1114
  * 加载条组件
940
1115
  * 用于显示 HTMX 请求的加载状态
941
1116
  */
942
1117
  declare function LoadingBar(): hono_jsx_jsx_dev_runtime.JSX.Element;
943
1118
 
944
- interface ObjectEditorProps extends FormFieldRendererProps {
945
- /** 对象的 Zod schema(用于自动渲染结构) */
946
- objectSchema?: z.ZodObject<any>;
1119
+ /**
1120
+ * 表格组件
1121
+ */
1122
+ /**
1123
+ * 表格组件 Props
1124
+ */
1125
+ interface TableProps<T = any> {
1126
+ /** 数据列表 */
1127
+ items: T[];
1128
+ /** 列定义 */
1129
+ columns: Array<{
1130
+ key: string;
1131
+ label: string;
1132
+ render?: (value: any, item: T) => any;
1133
+ }>;
1134
+ /** 操作列 */
1135
+ actions?: Array<{
1136
+ label: string;
1137
+ href: (item: T) => string;
1138
+ method?: string;
1139
+ class?: string;
1140
+ target?: string;
1141
+ }>;
1142
+ /** 分页信息 */
1143
+ pagination?: {
1144
+ page: number;
1145
+ pageSize: number;
1146
+ total: number;
1147
+ totalPages: number;
1148
+ baseUrl: string;
1149
+ currentParams?: Record<string, string | number | undefined>;
1150
+ };
1151
+ /** 表格标题 */
1152
+ title?: string;
1153
+ /** 表格操作按钮(如导出、清空、刷新等) */
1154
+ tableActions?: Array<{
1155
+ label: string;
1156
+ href?: string;
1157
+ hxGet?: string;
1158
+ hxPost?: string;
1159
+ hxDelete?: string;
1160
+ variant?: "primary" | "secondary" | "danger" | "ghost";
1161
+ hxConfirm?: string;
1162
+ }>;
1163
+ /** 操作列样式(link 或 button) */
1164
+ actionStyle?: "link" | "button";
947
1165
  }
948
- declare function ObjectEditor(props: ObjectEditorProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1166
+ /**
1167
+ * 表格组件
1168
+ */
1169
+ declare function Table<T extends {
1170
+ id: string | number;
1171
+ }>(props: TableProps<T>): hono_jsx_jsx_dev_runtime.JSX.Element;
1172
+
1173
+ /**
1174
+ * 分页组件
1175
+ */
1176
+ interface PaginationProps {
1177
+ /** 当前页码 */
1178
+ page: number;
1179
+ /** 每页数量 */
1180
+ pageSize: number;
1181
+ /** 总记录数 */
1182
+ total: number;
1183
+ /** 总页数 */
1184
+ totalPages: number;
1185
+ /** 基础 URL */
1186
+ baseUrl: string;
1187
+ /** 当前查询参数(用于保留筛选条件等) */
1188
+ currentParams?: Record<string, string | number | undefined>;
1189
+ }
1190
+ /**
1191
+ * 分页组件
1192
+ */
1193
+ declare function Pagination(props: PaginationProps): hono_jsx_jsx_dev_runtime.JSX.Element | null;
1194
+
1195
+ /**
1196
+ * 卡片组件
1197
+ */
1198
+ interface CardProps {
1199
+ /** 卡片内容 */
1200
+ children: any;
1201
+ /** 卡片标题 */
1202
+ title?: string;
1203
+ /** 自定义类名 */
1204
+ className?: string;
1205
+ /** 是否显示阴影 */
1206
+ shadow?: boolean;
1207
+ /** 是否显示边框 */
1208
+ bordered?: boolean;
1209
+ /** 是否去掉内边距 */
1210
+ noPadding?: boolean;
1211
+ }
1212
+ /**
1213
+ * 卡片组件
1214
+ */
1215
+ declare function Card(props: CardProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1216
+
1217
+ /**
1218
+ * 空状态组件
1219
+ */
1220
+ interface EmptyStateProps {
1221
+ /** 提示文本 */
1222
+ message?: string;
1223
+ /** 自定义内容 */
1224
+ children?: any;
1225
+ }
1226
+ /**
1227
+ * 空状态组件
1228
+ */
1229
+ declare function EmptyState(props: EmptyStateProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1230
+
1231
+ /**
1232
+ * 按钮组件
1233
+ * 只负责样式,其他属性(如 HTMX 属性)通过解构动态传递
1234
+ */
1235
+ interface ButtonProps {
1236
+ /** 按钮文本 */
1237
+ children: any;
1238
+ /** 按钮类型 */
1239
+ variant?: "primary" | "secondary" | "danger" | "ghost";
1240
+ /** 按钮大小 */
1241
+ size?: "sm" | "md" | "lg";
1242
+ /** 是否禁用 */
1243
+ disabled?: boolean;
1244
+ /** 自定义类名 */
1245
+ className?: string;
1246
+ /** 其他属性(包括 HTMX 属性、href 等) */
1247
+ [key: string]: any;
1248
+ }
1249
+ /**
1250
+ * 按钮组件
1251
+ * 只负责样式,其他属性通过解构动态传递
1252
+ */
1253
+ declare function Button(props: ButtonProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1254
+
1255
+ interface PermissionDeniedContentProps {
1256
+ /** 操作ID(权限标识) */
1257
+ operationId?: string;
1258
+ /** 请求路径 */
1259
+ fromPath?: string;
1260
+ /** 错误消息 */
1261
+ message?: string;
1262
+ /** 用户信息 */
1263
+ userInfo?: UserInfo | null;
1264
+ /** 插件选项 */
1265
+ pluginOptions?: HtmxAdminPluginOptions;
1266
+ /** 是否在弹框中显示(用于 HTMX 请求) */
1267
+ isDialog?: boolean;
1268
+ }
1269
+ /**
1270
+ * 权限提示内容组件(用于弹框或页面)
1271
+ */
1272
+ declare function PermissionDeniedContent(props: PermissionDeniedContentProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1273
+ /**
1274
+ * 权限提示页面组件(完整页面,用于整页加载)
1275
+ */
1276
+ declare function PermissionDeniedPage(props: PermissionDeniedContentProps): hono_jsx_jsx_dev_runtime.JSX.Element;
949
1277
 
950
1278
  interface StringArrayEditorProps extends FormFieldRendererProps {
951
1279
  /** 占位符文本(可选) */
@@ -973,6 +1301,12 @@ interface TagsEditorProps extends FormFieldRendererProps {
973
1301
  */
974
1302
  declare function TagsEditor(props: TagsEditorProps): hono_jsx_jsx_dev_runtime.JSX.Element;
975
1303
 
1304
+ interface ObjectEditorProps extends FormFieldRendererProps {
1305
+ /** 对象的 Zod schema(用于自动渲染结构) */
1306
+ objectSchema?: z.ZodObject<any>;
1307
+ }
1308
+ declare function ObjectEditor(props: ObjectEditorProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1309
+
976
1310
  /**
977
1311
  * 可拖拽排序列表组件
978
1312
  * 使用 HTML5 原生拖放 API 实现拖拽排序
@@ -1010,4 +1344,4 @@ interface SortableListProps {
1010
1344
  */
1011
1345
  declare function SortableList(props: SortableListProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1012
1346
 
1013
- export { type AuthProvider, BaseFeature, CustomFeature, DefaultCreateFeature, DefaultDeleteFeature, DefaultDetailFeature, DefaultEditFeature, DefaultListFeature, Dialog, type DialogProps, type DialogSize, ErrorAlert, type ErrorAlertProps, type Feature, type FeatureContext, type FeatureType, type FieldMetadata, HtmxAdminPlugin, type HtmxAdminPluginOptions, type ListParams, type ListResult, LoadingBar, type NavItemConfig, type Notification, type NotificationType, ObjectEditor, type ObjectEditorProps, type PageMetadata, PageModel, type PermissionResult, SortableList, StringArrayEditor, type StringArrayEditorProps, TagsEditor, type TagsEditorProps, type UserInfo, checkUserPermission, getUserInfo, modelNameToPath, parseListParams };
1347
+ export { type ActionButton, type AuthProvider, BaseFeature, BaseFormFeature, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, Card, type CardProps, CustomFeature, DefaultCreateFeature, DefaultDeleteFeature, DefaultDetailFeature, DefaultEditFeature, DefaultListFeature, Dialog, type DialogProps, type DialogSize, EmptyState, type EmptyStateProps, ErrorAlert, type ErrorAlertProps, type Feature, type FeatureContext, type FeatureType, type FieldGroup, type FieldMetadata, type FieldRenderer, type FieldRendererProps, type FieldRendererType, type FieldRendererTypeMap, FilterForm, type FormAction, type FormField, type FormFieldRenderer, type FormFieldRendererMap, type FormFieldRendererProps, Header, type HeaderProps, HtmxAdminPlugin, type HtmxAdminPluginOptions, type ListParams, type ListResult, LoadingBar, type ModelField, type NavItemConfig, type Notification, type NotificationType$1 as NotificationType, ObjectEditor, type ObjectEditorProps, type OpenMode, type PageMetadata, PageModel, Pagination, type PaginationProps, PermissionDeniedContent, type PermissionDeniedContentProps, PermissionDeniedPage, type PermissionResult, type ResolvedHtmxAdminPluginOptions, SortableList, type SortableListProps, StringArrayEditor, type StringArrayEditorProps, Table, type TableProps, TagsEditor, type TagsEditorProps, type UserInfo, checkUserPermission, getUserInfo, modelNameToPath, parseListParams };
package/dist/index.d.ts CHANGED
@@ -84,6 +84,39 @@ declare abstract class PageModel {
84
84
  * HtmxAdminPlugin 核心类型定义
85
85
  */
86
86
 
87
+ /**
88
+ * 操作按钮配置
89
+ */
90
+ interface ActionButton {
91
+ /** 按钮文本 */
92
+ label: string;
93
+ /** 链接地址(页面模式使用) */
94
+ href?: string;
95
+ /** HTTP 方法 */
96
+ method?: "get" | "post" | "put" | "delete";
97
+ /** 按钮样式 */
98
+ variant?: "primary" | "secondary" | "danger" | "ghost";
99
+ /** HTMX GET 请求(弹窗模式使用) */
100
+ hxGet?: string;
101
+ /** HTMX POST 请求 */
102
+ hxPost?: string;
103
+ /** HTMX PUT 请求 */
104
+ hxPut?: string;
105
+ /** HTMX DELETE 请求 */
106
+ hxDelete?: string;
107
+ /** 确认提示 */
108
+ confirm?: string;
109
+ /** 是否关闭弹窗(设置为 true 时,点击按钮会自动关闭弹窗,无需写代码) */
110
+ close?: boolean;
111
+ /** 是否提交表单(设置为 true 时,按钮会提交指定的表单,需要配合 formId 使用) */
112
+ submit?: boolean;
113
+ /** 表单 ID(当 submit 为 true 时,指定要提交的表单 ID) */
114
+ formId?: string;
115
+ /** 自定义类名 */
116
+ className?: string;
117
+ /** 链接目标(如 "_blank" 表示新窗口打开) */
118
+ target?: string;
119
+ }
87
120
  /**
88
121
  * 对话框大小类型
89
122
  */
@@ -390,20 +423,101 @@ interface FormField {
390
423
  /** HTML input step 属性(用于 number 类型,支持小数) */
391
424
  step?: string | number;
392
425
  }
426
+ /**
427
+ * 字段渲染器属性
428
+ * 统一接口,可在不同 Feature 中复用
429
+ */
430
+ interface FieldRendererProps<T = any, K extends keyof T = keyof T> {
431
+ /** 字段值 */
432
+ value: T[K];
433
+ /** 完整的初始数据项 */
434
+ item: Partial<T>;
435
+ /** 字段名 */
436
+ name: string;
437
+ /** 字段标签(没有会使用name的值作为标签) */
438
+ label: string;
439
+ }
393
440
  /**
394
441
  * 表单字段渲染器属性
395
442
  * 统一接口,可在不同 Feature 中复用
396
443
  */
397
- interface FormFieldRendererProps<T = any> {
444
+ interface FormFieldRendererProps<T = any, K extends keyof T = keyof T> extends FieldRendererProps<T, K> {
398
445
  /** 字段定义 */
399
446
  field: FormField;
400
- /** 当前值(已解析的对象/数组,不是 JSON 字符串) */
401
- value: any;
402
- /** 完整的初始数据项(用于编辑模式,包含所有字段的完整数据) */
403
- item?: Partial<T>;
404
- /** 字段名(用于更新隐藏字段) */
405
- name: string;
406
447
  }
448
+ /**
449
+ * 字段渲染器
450
+ * 用于渲染字段,可用于自定义渲染器
451
+ */
452
+ interface FieldRenderer<T extends FieldRendererProps = FieldRendererProps> {
453
+ (props: T): any;
454
+ }
455
+ /**
456
+ * 表单字段渲染器类型别名
457
+ * 用于统一表单字段渲染器的类型定义
458
+ * 支持字段名泛型,可以根据字段名推导出对应的值类型
459
+ */
460
+ type FormFieldRenderer<T = any, K extends keyof T = keyof T> = FieldRenderer<FormFieldRendererProps<T, K>>;
461
+ /**
462
+ * 表单字段渲染器映射类型
463
+ * 根据字段名自动推导对应的值类型,并严格约束 key 只能是 T 的字段名
464
+ *
465
+ * 使用方式:
466
+ * ```typescript
467
+ * formFieldRenderers: {
468
+ * banners: (props) => {
469
+ * // props.value 自动推导为 T["banners"] 类型
470
+ * return <BannerEditor {...props} value={props.value} />;
471
+ * }
472
+ * }
473
+ * ```
474
+ *
475
+ * TypeScript 会根据字段名自动推导对应的值类型,无需手动添加类型注解
476
+ * key 约束:只能使用 T 中存在的字段名,拼写错误会被 TypeScript 检查出来
477
+ * 例如:如果写成了 "tgas" 而不是 "tags",TypeScript 会报错
478
+ *
479
+ * 注意:为了支持类型推导,移除了索引签名,运行时访问字符串键时需要使用类型断言
480
+ */
481
+ /**
482
+ * 表单字段渲染器映射类型
483
+ * 根据字段名自动推导对应的值类型,并严格约束 key 只能是 T 的字段名
484
+ *
485
+ * 使用方式:
486
+ * ```typescript
487
+ * formFieldRenderers: {
488
+ * banners: (props) => {
489
+ * // props.value 自动推导为 T["banners"] 类型
490
+ * return <BannerEditor {...props} value={props.value} />;
491
+ * }
492
+ * }
493
+ * ```
494
+ *
495
+ * TypeScript 会根据字段名自动推导对应的值类型,无需手动添加类型注解
496
+ * key 约束:只能使用 T 中存在的字段名,拼写错误会被 TypeScript 检查出来
497
+ * 例如:如果写成了 "tgas" 而不是 "tags",TypeScript 会报错
498
+ *
499
+ * 注意:为了支持类型推导,移除了索引签名,运行时访问字符串键时需要使用类型断言
500
+ */
501
+ type FormFieldRendererMap<T = any> = {
502
+ [K in keyof T]?: FormFieldRenderer<T, K>;
503
+ };
504
+ /**
505
+ * 字段渲染器类型别名(用于详情页等非表单场景)
506
+ * 用于统一字段渲染器的类型定义
507
+ * 支持字段名泛型,可以根据字段名推导出对应的值类型
508
+ */
509
+ type FieldRendererType<T = any, K extends keyof T = keyof T> = FieldRenderer<FieldRendererProps<T, K>>;
510
+ /**
511
+ * 字段渲染器映射类型
512
+ * 根据字段名自动推导对应的值类型
513
+ * 使用 Record 类型保持兼容性,但在使用时可以通过类型断言获得字段级别的类型推导
514
+ *
515
+ * 注意:在使用时,TypeScript 会根据字段名自动推导对应的值类型
516
+ * 例如:fieldRenderers.banners 的类型会被推导为 FieldRendererType<T, "banners">
517
+ */
518
+ type FieldRendererTypeMap<T = any> = {
519
+ [K in keyof T]?: FieldRendererType<T, K>;
520
+ } & Record<string, FieldRendererType<T, any> | undefined> & Partial<Record<keyof T, FieldRendererType<T, keyof T>>>;
407
521
 
408
522
  /**
409
523
  * Feature 基类
@@ -468,6 +582,11 @@ declare abstract class BaseFeature implements Feature {
468
582
  render?(context: FeatureContext): Promise<any>;
469
583
  }
470
584
 
585
+ /**
586
+ * 表单 Feature 基类
587
+ * 提供创建和编辑 Feature 的公共逻辑
588
+ */
589
+
471
590
  /**
472
591
  * 表单操作类型
473
592
  */
@@ -484,7 +603,9 @@ declare abstract class BaseFormFeature<T extends {
484
603
  /** 当前请求的表单 ID(用于在 render 和 getActions 之间共享) */
485
604
  private currentFormId?;
486
605
  /** 自定义表单字段渲染器 */
487
- protected formFieldRenderers?: Record<string, (props: FormFieldRendererProps<T>) => any>;
606
+ protected formFieldRenderers?: {
607
+ [K in keyof T]?: FormFieldRenderer<T, K>;
608
+ };
488
609
  /**
489
610
  * 获取或生成表单 ID(确保在同一个请求中保持一致)
490
611
  */
@@ -591,6 +712,10 @@ declare class CustomFeature extends BaseFeature {
591
712
  render(context: FeatureContext): Promise<any>;
592
713
  }
593
714
 
715
+ /**
716
+ * 默认创建 Feature
717
+ */
718
+
594
719
  /**
595
720
  * 默认创建 Feature
596
721
  * 类型通过 schema 的 z.infer 推断
@@ -616,7 +741,7 @@ declare class DefaultCreateFeature<T extends {
616
741
  /** 字段分组配置(可选,用于将字段分组展示为 Tab) */
617
742
  groups?: FieldGroup[];
618
743
  /** 自定义表单字段渲染器 */
619
- formFieldRenderers?: Record<string, (props: FormFieldRendererProps<T>) => any>;
744
+ formFieldRenderers?: FormFieldRendererMap<T>;
620
745
  });
621
746
  getFormAction(): "create";
622
747
  getRoutes(): ({
@@ -689,7 +814,7 @@ declare class DefaultDetailFeature<T extends {
689
814
  /** 详情显示字段名(可选,不提供则显示 schema 中的所有字段) */
690
815
  detailFieldNames?: string[];
691
816
  /** 自定义字段渲染函数(可选,key 为字段名,value 为渲染函数) */
692
- fieldRenderers?: Record<string, (value: any, item: T) => any>;
817
+ fieldRenderers?: FieldRendererTypeMap<T>;
693
818
  /** 字段分组配置(可选,用于将字段分组展示为卡片) */
694
819
  groups?: FieldGroup[];
695
820
  });
@@ -703,6 +828,10 @@ declare class DefaultDetailFeature<T extends {
703
828
  getActions(context: FeatureContext): Promise<any>;
704
829
  }
705
830
 
831
+ /**
832
+ * 默认编辑 Feature
833
+ */
834
+
706
835
  /**
707
836
  * 默认编辑 Feature
708
837
  * 类型通过 schema 的 z.infer 推断
@@ -729,8 +858,11 @@ declare class DefaultEditFeature<T extends {
729
858
  formFieldNames?: string[];
730
859
  /** 字段分组配置(可选,用于将字段分组展示为 Tab) */
731
860
  groups?: FieldGroup[];
732
- /** 自定义表单字段渲染器 */
733
- formFieldRenderers?: Record<string, (props: FormFieldRendererProps<T>) => any>;
861
+ /** 自定义表单字段渲染器
862
+ * 使用 satisfies FormFieldRendererMap<T> 来检查 key 约束
863
+ * 例如:formFieldRenderers: { ... } satisfies FormFieldRendererMap<T>
864
+ */
865
+ formFieldRenderers?: FormFieldRendererMap<T>;
734
866
  });
735
867
  getFormAction(): "edit";
736
868
  getRoutes(): ({
@@ -935,17 +1067,213 @@ interface ErrorAlertProps {
935
1067
  */
936
1068
  declare function ErrorAlert(props: ErrorAlertProps): hono_jsx_jsx_dev_runtime.JSX.Element;
937
1069
 
1070
+ interface FilterFormProps {
1071
+ /** 筛选字段 */
1072
+ fields: FormField[];
1073
+ /** 列表路径(用于提交筛选表单) */
1074
+ listPath: string;
1075
+ /** 当前筛选参数 */
1076
+ currentFilters?: Record<string, any>;
1077
+ }
1078
+ /**
1079
+ * 筛选表单组件
1080
+ */
1081
+ declare function FilterForm(props: FilterFormProps): hono_jsx_jsx_dev_runtime.JSX.Element | null;
1082
+
1083
+ /**
1084
+ * 面包屑组件
1085
+ */
1086
+ interface BreadcrumbItem {
1087
+ label: string;
1088
+ href?: string;
1089
+ }
1090
+ interface BreadcrumbProps {
1091
+ items: BreadcrumbItem[];
1092
+ }
1093
+ /**
1094
+ * 面包屑组件
1095
+ */
1096
+ declare function Breadcrumb(props: BreadcrumbProps): hono_jsx_jsx_dev_runtime.JSX.Element | null;
1097
+
1098
+ interface HeaderProps {
1099
+ /** 面包屑项 */
1100
+ breadcrumbs?: BreadcrumbItem[];
1101
+ /** 用户信息 */
1102
+ userInfo?: UserInfo | null;
1103
+ /** 退出登录的URL */
1104
+ logoutUrl?: string;
1105
+ /** 页面标题(当没有面包屑时显示) */
1106
+ title?: string;
1107
+ }
1108
+ /**
1109
+ * 头部组件
1110
+ */
1111
+ declare function Header(props: HeaderProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1112
+
938
1113
  /**
939
1114
  * 加载条组件
940
1115
  * 用于显示 HTMX 请求的加载状态
941
1116
  */
942
1117
  declare function LoadingBar(): hono_jsx_jsx_dev_runtime.JSX.Element;
943
1118
 
944
- interface ObjectEditorProps extends FormFieldRendererProps {
945
- /** 对象的 Zod schema(用于自动渲染结构) */
946
- objectSchema?: z.ZodObject<any>;
1119
+ /**
1120
+ * 表格组件
1121
+ */
1122
+ /**
1123
+ * 表格组件 Props
1124
+ */
1125
+ interface TableProps<T = any> {
1126
+ /** 数据列表 */
1127
+ items: T[];
1128
+ /** 列定义 */
1129
+ columns: Array<{
1130
+ key: string;
1131
+ label: string;
1132
+ render?: (value: any, item: T) => any;
1133
+ }>;
1134
+ /** 操作列 */
1135
+ actions?: Array<{
1136
+ label: string;
1137
+ href: (item: T) => string;
1138
+ method?: string;
1139
+ class?: string;
1140
+ target?: string;
1141
+ }>;
1142
+ /** 分页信息 */
1143
+ pagination?: {
1144
+ page: number;
1145
+ pageSize: number;
1146
+ total: number;
1147
+ totalPages: number;
1148
+ baseUrl: string;
1149
+ currentParams?: Record<string, string | number | undefined>;
1150
+ };
1151
+ /** 表格标题 */
1152
+ title?: string;
1153
+ /** 表格操作按钮(如导出、清空、刷新等) */
1154
+ tableActions?: Array<{
1155
+ label: string;
1156
+ href?: string;
1157
+ hxGet?: string;
1158
+ hxPost?: string;
1159
+ hxDelete?: string;
1160
+ variant?: "primary" | "secondary" | "danger" | "ghost";
1161
+ hxConfirm?: string;
1162
+ }>;
1163
+ /** 操作列样式(link 或 button) */
1164
+ actionStyle?: "link" | "button";
947
1165
  }
948
- declare function ObjectEditor(props: ObjectEditorProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1166
+ /**
1167
+ * 表格组件
1168
+ */
1169
+ declare function Table<T extends {
1170
+ id: string | number;
1171
+ }>(props: TableProps<T>): hono_jsx_jsx_dev_runtime.JSX.Element;
1172
+
1173
+ /**
1174
+ * 分页组件
1175
+ */
1176
+ interface PaginationProps {
1177
+ /** 当前页码 */
1178
+ page: number;
1179
+ /** 每页数量 */
1180
+ pageSize: number;
1181
+ /** 总记录数 */
1182
+ total: number;
1183
+ /** 总页数 */
1184
+ totalPages: number;
1185
+ /** 基础 URL */
1186
+ baseUrl: string;
1187
+ /** 当前查询参数(用于保留筛选条件等) */
1188
+ currentParams?: Record<string, string | number | undefined>;
1189
+ }
1190
+ /**
1191
+ * 分页组件
1192
+ */
1193
+ declare function Pagination(props: PaginationProps): hono_jsx_jsx_dev_runtime.JSX.Element | null;
1194
+
1195
+ /**
1196
+ * 卡片组件
1197
+ */
1198
+ interface CardProps {
1199
+ /** 卡片内容 */
1200
+ children: any;
1201
+ /** 卡片标题 */
1202
+ title?: string;
1203
+ /** 自定义类名 */
1204
+ className?: string;
1205
+ /** 是否显示阴影 */
1206
+ shadow?: boolean;
1207
+ /** 是否显示边框 */
1208
+ bordered?: boolean;
1209
+ /** 是否去掉内边距 */
1210
+ noPadding?: boolean;
1211
+ }
1212
+ /**
1213
+ * 卡片组件
1214
+ */
1215
+ declare function Card(props: CardProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1216
+
1217
+ /**
1218
+ * 空状态组件
1219
+ */
1220
+ interface EmptyStateProps {
1221
+ /** 提示文本 */
1222
+ message?: string;
1223
+ /** 自定义内容 */
1224
+ children?: any;
1225
+ }
1226
+ /**
1227
+ * 空状态组件
1228
+ */
1229
+ declare function EmptyState(props: EmptyStateProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1230
+
1231
+ /**
1232
+ * 按钮组件
1233
+ * 只负责样式,其他属性(如 HTMX 属性)通过解构动态传递
1234
+ */
1235
+ interface ButtonProps {
1236
+ /** 按钮文本 */
1237
+ children: any;
1238
+ /** 按钮类型 */
1239
+ variant?: "primary" | "secondary" | "danger" | "ghost";
1240
+ /** 按钮大小 */
1241
+ size?: "sm" | "md" | "lg";
1242
+ /** 是否禁用 */
1243
+ disabled?: boolean;
1244
+ /** 自定义类名 */
1245
+ className?: string;
1246
+ /** 其他属性(包括 HTMX 属性、href 等) */
1247
+ [key: string]: any;
1248
+ }
1249
+ /**
1250
+ * 按钮组件
1251
+ * 只负责样式,其他属性通过解构动态传递
1252
+ */
1253
+ declare function Button(props: ButtonProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1254
+
1255
+ interface PermissionDeniedContentProps {
1256
+ /** 操作ID(权限标识) */
1257
+ operationId?: string;
1258
+ /** 请求路径 */
1259
+ fromPath?: string;
1260
+ /** 错误消息 */
1261
+ message?: string;
1262
+ /** 用户信息 */
1263
+ userInfo?: UserInfo | null;
1264
+ /** 插件选项 */
1265
+ pluginOptions?: HtmxAdminPluginOptions;
1266
+ /** 是否在弹框中显示(用于 HTMX 请求) */
1267
+ isDialog?: boolean;
1268
+ }
1269
+ /**
1270
+ * 权限提示内容组件(用于弹框或页面)
1271
+ */
1272
+ declare function PermissionDeniedContent(props: PermissionDeniedContentProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1273
+ /**
1274
+ * 权限提示页面组件(完整页面,用于整页加载)
1275
+ */
1276
+ declare function PermissionDeniedPage(props: PermissionDeniedContentProps): hono_jsx_jsx_dev_runtime.JSX.Element;
949
1277
 
950
1278
  interface StringArrayEditorProps extends FormFieldRendererProps {
951
1279
  /** 占位符文本(可选) */
@@ -973,6 +1301,12 @@ interface TagsEditorProps extends FormFieldRendererProps {
973
1301
  */
974
1302
  declare function TagsEditor(props: TagsEditorProps): hono_jsx_jsx_dev_runtime.JSX.Element;
975
1303
 
1304
+ interface ObjectEditorProps extends FormFieldRendererProps {
1305
+ /** 对象的 Zod schema(用于自动渲染结构) */
1306
+ objectSchema?: z.ZodObject<any>;
1307
+ }
1308
+ declare function ObjectEditor(props: ObjectEditorProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1309
+
976
1310
  /**
977
1311
  * 可拖拽排序列表组件
978
1312
  * 使用 HTML5 原生拖放 API 实现拖拽排序
@@ -1010,4 +1344,4 @@ interface SortableListProps {
1010
1344
  */
1011
1345
  declare function SortableList(props: SortableListProps): hono_jsx_jsx_dev_runtime.JSX.Element;
1012
1346
 
1013
- export { type AuthProvider, BaseFeature, CustomFeature, DefaultCreateFeature, DefaultDeleteFeature, DefaultDetailFeature, DefaultEditFeature, DefaultListFeature, Dialog, type DialogProps, type DialogSize, ErrorAlert, type ErrorAlertProps, type Feature, type FeatureContext, type FeatureType, type FieldMetadata, HtmxAdminPlugin, type HtmxAdminPluginOptions, type ListParams, type ListResult, LoadingBar, type NavItemConfig, type Notification, type NotificationType, ObjectEditor, type ObjectEditorProps, type PageMetadata, PageModel, type PermissionResult, SortableList, StringArrayEditor, type StringArrayEditorProps, TagsEditor, type TagsEditorProps, type UserInfo, checkUserPermission, getUserInfo, modelNameToPath, parseListParams };
1347
+ export { type ActionButton, type AuthProvider, BaseFeature, BaseFormFeature, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, Card, type CardProps, CustomFeature, DefaultCreateFeature, DefaultDeleteFeature, DefaultDetailFeature, DefaultEditFeature, DefaultListFeature, Dialog, type DialogProps, type DialogSize, EmptyState, type EmptyStateProps, ErrorAlert, type ErrorAlertProps, type Feature, type FeatureContext, type FeatureType, type FieldGroup, type FieldMetadata, type FieldRenderer, type FieldRendererProps, type FieldRendererType, type FieldRendererTypeMap, FilterForm, type FormAction, type FormField, type FormFieldRenderer, type FormFieldRendererMap, type FormFieldRendererProps, Header, type HeaderProps, HtmxAdminPlugin, type HtmxAdminPluginOptions, type ListParams, type ListResult, LoadingBar, type ModelField, type NavItemConfig, type Notification, type NotificationType$1 as NotificationType, ObjectEditor, type ObjectEditorProps, type OpenMode, type PageMetadata, PageModel, Pagination, type PaginationProps, PermissionDeniedContent, type PermissionDeniedContentProps, PermissionDeniedPage, type PermissionResult, type ResolvedHtmxAdminPluginOptions, SortableList, type SortableListProps, StringArrayEditor, type StringArrayEditorProps, Table, type TableProps, TagsEditor, type TagsEditorProps, type UserInfo, checkUserPermission, getUserInfo, modelNameToPath, parseListParams };
package/dist/index.js CHANGED
@@ -549,8 +549,9 @@ function renderFormField(field, initialData, formFieldRenderers) {
549
549
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: customRenderer({
550
550
  field,
551
551
  value: parsedValue,
552
- item: initialData,
553
- name: field.name
552
+ item: initialData || {},
553
+ name: field.name,
554
+ label: field.label
554
555
  }) }),
555
556
  field.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: field.description })
556
557
  ]
@@ -1847,7 +1848,12 @@ function renderDefaultValue(value) {
1847
1848
  function renderField(field, value, item) {
1848
1849
  let content;
1849
1850
  if (field.render) {
1850
- const rendered = field.render(value, item);
1851
+ const rendered = field.render({
1852
+ value,
1853
+ item,
1854
+ name: field.key,
1855
+ label: field.label
1856
+ });
1851
1857
  if (rendered === null || rendered === void 0) {
1852
1858
  content = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "-" });
1853
1859
  } else if (typeof rendered === "string" || typeof rendered === "number" || typeof rendered === "boolean") {
@@ -1983,11 +1989,14 @@ var DefaultDetailFeature = class extends BaseFeature {
1983
1989
  const groupFields = groupSchemas.map(
1984
1990
  ({ label, schema: schema2, fields: fieldNames }) => {
1985
1991
  const groupFields2 = parseSchemaToFields(schema2);
1986
- const detailFields2 = groupFields2.map((field) => ({
1987
- key: field.name,
1988
- label: field.label,
1989
- render: this.fieldRenderers?.[field.name]
1990
- }));
1992
+ const detailFields2 = groupFields2.map((field) => {
1993
+ const renderer = this.fieldRenderers?.[field.name];
1994
+ return {
1995
+ key: field.name,
1996
+ label: field.label,
1997
+ render: renderer
1998
+ };
1999
+ });
1991
2000
  return {
1992
2001
  label,
1993
2002
  fields: detailFields2,
@@ -2019,12 +2028,16 @@ var DefaultDetailFeature = class extends BaseFeature {
2019
2028
  }
2020
2029
  }
2021
2030
  const detailFieldNames = getFieldNamesFromFields(detailFields);
2022
- const fields = detailFieldNames.map((fieldName) => ({
2023
- key: fieldName,
2024
- label: getFieldLabelFromFields(this.fields || [], fieldName) || fieldName,
2025
- render: this.fieldRenderers?.[fieldName]
2026
- // 如果有自定义渲染函数则使用
2027
- }));
2031
+ const fields = detailFieldNames.map((fieldName) => {
2032
+ const label = getFieldLabelFromFields(this.fields || [], fieldName) || fieldName;
2033
+ const renderer = this.fieldRenderers?.[fieldName];
2034
+ return {
2035
+ key: fieldName,
2036
+ label,
2037
+ // 直接使用统一的渲染器类型
2038
+ render: renderer
2039
+ };
2040
+ });
2028
2041
  return /* @__PURE__ */ jsxRuntime.jsx(DetailPage, { item, fields });
2029
2042
  }
2030
2043
  async getActions(context) {
@@ -3809,11 +3822,11 @@ function SortableList(props) {
3809
3822
  function StringArrayEditor(props) {
3810
3823
  const {
3811
3824
  value,
3812
- name,
3813
3825
  placeholder = "\u8BF7\u8F93\u5165\u5185\u5BB9",
3814
3826
  allowEmpty = false,
3815
3827
  rows = 1
3816
3828
  } = props;
3829
+ const name = props.name;
3817
3830
  const initialItems = value || [];
3818
3831
  const initialDataJson = JSON.stringify({
3819
3832
  items: initialItems.map((item) => item || ""),
@@ -3980,7 +3993,8 @@ function ArrayItem({
3980
3993
  ] });
3981
3994
  }
3982
3995
  function TagsEditor(props) {
3983
- const { value, name, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3996
+ const { value, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3997
+ const name = props.name;
3984
3998
  const initialTags = value || [];
3985
3999
  const initialDataJson = JSON.stringify({
3986
4000
  tags: initialTags.map((tag) => tag || ""),
@@ -4264,7 +4278,7 @@ function ObjectEditor(props) {
4264
4278
  const initialValueJson = JSON.stringify(initialObject);
4265
4279
  JSON.stringify(fields.map((f) => f.name));
4266
4280
  const generateField = (field) => {
4267
- const fieldId = `${name}-${field.name}`;
4281
+ const fieldId = `${String(name)}-${field.name}`;
4268
4282
  const fieldValue = initialObject[field.name];
4269
4283
  const fieldValueStr = fieldValue === void 0 || fieldValue === null ? "" : typeof fieldValue === "object" ? JSON.stringify(fieldValue) : String(fieldValue);
4270
4284
  const requiredAttr = field.required ? "required" : "";
@@ -5334,6 +5348,10 @@ var HtmxAdminPlugin = class {
5334
5348
  };
5335
5349
 
5336
5350
  exports.BaseFeature = BaseFeature;
5351
+ exports.BaseFormFeature = BaseFormFeature;
5352
+ exports.Breadcrumb = Breadcrumb;
5353
+ exports.Button = Button;
5354
+ exports.Card = Card;
5337
5355
  exports.CustomFeature = CustomFeature;
5338
5356
  exports.DefaultCreateFeature = DefaultCreateFeature;
5339
5357
  exports.DefaultDeleteFeature = DefaultDeleteFeature;
@@ -5341,13 +5359,20 @@ exports.DefaultDetailFeature = DefaultDetailFeature;
5341
5359
  exports.DefaultEditFeature = DefaultEditFeature;
5342
5360
  exports.DefaultListFeature = DefaultListFeature;
5343
5361
  exports.Dialog = Dialog;
5362
+ exports.EmptyState = EmptyState;
5344
5363
  exports.ErrorAlert = ErrorAlert;
5364
+ exports.FilterForm = FilterForm;
5365
+ exports.Header = Header;
5345
5366
  exports.HtmxAdminPlugin = HtmxAdminPlugin;
5346
5367
  exports.LoadingBar = LoadingBar;
5347
5368
  exports.ObjectEditor = ObjectEditor;
5348
5369
  exports.PageModel = PageModel;
5370
+ exports.Pagination = Pagination;
5371
+ exports.PermissionDeniedContent = PermissionDeniedContent;
5372
+ exports.PermissionDeniedPage = PermissionDeniedPage;
5349
5373
  exports.SortableList = SortableList;
5350
5374
  exports.StringArrayEditor = StringArrayEditor;
5375
+ exports.Table = Table;
5351
5376
  exports.TagsEditor = TagsEditor;
5352
5377
  exports.checkUserPermission = checkUserPermission;
5353
5378
  exports.getUserInfo = getUserInfo;
package/dist/index.mjs CHANGED
@@ -547,8 +547,9 @@ function renderFormField(field, initialData, formFieldRenderers) {
547
547
  /* @__PURE__ */ jsx("div", { children: customRenderer({
548
548
  field,
549
549
  value: parsedValue,
550
- item: initialData,
551
- name: field.name
550
+ item: initialData || {},
551
+ name: field.name,
552
+ label: field.label
552
553
  }) }),
553
554
  field.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children: field.description })
554
555
  ]
@@ -1845,7 +1846,12 @@ function renderDefaultValue(value) {
1845
1846
  function renderField(field, value, item) {
1846
1847
  let content;
1847
1848
  if (field.render) {
1848
- const rendered = field.render(value, item);
1849
+ const rendered = field.render({
1850
+ value,
1851
+ item,
1852
+ name: field.key,
1853
+ label: field.label
1854
+ });
1849
1855
  if (rendered === null || rendered === void 0) {
1850
1856
  content = /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: "-" });
1851
1857
  } else if (typeof rendered === "string" || typeof rendered === "number" || typeof rendered === "boolean") {
@@ -1981,11 +1987,14 @@ var DefaultDetailFeature = class extends BaseFeature {
1981
1987
  const groupFields = groupSchemas.map(
1982
1988
  ({ label, schema: schema2, fields: fieldNames }) => {
1983
1989
  const groupFields2 = parseSchemaToFields(schema2);
1984
- const detailFields2 = groupFields2.map((field) => ({
1985
- key: field.name,
1986
- label: field.label,
1987
- render: this.fieldRenderers?.[field.name]
1988
- }));
1990
+ const detailFields2 = groupFields2.map((field) => {
1991
+ const renderer = this.fieldRenderers?.[field.name];
1992
+ return {
1993
+ key: field.name,
1994
+ label: field.label,
1995
+ render: renderer
1996
+ };
1997
+ });
1989
1998
  return {
1990
1999
  label,
1991
2000
  fields: detailFields2,
@@ -2017,12 +2026,16 @@ var DefaultDetailFeature = class extends BaseFeature {
2017
2026
  }
2018
2027
  }
2019
2028
  const detailFieldNames = getFieldNamesFromFields(detailFields);
2020
- const fields = detailFieldNames.map((fieldName) => ({
2021
- key: fieldName,
2022
- label: getFieldLabelFromFields(this.fields || [], fieldName) || fieldName,
2023
- render: this.fieldRenderers?.[fieldName]
2024
- // 如果有自定义渲染函数则使用
2025
- }));
2029
+ const fields = detailFieldNames.map((fieldName) => {
2030
+ const label = getFieldLabelFromFields(this.fields || [], fieldName) || fieldName;
2031
+ const renderer = this.fieldRenderers?.[fieldName];
2032
+ return {
2033
+ key: fieldName,
2034
+ label,
2035
+ // 直接使用统一的渲染器类型
2036
+ render: renderer
2037
+ };
2038
+ });
2026
2039
  return /* @__PURE__ */ jsx(DetailPage, { item, fields });
2027
2040
  }
2028
2041
  async getActions(context) {
@@ -3807,11 +3820,11 @@ function SortableList(props) {
3807
3820
  function StringArrayEditor(props) {
3808
3821
  const {
3809
3822
  value,
3810
- name,
3811
3823
  placeholder = "\u8BF7\u8F93\u5165\u5185\u5BB9",
3812
3824
  allowEmpty = false,
3813
3825
  rows = 1
3814
3826
  } = props;
3827
+ const name = props.name;
3815
3828
  const initialItems = value || [];
3816
3829
  const initialDataJson = JSON.stringify({
3817
3830
  items: initialItems.map((item) => item || ""),
@@ -3978,7 +3991,8 @@ function ArrayItem({
3978
3991
  ] });
3979
3992
  }
3980
3993
  function TagsEditor(props) {
3981
- const { value, name, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3994
+ const { value, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3995
+ const name = props.name;
3982
3996
  const initialTags = value || [];
3983
3997
  const initialDataJson = JSON.stringify({
3984
3998
  tags: initialTags.map((tag) => tag || ""),
@@ -4262,7 +4276,7 @@ function ObjectEditor(props) {
4262
4276
  const initialValueJson = JSON.stringify(initialObject);
4263
4277
  JSON.stringify(fields.map((f) => f.name));
4264
4278
  const generateField = (field) => {
4265
- const fieldId = `${name}-${field.name}`;
4279
+ const fieldId = `${String(name)}-${field.name}`;
4266
4280
  const fieldValue = initialObject[field.name];
4267
4281
  const fieldValueStr = fieldValue === void 0 || fieldValue === null ? "" : typeof fieldValue === "object" ? JSON.stringify(fieldValue) : String(fieldValue);
4268
4282
  const requiredAttr = field.required ? "required" : "";
@@ -5331,4 +5345,4 @@ var HtmxAdminPlugin = class {
5331
5345
  }
5332
5346
  };
5333
5347
 
5334
- export { BaseFeature, CustomFeature, DefaultCreateFeature, DefaultDeleteFeature, DefaultDetailFeature, DefaultEditFeature, DefaultListFeature, Dialog, ErrorAlert, HtmxAdminPlugin, LoadingBar, ObjectEditor, PageModel, SortableList, StringArrayEditor, TagsEditor, checkUserPermission, getUserInfo, modelNameToPath, parseListParams };
5348
+ export { BaseFeature, BaseFormFeature, Breadcrumb, Button, Card, CustomFeature, DefaultCreateFeature, DefaultDeleteFeature, DefaultDetailFeature, DefaultEditFeature, DefaultListFeature, Dialog, EmptyState, ErrorAlert, FilterForm, Header, HtmxAdminPlugin, LoadingBar, ObjectEditor, PageModel, Pagination, PermissionDeniedContent, PermissionDeniedPage, SortableList, StringArrayEditor, Table, TagsEditor, checkUserPermission, getUserInfo, modelNameToPath, parseListParams };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imean-service-engine-htmx-plugin",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "HtmxAdminPlugin for IMean Service Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",