imean-service-engine-htmx-plugin 2.6.0 → 2.8.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.js CHANGED
@@ -4,6 +4,7 @@ var jsxRuntime = require('hono/jsx/jsx-runtime');
4
4
  var fs = require('fs');
5
5
  var imeanServiceEngine = require('imean-service-engine');
6
6
  var path = require('path');
7
+ var zod = require('zod');
7
8
  var cookie = require('hono/cookie');
8
9
  var html = require('hono/html');
9
10
 
@@ -423,6 +424,212 @@ var PageModel = class {
423
424
  }
424
425
  };
425
426
 
427
+ // src/utils/schema-utils.ts
428
+ function parseSchemaToFields(schema) {
429
+ const def = schema._def;
430
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
431
+ if (!shape || typeof shape !== "object") {
432
+ return [];
433
+ }
434
+ const fields = [];
435
+ for (const [fieldName, fieldSchema] of Object.entries(shape)) {
436
+ if (["id", "createdAt", "updatedAt"].includes(fieldName)) {
437
+ continue;
438
+ }
439
+ const field = parseFieldSchema(fieldName, fieldSchema);
440
+ if (field) {
441
+ fields.push(field);
442
+ }
443
+ }
444
+ return fields;
445
+ }
446
+ function parseFieldSchema(fieldName, fieldSchema) {
447
+ if (!fieldSchema || !fieldSchema._def) {
448
+ return null;
449
+ }
450
+ const label = getFieldDescription(fieldSchema) || fieldName;
451
+ const { type, required, options, innerSchema, step } = analyzeFieldType(fieldSchema);
452
+ return {
453
+ name: fieldName,
454
+ label,
455
+ type,
456
+ required,
457
+ options,
458
+ step,
459
+ schema: innerSchema || fieldSchema
460
+ };
461
+ }
462
+ function getFieldDescription(schema) {
463
+ if (schema?.description) {
464
+ return schema.description;
465
+ }
466
+ const schemaDef = schema._def;
467
+ if (schemaDef?.description) {
468
+ return schemaDef.description;
469
+ }
470
+ if (schemaDef?.innerType) {
471
+ return getFieldDescription(schemaDef.innerType);
472
+ }
473
+ return void 0;
474
+ }
475
+ function analyzeFieldType(schema) {
476
+ const def = schema._def;
477
+ const typeName = def?.type || def?.typeName;
478
+ if (typeName === "optional" || typeName === "ZodOptional") {
479
+ const inner = analyzeFieldType(def.innerType);
480
+ return {
481
+ ...inner,
482
+ required: false,
483
+ innerSchema: def.innerType
484
+ };
485
+ }
486
+ if (typeName === "nullable" || typeName === "ZodNullable") {
487
+ const inner = analyzeFieldType(def.innerType);
488
+ return {
489
+ ...inner,
490
+ innerSchema: def.innerType
491
+ };
492
+ }
493
+ if (typeName === "enum" || typeName === "ZodEnum") {
494
+ const options = extractEnumValues(def);
495
+ return {
496
+ type: "select",
497
+ required: true,
498
+ options,
499
+ innerSchema: schema
500
+ };
501
+ }
502
+ if (typeName === "nativeEnum" || typeName === "ZodNativeEnum") {
503
+ const options = extractNativeEnumValues(def);
504
+ return {
505
+ type: "select",
506
+ required: true,
507
+ options,
508
+ innerSchema: schema
509
+ };
510
+ }
511
+ if (typeName === "string" || typeName === "ZodString") {
512
+ let fieldType = "text";
513
+ if (def?.checks) {
514
+ const hasEmailCheck = def.checks.some(
515
+ (check) => check.format === "email" || check.constructor?.name === "ZodEmail" || check._zod?.def?.format === "email"
516
+ );
517
+ if (hasEmailCheck) {
518
+ fieldType = "email";
519
+ } else {
520
+ const maxLengthCheck = def.checks.find(
521
+ (check) => check.constructor?.name === "$ZodCheckMaxLength" || check._zod?.def?.check === "max_length" || check._zod?.def?.maximum !== void 0
522
+ );
523
+ if (maxLengthCheck) {
524
+ const maxLength = maxLengthCheck._zod?.def?.maximum ?? maxLengthCheck.value ?? maxLengthCheck.maximum;
525
+ if (maxLength !== void 0 && maxLength > 50) {
526
+ fieldType = "textarea";
527
+ }
528
+ }
529
+ }
530
+ }
531
+ return {
532
+ type: fieldType,
533
+ required: true,
534
+ innerSchema: schema
535
+ };
536
+ }
537
+ if (typeName === "number" || typeName === "ZodNumber") {
538
+ let step = void 0;
539
+ if (def?.checks) {
540
+ const hasIntCheck = def.checks.some(
541
+ (check) => check.isInt === true || check.format === "safeint" || check.def?.format === "safeint" || check.kind === "int" || check.constructor?.name === "ZodInt" || check._zod?.def?.check === "int" || check._zod?.def?.kind === "int"
542
+ );
543
+ if (!hasIntCheck) {
544
+ step = "any";
545
+ }
546
+ } else {
547
+ step = "any";
548
+ }
549
+ return {
550
+ type: "number",
551
+ required: true,
552
+ innerSchema: schema,
553
+ step
554
+ };
555
+ }
556
+ if (typeName === "date" || typeName === "ZodDate") {
557
+ return {
558
+ type: "date",
559
+ required: true,
560
+ innerSchema: schema
561
+ };
562
+ }
563
+ if (typeName === "boolean" || typeName === "ZodBoolean") {
564
+ return {
565
+ type: "checkbox",
566
+ required: true,
567
+ innerSchema: schema
568
+ };
569
+ }
570
+ return {
571
+ type: "text",
572
+ required: true,
573
+ innerSchema: schema
574
+ };
575
+ }
576
+ function extractEnumValues(def) {
577
+ let values;
578
+ if (def?.values && Array.isArray(def.values)) {
579
+ values = def.values;
580
+ } else if (def?.entries && typeof def.entries === "object") {
581
+ values = Object.keys(def.entries);
582
+ }
583
+ if (values && values.length > 0) {
584
+ return values.map((value) => ({
585
+ value,
586
+ label: String(value)
587
+ }));
588
+ }
589
+ return void 0;
590
+ }
591
+ function extractNativeEnumValues(def) {
592
+ const enumValues = def.values || def._def?.values || {};
593
+ if (enumValues && typeof enumValues === "object") {
594
+ return Object.entries(enumValues).filter(
595
+ ([_, value]) => typeof value === "string" || typeof value === "number"
596
+ ).map(([key, value]) => ({
597
+ value,
598
+ label: key
599
+ }));
600
+ }
601
+ return void 0;
602
+ }
603
+ function filterFieldsByNames(fields, fieldNames) {
604
+ if (fieldNames === void 0) {
605
+ return fields;
606
+ }
607
+ if (fieldNames.length === 0) {
608
+ return [];
609
+ }
610
+ return fields.filter((field) => fieldNames.includes(field.name));
611
+ }
612
+ function modelFieldsToFormFields(fields) {
613
+ return fields.map((field) => ({
614
+ name: field.name,
615
+ type: field.type,
616
+ label: field.label,
617
+ required: field.required,
618
+ options: field.options,
619
+ step: field.step
620
+ }));
621
+ }
622
+ function getFieldNamesFromFields(fields) {
623
+ return fields.map((field) => field.name);
624
+ }
625
+ function getFieldByName(fields, fieldName) {
626
+ return fields.find((field) => field.name === fieldName);
627
+ }
628
+ function getFieldLabelFromFields(fields, fieldName) {
629
+ const field = getFieldByName(fields, fieldName);
630
+ return field?.label || fieldName;
631
+ }
632
+
426
633
  // src/features/base-feature.ts
