@wordrhyme/auto-crud 1.0.0 → 1.0.2

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
@@ -1408,7 +1408,12 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
1408
1408
  const [open, setOpen] = React.useState(false);
1409
1409
  const addButtonRef = React.useRef(null);
1410
1410
  const columns = React.useMemo(() => {
1411
- return table.getAllColumns().filter((column) => column.columnDef.enableColumnFilter);
1411
+ return table.getAllColumns().filter((column) => {
1412
+ if (!column.getCanFilter()) return false;
1413
+ const modes = column.columnDef.meta?.modes;
1414
+ if (modes && !modes.includes("advanced")) return false;
1415
+ return true;
1416
+ });
1412
1417
  }, [table]);
1413
1418
  const [filters, setFilters] = useReadableFilters(columns, { debounceMs });
1414
1419
  const debouncedSetFilters = useDebouncedCallback(setFilters, debounceMs);
@@ -1890,7 +1895,12 @@ const REMOVE_FILTER_SHORTCUTS = ["backspace", "delete"];
1890
1895
  function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = THROTTLE_MS$1, shallow = true, disabled,...props }) {
1891
1896
  const id = React.useId();
1892
1897
  const columns = React.useMemo(() => {
1893
- return table.getAllColumns().filter((column) => column.columnDef.enableColumnFilter);
1898
+ return table.getAllColumns().filter((column) => {
1899
+ if (!column.columnDef.enableColumnFilter) return false;
1900
+ const modes = column.columnDef.meta?.modes;
1901
+ if (modes && !modes.includes("command")) return false;
1902
+ return true;
1903
+ });
1894
1904
  }, [table]);
1895
1905
  const [open, setOpen] = React.useState(false);
1896
1906
  const [selectedColumn, setSelectedColumn] = React.useState(null);
@@ -2647,7 +2657,7 @@ function DataTableViewOptions({ table, disabled,...props }) {
2647
2657
  //#endregion
2648
2658
  //#region src/components/auto-crud/auto-table-simple-filters.tsx
2649
2659
  function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange }) {
2650
- const columns = React.useMemo(() => table.getAllColumns().filter((col) => col.columnDef.enableColumnFilter), [table]);
2660
+ const columns = React.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()), [table]);
2651
2661
  const queryStateOptions = table.options.meta?.queryStateOptions;
2652
2662
  const [queryFilters, setQueryFilters] = useReadableFilters(columns, {
2653
2663
  ...queryStateOptions,
@@ -2667,9 +2677,10 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2667
2677
  React.useEffect(() => {
2668
2678
  setMounted(true);
2669
2679
  }, []);
2670
- const getFilterValue = (columnId) => {
2671
- return filters.find((f) => f.id === columnId)?.value;
2672
- };
2680
+ const filterMap = React.useMemo(() => new Map(filters.map((f) => [f.id, f])), [filters]);
2681
+ const getFilterValue = React.useCallback((columnId) => {
2682
+ return filterMap.get(columnId)?.value;
2683
+ }, [filterMap]);
2673
2684
  const updateFilter = React.useCallback((columnId, value) => {
2674
2685
  const column = columns.find((c) => c.id === columnId);
2675
2686
  if (!column) return;
@@ -2692,7 +2703,14 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2692
2703
  setFilters
2693
2704
  ]);
2694
2705
  const hasFilters = filters.length > 0;
2695
- const filterColumns = React.useMemo(() => columns.filter((col) => col.columnDef.meta?.variant && col.columnDef.enableColumnFilter !== false), [columns]);
2706
+ const filterColumns = React.useMemo(() => columns.filter((col) => {
2707
+ const meta = col.columnDef.meta;
2708
+ const enableFilter = col.columnDef.enableColumnFilter !== false;
2709
+ if (!meta?.variant || !enableFilter) return false;
2710
+ const modes = meta.modes;
2711
+ if (modes && !modes.includes("simple")) return false;
2712
+ return true;
2713
+ }), [columns]);
2696
2714
  const [expanded, setExpanded] = React.useState(false);
2697
2715
  const [visibleCount, setVisibleCount] = React.useState(null);
2698
2716
  const containerRef = React.useRef(null);
@@ -2832,7 +2850,7 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2832
2850
  }
2833
2851
  function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
2834
2852
  const [open, setOpen] = React.useState(false);
2835
- const selectedValues = new Set(value);
2853
+ const selectedValues = React.useMemo(() => new Set(value), [value]);
2836
2854
  const onItemSelect = (option, isSelected) => {
2837
2855
  if (multiple) {
2838
2856
  const newValues = new Set(selectedValues);
@@ -2863,6 +2881,12 @@ function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
2863
2881
  tabIndex: 0,
2864
2882
  className: "rounded-sm opacity-70 hover:opacity-100",
2865
2883
  onClick: onReset,
2884
+ onKeyDown: (e) => {
2885
+ if (e.key === "Enter" || e.key === " ") {
2886
+ e.preventDefault();
2887
+ onReset(e);
2888
+ }
2889
+ },
2866
2890
  children: /* @__PURE__ */ jsx(XCircle, {})
2867
2891
  }) : /* @__PURE__ */ jsx(PlusCircle, {}),
2868
2892
  title,
@@ -2970,6 +2994,12 @@ function SimpleSliderFilter({ title, range, unit, value, onChange }) {
2970
2994
  tabIndex: 0,
2971
2995
  className: "rounded-sm opacity-70 hover:opacity-100",
2972
2996
  onClick: onReset,
2997
+ onKeyDown: (e) => {
2998
+ if (e.key === "Enter" || e.key === " ") {
2999
+ e.preventDefault();
3000
+ onReset(e);
3001
+ }
3002
+ },
2973
3003
  children: /* @__PURE__ */ jsx(XCircle, {})
2974
3004
  }) : /* @__PURE__ */ jsx(PlusCircle, {}),
2975
3005
  /* @__PURE__ */ jsx("span", { children: title }),
@@ -3093,6 +3123,12 @@ function SimpleDateFilter({ title, multiple, value, onChange }) {
3093
3123
  tabIndex: 0,
3094
3124
  className: "rounded-sm opacity-70 hover:opacity-100",
3095
3125
  onClick: onReset,
3126
+ onKeyDown: (e) => {
3127
+ if (e.key === "Enter" || e.key === " ") {
3128
+ e.preventDefault();
3129
+ onReset(e);
3130
+ }
3131
+ },
3096
3132
  children: /* @__PURE__ */ jsx(XCircle, {})
3097
3133
  }) : /* @__PURE__ */ jsx(CalendarIcon, {}),
3098
3134
  /* @__PURE__ */ jsx("span", { children: title }),
