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