427
634
  var BaseFeature = class {
428
635
  /** Feature 名称 */
@@ -436,15 +643,19 @@ var BaseFeature = class {
436
643
  /** 是否允许点击遮罩区域关闭弹窗(默认 true,设置为 false 时只能通过关闭按钮关闭) */
437
644
  closeOnBackdropClick;
438
645
  /** Schema(可选,由子类在构造函数中设置) */
439
- schema;
646
+ schema = zod.z.object({});
440
647
  /** 解析后的字段列表(可选,由子类在构造函数中设置) */
441
- fields;
648
+ fields = [];
442
649
  constructor(options) {
443
650
  this.name = options.name;
444
651
  this.type = options.type;
445
652
  this.permission = options.permission;
446
653
  this.dialogSize = options.dialogSize;
447
654
  this.closeOnBackdropClick = options.closeOnBackdropClick;
655
+ this.schema = options.schema || zod.z.object({});
656
+ if (this.schema) {
657
+ this.fields = parseSchemaToFields(this.schema);
658
+ }
448
659
  }
449
660
  /**
450
661
  * 获取动态标题(默认实现:返回 PageMetadata 的 title)
@@ -476,26 +687,6 @@ var BaseFeature = class {
476
687
  return await this.render?.(context) ?? null;
477
688
  }
478
689
  };
479
- function getFieldValue(field, initialData) {
480
- if (initialData && Object.prototype.hasOwnProperty.call(initialData, field.name)) {
481
- const value = initialData[field.name];
482
- if (value === null || value === void 0) {
483
- return "";
484
- }
485
- if (typeof value === "object") {
486
- if (value instanceof Date) {
487
- return value.toISOString();
488
- }
489
- try {
490
- return JSON.stringify(value);
491
- } catch (e) {
492
- return "";
493
- }
494
- }
495
- return String(value);
496
- }
497
- return "";
498
- }
499
690
  function isJsonString(value) {
500
691
  if (!value || typeof value !== "string") {
501
692
  return false;
@@ -515,7 +706,9 @@ function formatJsonString(value) {
515
706
  }
516
707
  }
517
708
  function renderFormField(field, initialData, formFieldRenderers) {
518
- const value = getFieldValue(field, initialData);
709
+ const value = initialData?.[field.name];
710
+ field.label || field.name;
711
+ const fieldSchema = field.schema;
519
712
  const customRenderer = formFieldRenderers?.[field.name];
520
713
  if (customRenderer) {
521
714
  let parsedValue = null;
@@ -549,10 +742,10 @@ function renderFormField(field, initialData, formFieldRenderers) {
549
742
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: customRenderer({
550
743
  field,
551
744
  value: parsedValue,
552
- item: initialData,
553
- name: field.name
554
- }) }),
555
- field.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: field.description })
745
+ item: initialData || {},
746
+ name: field.name,
747
+ label: field.label
748
+ }) })
556
749
  ]
557
750
  },
558
751
  field.name
@@ -560,41 +753,38 @@ function renderFormField(field, initialData, formFieldRenderers) {
560
753
  }
561
754
  if (field.type === "checkbox") {
562
755
  const isChecked = value === "true" || value === "1" || value === "on" || String(value).toLowerCase() === "true";
563
- return /* @__PURE__ */ jsxRuntime.jsxs(
756
+ return /* @__PURE__ */ jsxRuntime.jsx(
564
757
  "div",
565
758
  {
566
759
  className: "space-y-2",
567
760
  "data-testid": `field-${field.name}`,
568
- children: [
569
- /* @__PURE__ */ jsxRuntime.jsxs(
570
- "label",
571
- {
572
- htmlFor: field.name,
573
- className: "flex items-center gap-3 cursor-pointer group py-2.5 px-3 rounded-lg hover:bg-gray-50 transition-colors border border-transparent hover:border-gray-200",
574
- "data-testid": `label-${field.name}`,
575
- children: [
576
- /* @__PURE__ */ jsxRuntime.jsx(
577
- "input",
578
- {
579
- type: "checkbox",
580
- id: field.name,
581
- name: field.name,
582
- value: "true",
583
- checked: isChecked,
584
- required: field.required,
585
- className: "w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 cursor-pointer transition-all flex-shrink-0",
586
- "data-testid": `input-${field.name}`
587
- }
588
- ),
589
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold text-gray-700 select-none flex-1", children: [
590
- field.label,
591
- field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-1", children: "*" })
592
- ] })
593
- ]
594
- }
595
- ),
596
- field.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 ml-8", children: field.description })
597
- ]
761
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
762
+ "label",
763
+ {
764
+ htmlFor: field.name,
765
+ className: "flex items-center gap-3 cursor-pointer group py-2.5 px-3 rounded-lg hover:bg-gray-50 transition-colors border border-transparent hover:border-gray-200",
766
+ "data-testid": `label-${field.name}`,
767
+ children: [
768
+ /* @__PURE__ */ jsxRuntime.jsx(
769
+ "input",
770
+ {
771
+ type: "checkbox",
772
+ id: field.name,
773
+ name: field.name,
774
+ value: "true",
775
+ checked: isChecked,
776
+ required: field.required,
777
+ className: "w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 cursor-pointer transition-all flex-shrink-0",
778
+ "data-testid": `input-${field.name}`
779
+ }
780
+ ),
781
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold text-gray-700 select-none flex-1", children: [
782
+ field.label,
783
+ field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-1", children: "*" })
784
+ ] })
785
+ ]
786
+ }
787
+ )
598
788
  },
599
789
  field.name
600
790
  );
@@ -624,7 +814,7 @@ function renderFormField(field, initialData, formFieldRenderers) {
624
814
  id: field.name,
625
815
  name: field.name,
626
816
  required: field.required,
627
- placeholder: field.placeholder || (isJsonString(value) ? "JSON \u683C\u5F0F\u6570\u636E" : ""),
817
+ placeholder: fieldSchema.description || (isJsonString(value) ? "JSON \u683C\u5F0F\u6570\u636E" : ""),
628
818
  rows: isJsonString(value) ? Math.max(10, value.split("\n").length) : Math.max(5, Math.ceil(value.length / 100)),
629
819
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 resize-y font-mono text-sm",
630
820
  "data-testid": `input-${field.name}`,
@@ -664,7 +854,7 @@ function renderFormField(field, initialData, formFieldRenderers) {
664
854
  id: field.name,
665
855
  name: field.name,
666
856
  required: field.required,
667
- placeholder: field.placeholder,
857
+ placeholder: fieldSchema.description || "",
668
858
  step: field.type === "number" ? field.step ?? void 0 : void 0,
669
859
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200",
670
860
  value,
@@ -672,7 +862,7 @@ function renderFormField(field, initialData, formFieldRenderers) {
672
862
  },
673
863
  `${field.name}-${value}`
674
864
  ),
675
- field.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: field.description })
865
+ fieldSchema.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: fieldSchema.description })
676
866
  ]
677
867
  },
678
868
  field.name
@@ -680,7 +870,6 @@ function renderFormField(field, initialData, formFieldRenderers) {
680
870
  }