@@ -3705,33 +3741,47 @@ function inferFilterVariantByFieldName(fieldName) {
3705
3741
 
3706
3742
  //#endregion
3707
3743
  //#region src/lib/schema-bridge/zod-to-columns.tsx
3744
+ const parsedFieldCache = /* @__PURE__ */ new WeakMap();
3745
+ const baseTableSchemaCache = /* @__PURE__ */ new WeakMap();
3708
3746
  /**
3709
3747
  * 解析 Zod 字段类型
3710
3748
  */
3711
3749
  function parseZodField(schema) {
3750
+ const cached = parsedFieldCache.get(schema);
3751
+ if (cached) return cached;
3752
+ const finalize = (result) => {
3753
+ parsedFieldCache.set(schema, result);
3754
+ return result;
3755
+ };
3712
3756
  const def = schema._zod?.def ?? schema._def;
3713
- const testNull = schema.safeParse(null);
3714
- const required = !schema.safeParse(void 0).success && !testNull.success;
3715
3757
  const typeName = def?.typeName;
3716
- if (def?.type === "nullable" || def?.type === "optional" || typeName === "ZodNullable" || typeName === "ZodOptional") {
3758
+ const isOptional = def?.type === "optional" || typeName === "ZodOptional";
3759
+ const isNullable = def?.type === "nullable" || typeName === "ZodNullable";
3760
+ if (isOptional || isNullable) {
3717
3761
  const innerType = def?.innerType;
3718
- if (innerType) return {
3762
+ if (innerType) return finalize({
3719
3763
  ...parseZodField(innerType),
3720
3764
  required: false
3721
- };
3765
+ });
3722
3766
  }
3723
- if (def?.type === "boolean") return {
3767
+ let required;
3768
+ if (isOptional || isNullable) required = false;
3769
+ else {
3770
+ const testNull = schema.safeParse(null);
3771
+ required = !schema.safeParse(void 0).success && !testNull.success;
3772
+ }
3773
+ if (def?.type === "boolean") return finalize({
3724
3774
  type: "boolean",
3725
3775
  required
3726
- };
3727
- if (def?.type === "number") return {
3776
+ });
3777
+ if (def?.type === "number") return finalize({
3728
3778
  type: "number",
3729
3779
  required
3730
- };
3731
- if (def?.type === "date") return {
3780
+ });
3781
+ if (def?.type === "date") return finalize({
3732
3782
  type: "date",
3733
3783
  required
3734
- };
3784
+ });
3735
3785
  if (def?.type === "enum") {
3736
3786
  let enumValues$1;
3737
3787
  if (def?.entries) {
@@ -3742,20 +3792,20 @@ function parseZodField(schema) {
3742
3792
  const values = def.values;
3743
3793
  if (Array.isArray(values)) enumValues$1 = values.map(String);
3744
3794
  }
3745
- if (enumValues$1 && enumValues$1.length > 0) return {
3795
+ if (enumValues$1 && enumValues$1.length > 0) return finalize({
3746
3796
  type: "enum",
3747
3797
  required,
3748
3798
  enumValues: enumValues$1
3749
- };
3799
+ });
3750
3800
  }
3751
- if (def?.type === "string") return {
3801
+ if (def?.type === "string") return finalize({
3752
3802
  type: "string",
3753
3803
  required
3754
- };
3755
- if (typeName === "ZodBoolean") return {
3804
+ });
3805
+ if (typeName === "ZodBoolean") return finalize({
3756
3806
  type: "boolean",
3757
3807
  required
3758
- };
3808
+ });
3759
3809
  let enumValues;
3760
3810
  if (def?.entries) {
3761
3811
  const entries = def.entries;
@@ -3765,51 +3815,51 @@ function parseZodField(schema) {
3765
3815
  const values = def.values;
3766
3816
  if (Array.isArray(values)) enumValues = values.map(String);
3767
3817
  }
3768
- if (enumValues && Array.isArray(enumValues) && enumValues.length > 0) return {
3818
+ if (enumValues && Array.isArray(enumValues) && enumValues.length > 0) return finalize({
3769
3819
  type: "enum",
3770
3820
  required,
3771
3821
  enumValues
3772
- };
3773
- if (typeName === "ZodNumber") return {
3822
+ });
3823
+ if (typeName === "ZodNumber") return finalize({
3774
3824
  type: "number",
3775
3825
  required
3776
- };
3777
- if (typeName === "ZodDate") return {
3826
+ });
3827
+ if (typeName === "ZodDate") return finalize({
3778
3828
  type: "date",
3779
3829
  required
3780
- };
3781
- if (typeName === "ZodArray") return {
3830
+ });
3831
+ if (typeName === "ZodArray") return finalize({
3782
3832
  type: "array",
3783
3833
  required
3784
- };
3785
- if (typeName === "ZodObject") return {
3834
+ });
3835
+ if (typeName === "ZodObject") return finalize({
3786
3836
  type: "object",
3787
3837
  required
3788
- };
3838
+ });
3789
3839
  const testString = schema.safeParse("test");
3790
3840
  const testNumber = schema.safeParse(123);
3791
3841
  const testBoolean = schema.safeParse(true);
3792
3842
  const testDate = schema.safeParse(17040672e5);
3793
- if (testBoolean.success && !testString.success && !testNumber.success) return {
3843
+ if (testBoolean.success && !testString.success && !testNumber.success) return finalize({
3794
3844
  type: "boolean",
3795
3845
  required
3796
- };
3797
- if (testDate.success && !testString.success) return {
3846
+ });
3847
+ if (testDate.success && !testString.success) return finalize({
3798
3848
  type: "date",
3799
3849
  required
3800
- };
3801
- if (testNumber.success && !testString.success) return {
3850
+ });
3851
+ if (testNumber.success && !testString.success) return finalize({
3802
3852
  type: "number",
3803
3853
  required
3804
- };
3805
- if (schema.safeParse([]).success && !testString.success) return {
3854
+ });
3855
+ if (schema.safeParse([]).success && !testString.success) return finalize({
3806
3856
  type: "array",
3807
3857
  required
3808
- };
3809
- return {
3858
+ });
3859
+ return finalize({
3810
3860
  type: "string",
3811
3861
  required
3812
- };
3862
+ });
3813
3863
  }
3814
3864
  /**
3815
3865
  * 渲染单元格内容
@@ -3851,9 +3901,14 @@ function renderCell(value, type) {
3851
3901
  * 从 Zod Schema 创建 Table Schema (ColumnDef 数组)
3852
3902
  */
3853
3903
  function createTableSchema(schema, options) {
3904
+ const { overrides, exclude = [] } = options ?? {};
3905
+ const canCache = !(!!overrides && Object.keys(overrides).length > 0) && exclude.length === 0;
3906
+ if (canCache) {
3907
+ const cached = baseTableSchemaCache.get(schema);
3908
+ if (cached) return [...cached];
3909
+ }
3854
3910
  const shape = schema.shape;
3855
3911
  const columns = [];
3856
- const { overrides, exclude = [] } = options ?? {};
3857
3912
  for (const [key, fieldSchema] of Object.entries(shape)) {
3858
3913
  if (exclude.includes(key)) continue;
3859
3914
  const override = overrides?.[key];
@@ -3888,6 +3943,7 @@ function createTableSchema(schema, options) {
3888
3943
  ...restOverride
3889
3944
  });
3890
3945
  }
3946
+ if (canCache) baseTableSchemaCache.set(schema, columns);
3891
3947
  return columns;
3892
3948
  }
3893
3949
  /**
@@ -3916,8 +3972,7 @@ function createSelectColumn() {
3916
3972
  /**
3917
3973
  * 创建操作列
3918
3974
  */
3919
- function createActionsColumn(config) {
3920
- const { onEdit, onDelete, onView, onCopy } = config;
3975
+ function createActionsColumn(items) {
3921
3976
  return {
3922
3977
  id: "actions",
3923
3978
  cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
@@ -3928,29 +3983,14 @@ function createActionsColumn(config) {
3928
3983
  className: "flex size-8 p-0 data-[state=open]:bg-muted",
3929
3984
  children: /* @__PURE__ */ jsx(Ellipsis, { className: "size-4" })
3930
3985
  })
3931
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
3986
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
3932
3987
  align: "end",
3933
3988
  className: "w-40",
3934
- children: [
3935
- onView && /* @__PURE__ */ jsx(DropdownMenuItem, {
3936
- onClick: () => onView(row.original),
3937
- children: "查看"
3938
- }),
3939
- onEdit && /* @__PURE__ */ jsx(DropdownMenuItem, {
3940
- onClick: () => onEdit(row.original),
3941
- children: "编辑"
3942
- }),
3943
- onCopy && /* @__PURE__ */ jsx(DropdownMenuItem, {
3944
- onClick: () => onCopy(row.original),
3945
- children: "复制"
3946
- }),
3947
- (onView || onEdit || onCopy) && onDelete && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
3948
- onDelete && /* @__PURE__ */ jsx(DropdownMenuItem, {
3949
- className: "text-destructive",
3950
- onClick: () => onDelete(row.original),
3951
- children: "删除"
3952
- })
3953
- ]
3989
+ children: items.map((item, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [item.separator && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsx(DropdownMenuItem, {
3990
+ className: item.variant === "destructive" ? "text-destructive" : "",
3991
+ onClick: () => item.onClick(row.original),
3992
+ children: item.label
3993
+ })] }, i))
3954
3994
  })] }),
3955
3995
  size: 40
3956
3996
  };
@@ -4404,6 +4444,13 @@ function useDataTable(props) {
4404
4444
  filterableColumns,
4405
4445
  enableAdvancedFilter
4406
4446
  ]);
4447
+ const getCoreRowModelFn = React.useMemo(() => getCoreRowModel(), []);
4448
+ const getFilteredRowModelFn = React.useMemo(() => getFilteredRowModel(), []);
4449
+ const getPaginationRowModelFn = React.useMemo(() => getPaginationRowModel(), []);
4450
+ const getSortedRowModelFn = React.useMemo(() => getSortedRowModel(), []);
4451
+ const getFacetedRowModelFn = React.useMemo(() => getFacetedRowModel(), []);
4452
+ const getFacetedUniqueValuesFn = React.useMemo(() => getFacetedUniqueValues(), []);
4453
+ const getFacetedMinMaxValuesFn = React.useMemo(() => getFacetedMinMaxValues(), []);
4407
4454
  const table = useReactTable({
4408
4455
  ...tableProps,
4409
4456
  columns,
@@ -4426,13 +4473,13 @@ function useDataTable(props) {
4426
4473
  onSortingChange,
4427
4474
  onColumnFiltersChange,
4428
4475
  onColumnVisibilityChange: setColumnVisibility,
4429
- getCoreRowModel: getCoreRowModel(),
4430
- getFilteredRowModel: getFilteredRowModel(),
4431
- getPaginationRowModel: getPaginationRowModel(),
4432
- getSortedRowModel: getSortedRowModel(),
4433
- getFacetedRowModel: getFacetedRowModel(),
4434
- getFacetedUniqueValues: getFacetedUniqueValues(),
4435
- getFacetedMinMaxValues: getFacetedMinMaxValues(),
4476
+ getCoreRowModel: getCoreRowModelFn,
4477
+ getFilteredRowModel: getFilteredRowModelFn,
4478
+ getPaginationRowModel: getPaginationRowModelFn,
4479
+ getSortedRowModel: getSortedRowModelFn,
4480
+ getFacetedRowModel: getFacetedRowModelFn,
4481
+ getFacetedUniqueValues: getFacetedUniqueValuesFn,
4482
+ getFacetedMinMaxValues: getFacetedMinMaxValuesFn,
4436
4483
  manualPagination: true,
4437
4484
  manualSorting: true,
4438
4485
  manualFiltering: true,
@@ -4463,6 +4510,11 @@ function useDataTable(props) {
4463
4510
 
4464
4511
  //#endregion
4465
4512
  //#region src/components/auto-crud/auto-table.tsx
4513
+ const DEFAULT_MODES = [
4514
+ "simple",
4515
+ "advanced",
4516
+ "command"
4517
+ ];
4466
4518
  /** 过滤模式配置 */
4467
4519
  const filterModeConfig = {
4468
4520
  simple: {
@@ -4482,11 +4534,7 @@ const filterModeConfig = {
4482
4534
  }
4483
4535
  };
4484
4536
  function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, initialSorting, enableExport = true, onSelectedCountChange, getSelectedRows }) {
4485
- const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : [
4486
- "simple",
4487
- "advanced",
4488
- "command"
4489
- ];
4537
+ const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
4490
4538
  const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
4491
4539
  const showToggle = modes.length > 1;
4492
4540
  const { table, shallow, debounceMs, throttleMs } = useDataTable({
@@ -4508,13 +4556,18 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4508
4556
  ]),
4509
4557
  pageCount,
4510
4558
  enableAdvancedFilter: true,
4511
- initialState: {
4559
+ initialState: useMemo(() => ({
4512
4560
  sorting: initialSorting,
4513
4561
  columnPinning: pinnedColumns ?? {
4514
4562
  left: enableRowSelection ? ["select"] : [],
4515
4563
  right: actions ? ["actions"] : []
4516
4564
  }
4517
- },
4565
+ }), [
4566
+ initialSorting,
4567
+ pinnedColumns,
4568
+ enableRowSelection,
4569
+ actions
4570
+ ]),
4518
4571
  shallow: false,
4519
4572
  clearOnDefault: true
4520
4573
  });
@@ -4555,7 +4608,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4555
4608
  })
4556
4609
  })
4557
4610
  })] }) : null;
4558
- const renderFilters = () => {
4611
+ const filtersContent = useMemo(() => {
4559
4612
  switch (currentMode) {
4560
4613
  case "simple": return /* @__PURE__ */ jsx(AutoTableSimpleFilters, {
4561
4614
  table,
@@ -4575,7 +4628,13 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4575
4628
  align: "start"
4576
4629
  });
4577
4630
  }
4578
- };
4631
+ }, [
4632
+ currentMode,
4633
+ table,
4634
+ shallow,
4635
+ debounceMs,
4636
+ throttleMs
4637
+ ]);
4579
4638
  return /* @__PURE__ */ jsxs("div", {
4580
4639
  className: "space-y-4",
4581
4640
  children: [
@@ -4587,7 +4646,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4587
4646
  children: [currentMode !== "simple" && /* @__PURE__ */ jsx(DataTableSortList, {
4588
4647
  table,
4589
4648
  align: "start"
4590
- }), renderFilters()]
4649
+ }), filtersContent]
4591
4650
  }), /* @__PURE__ */ jsxs("div", {
4592
4651
  className: "flex items-center gap-2",
4593
4652
  children: [
@@ -4819,10 +4878,462 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
4819
4878
  }
4820
4879
  const AutoForm = forwardRef(AutoFormInner);
4821
4880
 
4881
+ //#endregion
4882
+ //#region src/i18n/locale.ts
4883
+ const zhCN = {
4884
+ toolbar: {
4885
+ create: "新建",
4886
+ import: "导入",
4887
+ export: "导出",
4888
+ exportSelected: (count) => `导出(${count})`
4889
+ },
4890
+ rowActions: {
4891
+ view: "查看",
4892
+ edit: "编辑",
4893
+ copy: "复制",
4894
+ delete: "删除"
4895
+ },
4896
+ viewModal: { title: "详情" },
4897
+ boolean: {
4898
+ true: "是",
4899
+ false: "否"
4900
+ },
4901
+ formModal: {
4902
+ createTitle: "新建",
4903
+ editTitle: "编辑",
4904
+ cancel: "取消",
4905
+ create: "创建",
4906
+ save: "保存",
4907
+ submitting: "处理中..."
4908
+ },
4909
+ deleteModal: {
4910
+ title: "确认删除",
4911
+ description: "此操作无法撤销。确定要删除这条记录吗?",
4912
+ cancel: "取消",
4913
+ confirm: "确认删除",
4914
+ confirming: "删除中..."
4915
+ },
4916
+ importDialog: {
4917
+ title: "导入数据",
4918
+ uploadDescription: "上传 CSV 或 JSON 文件以批量导入数据",
4919
+ previewDescription: (count) => `已解析 ${count} 条数据,确认后开始导入`,
4920
+ importingDescription: "正在导入数据...",
4921
+ doneDescription: "导入完成",
4922
+ dragHint: "拖拽文件到此处,或点击选择文件",
4923
+ formatHint: "支持 CSV、JSON 格式",
4924
+ downloadTemplate: "下载 CSV 模板",
4925
+ moreColumns: (count) => `+${count} 列`,
4926
+ previewSummary: (rows, fields, format, previewLimit) => `共 ${rows} 条数据${previewLimit ? `(预览前 ${previewLimit} 条)` : ""},${fields} 个字段,格式: ${format.toUpperCase()}`,
4927
+ reselect: "重新选择",
4928
+ confirmImport: "确认导入",
4929
+ importingRows: (count) => `正在导入 ${count} 条数据...`,
4930
+ resultCreated: "新建",
4931
+ resultUpdated: "更新",
4932
+ resultSkipped: "跳过",
4933
+ resultFailed: "验证失败",
4934
+ failedRowHeader: "行号",
4935
+ failedErrorHeader: "错误",
4936
+ close: "关闭",
4937
+ continueImport: "继续导入",
4938
+ errorNoData: "文件中没有数据行",
4939
+ errorParseFailed: "文件解析失败",
4940
+ errorImportFailed: "导入失败"
4941
+ }
4942
+ };
4943
+ const enUS = {
4944
+ toolbar: {
4945
+ create: "New",
4946
+ import: "Import",
4947
+ export: "Export",
4948
+ exportSelected: (count) => `Export (${count})`
4949
+ },
4950
+ rowActions: {
4951
+ view: "View",
4952
+ edit: "Edit",
4953
+ copy: "Copy",
4954
+ delete: "Delete"
4955
+ },
4956
+ viewModal: { title: "Details" },
4957
+ boolean: {
4958
+ true: "Yes",
4959
+ false: "No"
4960
+ },
4961
+ formModal: {
4962
+ createTitle: "New",
4963
+ editTitle: "Edit",
4964
+ cancel: "Cancel",
4965
+ create: "Create",
4966
+ save: "Save",
4967
+ submitting: "Processing..."
4968
+ },
4969
+ deleteModal: {
4970
+ title: "Confirm Delete",
4971
+ description: "This action cannot be undone. Are you sure you want to delete this record?",
4972
+ cancel: "Cancel",
4973
+ confirm: "Delete",
4974
+ confirming: "Deleting..."
4975
+ },
4976
+ importDialog: {
4977
+ title: "Import Data",
4978
+ uploadDescription: "Upload a CSV or JSON file to bulk import data",
4979
+ previewDescription: (count) => `Parsed ${count} records, confirm to start importing`,
4980
+ importingDescription: "Importing data...",
4981
+ doneDescription: "Import complete",
4982
+ dragHint: "Drag file here, or click to select",
4983
+ formatHint: "Supports CSV, JSON formats",
4984
+ downloadTemplate: "Download CSV Template",
4985
+ moreColumns: (count) => `+${count} more`,
4986
+ previewSummary: (rows, fields, format, previewLimit) => `${rows} records${previewLimit ? ` (preview first ${previewLimit})` : ""}, ${fields} fields, format: ${format.toUpperCase()}`,
4987
+ reselect: "Reselect",
4988
+ confirmImport: "Confirm Import",
4989
+ importingRows: (count) => `Importing ${count} records...`,
4990
+ resultCreated: "Created",
4991
+ resultUpdated: "Updated",
4992
+ resultSkipped: "Skipped",
4993
+ resultFailed: "Failed",
4994
+ failedRowHeader: "Row",
4995
+ failedErrorHeader: "Error",
4996
+ close: "Close",
4997
+ continueImport: "Import More",
4998
+ errorNoData: "No data rows found in file",
4999
+ errorParseFailed: "Failed to parse file",
5000
+ errorImportFailed: "Import failed"
5001
+ }
5002
+ };
5003
+ const jaJP = {
5004
+ toolbar: {
5005
+ create: "新規作成",
5006
+ import: "インポート",
5007
+ export: "エクスポート",
5008
+ exportSelected: (count) => `エクスポート(${count})`
5009
+ },
5010
+ rowActions: {
5011
+ view: "詳細",
5012
+ edit: "編集",
5013
+ copy: "コピー",
5014
+ delete: "削除"
5015
+ },
5016
+ viewModal: { title: "詳細" },
5017
+ boolean: {
5018
+ true: "はい",
5019
+ false: "いいえ"
5020
+ },
5021
+ formModal: {
5022
+ createTitle: "新規作成",
5023
+ editTitle: "編集",
5024
+ cancel: "キャンセル",
5025
+ create: "作成",
5026
+ save: "保存",
5027
+ submitting: "処理中..."
5028
+ },
5029
+ deleteModal: {
5030
+ title: "削除の確認",
5031
+ description: "この操作は元に戻せません。このレコードを削除してもよろしいですか?",
5032
+ cancel: "キャンセル",
5033
+ confirm: "削除する",
5034
+ confirming: "削除中..."
5035
+ },
5036
+ importDialog: {
5037
+ title: "データのインポート",
5038
+ uploadDescription: "CSVまたはJSONファイルをアップロードして一括インポート",
5039
+ previewDescription: (count) => `${count}件のデータを解析しました。確認してインポートを開始します`,
5040
+ importingDescription: "データをインポート中...",
5041
+ doneDescription: "インポート完了",
5042
+ dragHint: "ファイルをここにドラッグするか、クリックして選択",
5043
+ formatHint: "CSV・JSON形式に対応",
5044
+ downloadTemplate: "CSVテンプレートをダウンロード",
5045
+ moreColumns: (count) => `+${count}列`,
5046
+ previewSummary: (rows, fields, format, previewLimit) => `${rows}件${previewLimit ? `(上位${previewLimit}件プレビュー)` : ""}、${fields}フィールド、形式: ${format.toUpperCase()}`,
5047
+ reselect: "再選択",
5048
+ confirmImport: "インポートを確認",
5049
+ importingRows: (count) => `${count}件をインポート中...`,
5050
+ resultCreated: "新規作成",
5051
+ resultUpdated: "更新",
5052
+ resultSkipped: "スキップ",
5053
+ resultFailed: "失敗",
5054
+ failedRowHeader: "行番号",
5055
+ failedErrorHeader: "エラー",
5056
+ close: "閉じる",
5057
+ continueImport: "続けてインポート",
5058
+ errorNoData: "ファイルにデータ行がありません",
5059
+ errorParseFailed: "ファイルの解析に失敗しました",
5060
+ errorImportFailed: "インポートに失敗しました"
5061
+ }
5062
+ };
5063
+ const koKR = {
5064
+ toolbar: {
5065
+ create: "새로 만들기",
5066
+ import: "가져오기",
5067
+ export: "내보내기",
5068
+ exportSelected: (count) => `내보내기(${count})`
5069
+ },
5070
+ rowActions: {
5071
+ view: "보기",
5072
+ edit: "편집",
5073
+ copy: "복사",
5074
+ delete: "삭제"
5075
+ },
5076
+ viewModal: { title: "상세 정보" },
5077
+ boolean: {
5078
+ true: "예",
5079
+ false: "아니요"
5080
+ },
5081
+ formModal: {
5082
+ createTitle: "새로 만들기",
5083
+ editTitle: "편집",
5084
+ cancel: "취소",
5085
+ create: "만들기",
5086
+ save: "저장",
5087
+ submitting: "처리 중..."
5088
+ },
5089
+ deleteModal: {
5090
+ title: "삭제 확인",
5091
+ description: "이 작업은 취소할 수 없습니다. 이 레코드를 삭제하시겠습니까?",
5092
+ cancel: "취소",
5093
+ confirm: "삭제",
5094
+ confirming: "삭제 중..."
5095
+ },
5096
+ importDialog: {
5097
+ title: "데이터 가져오기",
5098
+ uploadDescription: "CSV 또는 JSON 파일을 업로드하여 일괄 가져오기",
5099
+ previewDescription: (count) => `${count}개의 데이터를 분석했습니다. 확인 후 가져오기를 시작합니다`,
5100
+ importingDescription: "데이터를 가져오는 중...",
5101
+ doneDescription: "가져오기 완료",
5102
+ dragHint: "파일을 여기에 드래그하거나 클릭하여 선택",
5103
+ formatHint: "CSV, JSON 형식 지원",
5104
+ downloadTemplate: "CSV 템플릿 다운로드",
5105
+ moreColumns: (count) => `+${count}열 더`,
5106
+ previewSummary: (rows, fields, format, previewLimit) => `${rows}개 레코드${previewLimit ? ` (상위 ${previewLimit}개 미리보기)` : ""}, ${fields}개 필드, 형식: ${format.toUpperCase()}`,
5107
+ reselect: "다시 선택",
5108
+ confirmImport: "가져오기 확인",
5109
+ importingRows: (count) => `${count}개의 레코드를 가져오는 중...`,
5110
+ resultCreated: "생성됨",
5111
+ resultUpdated: "업데이트됨",
5112
+ resultSkipped: "건너뜀",
5113
+ resultFailed: "실패",
5114
+ failedRowHeader: "행 번호",
5115
+ failedErrorHeader: "오류",
5116
+ close: "닫기",
5117
+ continueImport: "계속 가져오기",
5118
+ errorNoData: "파일에 데이터 행이 없습니다",
5119
+ errorParseFailed: "파일 파싱에 실패했습니다",
5120
+ errorImportFailed: "가져오기에 실패했습니다"
5121
+ }
5122
+ };
5123
+ const frFR = {
5124
+ toolbar: {
5125
+ create: "Nouveau",
5126
+ import: "Importer",
5127
+ export: "Exporter",
5128
+ exportSelected: (count) => `Exporter (${count})`
5129
+ },
5130
+ rowActions: {
5131
+ view: "Voir",
5132
+ edit: "Modifier",
5133
+ copy: "Copier",
5134
+ delete: "Supprimer"
5135
+ },
5136
+ viewModal: { title: "Détails" },
5137
+ boolean: {
5138
+ true: "Oui",
5139
+ false: "Non"
5140
+ },
5141
+ formModal: {
5142
+ createTitle: "Nouveau",
5143
+ editTitle: "Modifier",
5144
+ cancel: "Annuler",
5145
+ create: "Créer",
5146
+ save: "Enregistrer",
5147
+ submitting: "En cours..."
5148
+ },
5149
+ deleteModal: {
5150
+ title: "Confirmer la suppression",
5151
+ description: "Cette action est irréversible. Voulez-vous vraiment supprimer cet enregistrement ?",
5152
+ cancel: "Annuler",
5153
+ confirm: "Supprimer",
5154
+ confirming: "Suppression..."
5155
+ },
5156
+ importDialog: {
5157
+ title: "Importer des données",
5158
+ uploadDescription: "Téléchargez un fichier CSV ou JSON pour importer en masse",
5159
+ previewDescription: (count) => `${count} enregistrements analysés, confirmez pour démarrer l'import`,
5160
+ importingDescription: "Importation en cours...",
5161
+ doneDescription: "Import terminé",
5162
+ dragHint: "Glissez un fichier ici ou cliquez pour sélectionner",
5163
+ formatHint: "Formats CSV et JSON acceptés",
5164
+ downloadTemplate: "Télécharger le modèle CSV",
5165
+ moreColumns: (count) => `+${count} colonnes`,
5166
+ previewSummary: (rows, fields, format, previewLimit) => `${rows} enregistrements${previewLimit ? ` (aperçu des ${previewLimit} premiers)` : ""}, ${fields} champs, format : ${format.toUpperCase()}`,
5167
+ reselect: "Resélectionner",
5168
+ confirmImport: "Confirmer l'import",
5169
+ importingRows: (count) => `Importation de ${count} enregistrements...`,
5170
+ resultCreated: "Créés",
5171
+ resultUpdated: "Mis à jour",
5172
+ resultSkipped: "Ignorés",
5173
+ resultFailed: "Échoués",
5174
+ failedRowHeader: "Ligne",
5175
+ failedErrorHeader: "Erreur",
5176
+ close: "Fermer",
5177
+ continueImport: "Continuer l'import",
5178
+ errorNoData: "Aucune ligne de données dans le fichier",
5179
+ errorParseFailed: "Échec de l'analyse du fichier",
5180
+ errorImportFailed: "Échec de l'importation"
5181
+ }
5182
+ };
5183
+ const deDE = {
5184
+ toolbar: {
5185
+ create: "Neu",
5186
+ import: "Importieren",
5187
+ export: "Exportieren",
5188
+ exportSelected: (count) => `Exportieren (${count})`
5189
+ },
5190
+ rowActions: {
5191
+ view: "Anzeigen",
5192
+ edit: "Bearbeiten",
5193
+ copy: "Kopieren",
5194
+ delete: "Löschen"
5195
+ },
5196
+ viewModal: { title: "Details" },
5197
+ boolean: {
5198
+ true: "Ja",
5199
+ false: "Nein"
5200
+ },
5201
+ formModal: {
5202
+ createTitle: "Neu",
5203
+ editTitle: "Bearbeiten",
5204
+ cancel: "Abbrechen",
5205
+ create: "Erstellen",
5206
+ save: "Speichern",
5207
+ submitting: "Wird verarbeitet..."
5208
+ },
5209
+ deleteModal: {
5210
+ title: "Löschen bestätigen",
5211
+ description: "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie diesen Datensatz wirklich löschen?",
5212
+ cancel: "Abbrechen",
5213
+ confirm: "Löschen",
5214
+ confirming: "Wird gelöscht..."
5215
+ },
5216
+ importDialog: {
5217
+ title: "Daten importieren",
5218
+ uploadDescription: "CSV- oder JSON-Datei hochladen, um Daten massenweise zu importieren",
5219
+ previewDescription: (count) => `${count} Datensätze analysiert, bestätigen Sie den Import`,
5220
+ importingDescription: "Daten werden importiert...",
5221
+ doneDescription: "Import abgeschlossen",
5222
+ dragHint: "Datei hierher ziehen oder klicken zum Auswählen",
5223
+ formatHint: "CSV- und JSON-Formate werden unterstützt",
5224
+ downloadTemplate: "CSV-Vorlage herunterladen",
5225
+ moreColumns: (count) => `+${count} Spalten`,
5226
+ previewSummary: (rows, fields, format, previewLimit) => `${rows} Datensätze${previewLimit ? ` (Vorschau der ersten ${previewLimit})` : ""}, ${fields} Felder, Format: ${format.toUpperCase()}`,
5227
+ reselect: "Neu auswählen",
5228
+ confirmImport: "Import bestätigen",
5229
+ importingRows: (count) => `${count} Datensätze werden importiert...`,
5230
+ resultCreated: "Erstellt",
5231
+ resultUpdated: "Aktualisiert",
5232
+ resultSkipped: "Übersprungen",
5233
+ resultFailed: "Fehlgeschlagen",
5234
+ failedRowHeader: "Zeile",
5235
+ failedErrorHeader: "Fehler",
5236
+ close: "Schließen",
5237
+ continueImport: "Weiteren Import starten",
5238
+ errorNoData: "Keine Datenzeilen in der Datei",
5239
+ errorParseFailed: "Datei konnte nicht analysiert werden",
5240
+ errorImportFailed: "Import fehlgeschlagen"
5241
+ }
5242
+ };
5243
+ const esES = {
5244
+ toolbar: {
5245
+ create: "Nuevo",
5246
+ import: "Importar",
5247
+ export: "Exportar",
5248
+ exportSelected: (count) => `Exportar (${count})`
5249
+ },
5250
+ rowActions: {
5251
+ view: "Ver",
5252
+ edit: "Editar",
5253
+ copy: "Copiar",
5254
+ delete: "Eliminar"
5255
+ },
5256
+ viewModal: { title: "Detalles" },
5257
+ boolean: {
5258
+ true: "Sí",
5259
+ false: "No"
5260
+ },
5261
+ formModal: {
5262
+ createTitle: "Nuevo",
5263
+ editTitle: "Editar",
5264
+ cancel: "Cancelar",
5265
+ create: "Crear",
5266
+ save: "Guardar",
5267
+ submitting: "Procesando..."
5268
+ },
5269
+ deleteModal: {
5270
+ title: "Confirmar eliminación",
5271
+ description: "Esta acción no se puede deshacer. ¿Está seguro de que desea eliminar este registro?",
5272
+ cancel: "Cancelar",
5273
+ confirm: "Eliminar",
5274
+ confirming: "Eliminando..."
5275
+ },
5276
+ importDialog: {
5277
+ title: "Importar datos",
5278
+ uploadDescription: "Suba un archivo CSV o JSON para importar datos de forma masiva",
5279
+ previewDescription: (count) => `Se analizaron ${count} registros, confirme para iniciar la importación`,
5280
+ importingDescription: "Importando datos...",
5281
+ doneDescription: "Importación completada",
5282
+ dragHint: "Arrastre el archivo aquí o haga clic para seleccionar",
5283
+ formatHint: "Formatos CSV y JSON compatibles",
5284
+ downloadTemplate: "Descargar plantilla CSV",
5285
+ moreColumns: (count) => `+${count} columnas`,
5286
+ previewSummary: (rows, fields, format, previewLimit) => `${rows} registros${previewLimit ? ` (vista previa de los primeros ${previewLimit})` : ""}, ${fields} campos, formato: ${format.toUpperCase()}`,
5287
+ reselect: "Volver a seleccionar",
5288
+ confirmImport: "Confirmar importación",
5289
+ importingRows: (count) => `Importando ${count} registros...`,
5290
+ resultCreated: "Creados",
5291
+ resultUpdated: "Actualizados",
5292
+ resultSkipped: "Omitidos",
5293
+ resultFailed: "Fallidos",
5294
+ failedRowHeader: "Fila",
5295
+ failedErrorHeader: "Error",
5296
+ close: "Cerrar",
5297
+ continueImport: "Continuar importando",
5298
+ errorNoData: "No se encontraron filas de datos en el archivo",
5299
+ errorParseFailed: "Error al analizar el archivo",
5300
+ errorImportFailed: "Error en la importación"
5301
+ }
5302
+ };
5303
+ const BUILTIN_LOCALES = {
5304
+ "zh-CN": zhCN,
5305
+ "en-US": enUS,
5306
+ "ja-JP": jaJP,
5307
+ "ko-KR": koKR,
5308
+ "fr-FR": frFR,
5309
+ "de-DE": deDE,
5310
+ "es-ES": esES
5311
+ };
5312
+ function deepMerge(target, source) {
5313
+ const result = { ...target };
5314
+ for (const key of Object.keys(source)) {
5315
+ const sv = source[key];
5316
+ const tv = target[key];
5317
+ if (sv !== void 0 && sv !== null && typeof sv === "object" && !Array.isArray(sv) && typeof tv === "object" && tv !== null && !Array.isArray(tv)) result[key] = deepMerge(tv, sv);
5318
+ else if (sv !== void 0) result[key] = sv;
5319
+ }
5320
+ return result;
5321
+ }
5322
+ /**
5323
+ * 解析 locale prop 为完整的 AutoCrudLocale
5324
+ * - string → 内置语言(fallback: zh-CN)
5325
+ * - object → 以 zh-CN 为基础深合并
5326
+ */
5327
+ function resolveLocale(localeProp) {
5328
+ if (!localeProp) return zhCN;
5329
+ if (typeof localeProp === "string") return BUILTIN_LOCALES[localeProp] ?? zhCN;
5330
+ return deepMerge(zhCN, localeProp);
5331
+ }
5332
+
4822
5333
  //#endregion
4823
5334
  //#region src/components/auto-crud/crud-form-modal.tsx
4824
- function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, title, labelAlign, labelWidth }) {
4825
- const defaultTitle = mode === "create" ? "新建" : "编辑";
5335
+ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, title, locale = zhCN.formModal, labelAlign, labelWidth }) {
5336
+ const defaultTitle = mode === "create" ? locale.createTitle : locale.editTitle;
4826
5337
  const formRef = useRef(null);
4827
5338
  const handleSubmit = async () => {
4828
5339
  await formRef.current?.submit();
@@ -4838,12 +5349,12 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
4838
5349
  type: "button",
4839
5350
  variant: "outline",
4840
5351
  onClick: () => onOpenChange(false),
4841
- children: "取消"
5352
+ children: locale.cancel
4842
5353
  }), /* @__PURE__ */ jsx(Button, {
4843
5354
  type: "button",
4844
5355
  onClick: handleSubmit,
4845
5356
  disabled: loading,
4846
- children: loading ? "处理中..." : mode === "create" ? "创建" : "保存"
5357
+ children: loading ? locale.submitting : mode === "create" ? locale.create : locale.save
4847
5358
  })]