681
871
  function FormPage(props) {
682
872
  const {
683
- fields,
684
873
  groups,
685
874
  submitUrl,
686
875
  method = "post",
@@ -690,12 +879,6 @@ function FormPage(props) {
690
879
  formFieldRenderers
691
880
  } = props;
692
881
  const finalFormId = formId || `form-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
693
- if (process.env.NODE_ENV === "development" && initialData) {
694
- const fieldNames = fields ? fields.map((f) => f.name).join(", ") : groups ? groups.map((g) => g.fields.map((f) => f.name).join(", ")).join(" | ") : "";
695
- console.log(
696
- `[FormPage] initialData: ${JSON.stringify(initialData)}, fields: ${fieldNames}`
697
- );
698
- }
699
882
  return /* @__PURE__ */ jsxRuntime.jsxs(
700
883
  "div",
701
884
  {
@@ -743,7 +926,7 @@ function FormPage(props) {
743
926
  ]
744
927
  }
745
928
  ),
746
- groups && groups.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
929
+ groups && groups.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
747
930
  /* @__PURE__ */ jsxRuntime.jsx(
748
931
  "div",
749
932
  {
@@ -795,26 +978,25 @@ function FormPage(props) {
795
978
  )) }) })
796
979
  }
797
980
  )
798
- ] }) : (
799
- /* 平铺模式(向后兼容) */
981
+ ] }) : groups && groups.length === 1 ? (
982
+ /* 平铺模式 - 单个分组时不显示 Tab */
800
983
  /* @__PURE__ */ jsxRuntime.jsx(
801
984
  "form",
802
985
  {
803
986
  id: finalFormId,
804
987
  method: method === "put" ? "post" : method,
805
988
  action: submitUrl,
806
- "hx-boost": "true",
807
- "hx-trigger": "click from:#form-submit-button",
808
- ...method === "put" ? { "hx-put": submitUrl } : {},
989
+ ...method === "put" ? { "hx-put": submitUrl } : { "hx-post": submitUrl },
809
990
  "hx-indicator": "#form-loading-indicator",
991
+ "hx-sync": "this:abort",
810
992
  className: "space-y-6",
811
993
  "data-testid": "form",
812
- children: fields && fields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg border border-gray-200 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 space-y-6", children: fields.map(
994
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg border border-gray-200 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 space-y-6", children: groups[0].fields.map(
813
995
  (field) => renderFormField(field, initialData, formFieldRenderers)
814
996
  ) }) })
815
997
  }
816
998
  )
817
- )
999
+ ) : null
818
1000
  ]
819
1001
  }
820
1002
  );
@@ -1000,180 +1182,52 @@ function compressArrays(obj, arrayIndicesMap) {
1000
1182
  }
1001
1183
 
1002
1184
  // src/utils/form-data-processor.ts
1003
- function parseNestedFormData2(flatData) {
1004
- return parseNestedFormData(flatData);
1005
- }
1006
- function preprocessFormData(data, zodSchema) {
1007
- if (!zodSchema) {
1008
- return data;
1009
- }
1010
- const processed = { ...data };
1011
- const def = zodSchema._zod?.def || zodSchema._def;
1012
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
1013
- if (!shape || typeof shape !== "object") {
1014
- return processed;
1015
- }
1016
- for (const [fieldName, fieldSchema] of Object.entries(shape)) {
1017
- const value = processed[fieldName];
1018
- if (value === void 0) {
1019
- continue;
1020
- }
1021
- if (value === null) {
1022
- processed[fieldName] = void 0;
1023
- continue;
1024
- }
1025
- if (value === "") {
1026
- processed[fieldName] = void 0;
1027
- continue;
1028
- }
1029
- const fieldDef = fieldSchema._zod?.def || fieldSchema._def;
1030
- let typeName = fieldDef?.type;
1031
- let actualSchema = fieldSchema;
1032
- if (typeName === "optional") {
1033
- const innerType = fieldDef?.innerType;
1034
- if (innerType) {
1035
- actualSchema = innerType;
1036
- const innerDef = innerType._zod?.def || innerType._def;
1037
- typeName = innerDef?.type;
1038
- }
1039
- }
1040
- if (typeName === "number" || typeName === "int") {
1041
- if (typeof value === "string") {
1042
- const trimmed = value.trim();
1043
- if (trimmed !== "") {
1044
- const numValue = Number(trimmed);
1045
- if (!isNaN(numValue)) {
1046
- processed[fieldName] = numValue;
1047
- }
1048
- }
1049
- } else if (typeof value === "number") {
1050
- processed[fieldName] = value;
1051
- }
1052
- }
1053
- if (typeName === "boolean") {
1054
- if (typeof value === "string") {
1055
- const trimmed = value.trim().toLowerCase();
1056
- if (trimmed === "true" || trimmed === "1" || trimmed === "on") {
1057
- processed[fieldName] = true;
1058
- } else if (trimmed === "false" || trimmed === "0" || trimmed === "off" || trimmed === "") {
1059
- processed[fieldName] = false;
1060
- }
1061
- } else if (typeof value === "boolean") {
1062
- processed[fieldName] = value;
1063
- }
1064
- }
1065
- if (typeName === "array") {
1066
- if (Array.isArray(value)) {
1067
- const arraySchema = actualSchema;
1068
- const arrayDef = arraySchema._zod?.def || arraySchema._def;
1069
- let elementType = arrayDef?.element;
1070
- if (!elementType) {
1071
- processed[fieldName] = value;
1072
- } else {
1073
- const elementDef = elementType._zod?.def || elementType._def;
1074
- let elementTypeName = elementDef?.type;
1075
- if (elementTypeName === "optional") {
1076
- const innerElementType = elementDef?.innerType;
1077
- if (innerElementType) {
1078
- elementType = innerElementType;
1079
- const innerElementDef = innerElementType._zod?.def || innerElementType._def;
1080
- elementTypeName = innerElementDef?.type;
1081
- }
1082
- }
1083
- processed[fieldName] = value.map((item) => {
1084
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
1085
- if (elementTypeName === "object") {
1086
- const processedItem = preprocessFormData(
1087
- item,
1088
- elementType
1089
- );
1090
- return { ...item, ...processedItem };
1091
- }
1092
- return item;
1093
- }
1094
- if (typeof item === "string") {
1095
- return convertValueByType(item, elementType);
1096
- }
1097
- if (Array.isArray(item)) {
1098
- const elementDef2 = elementType._zod?.def || elementType._def;
1099
- const nestedInnerType = elementDef2?.element;
1100
- if (nestedInnerType) {
1101
- return item.map((subItem) => {
1102
- if (typeof subItem === "string") {
1103
- return convertValueByType(subItem, nestedInnerType);
1104
- }
1105
- if (typeof subItem === "object" && subItem !== null) {
1106
- const subItemDef = nestedInnerType._zod?.def || nestedInnerType._def;
1107
- if (subItemDef?.type === "object") {
1108
- return preprocessFormData(
1109
- subItem,
1110
- nestedInnerType
1111
- );
1112
- }
1113
- }
1114
- return subItem;
1115
- });
1116
- }
1117
- return item;
1118
- }
1119
- return item;
1120
- });
1121
- }
1122
- } else if (typeof value === "string") {
1123
- const trimmed = value.trim();
1124
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
1125
- try {
1126
- const parsed = JSON.parse(trimmed);
1127
- processed[fieldName] = parsed;
1128
- } catch (e) {
1129
- }
1130
- } else {
1131
- const parts = trimmed.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
1132
- processed[fieldName] = parts;
1133
- }
1134
- }
1135
- }
1136
- if (typeName === "object") {
1137
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1138
- const objectSchema = actualSchema;
1139
- processed[fieldName] = preprocessFormData(value, objectSchema);
1140
- } else if (typeof value === "string" && value.trim() !== "") {
1141
- try {
1142
- const trimmed = value.trim();
1143
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
1144
- const parsed = JSON.parse(trimmed);
1145
- processed[fieldName] = parsed;
1146
- }
1147
- } catch (e) {
1148
- }
1149
- }
1150
- }
1151
- if (typeName === "any" || typeName === "unknown") {
1152
- if (typeof value === "string" && value.trim() !== "") {
1153
- try {
1154
- const trimmed = value.trim();
1155
- if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
1156
- const parsed = JSON.parse(trimmed);
1157
- processed[fieldName] = parsed;
1158
- }
1159
- } catch (e) {
1160
- }
1161
- }
1162
- }
1163
- }
1164
- return processed;
1185
+ function getZodDef(schema) {
1186
+ return schema._zod?.def || schema._def;
1165
1187
  }
1166
- function convertValueByType(value, schema) {
1167
- const def = schema._zod?.def || schema._def;
1188
+ function resolveZodType(schema) {
1189
+ let currentSchema = schema;
1190
+ let isOptional = false;
1191
+ let def = getZodDef(currentSchema);
1168
1192
  let typeName = def?.type;
1169
1193
  if (typeName === "optional") {
1194
+ isOptional = true;
1170
1195
  const innerType = def?.innerType;
1171
1196
  if (innerType) {
1172
- const innerDef = innerType._zod?.def || innerType._def;
1173
- typeName = innerDef?.type;
1197
+ currentSchema = innerType;
1198
+ def = getZodDef(innerType);
1199
+ typeName = def?.type;
1174
1200
  }
1175
1201
  }
1176
- if (typeName === "number" || typeName === "int") {
1202
+ if (typeName === "default" || typeName === "ZodDefault") {
1203
+ const innerType = def?.innerType;
1204
+ if (innerType) {
1205
+ currentSchema = innerType;
1206
+ def = getZodDef(innerType);
1207
+ typeName = def?.type;
1208
+ }
1209
+ }
1210
+ while (typeName === "refinement" || typeName === "ZodEffects") {
1211
+ const innerType = def?.schema || def?.innerType;
1212
+ if (innerType) {
1213
+ currentSchema = innerType;
1214
+ def = getZodDef(innerType);
1215
+ typeName = def?.type;
1216
+ } else {
1217
+ break;
1218
+ }
1219
+ }
1220
+ return {
1221
+ typeName: typeName || "any",
1222
+ actualSchema: currentSchema,
1223
+ isOptional
1224
+ };
1225
+ }
1226
+ function parseNestedFormData2(flatData) {
1227
+ return parseNestedFormData(flatData);
1228
+ }
1229
+ function convertNumber(value) {
1230
+ if (typeof value === "string") {
1177
1231
  const trimmed = value.trim();
1178
1232
  if (trimmed !== "") {
1179
1233
  const numValue = Number(trimmed);
@@ -1182,222 +1236,172 @@ function convertValueByType(value, schema) {
1182
1236
  }
1183
1237
  }
1184
1238
  return value;
1185
- } else if (typeName === "boolean") {
1239
+ }
1240
+ if (typeof value === "number") {
1241
+ return value;
1242
+ }
1243
+ return value;
1244
+ }
1245
+ function convertBoolean(value) {
1246
+ if (typeof value === "string") {
1186
1247
  const trimmed = value.trim().toLowerCase();
1187
1248
  if (trimmed === "true" || trimmed === "1" || trimmed === "on") {
1188
1249
  return true;
1189
- } else if (trimmed === "false" || trimmed === "0" || trimmed === "off" || trimmed === "") {
1250
+ }
1251
+ if (trimmed === "false" || trimmed === "0" || trimmed === "off" || trimmed === "") {
1190
1252
  return false;
1191
1253
  }
1192
1254
  return value;
1193
1255
  }
1256
+ if (typeof value === "boolean") {
1257
+ return value;
1258
+ }
1194
1259
  return value;
1195
1260
  }
1196
-
1197
- // src/utils/schema-utils.ts
1198
- function parseSchemaToFields(schema) {
1199
- const def = schema._def;
1200
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
1201
- if (!shape || typeof shape !== "object") {
1202
- return [];
1203
- }
1204
- const fields = [];
1205
- for (const [fieldName, fieldSchema] of Object.entries(shape)) {
1206
- if (["id", "createdAt", "updatedAt"].includes(fieldName)) {
1207
- continue;
1208
- }
1209
- const field = parseFieldSchema(fieldName, fieldSchema);
1210
- if (field) {
1211
- fields.push(field);
1261
+ function processArrayElement(item, elementType) {
1262
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
1263
+ const { typeName } = resolveZodType(elementType);
1264
+ if (typeName === "object") {
1265
+ const processedItem = preprocessFormData(
1266
+ item,
1267
+ elementType
1268
+ );
1269
+ return { ...item, ...processedItem };
1212
1270
  }
1271
+ return item;
1213
1272
  }
1214
- return fields;
1215
- }
1216
- function parseFieldSchema(fieldName, fieldSchema) {
1217
- if (!fieldSchema || !fieldSchema._def) {
1218
- return null;
1219
- }
1220
- const label = getFieldDescription(fieldSchema) || fieldName;
1221
- const { type, required, options, innerSchema, step } = analyzeFieldType(fieldSchema);
1222
- return {
1223
- name: fieldName,
1224
- label,
1225
- type,
1226
- required,
1227
- options,
1228
- step,
1229
- schema: innerSchema || fieldSchema
1230
- };
1231
- }
1232
- function getFieldDescription(schema) {
1233
- if (schema?.description) {
1234
- return schema.description;
1235
- }
1236
- const schemaDef = schema._def;
1237
- if (schemaDef?.description) {
1238
- return schemaDef.description;
1239
- }
1240
- if (schemaDef?.innerType) {
1241
- return getFieldDescription(schemaDef.innerType);
1242
- }
1243
- return void 0;
1244
- }
1245
- function analyzeFieldType(schema) {
1246
- const def = schema._def;
1247
- const typeName = def?.type || def?.typeName;
1248
- if (typeName === "optional" || typeName === "ZodOptional") {
1249
- const inner = analyzeFieldType(def.innerType);
1250
- return {
1251
- ...inner,
1252
- required: false,
1253
- innerSchema: def.innerType
1254
- };
1255
- }
1256
- if (typeName === "nullable" || typeName === "ZodNullable") {
1257
- const inner = analyzeFieldType(def.innerType);
1258
- return {
1259
- ...inner,
1260
- innerSchema: def.innerType
1261
- };
1262
- }
1263
- if (typeName === "enum" || typeName === "ZodEnum") {
1264
- const options = extractEnumValues(def);
1265
- return {
1266
- type: "select",
1267
- required: true,
1268
- options,
1269
- innerSchema: schema
1270
- };
1271
- }
1272
- if (typeName === "nativeEnum" || typeName === "ZodNativeEnum") {
1273
- const options = extractNativeEnumValues(def);
1274
- return {
1275
- type: "select",
1276
- required: true,
1277
- options,
1278
- innerSchema: schema
1279
- };
1273
+ if (typeof item === "string") {
1274
+ return convertValueByType(item, elementType);
1280
1275
  }
1281
- if (typeName === "string" || typeName === "ZodString") {
1282
- let fieldType = "text";
1283
- if (def?.checks) {
1284
- const hasEmailCheck = def.checks.some(
1285
- (check) => check.format === "email" || check.constructor?.name === "ZodEmail" || check._zod?.def?.format === "email"
1286
- );
1287
- if (hasEmailCheck) {
1288
- fieldType = "email";
1289
- } else {
1290
- const maxLengthCheck = def.checks.find(
1291
- (check) => check.constructor?.name === "$ZodCheckMaxLength" || check._zod?.def?.check === "max_length" || check._zod?.def?.maximum !== void 0
1292
- );
1293
- if (maxLengthCheck) {
1294
- const maxLength = maxLengthCheck._zod?.def?.maximum ?? maxLengthCheck.value ?? maxLengthCheck.maximum;
1295
- if (maxLength !== void 0 && maxLength > 50) {
1296
- fieldType = "textarea";
1276
+ if (Array.isArray(item)) {
1277
+ const elementDef = getZodDef(elementType);
1278
+ const nestedInnerType = elementDef?.element;
1279
+ if (nestedInnerType) {
1280
+ return item.map((subItem) => {
1281
+ if (typeof subItem === "string") {
1282
+ return convertValueByType(subItem, nestedInnerType);
1283
+ }
1284
+ if (typeof subItem === "object" && subItem !== null) {
1285
+ const subItemDef = getZodDef(nestedInnerType);
1286
+ if (subItemDef?.type === "object") {
1287
+ return preprocessFormData(
1288
+ subItem,
1289
+ nestedInnerType
1290
+ );
1297
1291
  }
1298
1292
  }
1299
- }
1300
- }
1301
- return {
1302
- type: fieldType,
1303
- required: true,
1304
- innerSchema: schema
1305
- };
1306
- }
1307
- if (typeName === "number" || typeName === "ZodNumber") {
1308
- let step = void 0;
1309
- if (def?.checks) {
1310
- const hasIntCheck = def.checks.some(
1311
- (check) => check.isInt === true || check.format === "safeint" || check.def?.format === "safeint" || check.kind === "int" || check.constructor?.name === "ZodInt" || check._zod?.def?.check === "int" || check._zod?.def?.kind === "int"
1312
- );
1313
- if (!hasIntCheck) {
1314
- step = "any";
1315
- }
1316
- } else {
1317
- step = "any";
1293
+ return subItem;
1294
+ });
1318
1295
  }
1319
- return {
1320
- type: "number",
1321
- required: true,
1322
- innerSchema: schema,
1323
- step
1324
- };
1325
- }
1326
- if (typeName === "date" || typeName === "ZodDate") {
1327
- return {
1328
- type: "date",
1329
- required: true,
1330
- innerSchema: schema
1331
- };
1332
- }
1333
- if (typeName === "boolean" || typeName === "ZodBoolean") {
1334
- return {
1335
- type: "checkbox",
1336
- required: true,
1337
- innerSchema: schema
1338
- };
1339
- }
1340
- return {
1341
- type: "text",
1342
- required: true,
1343
- innerSchema: schema
1344
- };
1345
- }
1346
- function extractEnumValues(def) {
1347
- let values;
1348
- if (def?.values && Array.isArray(def.values)) {
1349
- values = def.values;
1350
- } else if (def?.entries && typeof def.entries === "object") {
1351
- values = Object.keys(def.entries);
1352
- }
1353
- if (values && values.length > 0) {
1354
- return values.map((value) => ({
1355
- value,
1356
- label: String(value)
1357
- }));
1358
- }
1359
- return void 0;
1360
- }
1361
- function extractNativeEnumValues(def) {
1362
- const enumValues = def.values || def._def?.values || {};
1363
- if (enumValues && typeof enumValues === "object") {
1364
- return Object.entries(enumValues).filter(
1365
- ([_, value]) => typeof value === "string" || typeof value === "number"
1366
- ).map(([key, value]) => ({
1367
- value,
1368
- label: key
1369
- }));
1296
+ return item;
1370
1297
  }
1371
- return void 0;
1298
+ return item;
1372
1299
  }
1373
- function filterFieldsByNames(fields, fieldNames) {
1374
- if (fieldNames === void 0) {
1375
- return fields;
1300
+ function processArrayType(value, actualSchema) {
1301
+ if (Array.isArray(value)) {
1302
+ const arrayDef = getZodDef(actualSchema);
1303
+ let elementType = arrayDef?.element;
1304
+ if (!elementType) {
1305
+ return value;
1306
+ }
1307
+ const { actualSchema: resolvedElementType } = resolveZodType(elementType);
1308
+ return value.map((item) => processArrayElement(item, resolvedElementType));
1376
1309
  }
1377
- if (fieldNames.length === 0) {
1378
- return [];
1310
+ if (typeof value === "string") {
1311
+ const trimmed = value.trim();
1312
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
1313
+ try {
1314
+ return JSON.parse(trimmed);
1315
+ } catch {
1316
+ }
1317
+ } else {
1318
+ return trimmed.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
1319
+ }
1379
1320
  }
1380
- return fields.filter((field) => fieldNames.includes(field.name));
1321
+ return value;
1381
1322
  }
1382
- function modelFieldsToFormFields(fields) {
1383
- return fields.map((field) => ({
1384
- name: field.name,
1385
- type: field.type,
1386
- label: field.label,
1387
- required: field.required,
1388
- options: field.options,
1389
- step: field.step
1390
- }));
1323
+ function processObjectType(value, actualSchema) {
1324
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1325
+ return preprocessFormData(value, actualSchema);
1326
+ }
1327
+ if (typeof value === "string" && value.trim() !== "") {
1328
+ const trimmed = value.trim();
1329
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
1330
+ try {
1331
+ return JSON.parse(trimmed);
1332
+ } catch {
1333
+ }
1334
+ }
1335
+ }
1336
+ return value;
1391
1337
  }
1392
- function getFieldNamesFromFields(fields) {
1393
- return fields.map((field) => field.name);
1338
+ function processAnyType(value) {
1339
+ if (typeof value === "string" && value.trim() !== "") {
1340
+ const trimmed = value.trim();
1341
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
1342
+ try {
1343
+ return JSON.parse(trimmed);
1344
+ } catch {
1345
+ }
1346
+ }
1347
+ }
1348
+ return value;
1394
1349
  }
1395
- function getFieldByName(fields, fieldName) {
1396
- return fields.find((field) => field.name === fieldName);
1350
+ function preprocessFormData(data, zodSchema) {
1351
+ if (!zodSchema) {
1352
+ return data;
1353
+ }
1354
+ const processed = { ...data };
1355
+ const def = getZodDef(zodSchema);
1356
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
1357
+ if (!shape || typeof shape !== "object") {
1358
+ return processed;
1359
+ }
1360
+ for (const [fieldName, fieldSchema] of Object.entries(shape)) {
1361
+ const value = processed[fieldName];
1362
+ if (value === void 0) {
1363
+ continue;
1364
+ }
1365
+ if (value === null || value === "") {
1366
+ processed[fieldName] = void 0;
1367
+ continue;
1368
+ }
1369
+ const { typeName, actualSchema } = resolveZodType(
1370
+ fieldSchema
1371
+ );
1372
+ switch (typeName) {
1373
+ case "number":
1374
+ case "int":
1375
+ processed[fieldName] = convertNumber(value);
1376
+ break;
1377
+ case "boolean":
1378
+ processed[fieldName] = convertBoolean(value);
1379
+ break;
1380
+ case "array":
1381
+ processed[fieldName] = processArrayType(value, actualSchema);
1382
+ break;
1383
+ case "object":
1384
+ processed[fieldName] = processObjectType(value, actualSchema);
1385
+ break;
1386
+ case "any":
1387
+ case "unknown":
1388
+ processed[fieldName] = processAnyType(value);
1389
+ break;
1390
+ }
1391
+ }
1392
+ return processed;
1397
1393
  }
1398
- function getFieldLabelFromFields(fields, fieldName) {
1399
- const field = getFieldByName(fields, fieldName);
1400
- return field?.label || fieldName;
1394
+ function convertValueByType(value, schema) {
1395
+ const { typeName } = resolveZodType(schema);
1396
+ switch (typeName) {
1397
+ case "number":
1398
+ case "int":
1399
+ return convertNumber(value);
1400
+ case "boolean":
1401
+ return convertBoolean(value);
1402
+ default:
1403
+ return value;
1404
+ }
1401
1405
  }
1402
1406
  var BaseFormFeature = class extends BaseFeature {
1403
1407
  titleGetter;
@@ -1421,7 +1425,8 @@ var BaseFormFeature = class extends BaseFeature {
1421
1425
  type: options.type,
1422
1426
  permission: options.permission,
1423
1427
  dialogSize: options.dialogSize,
1424
- closeOnBackdropClick: options.closeOnBackdropClick
1428
+ closeOnBackdropClick: options.closeOnBackdropClick,
1429
+ schema: options.schema
1425
1430
  });
1426
1431
  this.titleGetter = options.getTitle;
1427
1432
  this.descriptionGetter = options.getDescription;
@@ -1434,7 +1439,7 @@ var BaseFormFeature = class extends BaseFeature {
1434
1439
  if (this.getFormAction() === "edit") {
1435
1440
  try {
1436
1441
  const initialData = await this.getInitialData(context);
1437
- if (initialData && initialData.id) {
1442
+ if (initialData) {
1438
1443
  return await this.titleGetter(context, initialData);
1439
1444
  }
1440
1445
  } catch (error) {
@@ -1454,7 +1459,7 @@ var BaseFormFeature = class extends BaseFeature {
1454
1459
  if (this.getFormAction() === "edit") {
1455
1460
  try {
1456
1461
  const initialData = await this.getInitialData(context);
1457
- if (initialData && initialData.id) {
1462
+ if (initialData) {
1458
1463
  return await this.descriptionGetter(context, initialData);
1459
1464
  }
1460
1465
  } catch (error) {
@@ -1478,17 +1483,13 @@ var BaseFormFeature = class extends BaseFeature {
1478
1483
  * 处理请求
1479
1484
  */
1480
1485
  async handle(context) {
1481
- const ctx = context.ctx;
1482
- this.currentFormId = void 0;
1483
- if (ctx.req.method === "GET") {
1486
+ if (context.ctx.req.method === "GET") {
1484
1487
  return this.render(context);
1485
- } else if (ctx.req.method === "POST" || ctx.req.method === "PUT" || ctx.req.method === "PATCH") {
1486
- const originalData = { ...context.body };
1487
- const nestedData = parseNestedFormData2(context.body);
1488
- let data = this.preprocessFormData(nestedData);
1489
- if (!this.schema) {
1490
- throw new Error("Schema is required for form validation");
1491
- }
1488
+ }
1489
+ const formData = this.preprocessFormData(context.body);
1490
+ const nestedData = parseNestedFormData2(formData);
1491
+ let data = this.preprocessFormData(nestedData);
1492
+ if (this.schema) {
1492
1493
  const parseResult = this.schema.safeParse(data);
1493
1494
  if (!parseResult.success) {
1494
1495
  const issues = parseResult.error.issues || [];
@@ -1497,35 +1498,33 @@ var BaseFormFeature = class extends BaseFeature {
1497
1498
  const errorMessage = firstError.message;
1498
1499
  const errorText = fieldName ? `${fieldName}: ${errorMessage}` : errorMessage;
1499
1500
  context.sendError("\u9A8C\u8BC1\u5931\u8D25", errorText);
1500
- return this.render(context, originalData);
1501
- }
1502
- const item = await this.handleSubmit(
1503
- context,
1504
- parseResult.data
1505
- );
1506
- if (!item) {
1507
- context.sendError(
1508
- this.getFormAction() === "create" ? "\u521B\u5EFA\u5931\u8D25" : "\u66F4\u65B0\u5931\u8D25",
1509
- "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"
1510
- );
1511
- return this.render(context, originalData);
1501
+ return null;
1512
1502
  }
1513
- const actionText = this.getFormAction() === "create" ? "\u521B\u5EFA" : "\u66F4\u65B0";
1514
- context.sendSuccess(
1515
- `${actionText}\u6210\u529F`,
1516
- `${context.model.getMetadata().title}\u5DF2\u6210\u529F${actionText}`
1503
+ data = parseResult.data;
1504
+ }
1505
+ const item = await this.handleSubmit(context, data);
1506
+ if (!item) {
1507
+ context.sendError(
1508
+ this.getFormAction() === "create" ? "\u521B\u5EFA\u5931\u8D25" : "\u66F4\u65B0\u5931\u8D25",
1509
+ "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"
1517
1510
  );
1518
- if (context.isDialog) {
1519
- context.setRefresh(true);
1520
- if (context.redirectUrl) {
1521
- context.redirectUrl = void 0;
1522
- }
1523
- return null;
1524
- } else {
1525
- const redirectUrl = this.getSuccessRedirectUrl(context, item);
1526
- context.redirect(redirectUrl);
1527
- return null;
1511
+ return null;
1512
+ }
1513
+ const actionText = this.getFormAction() === "create" ? "\u521B\u5EFA" : "\u66F4\u65B0";
1514
+ context.sendSuccess(
1515
+ `${actionText}\u6210\u529F`,
1516
+ `${context.model.getMetadata().title}\u5DF2\u6210\u529F${actionText}`
1517
+ );
1518
+ if (context.isDialog) {
1519
+ context.setRefresh(true);
1520
+ if (context.redirectUrl) {
1521
+ context.redirectUrl = void 0;
1528
1522
  }
1523
+ return null;
1524
+ } else {
1525
+ const redirectUrl = this.getSuccessRedirectUrl(context, item);
1526
+ context.redirect(redirectUrl);
1527
+ return null;
1529
1528
  }
1530
1529
  }
1531
1530
  formFieldNames;
@@ -1534,15 +1533,9 @@ var BaseFormFeature = class extends BaseFeature {
1534
1533
  * 渲染表单页面
1535
1534
  */
1536
1535
  async render(context, initialData) {
1537
- let formData;
1538
- if (this.getFormAction() === "edit") {
1539
- if (initialData) {
1540
- formData = initialData;
1541
- } else {
1542
- formData = await this.getInitialData(context);
1543
- }
1544
- } else {
1545
- formData = initialData;
1536
+ let formData = initialData;
1537
+ if (!initialData) {
1538
+ formData = await this.getInitialData(context);
1546
1539
  }
1547
1540
  let submitUrl = this.getSubmitUrl(context);
1548
1541
  if (context.isDialog) {
@@ -1552,12 +1545,9 @@ var BaseFormFeature = class extends BaseFeature {
1552
1545
  }
1553
1546
  const method = this.getFormAction() === "create" ? "post" : "put";
1554
1547
  const formId = this.getFormId(context);
1548
+ let formGroups = [];
1555
1549
  if (this.groups && this.groups.length > 0) {
1556
- if (!this.schema) {
1557
- throw new Error("Schema is required when using groups");
1558
- }
1559
- const schema = this.schema;
1560
- const groupSchemas = this.groups.map((group) => {
1550
+ formGroups = this.groups.map((group) => {
1561
1551
  const pickObject = group.fields.reduce(
1562
1552
  (acc, fieldName) => {
1563
1553
  acc[fieldName] = true;
@@ -1567,39 +1557,27 @@ var BaseFormFeature = class extends BaseFeature {
1567
1557
  );
1568
1558
  return {
1569
1559
  label: group.label,
1570
- schema: schema.pick(pickObject),
1571
- fields: group.fields
1560
+ schema: this.schema.pick(pickObject),
1561
+ fields: group.fields.map((name) => {
1562
+ const field = this.fields.find((f) => f.name === name);
1563
+ return field;
1564
+ })
1572
1565
  };
1573
1566
  });
1574
- const groupFields = groupSchemas.map(
1575
- ({ label, schema: schema2, fields: fieldNames }) => {
1576
- const groupFields2 = parseSchemaToFields(schema2);
1577
- const formFields = modelFieldsToFormFields(groupFields2);
1578
- return {
1579
- label,
1580
- fields: formFields
1581
- };
1582
- }
1583
- );
1584
- return /* @__PURE__ */ jsxRuntime.jsx(
1585
- FormPage,
1567
+ } else {
1568
+ const formFields = !this.formFieldNames ? this.fields : this.fields.filter((f) => this.formFieldNames?.includes(f.name));
1569
+ formGroups = [
1586
1570
  {
1587
- groups: groupFields,
1588
- submitUrl,
1589
- method,
1590
- initialData: formData,
1591
- formId,
1592
- isDialog: context.isDialog,
1593
- formFieldRenderers: this.formFieldRenderers
1571
+ label: "\u9ED8\u8BA4\u5206\u7EC4",
1572
+ fields: formFields,
1573
+ schema: this.schema
1594
1574
  }
1595
- );
1575
+ ];
1596
1576
  }
1597
- const filteredFields = this.formFieldNames ? filterFieldsByNames(this.fields || [], this.formFieldNames) : this.fields || [];
1598
- const fields = modelFieldsToFormFields(filteredFields);
1599
1577
  return /* @__PURE__ */ jsxRuntime.jsx(
1600
1578
  FormPage,
1601
1579
  {
1602
- fields,
1580
+ groups: formGroups,
1603
1581
  submitUrl,
1604
1582
  method,
1605
1583
  initialData: formData,
@@ -1800,90 +1778,259 @@ function Card(props) {
1800
1778
  }
1801
1779
  );
1802
1780
  }
1803
- function renderDefaultValue(value) {
1781
+ function renderEmptyValue() {
1782
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "-" });
1783
+ }
1784
+ function renderBoolean(value) {
1785
+ return /* @__PURE__ */ jsxRuntime.jsx(
1786
+ "span",
1787
+ {
1788
+ className: `px-2 py-1 rounded text-sm font-medium ${value ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-600"}`,
1789
+ children: value ? "\u662F" : "\u5426"
1790
+ }
1791
+ );
1792
+ }
1793
+ function renderNumber(value) {
1794
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: value.toLocaleString() });
1795
+ }
1796
+ function renderDate(value) {
1797
+ const date = value instanceof Date ? value : new Date(value);
1798
+ if (isNaN(date.getTime())) {
1799
+ return renderEmptyValue();
1800
+ }
1801
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: date.toLocaleString() });
1802
+ }
1803
+ function renderSimpleArray(value) {
1804
+ if (value.length === 0) {
1805
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "\u6682\u65E0\u6570\u636E" });
1806
+ }
1807
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
1808
+ "span",
1809
+ {
1810
+ className: "px-2 py-1 bg-gray-100 text-gray-700 rounded text-sm",
1811
+ children: String(item)
1812
+ },
1813
+ index
1814
+ )) });
1815
+ }
1816
+ function renderObjectArray(value) {
1817
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-600", children: [
1818
+ "\u5305\u542B ",
1819
+ value.length,
1820
+ " \u9879\uFF08\u5BF9\u8C61\u6570\u7EC4\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\uFF09"
1821
+ ] });
1822
+ }
1823
+ function renderObject(value) {
1824
+ return /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-gray-50 border border-gray-200 rounded p-3 text-xs overflow-x-auto", children: JSON.stringify(value, null, 2) });
1825
+ }
1826
+ function DetailFieldRenderer(props) {
1827
+ const { value, field } = props;
1804
1828
  if (value === null || value === void 0) {
1805
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "-" });
1829
+ return renderEmptyValue();
1830
+ }
1831
+ if (field) {
1832
+ switch (field.type) {
1833
+ case "checkbox":
1834
+ if (typeof value === "boolean") {
1835
+ return renderBoolean(value);
1836
+ }
1837
+ if (value === "true" || value === "1" || value === "on" || String(value).toLowerCase() === "true") {
1838
+ return renderBoolean(true);
1839
+ }
1840
+ if (value === "false" || value === "0" || String(value).toLowerCase() === "false") {
1841
+ return renderBoolean(false);
1842
+ }
1843
+ break;
1844
+ case "number":
1845
+ if (typeof value === "number") {
1846
+ return renderNumber(value);
1847
+ }
1848
+ const numValue = Number(value);
1849
+ if (!isNaN(numValue)) {
1850
+ return renderNumber(numValue);
1851
+ }
1852
+ break;
1853
+ case "date":
1854
+ if (value instanceof Date) {
1855
+ return renderDate(value);
1856
+ }
1857
+ if (typeof value === "string") {
1858
+ return renderDate(value);
1859
+ }
1860
+ break;
1861
+ case "email":
1862
+ case "url":
1863
+ if (typeof value === "string" && value.trim()) {
1864
+ return /* @__PURE__ */ jsxRuntime.jsx(
1865
+ "a",
1866
+ {
1867
+ href: value,
1868
+ target: "_blank",
1869
+ rel: "noopener noreferrer",
1870
+ className: "text-blue-600 hover:text-blue-800 hover:underline",
1871
+ children: value
1872
+ }
1873
+ );
1874
+ }
1875
+ break;
1876
+ case "select":
1877
+ if (field.options && field.options.length > 0) {
1878
+ const option = field.options.find(
1879
+ (opt) => String(opt.value) === String(value)
1880
+ );
1881
+ if (option) {
1882
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: option.label });
1883
+ }
1884
+ }
1885
+ break;
1886
+ }
1806
1887
  }