4848
5359
  }),
4849
5360
  children: /* @__PURE__ */ jsx(AutoForm, {
@@ -4863,7 +5374,7 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
4863
5374
 
4864
5375
  //#endregion
4865
5376
  //#region src/components/auto-crud/import-dialog.tsx
4866
- function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导入数据" }) {
5377
+ function ImportDialog({ open, onOpenChange, onImport, columns = [], title, locale = zhCN.importDialog }) {
4867
5378
  const [step, setStep] = React.useState("upload");
4868
5379
  const [parsedData, setParsedData] = React.useState(null);
4869
5380
  const [error, setError] = React.useState(null);
@@ -4886,13 +5397,13 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
4886
5397
  try {
4887
5398
  const data = await parseImportFile(file);
4888
5399
  if (data.rows.length === 0) {
4889
- setError("文件中没有数据行");
5400
+ setError(locale.errorNoData);
4890
5401
  return;
4891
5402
  }
4892
5403
  setParsedData(data);
4893
5404
  setStep("preview");
4894
5405
  } catch (e) {
4895
- setError(e instanceof Error ? e.message : "文件解析失败");
5406
+ setError(e instanceof Error ? e.message : locale.errorParseFailed);
4896
5407
  }
4897
5408
  }, []);
4898
5409
  const handleDrop = React.useCallback((e) => {
@@ -4912,7 +5423,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
4912
5423
  setResult(await onImport(parsedData.rows));
4913
5424
  setStep("result");
4914
5425
  } catch (e) {
4915
- setError(e instanceof Error ? e.message : "导入失败");
5426
+ setError(e instanceof Error ? e.message : locale.errorImportFailed);
4916
5427
  setStep("preview");
4917
5428
  }
4918
5429
  }, [parsedData, onImport]);