1807
1888
  if (Array.isArray(value)) {
1808
1889
  if (value.length === 0) {
1809
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "\u6682\u65E0\u6570\u636E" });
1890
+ return renderEmptyValue();
1810
1891
  }
1811
1892
  if (value.length > 0 && typeof value[0] === "object" && value[0] !== null) {
1812
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-600", children: [
1813
- "\u5305\u542B ",
1814
- value.length,
1815
- " \u9879\uFF08\u5BF9\u8C61\u6570\u7EC4\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\uFF09"
1893
+ return renderObjectArray(value);
1894
+ }
1895
+ return renderSimpleArray(value);
1896
+ }
1897
+ if (typeof value === "object") {
1898
+ if (value instanceof Date) {
1899
+ return renderDate(value);
1900
+ }
1901
+ return renderObject(value);
1902
+ }
1903
+ if (typeof value === "boolean") {
1904
+ return renderBoolean(value);
1905
+ }
1906
+ if (typeof value === "number") {
1907
+ return renderNumber(value);
1908
+ }
1909
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: String(value) });
1910
+ }
1911
+ function ColumnRenderer(value, field) {
1912
+ if (value === null || value === void 0) {
1913
+ return renderEmptyValue();
1914
+ }
1915
+ if (field) {
1916
+ switch (field.type) {
1917
+ case "checkbox":
1918
+ if (typeof value === "boolean") {
1919
+ return renderBoolean(value);
1920
+ }
1921
+ if (value === "true" || value === "1" || value === "on" || String(value).toLowerCase() === "true") {
1922
+ return renderBoolean(true);
1923
+ }
1924
+ if (value === "false" || value === "0" || String(value).toLowerCase() === "false") {
1925
+ return renderBoolean(false);
1926
+ }
1927
+ break;
1928
+ case "number":
1929
+ if (typeof value === "number") {
1930
+ return renderNumber(value);
1931
+ }
1932
+ const numValue = Number(value);
1933
+ if (!isNaN(numValue)) {
1934
+ return renderNumber(numValue);
1935
+ }
1936
+ break;
1937
+ case "date":
1938
+ if (value instanceof Date) {
1939
+ return renderDate(value);
1940
+ }
1941
+ if (typeof value === "string") {
1942
+ return renderDate(value);
1943
+ }
1944
+ break;
1945
+ case "email":
1946
+ case "url":
1947
+ if (typeof value === "string" && value.trim()) {
1948
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-blue-600", children: value });
1949
+ }
1950
+ break;
1951
+ case "select":
1952
+ if (field.options && field.options.length > 0) {
1953
+ const option = field.options.find(
1954
+ (opt) => String(opt.value) === String(value)
1955
+ );
1956
+ if (option) {
1957
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: option.label });
1958
+ }
1959
+ }
1960
+ break;
1961
+ case "textarea":
1962
+ if (typeof value === "string" && value.length > 50) {
1963
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { title: value, className: "truncate max-w-xs block", children: [
1964
+ value.substring(0, 50),
1965
+ "..."
1966
+ ] });
1967
+ }
1968
+ break;
1969
+ }
1970
+ }
1971
+ if (Array.isArray(value)) {
1972
+ if (value.length === 0) {
1973
+ return renderEmptyValue();
1974
+ }
1975
+ if (value.length > 0 && typeof value[0] !== "object") {
1976
+ const displayItems = value.slice(0, 3).map(String);
1977
+ const remaining = value.length - 3;
1978
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", children: [
1979
+ displayItems.join(", "),
1980
+ remaining > 0 && ` +${remaining}`
1816
1981
  ] });