@@ -4932,11 +5443,11 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
4932
5443
  onOpenChange: handleOpenChange,
4933
5444
  children: /* @__PURE__ */ jsxs(DialogContent, {
4934
5445
  className: "sm:max-w-[600px]",
4935
- children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
4936
- step === "upload" && "上传 CSV 或 JSON 文件以批量导入数据",
4937
- step === "preview" && `已解析 ${parsedData?.rows.length ?? 0} 条数据,确认后开始导入`,
4938
- step === "importing" && "正在导入数据...",
4939
- step === "result" && "导入完成"
5446
+ children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: title ?? locale.title }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
5447
+ step === "upload" && locale.uploadDescription,
5448
+ step === "preview" && locale.previewDescription(parsedData?.rows.length ?? 0),
5449
+ step === "importing" && locale.importingDescription,
5450
+ step === "result" && locale.doneDescription
4940
5451
  ] })] }), /* @__PURE__ */ jsxs("div", {
4941
5452
  className: "space-y-4",
4942
5453
  children: [
@@ -4967,11 +5478,11 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
4967
5478
  }),
4968
5479
  /* @__PURE__ */ jsx("p", {
4969
5480
  className: "text-sm text-muted-foreground",
4970
- children: "拖拽文件到此处,或点击选择文件"
5481
+ children: locale.dragHint
4971
5482
  }),
4972
5483
  /* @__PURE__ */ jsx("p", {
4973
5484
  className: "text-xs text-muted-foreground/60",
4974
- children: "支持 CSV、JSON 格式"
5485
+ children: locale.formatHint
4975
5486
  })
4976
5487
  ]
4977
5488
  }), /* @__PURE__ */ jsx("input", {
@@ -4986,7 +5497,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
4986
5497
  size: "sm",
4987
5498
  className: "w-full",
4988
5499
  onClick: handleDownloadTemplate,
4989
- children: "下载 CSV 模板"
5500
+ children: locale.downloadTemplate
4990
5501
  })] }),
4991
5502
  step === "preview" && parsedData && /* @__PURE__ */ jsxs(Fragment, { children: [
4992
5503
  /* @__PURE__ */ jsx("div", {
@@ -5006,13 +5517,9 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5006
5517
  className: "px-3 py-2 text-left font-medium text-muted-foreground",
5007
5518
  children: h
5008
5519
  }, h)),
5009
- parsedData.headers.length > 6 && /* @__PURE__ */ jsxs("th", {
5520
+ parsedData.headers.length > 6 && /* @__PURE__ */ jsx("th", {
5010
5521
  className: "px-3 py-2 text-left font-medium text-muted-foreground",
5011
- children: [
5012
- "+",
5013
- parsedData.headers.length - 6,
5014
- " 列"
5015
- ]
5522
+ children: locale.moreColumns(parsedData.headers.length - 6)
5016
5523
  })
5017
5524
  ] })
5018
5525
  }), /* @__PURE__ */ jsx("tbody", { children: parsedData.rows.slice(0, 10).map((row, i) => /* @__PURE__ */ jsxs("tr", {
@@ -5035,40 +5542,27 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5035
5542
  })
5036
5543
  })
5037
5544
  }),
5038
- /* @__PURE__ */ jsxs("p", {
5545
+ /* @__PURE__ */ jsx("p", {
5039
5546
  className: "text-sm text-muted-foreground",
5040
- children: [
5041
- "共 ",
5042
- parsedData.rows.length,
5043
- " 条数据",
5044
- parsedData.rows.length > 10 && "(预览前 10 条)",
5045
- ",",
5046
- parsedData.headers.length,
5047
- " 个字段 ,格式: ",
5048
- parsedData.format.toUpperCase()
5049
- ]
5547
+ children: locale.previewSummary(parsedData.rows.length, parsedData.headers.length, parsedData.format, parsedData.rows.length > 10 ? 10 : void 0)
5050
5548
  }),
5051
5549
  /* @__PURE__ */ jsxs("div", {
5052
5550
  className: "flex gap-2 justify-end",
5053
5551
  children: [/* @__PURE__ */ jsx(Button, {
5054
5552
  variant: "outline",
5055
5553
  onClick: reset,
5056
- children: "重新选择"
5554
+ children: locale.reselect
5057
5555
  }), /* @__PURE__ */ jsx(Button, {
5058
5556
  onClick: handleImport,
5059
- children: "确认导入"
5557
+ children: locale.confirmImport
5060
5558
  })]
5061
5559
  })
5062
5560
  ] }),
5063
5561
  step === "importing" && /* @__PURE__ */ jsxs("div", {
5064
5562
  className: "flex flex-col items-center gap-4 py-8",
5065
- children: [/* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" }), /* @__PURE__ */ jsxs("p", {
5563
+ children: [/* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" }), /* @__PURE__ */ jsx("p", {
5066
5564
  className: "text-sm text-muted-foreground",
5067
- children: [
5068
- "正在导入 ",
5069
- parsedData?.rows.length ?? 0,
5070
- " 条数据..."
5071
- ]
5565
+ children: locale.importingRows(parsedData?.rows.length ?? 0)
5072
5566
  })]
5073
5567
  }),
5074
5568
  step === "result" && result && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
@@ -5083,7 +5577,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5083
5577
  children: result.success
5084
5578
  }), /* @__PURE__ */ jsx("p", {
5085
5579
  className: "text-xs text-green-600/80",
5086
- children: "新建"
5580
+ children: locale.resultCreated
5087
5581
  })]
5088
5582
  }),
5089
5583
  (result.updated ?? 0) > 0 && /* @__PURE__ */ jsxs("div", {
@@ -5093,7 +5587,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5093
5587
  children: result.updated
5094
5588
  }), /* @__PURE__ */ jsx("p", {
5095
5589
  className: "text-xs text-blue-600/80",
5096
- children: "更新"
5590
+ children: locale.resultUpdated
5097
5591
  })]
5098
5592
  }),
5099
5593
  result.skipped > 0 && /* @__PURE__ */ jsxs("div", {
@@ -5103,7 +5597,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5103
5597
  children: result.skipped
5104
5598
  }), /* @__PURE__ */ jsx("p", {
5105
5599
  className: "text-xs text-yellow-600/80",
5106
- children: "跳过"
5600
+ children: locale.resultSkipped
5107
5601
  })]
5108
5602
  }),
5109
5603
  result.failed.length > 0 && /* @__PURE__ */ jsxs("div", {
@@ -5113,7 +5607,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5113
5607
  children: result.failed.length
5114
5608
  }), /* @__PURE__ */ jsx("p", {
5115
5609
  className: "text-xs text-red-600/80",
5116
- children: "验证失败"
5610
+ children: locale.resultFailed
5117
5611
  })]
5118
5612
  })
5119
5613
  ]
@@ -5127,10 +5621,10 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5127
5621
  className: "sticky top-0 bg-red-50 dark:bg-red-950/30",
5128
5622
  children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
5129
5623
  className: "px-3 py-2 text-left font-medium text-red-600",
5130
- children: "行号"
5624
+ children: locale.failedRowHeader
5131
5625
  }), /* @__PURE__ */ jsx("th", {
5132
5626
  className: "px-3 py-2 text-left font-medium text-red-600",
5133
- children: "错误"
5627
+ children: locale.failedErrorHeader
5134
5628
  })] })
5135
5629
  }), /* @__PURE__ */ jsx("tbody", { children: result.failed.map((f) => /* @__PURE__ */ jsxs("tr", {
5136
5630
  className: "border-t border-red-100 dark:border-red-900",
@@ -5150,11 +5644,11 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5150
5644
  children: [/* @__PURE__ */ jsx(Button, {
5151
5645
  variant: "outline",
5152
5646
  onClick: () => handleOpenChange(false),
5153
- children: "关闭"
5647
+ children: locale.close
5154
5648
  }), /* @__PURE__ */ jsx(Button, {
5155
5649
  variant: "outline",
5156
5650
  onClick: reset,
5157
- children: "继续导入"
5651
+ children: locale.continueImport
5158
5652
  })]
5159
5653
  })] }),
5160
5654
  error && /* @__PURE__ */ jsx("div", {
@@ -5176,26 +5670,51 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
5176
5670
  function buildTableOverrides(fields, legacyOverrides) {
5177
5671
  const result = { ...legacyOverrides };
5178
5672
  if (fields) for (const [key, config] of Object.entries(fields)) {
5673
+ const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
5179
5674
  let filterMeta;
5180
- if (config.filter && config.filter.enabled !== false && !config.filter.hidden) {
5181
- const { enabled: _, hidden: __,...filterProps } = config.filter;
5182
- if (Object.keys(filterProps).length > 0) filterMeta = filterProps;
5675
+ if (config.filter && typeof config.filter === "object") {
5676
+ if (config.filter.enabled !== false && config.filter.hidden !== true) {
5677
+ const { enabled: _, hidden: __,...filterProps } = config.filter;
5678
+ if (Object.keys(filterProps).length > 0) filterMeta = filterProps;
5679
+ }
5680
+ }
5681
+ if (config.filter === false) result[key] = {
5682
+ ...result[key],
5683
+ enableColumnFilter: false
5684
+ };
5685
+ else if (config.filter && typeof config.filter === "object") {
5686
+ if (config.filter.enabled === false || config.filter.hidden === true) result[key] = {
5687
+ ...result[key],
5688
+ enableColumnFilter: false
5689
+ };
5183
5690
  }
5184
- result[key] = {
5691
+ if (tableMeta || filterMeta) result[key] = {
5185
5692
  ...result[key],
5186
- ...config.label && { label: config.label },
5187
- ...config.hidden && { hidden: true },
5188
- ...config.table,
5189
- ...filterMeta && { meta: {
5693
+ meta: {
5190
5694
  ...result[key]?.meta ?? {},
5191
- ...config.table?.meta ?? {},
5192
- ...filterMeta
5193
- } }
5695
+ ...tableMeta ?? {},
5696
+ ...filterMeta ?? {}
5697
+ }
5194
5698
  };
5195
- if (config.filter?.enabled === false || config.filter?.hidden === true) result[key] = {
5699
+ if (config.label) result[key] = {
5196
5700
  ...result[key],
5197
- enableColumnFilter: false
5701
+ label: config.label
5198
5702
  };
5703
+ if (config.hidden) result[key] = {
5704
+ ...result[key],
5705
+ hidden: true
5706
+ };
5707
+ if (config.table === false) result[key] = {
5708
+ ...result[key],
5709
+ hidden: true
5710
+ };
5711
+ else if (config.table && typeof config.table === "object") {
5712
+ const { meta,...tableProps } = config.table;
5713
+ result[key] = {
5714
+ ...result[key],
5715
+ ...tableProps
5716
+ };
5717
+ }
5199
5718
  }
5200
5719
  return result;
5201
5720
  }
@@ -5209,12 +5728,24 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
5209
5728
  ...result[field],
5210
5729
  "x-hidden": true
5211
5730
  };
5212
- if (fields) for (const [key, config] of Object.entries(fields)) result[key] = {
5213
- ...result[key],
5214
- ...config.label && { title: config.label },
5215
- ...config.hidden && { "x-hidden": true },
5216
- ...config.form
5217
- };
5731
+ if (fields) for (const [key, config] of Object.entries(fields)) {
5732
+ if (config.label) result[key] = {
5733
+ ...result[key],
5734
+ title: config.label
5735
+ };
5736
+ if (config.hidden) result[key] = {
5737
+ ...result[key],
5738
+ "x-hidden": true
5739
+ };
5740
+ if (config.form === false) result[key] = {
5741
+ ...result[key],
5742
+ "x-hidden": true
5743
+ };
5744
+ else if (config.form && typeof config.form === "object") result[key] = {
5745
+ ...result[key],
5746
+ ...config.form
5747
+ };
5748
+ }
5218
5749
  return result;
5219
5750
  }
5220
5751
  /**
@@ -5223,7 +5754,9 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
5223
5754
  function buildHiddenColumns(fields, legacyHidden, denyFields) {
5224
5755
  const hidden = new Set([...legacyHidden ?? [], ...denyFields ?? []]);
5225
5756
  if (fields) {
5226
- for (const [key, config] of Object.entries(fields)) if (config.hidden || config.table?.hidden) hidden.add(key);
5757
+ for (const [key, config] of Object.entries(fields)) if (config.hidden) hidden.add(key);
5758
+ else if (config.table === false) hidden.add(key);
5759
+ else if (config.table && typeof config.table === "object" && config.table.hidden) hidden.add(key);
5227
5760
  }
5228
5761
  return Array.from(hidden);
5229
5762
  }
@@ -5265,13 +5798,13 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
5265
5798
  /**
5266
5799
  * 渲染字段值
5267
5800
  */
5268
- function renderFieldValue(value, type) {
5801
+ function renderFieldValue(value, type, booleanLocale) {
5269
5802
  if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
5270
5803
  className: "text-muted-foreground",
5271
5804
  children: "-"
5272
5805
  });
5273
5806
  switch (type) {
5274
- case "boolean": return value ? "是" : "否";
5807
+ case "boolean": return value ? booleanLocale.true : booleanLocale.false;
5275
5808
  case "date": return formatDate(value);
5276
5809
  case "enum": return /* @__PURE__ */ jsx(Badge, {
5277
5810
  variant: "outline",
@@ -5294,7 +5827,7 @@ function renderFieldValue(value, type) {
5294
5827
  /**
5295
5828
  * ViewModal 组件 - 详情查看弹窗
5296
5829
  */
5297
- function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, denyFields }) {
5830
+ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, denyFields, locale }) {
5298
5831
  if (!data) return null;
5299
5832
  const shape = schema.shape;
5300
5833
  const denySet = new Set(denyFields ?? []);
@@ -5314,7 +5847,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
5314
5847
  children: label
5315
5848
  }), /* @__PURE__ */ jsx("dd", {
5316
5849
  className: "col-span-2 text-sm",
5317
- children: renderFieldValue(value, parsed.type)
5850
+ children: renderFieldValue(value, parsed.type, locale.boolean)
5318
5851
  })]
5319
5852
  }, key);
5320
5853
  })