1817
1982
  }
1818
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
1819
- "span",
1820
- {
1821
- className: "px-2 py-1 bg-gray-100 text-gray-700 rounded text-sm",
1822
- children: String(item)
1823
- },
1824
- index
1825
- )) });
1983
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-600", children: [
1984
+ value.length,
1985
+ " \u9879"
1986
+ ] });
1826
1987
  }
1827
1988
  if (typeof value === "object") {
1828
1989
  if (value instanceof Date) {
1829
- return /* @__PURE__ */ jsxRuntime.jsx("span", { children: value.toLocaleString() });
1990
+ return renderDate(value);
1830
1991
  }
1831
- return /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-gray-50 border border-gray-200 rounded p-3 text-xs overflow-x-auto", children: JSON.stringify(value, null, 2) });
1992
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500 text-sm", children: "[\u5BF9\u8C61]" });
1832
1993
  }
1833
1994
  if (typeof value === "boolean") {
1834
- return /* @__PURE__ */ jsxRuntime.jsx(
1835
- "span",
1836
- {
1837
- className: `px-2 py-1 rounded text-sm font-medium ${value ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-600"}`,
1838
- children: value ? "\u662F" : "\u5426"
1839
- }
1840
- );
1995
+ return renderBoolean(value);
1841
1996
  }
1842
1997
  if (typeof value === "number") {
1843
- return /* @__PURE__ */ jsxRuntime.jsx("span", { children: value.toLocaleString() });
1998
+ return renderNumber(value);
1999
+ }
2000
+ if (typeof value === "string" && value.length > 50) {
2001
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { title: value, className: "truncate max-w-xs block", children: [
2002
+ value.substring(0, 50),
2003
+ "..."
2004
+ ] });
1844
2005
  }
1845
2006
  return /* @__PURE__ */ jsxRuntime.jsx("span", { children: String(value) });
1846
2007
  }
1847
- function renderField(field, value, item) {
1848
- let content;
1849
- if (field.render) {
1850
- const rendered = field.render(value, item);
1851
- if (rendered === null || rendered === void 0) {
1852
- content = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "-" });
1853
- } else if (typeof rendered === "string" || typeof rendered === "number" || typeof rendered === "boolean") {
1854
- content = /* @__PURE__ */ jsxRuntime.jsx("span", { children: String(rendered) });
1855
- } else {
1856
- content = rendered;
2008
+ function getFieldLabel(fieldName, fieldLabels) {
2009
+ return fieldLabels?.[fieldName] || fieldName;
2010
+ }
2011
+ function getFieldRenderer(fieldName, fieldRenderers, fieldDefinitions) {
2012
+ if (fieldRenderers) {
2013
+ const customRenderer = fieldRenderers[fieldName];
2014
+ if (customRenderer) {
2015
+ return customRenderer;
1857
2016
  }
1858
- } else {
1859
- content = renderDefaultValue(value);
1860
2017
  }
1861
- return /* @__PURE__ */ jsxRuntime.jsxs(
1862
- "div",
1863
- {
1864
- className: "\r\n px-4 py-4 sm:px-6 sm:py-5\r\n flex flex-col\r\n hover:bg-gray-50/50 transition-colors duration-150\r\n group\r\n gap-1.5\r\n ",
1865
- children: [
1866
- /* @__PURE__ */ jsxRuntime.jsx(
1867
- "dt",
1868
- {
1869
- className: "\r\n text-xs sm:text-sm font-semibold text-gray-600 sm:text-gray-700\r\n flex items-start\r\n leading-tight sm:leading-5\r\n tracking-wide\r\n ",
1870
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 uppercase sm:normal-case", children: field.label })
1871
- }
1872
- ),
1873
- /* @__PURE__ */ jsxRuntime.jsx(
1874
- "dd",
1875
- {
1876
- className: "\r\n text-sm sm:text-base text-gray-900\r\n break-words\r\n leading-relaxed\r\n min-w-0\r\n ",
1877
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0", children: content })
1878
- }
1879
- )
1880
- ]
1881
- },
1882
- field.key
1883
- );
2018
+ return ((props) => {
2019
+ const field = fieldDefinitions?.[fieldName];
2020
+ return DetailFieldRenderer({ ...props, field });
2021
+ });
2022
+ }
2023
+ function renderFieldValue(rendered) {
2024
+ if (rendered === null || rendered === void 0) {
2025
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "-" });
2026
+ }
2027
+ if (typeof rendered === "string" || typeof rendered === "number" || typeof rendered === "boolean") {
2028
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: String(rendered) });
2029
+ }
2030
+ return rendered;
1884
2031
  }
1885
2032
  function DetailPage(props) {
1886
- const { item, fields, groups } = props;
2033
+ const { item, fields, groups, fieldLabels, fieldDefinitions, fieldRenderers } = props;
1887
2034
  if (groups && groups.length > 0) {
1888
2035
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-6", children: groups.map((group, groupIndex) => /* @__PURE__ */ jsxRuntime.jsx(
1889
2036
  Card,
@@ -1892,18 +2039,78 @@ function DetailPage(props) {
1892
2039
  shadow: true,
1893
2040
  bordered: true,
1894
2041
  noPadding: true,
1895
- children: /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "divide-y divide-gray-100", children: group.fields.map((field) => {
1896
- const value = group.values[field.key];
1897
- return renderField(field, value, item);
2042
+ children: /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "divide-y divide-gray-100", children: group.fields.map((fieldName) => {
2043
+ const value = group.values[fieldName];
2044
+ const label = getFieldLabel(fieldName, fieldLabels);
2045
+ const Renderer = getFieldRenderer(fieldName, fieldRenderers, fieldDefinitions);
2046
+ const rendered = Renderer({
2047
+ value,
2048
+ item,
2049
+ name: fieldName,
2050
+ label
2051
+ });
2052
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2053
+ "div",
2054
+ {
2055
+ className: "\r\n px-4 py-4 sm:px-6 sm:py-5\r\n flex flex-col\r\n hover:bg-gray-50/50 transition-colors duration-150\r\n group\r\n gap-1.5\r\n ",
2056
+ children: [
2057
+ /* @__PURE__ */ jsxRuntime.jsx(
2058
+ "dt",
2059
+ {
2060
+ className: "\r\n text-xs sm:text-sm font-semibold text-gray-600 sm:text-gray-700\r\n flex items-start\r\n leading-tight sm:leading-5\r\n tracking-wide\r\n ",
2061
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 uppercase sm:normal-case", children: label })
2062
+ }
2063
+ ),
2064
+ /* @__PURE__ */ jsxRuntime.jsx(
2065
+ "dd",
2066
+ {
2067
+ className: "\r\n text-sm sm:text-base text-gray-900\r\n break-words\r\n leading-relaxed\r\n min-w-0\r\n ",
2068
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0", children: renderFieldValue(rendered) })
2069
+ }
2070
+ )
2071
+ ]
2072
+ },
2073
+ fieldName
2074
+ );
1898
2075
  }) })
1899
2076
  },
1900
2077
  groupIndex
1901
2078
  )) });
1902
2079
  }
1903
2080
  if (fields && fields.length > 0) {
1904
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "divide-y divide-gray-100", children: fields.map((field) => {
1905
- const value = item[field.key];
1906
- return renderField(field, value, item);
2081
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "divide-y divide-gray-100", children: fields.map((fieldName) => {
2082
+ const value = item[fieldName];
2083
+ const label = getFieldLabel(fieldName, fieldLabels);
2084
+ const Renderer = getFieldRenderer(fieldName, fieldRenderers, fieldDefinitions);
2085
+ const rendered = Renderer({
2086
+ value,
2087
+ item,
2088
+ name: fieldName,
2089
+ label
2090
+ });
2091
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2092
+ "div",
2093
+ {
2094
+ className: "\r\n px-4 py-4 sm:px-6 sm:py-5\r\n flex flex-col\r\n hover:bg-gray-50/50 transition-colors duration-150\r\n group\r\n gap-1.5\r\n ",
2095
+ children: [
2096
+ /* @__PURE__ */ jsxRuntime.jsx(
2097
+ "dt",
2098
+ {
2099
+ className: "\r\n text-xs sm:text-sm font-semibold text-gray-600 sm:text-gray-700\r\n flex items-start\r\n leading-tight sm:leading-5\r\n tracking-wide\r\n ",
2100
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 uppercase sm:normal-case", children: label })
2101
+ }
2102
+ ),
2103
+ /* @__PURE__ */ jsxRuntime.jsx(
2104
+ "dd",
2105
+ {
2106
+ className: "\r\n text-sm sm:text-base text-gray-900\r\n break-words\r\n leading-relaxed\r\n min-w-0\r\n ",
2107
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0", children: renderFieldValue(rendered) })
2108
+ }
2109
+ )
2110
+ ]
2111
+ },
2112
+ fieldName
2113
+ );
1907
2114
  }) }) });
1908
2115
  }
1909
2116
  return null;
@@ -1980,28 +2187,35 @@ var DefaultDetailFeature = class extends BaseFeature {
1980
2187
  fields: group.fields
1981
2188
  };
1982
2189
  });
1983
- const groupFields = groupSchemas.map(
1984
- ({ label, schema: schema2, fields: fieldNames }) => {
1985
- 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
- }));
1991
- return {
1992
- label,
1993
- fields: detailFields2,
1994
- values: fieldNames.reduce(
1995
- (acc, fieldName) => {
1996
- acc[fieldName] = item[fieldName];
1997
- return acc;
1998
- },
1999
- {}
2000
- )
2001
- };
2190
+ const fieldLabels2 = {};
2191
+ const fieldDefinitions2 = {};
2192
+ (this.fields || []).forEach((field) => {
2193
+ fieldLabels2[field.name] = field.label;
2194
+ fieldDefinitions2[field.name] = field;
2195
+ });
2196
+ const groupFields = groupSchemas.map(({ label, fields: fieldNames }) => {
2197
+ return {
2198
+ label,
2199
+ fields: fieldNames,
2200
+ values: fieldNames.reduce(
2201
+ (acc, fieldName) => {
2202
+ acc[fieldName] = item[fieldName];
2203
+ return acc;
2204
+ },
2205
+ {}
2206
+ )
2207
+ };
2208
+ });
2209
+ return /* @__PURE__ */ jsxRuntime.jsx(
2210
+ DetailPage,
2211
+ {
2212
+ item,
2213
+ groups: groupFields,
2214
+ fieldLabels: fieldLabels2,
2215
+ fieldDefinitions: fieldDefinitions2,
2216
+ fieldRenderers: this.fieldRenderers
2002
2217
  }
2003
2218
  );
2004
- return /* @__PURE__ */ jsxRuntime.jsx(DetailPage, { item, groups: groupFields });
2005
2219
  }
2006
2220
  const detailFields = this.detailFieldNames ? filterFieldsByNames(this.fields || [], this.detailFieldNames) : this.fields || [];
2007
2221
  if (this.detailFieldNames) {
@@ -2019,13 +2233,25 @@ var DefaultDetailFeature = class extends BaseFeature {
2019
2233
  }
2020
2234
  }
2021
2235
  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