@@ -5322,23 +5855,89 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
5322
5855
  if (variant === "sheet") return /* @__PURE__ */ jsx(Sheet, {
5323
5856
  open,
5324
5857
  onOpenChange,
5325
- children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "详情" }) }), content] })
5858
+ children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: locale.viewModal.title }) }), content] })
5326
5859
  });
5327
5860
  return /* @__PURE__ */ jsx(Dialog, {
5328
5861
  open,
5329
5862
  onOpenChange,
5330
5863
  children: /* @__PURE__ */ jsxs(DialogContent, {
5331
5864
  className: "max-w-2xl max-h-[80vh] overflow-y-auto",
5332
- children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "详情" }) }), content]
5865
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: locale.viewModal.title }) }), content]
5333
5866
  })
5334
5867
  });
5335
5868
  }
5869
+ function resolveActions(items, defaults, rowActionsLocale) {
5870
+ const defaultItems = [
5871
+ {
5872
+ label: rowActionsLocale.view,
5873
+ onClick: defaults.openView
5874
+ },
5875
+ ...defaults.openEdit ? [{
5876
+ label: rowActionsLocale.edit,
5877
+ onClick: defaults.openEdit
5878
+ }] : [],
5879
+ ...defaults.copyRow ? [{
5880
+ label: rowActionsLocale.copy,
5881
+ onClick: defaults.copyRow
5882
+ }] : [],
5883
+ ...defaults.openDelete ? [{
5884
+ label: rowActionsLocale.delete,
5885
+ onClick: defaults.openDelete,
5886
+ separator: true,
5887
+ variant: "destructive"
5888
+ }] : []
5889
+ ];
5890
+ if (!items || items.length === 0) return defaultItems;
5891
+ if (!items.some((i) => i.type !== "custom")) {
5892
+ const startItems = items.filter((i) => i.type === "custom" && i.position === "start").map(({ label, onClick, separator, variant }) => ({
5893
+ label,
5894
+ onClick,
5895
+ separator,
5896
+ variant
5897
+ }));
5898
+ const endItems = items.filter((i) => i.type === "custom" && i.position !== "start").map(({ label, onClick, separator, variant }) => ({
5899
+ label,
5900
+ onClick,
5901
+ separator,
5902
+ variant
5903
+ }));
5904
+ return [
5905
+ ...startItems,
5906
+ ...defaultItems,
5907
+ ...endItems
5908
+ ];
5909
+ }
5910
+ const handlerMap = {
5911
+ view: defaults.openView,
5912
+ edit: defaults.openEdit,
5913
+ copy: defaults.copyRow,
5914
+ delete: defaults.openDelete
5915
+ };
5916
+ const variantMap = { delete: "destructive" };
5917
+ return items.flatMap((item) => {
5918
+ if (item.type === "custom") return [{
5919
+ label: item.label,
5920
+ onClick: item.onClick,
5921
+ separator: item.separator,
5922
+ variant: item.variant
5923
+ }];
5924
+ const handler = item.onClick ?? handlerMap[item.type];
5925
+ if (!handler) return [];
5926
+ return [{
5927
+ label: item.label ?? rowActionsLocale[item.type],
5928
+ onClick: handler,
5929
+ separator: item.separator,
5930
+ variant: variantMap[item.type]
5931
+ }];
5932
+ });
5933
+ }
5336
5934
  /**
5337
5935
  * AutoCrudTable 组件
5338
5936
  *
5339
5937
  * 高级 CRUD 表格组件,封装了完整的增删改查流程
5340
5938
  */
5341
- function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, slots, permissions }) {
5939
+ function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, slots, permissions, actions: actionItems, locale: localeProp }) {
5940
+ const locale = resolveLocale(localeProp);
5342
5941
  const can = {
5343
5942
  create: permissions?.can?.create ?? true,
5344
5943
  update: permissions?.can?.update ?? true,
@@ -5389,10 +5988,22 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
5389
5988
  setExporting(false);
5390
5989
  }
5391
5990
  }, [selectedCount, handleExport]);
5392
- const tableOverrides = buildTableOverrides(fields, tableConfig?.overrides);
5393
- const formOverrides = buildFormOverrides(fields, formConfig?.overrides, denyFields);
5394
- const hiddenColumns = buildHiddenColumns(fields, tableConfig?.hidden, denyFields);
5395
- const batchFields = buildBatchUpdateFields(schema, tableConfig?.batchFields, fields);
5991
+ const tableOverrides = React.useMemo(() => buildTableOverrides(fields, tableConfig?.overrides), [fields, tableConfig?.overrides]);
5992
+ const formOverrides = React.useMemo(() => buildFormOverrides(fields, formConfig?.overrides, denyFields), [
5993
+ fields,
5994
+ formConfig?.overrides,
5995
+ denyFields
5996
+ ]);
5997
+ const hiddenColumns = React.useMemo(() => buildHiddenColumns(fields, tableConfig?.hidden, denyFields), [
5998
+ fields,
5999
+ tableConfig?.hidden,
6000
+ denyFields
6001
+ ]);
6002
+ const batchFields = React.useMemo(() => buildBatchUpdateFields(schema, tableConfig?.batchFields, fields), [
6003
+ schema,
6004
+ tableConfig?.batchFields,
6005
+ fields
6006
+ ]);
5396
6007
  return /* @__PURE__ */ jsxs("div", {
5397
6008
  className: "space-y-4",
5398
6009
  children: [
@@ -5419,18 +6030,18 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
5419
6030
  variant: "outline",
5420
6031
  size: "sm",
5421
6032
  onClick: () => setImportOpen(true),
5422
- children: [/* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }), "导入"]
6033
+ children: [/* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }), locale.toolbar.import]
5423
6034
  }),
5424
6035
  canExport && /* @__PURE__ */ jsxs(Button, {
5425
6036
  variant: "outline",
5426
6037
  size: "sm",
5427
6038
  onClick: handleExportClick,
5428
6039
  disabled: exporting || selectedCount === 0 && !resource.handlers.export,
5429
- children: [exporting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "mr-2 h-4 w-4" }), selectedCount > 0 ? `导出(${selectedCount})` : "导出"]
6040
+ children: [exporting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "mr-2 h-4 w-4" }), selectedCount > 0 ? locale.toolbar.exportSelected(selectedCount) : locale.toolbar.export]
5430
6041
  }),
5431
6042
  can.create && /* @__PURE__ */ jsx(Button, {
5432
6043
  onClick: resource.handlers.openCreate,
5433
- children: "新建"
6044
+ children: locale.toolbar.create
5434
6045
  })
5435
6046
  ]
5436
6047
  })]
@@ -5442,12 +6053,12 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
5442
6053
  overrides: tableOverrides,
5443
6054
  exclude: hiddenColumns,
5444
6055
  filterMode: tableConfig?.filterModes,
5445
- actions: {
5446
- onView: resource.handlers.openView,
5447
- onEdit: can.update ? resource.handlers.openEdit : void 0,
5448
- onCopy: can.create ? resource.handlers.copyRow : void 0,
5449
- onDelete: can.delete ? resource.handlers.openDelete : void 0
5450
- },
6056
+ actions: resolveActions(actionItems, {
6057
+ openView: resource.handlers.openView,
6058
+ openEdit: can.update ? resource.handlers.openEdit : void 0,
6059
+ copyRow: can.create ? resource.handlers.copyRow : void 0,
6060
+ openDelete: can.delete ? resource.handlers.openDelete : void 0
6061
+ }, locale.rowActions),
5451
6062
  onDeleteSelected: can.delete ? resource.handlers.deleteMany : void 0,
5452
6063
  onUpdateSelected: can.update ? resource.handlers.updateMany : void 0,
5453
6064
  batchUpdateFields: can.update ? batchFields : void 0,
@@ -5468,7 +6079,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
5468
6079
  },
5469
6080
  loading: resource.mutations.isCreating || resource.mutations.isUpdating,
5470
6081
  variant: resource.modal.variant,
5471
- overrides: formOverrides
6082
+ overrides: formOverrides,
6083
+ locale: locale.formModal
5472
6084
  }),
5473
6085
  /* @__PURE__ */ jsx(ViewModal, {
5474
6086
  open: resource.modal.viewOpen,
@@ -5477,22 +6089,24 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
5477
6089
  data: resource.modal.selected,
5478
6090
  schema,
5479
6091
  fields,
5480
- denyFields
6092
+ denyFields,
6093
+ locale
5481
6094
  }),
5482
6095
  /* @__PURE__ */ jsx(AlertDialog, {
5483
6096
  open: resource.modal.deleteOpen,
5484
6097
  onOpenChange: (open) => !open && resource.handlers.closeModals(),
5485
- children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: "确认删除" }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: "此操作无法撤销。确定要删除这条记录吗?" })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: "取消" }), /* @__PURE__ */ jsx(AlertDialogAction, {
6098
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: locale.deleteModal.title }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: locale.deleteModal.description })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: locale.deleteModal.cancel }), /* @__PURE__ */ jsx(AlertDialogAction, {
5486
6099
  onClick: resource.handlers.confirmDelete,
5487
6100
  disabled: resource.mutations.isDeleting,
5488
- children: resource.mutations.isDeleting ? "删除中..." : "确认删除"
6101
+ children: resource.mutations.isDeleting ? locale.deleteModal.confirming : locale.deleteModal.confirm
5489
6102
  })] })] })
5490
6103
  }),
5491
6104
  canImport && resource.handlers.import && /* @__PURE__ */ jsx(ImportDialog, {
5492
6105
  open: importOpen,
5493
6106
  onOpenChange: setImportOpen,
5494
6107
  onImport: resource.handlers.import,
5495
- columns: importColumns
6108
+ columns: importColumns,
6109
+ locale: locale.importDialog
5496
6110
  })
5497
6111
  ]
5498
6112
  });