- }));
2028
- return /* @__PURE__ */ jsxRuntime.jsx(DetailPage, { item, fields });
2236
+ const fieldLabels = {};
2237
+ const fieldDefinitions = {};
2238
+ detailFieldNames.forEach((fieldName) => {
2239
+ fieldLabels[fieldName] = getFieldLabelFromFields(this.fields || [], fieldName) || fieldName;
2240
+ const field = detailFields.find((f) => f.name === fieldName);
2241
+ if (field) {
2242
+ fieldDefinitions[fieldName] = field;
2243
+ }
2244
+ });
2245
+ return /* @__PURE__ */ jsxRuntime.jsx(
2246
+ DetailPage,
2247
+ {
2248
+ item,
2249
+ fields: detailFieldNames,
2250
+ fieldLabels,
2251
+ fieldDefinitions,
2252
+ fieldRenderers: this.fieldRenderers
2253
+ }
2254
+ );
2029
2255
  }
2030
2256
  async getActions(context) {
2031
2257
  const id = context.params.id;
@@ -2084,6 +2310,7 @@ var DefaultEditFeature = class extends BaseFormFeature {
2084
2310
  permission: options.permission || `${options.permissionPrefix}.edit`,
2085
2311
  dialogSize: options.dialogSize,
2086
2312
  closeOnBackdropClick: options.closeOnBackdropClick,
2313
+ schema: options.schema,
2087
2314
  getTitle: options.getTitle ? (context, item) => {
2088
2315
  if (item) {
2089
2316
  return options.getTitle(item, context);
@@ -2097,8 +2324,6 @@ var DefaultEditFeature = class extends BaseFormFeature {
2097
2324
  return void 0;
2098
2325
  } : void 0
2099
2326
  });
2100
- this.schema = options.schema;
2101
- this.fields = parseSchemaToFields(options.schema);
2102
2327
  this.getItem = options.getItem;
2103
2328
  this.updateItem = options.updateItem;
2104
2329
  this.formFieldNames = options.formFieldNames;
@@ -2171,7 +2396,7 @@ function FilterForm(props) {
2171
2396
  listPath,
2172
2397
  currentFilters = {}
2173
2398
  } = props;
2174
- function getFieldValue2(field) {
2399
+ function getFieldValue(field) {
2175
2400
  const value = currentFilters[field.name];
2176
2401
  if (value === null || value === void 0 || value === "") {
2177
2402
  return "";
@@ -2208,10 +2433,10 @@ function FilterForm(props) {
2208
2433
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 bg-white",
2209
2434
  "data-testid": `filter-select-${field.name}`,
2210
2435
  children: [
2211
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", selected: getFieldValue2(field) === "", children: "\u5168\u90E8" }),
2436
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", selected: getFieldValue(field) === "", children: "\u5168\u90E8" }),
2212
2437
  field.options?.map((option) => {
2213
2438
  const optionValue = String(option.value);
2214
- const isSelected = getFieldValue2(field) === optionValue;
2439
+ const isSelected = getFieldValue(field) === optionValue;
2215
2440
  return /* @__PURE__ */ jsxRuntime.jsx("option", { value: optionValue, selected: isSelected, children: option.label }, optionValue);
2216
2441
  })
2217
2442
  ]
@@ -2222,7 +2447,7 @@ function FilterForm(props) {
2222
2447
  id: `filter-${field.name}`,
2223
2448
  name: field.name,
2224
2449
  type: "date",
2225
- value: getFieldValue2(field),
2450
+ value: getFieldValue(field),
2226
2451
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 bg-white",
2227
2452
  "data-testid": `filter-input-${field.name}`
2228
2453
  }
@@ -2232,7 +2457,7 @@ function FilterForm(props) {
2232
2457
  id: `filter-${field.name}`,
2233
2458
  name: field.name,
2234
2459
  type: "number",
2235
- value: getFieldValue2(field),
2460
+ value: getFieldValue(field),
2236
2461
  placeholder: field.placeholder || `\u8BF7\u8F93\u5165${field.label}`,
2237
2462
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 bg-white",
2238
2463
  "data-testid": `filter-input-${field.name}`
@@ -2243,7 +2468,7 @@ function FilterForm(props) {
2243
2468
  id: `filter-${field.name}`,
2244
2469
  name: field.name,
2245
2470
  type: "text",
2246
- value: getFieldValue2(field),
2471
+ value: getFieldValue(field),
2247
2472
  placeholder: field.placeholder || `\u8BF7\u8F93\u5165${field.label}`,
2248
2473
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 bg-white",
2249
2474
  "data-testid": `filter-input-${field.name}`
@@ -2741,12 +2966,26 @@ var DefaultListFeature = class extends BaseFeature {
2741
2966
  const listFields = this.listFieldNames ? filterFieldsByNames(this.fields || [], this.listFieldNames) : this.fields || [];
2742
2967
  const listFieldNames = getFieldNamesFromFields(listFields);
2743
2968
  const filterFields = this.filterSchema ? modelFieldsToFormFields(parseSchemaToFields(this.filterSchema)) : [];
2744
- const columns = listFieldNames.map((fieldName) => ({
2745
- key: fieldName,
2746
- label: getFieldLabelFromFields(this.fields || [], fieldName),
2747
- render: this.columnRenderers?.[fieldName]
2748
- // 如果有自定义渲染函数则使用
2749
- }));
2969
+ const columns = listFieldNames.map((fieldName) => {
2970
+ const field = this.fields?.find((f) => f.name === fieldName);
2971
+ const label = getFieldLabelFromFields(this.fields || [], fieldName);
2972
+ const customRenderer = this.columnRenderers?.[fieldName];
2973
+ return {
2974
+ key: fieldName,
2975
+ label,
2976
+ render: (value, item) => {
2977
+ if (customRenderer) {
2978
+ return customRenderer({
2979
+ value,
2980
+ item,
2981
+ name: fieldName,
2982
+ label
2983
+ });
2984
+ }
2985
+ return ColumnRenderer(value, field);
2986
+ }
2987
+ };
2988
+ });
2750
2989
  const model = context.model;
2751
2990
  const prefix = context.prefix || "";
2752
2991
  const basePath = `${prefix}/${model.modelName}`;
@@ -3809,11 +4048,11 @@ function SortableList(props) {
3809
4048
  function StringArrayEditor(props) {
3810
4049
  const {
3811
4050
  value,
3812
- name,
3813
4051
  placeholder = "\u8BF7\u8F93\u5165\u5185\u5BB9",
3814
4052
  allowEmpty = false,
3815
4053
  rows = 1
3816
4054
  } = props;
4055
+ const name = props.name;
3817
4056
  const initialItems = value || [];
3818
4057
  const initialDataJson = JSON.stringify({
3819
4058
  items: initialItems.map((item) => item || ""),
@@ -3980,7 +4219,8 @@ function ArrayItem({
3980
4219
  ] });
3981
4220
  }
3982
4221
  function TagsEditor(props) {
3983
- const { value, name, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
4222
+ const { value, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
4223
+ const name = props.name;
3984
4224
  const initialTags = value || [];
3985
4225
  const initialDataJson = JSON.stringify({
3986
4226
  tags: initialTags.map((tag) => tag || ""),
@@ -4264,7 +4504,7 @@ function ObjectEditor(props) {
4264
4504
  const initialValueJson = JSON.stringify(initialObject);
4265
4505
  JSON.stringify(fields.map((f) => f.name));
4266
4506
  const generateField = (field) => {
4267
- const fieldId = `${name}-${field.name}`;
4507
+ const fieldId = `${String(name)}-${field.name}`;
4268
4508
  const fieldValue = initialObject[field.name];
4269
4509
  const fieldValueStr = fieldValue === void 0 || fieldValue === null ? "" : typeof fieldValue === "object" ? JSON.stringify(fieldValue) : String(fieldValue);
4270
4510
  const requiredAttr = field.required ? "required" : "";
@@ -5334,20 +5574,33 @@ var HtmxAdminPlugin = class {
5334
5574
  };
5335
5575
 
5336
5576
  exports.BaseFeature = BaseFeature;
5577
+ exports.BaseFormFeature = BaseFormFeature;
5578
+ exports.Breadcrumb = Breadcrumb;
5579
+ exports.Button = Button;
5580
+ exports.Card = Card;
5581
+ exports.ColumnRenderer = ColumnRenderer;
5337
5582
  exports.CustomFeature = CustomFeature;
5338
5583
  exports.DefaultCreateFeature = DefaultCreateFeature;
5339
5584
  exports.DefaultDeleteFeature = DefaultDeleteFeature;
5340
5585
  exports.DefaultDetailFeature = DefaultDetailFeature;
5341
5586
  exports.DefaultEditFeature = DefaultEditFeature;
5342
5587
  exports.DefaultListFeature = DefaultListFeature;
5588
+ exports.DetailFieldRenderer = DetailFieldRenderer;
5343
5589
  exports.Dialog = Dialog;
5590
+ exports.EmptyState = EmptyState;
5344
5591
  exports.ErrorAlert = ErrorAlert;
5592
+ exports.FilterForm = FilterForm;
5593
+ exports.Header = Header;
5345
5594
  exports.HtmxAdminPlugin = HtmxAdminPlugin;
5346
5595
  exports.LoadingBar = LoadingBar;
5347
5596
  exports.ObjectEditor = ObjectEditor;
5348
5597
  exports.PageModel = PageModel;
5598
+ exports.Pagination = Pagination;
5599
+ exports.PermissionDeniedContent = PermissionDeniedContent;
5600
+ exports.PermissionDeniedPage = PermissionDeniedPage;
5349
5601
  exports.SortableList = SortableList;
5350
5602
  exports.StringArrayEditor = StringArrayEditor;
5603
+ exports.Table = Table;
5351
5604
  exports.TagsEditor = TagsEditor;
5352
5605
  exports.checkUserPermission = checkUserPermission;
5353
5606
  exports.getUserInfo = getUserInfo;