@@ -6124,6 +6738,8 @@ function DataTableToolbarFilter({ column }) {
6124
6738
  const columnMeta = column.columnDef.meta;
6125
6739
  return React.useCallback(() => {
6126
6740
  if (!columnMeta?.variant) return null;
6741
+ const modes = columnMeta.modes;
6742
+ if (modes && !modes.includes("advanced")) return null;
6127
6743
  switch (columnMeta.variant) {
6128
6744
  case "text": return /* @__PURE__ */ jsx(Input, {
6129
6745
  placeholder: columnMeta.placeholder ?? columnMeta.label,
@@ -6245,6 +6861,10 @@ function modalReducer(state, action) {
6245
6861
  function useAutoCrudResource({ router, schema, query: queryTransform, options = {} }) {
6246
6862
  const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher } = options;
6247
6863
  const toast$1 = useMemo(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
6864
+ const hooksRef = useRef(hooks);
6865
+ useEffect(() => {
6866
+ hooksRef.current = hooks;
6867
+ }, [hooks]);
6248
6868
  const columns = useMemo(() => createTableSchema(schema), [schema]);
6249
6869
  const [urlParams] = useQueryStates({
6250
6870
  page: parseAsInteger.withDefault(1),
@@ -6308,15 +6928,16 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
6308
6928
  payload: variant
6309
6929
  }), []);
6310
6930
  const submitCreate = useCallback(async (values) => {
6931
+ const currentHooks = hooksRef.current;
6311
6932
  try {
6312
- if (hooks?.beforeCreate) {
6313
- const result = await hooks.beforeCreate(values);
6933
+ if (currentHooks?.beforeCreate) {
6934
+ const result = await currentHooks.beforeCreate(values);
6314
6935
  if (typeof result === "boolean") {
6315
6936
  if (result) {
6316
6937
  toast$1.success("创建成功");
6317
6938
  closeModals();
6318
6939
  listQuery.refetch();
6319
- hooks?.onSuccess?.("create", values);
6940
+ currentHooks?.onSuccess?.("create", values);
6320
6941
  }
6321
6942
  return;
6322
6943
  }
@@ -6327,36 +6948,36 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
6327
6948
  toast$1.success("创建成功");
6328
6949
  closeModals();
6329
6950
  listQuery.refetch();
6330
- hooks?.onSuccess?.("create", data);
6951
+ currentHooks?.onSuccess?.("create", data);
6331
6952
  },
6332
6953
  onError: (error) => {
6333
6954
  const err = error;
6334
6955
  toast$1.error(`创建失败: ${err.message}`);
6335
- hooks?.onError?.("create", err);
6956
+ currentHooks?.onError?.("create", err);
6336
6957
  }
6337
6958
  });
6338
6959
  } catch (error) {
6339
6960
  const err = error;
6340
6961
  toast$1.error(`创建失败: ${err.message}`);
6341
- hooks?.onError?.("create", err);
6962
+ currentHooks?.onError?.("create", err);
6342
6963
  }
6343
6964
  }, [
6344
6965
  createMutation,
6345
6966
  closeModals,
6346
- listQuery,
6347
- hooks
6967
+ listQuery
6348
6968
  ]);
6349
6969
  const submitUpdate = useCallback(async (values) => {
6970
+ const currentHooks = hooksRef.current;
6350
6971
  if (!modal.selected) return;
6351
6972
  try {
6352
- if (hooks?.beforeUpdate) {
6353
- const result = await hooks.beforeUpdate(values, modal.selected);
6973
+ if (currentHooks?.beforeUpdate) {
6974
+ const result = await currentHooks.beforeUpdate(values, modal.selected);
6354
6975
  if (typeof result === "boolean") {
6355
6976
  if (result) {
6356
6977
  toast$1.success("更新成功");
6357
6978
  closeModals();
6358
6979
  listQuery.refetch();
6359
- hooks?.onSuccess?.("update", values);
6980
+ currentHooks?.onSuccess?.("update", values);
6360
6981
  }
6361
6982
  return;
6362
6983
  }
@@ -6371,38 +6992,38 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
6371
6992
  toast$1.success("更新成功");
6372
6993
  closeModals();
6373
6994
  listQuery.refetch();
6374
- hooks?.onSuccess?.("update", data);
6995
+ currentHooks?.onSuccess?.("update", data);
6375
6996
  },
6376
6997
  onError: (error) => {
6377
6998
  const err = error;
6378
6999
  toast$1.error(`更新失败: ${err.message}`);
6379
- hooks?.onError?.("update", err);
7000
+ currentHooks?.onError?.("update", err);
6380
7001
  }
6381
7002
  });
6382
7003
  } catch (error) {
6383
7004
  const err = error;
6384
7005
  toast$1.error(`更新失败: ${err.message}`);
6385
- hooks?.onError?.("update", err);
7006
+ currentHooks?.onError?.("update", err);
6386
7007
  }
6387
7008
  }, [
6388
7009
  modal.selected,
6389
7010
  idKey,
6390
7011
  updateMutation,
6391
7012
  closeModals,
6392
- listQuery,
6393
- hooks
7013
+ listQuery
6394
7014
  ]);
6395
7015
  const confirmDelete = useCallback(async () => {
7016
+ const currentHooks = hooksRef.current;
6396
7017
  if (!modal.selected) return;
6397
7018
  try {
6398
- if (hooks?.beforeDelete) {
6399
- const result = await hooks.beforeDelete(modal.selected);
7019
+ if (currentHooks?.beforeDelete) {
7020
+ const result = await currentHooks.beforeDelete(modal.selected);
6400
7021
  if (typeof result === "boolean") {
6401
7022
  if (result) {
6402
7023
  toast$1.success("删除成功");
6403
7024
  closeModals();
6404
7025
  listQuery.refetch();
6405
- hooks?.onSuccess?.("delete", modal.selected);
7026
+ currentHooks?.onSuccess?.("delete", modal.selected);
6406
7027
  }
6407
7028
  return;
6408
7029
  }
@@ -6413,28 +7034,28 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
6413
7034
  toast$1.success("删除成功");
6414
7035
  closeModals();
6415
7036
  listQuery.refetch();
6416
- hooks?.onSuccess?.("delete", data);
7037
+ currentHooks?.onSuccess?.("delete", data);
6417
7038
  },
6418
7039
  onError: (error) => {
6419
7040
  const err = error;
6420
7041
  toast$1.error(`删除失败: ${err.message}`);
6421
- hooks?.onError?.("delete", err);
7042
+ currentHooks?.onError?.("delete", err);
6422
7043
  }
6423
7044
  });
6424
7045
  } catch (error) {
6425
7046
  const err = error;
6426
7047
  toast$1.error(`删除失败: ${err.message}`);
6427
- hooks?.onError?.("delete", err);
7048
+ currentHooks?.onError?.("delete", err);
6428
7049
  }
6429
7050
  }, [
6430
7051
  modal.selected,
6431
7052
  idKey,
6432
7053
  deleteMutation,
6433
7054
  closeModals,
6434
- listQuery,
6435
- hooks
7055
+ listQuery
6436
7056
  ]);
6437
7057
  const deleteMany = useCallback((rows) => {
7058
+ const currentHooks = hooksRef.current;
6438
7059
  if (!deleteManyMutation) {
6439
7060
  toast$1.error("批量删除功能未启用");
6440
7061
  return;
@@ -6444,21 +7065,21 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
6444
7065
  onSuccess: (data) => {
6445
7066
  toast$1.success(`成功删除 ${ids.length} 条记录`);
6446
7067
  listQuery.refetch();
6447
- hooks?.onSuccess?.("delete", data);
7068
+ currentHooks?.onSuccess?.("delete", data);
6448
7069
  },
6449
7070
  onError: (error) => {
6450
7071
  const err = error;
6451
7072
  toast$1.error(`批量删除失败: ${err.message}`);
6452
- hooks?.onError?.("delete", err);
7073
+ currentHooks?.onError?.("delete", err);
6453
7074
  }
6454
7075
  });
6455
7076
  }, [
6456
7077
  deleteManyMutation,
6457
7078
  idKey,
6458
- listQuery,
6459
- hooks
7079
+ listQuery
6460
7080
  ]);
6461
7081
  const updateMany = useCallback((rows, data) => {
7082
+ const currentHooks = hooksRef.current;
6462
7083
  if (!updateManyMutation) {
6463
7084
  toast$1.error("批量更新功能未启用");
6464
7085
  return;
@@ -6471,19 +7092,18 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
6471
7092
  onSuccess: (result) => {
6472
7093
  toast$1.success(`成功更新 ${ids.length} 条记录`);
6473
7094
  listQuery.refetch();
6474
- hooks?.onSuccess?.("update", result);
7095
+ currentHooks?.onSuccess?.("update", result);
6475
7096
  },
6476
7097
  onError: (error) => {
6477
7098
  const err = error;
6478
7099
  toast$1.error(`批量更新失败: ${err.message}`);
6479
- hooks?.onError?.("update", err);
7100
+ currentHooks?.onError?.("update", err);
6480
7101
  }
6481
7102
  });
6482
7103
  }, [
6483
7104
  updateManyMutation,
6484
7105
  idKey,
6485
- listQuery,
6486
- hooks
7106
+ listQuery
6487
7107
  ]);
6488
7108
  const copyRow = useCallback((row) => {
6489
7109
  dispatch({
@@ -6856,4 +7476,4 @@ function createMemoryDataSource(initialData = []) {
6856
7476
  }
6857
7477
 
6858
7478
  //#endregion
6859
- export { AutoCrudTable, AutoForm, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, CrudFormModal, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableViewOptions, ExportDialog, ImportDialog, SchemaAdapter, cn, coerceRowValues, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, dataToCSV, downloadCSVTemplate, exportAllToCSV, exportTableToCSV, formatDate, generateCSVTemplate, getUrlParams, humanize, noopToastAdapter, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, setSearchParams, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates };
7479
+ export { AutoCrudTable, AutoForm, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, CrudFormModal, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableViewOptions, ExportDialog, ImportDialog, SchemaAdapter, cn, coerceRowValues, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setSearchParams, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };