@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/README.md +76 -7
- package/dist/index.cjs +841 -213
- package/dist/index.d.cts +188 -36
- package/dist/index.d.ts +188 -36
- package/dist/index.js +833 -213
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1450,7 +1450,12 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
|
|
|
1450
1450
|
const [open, setOpen] = react.useState(false);
|
|
1451
1451
|
const addButtonRef = react.useRef(null);
|
|
1452
1452
|
const columns = react.useMemo(() => {
|
|
1453
|
-
return table.getAllColumns().filter((column) =>
|
|
1453
|
+
return table.getAllColumns().filter((column) => {
|
|
1454
|
+
if (!column.getCanFilter()) return false;
|
|
1455
|
+
const modes = column.columnDef.meta?.modes;
|
|
1456
|
+
if (modes && !modes.includes("advanced")) return false;
|
|
1457
|
+
return true;
|
|
1458
|
+
});
|
|
1454
1459
|
}, [table]);
|
|
1455
1460
|
const [filters, setFilters] = useReadableFilters(columns, { debounceMs });
|
|
1456
1461
|
const debouncedSetFilters = useDebouncedCallback(setFilters, debounceMs);
|
|
@@ -1932,7 +1937,12 @@ const REMOVE_FILTER_SHORTCUTS = ["backspace", "delete"];
|
|
|
1932
1937
|
function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = THROTTLE_MS$1, shallow = true, disabled,...props }) {
|
|
1933
1938
|
const id = react.useId();
|
|
1934
1939
|
const columns = react.useMemo(() => {
|
|
1935
|
-
return table.getAllColumns().filter((column) =>
|
|
1940
|
+
return table.getAllColumns().filter((column) => {
|
|
1941
|
+
if (!column.columnDef.enableColumnFilter) return false;
|
|
1942
|
+
const modes = column.columnDef.meta?.modes;
|
|
1943
|
+
if (modes && !modes.includes("command")) return false;
|
|
1944
|
+
return true;
|
|
1945
|
+
});
|
|
1936
1946
|
}, [table]);
|
|
1937
1947
|
const [open, setOpen] = react.useState(false);
|
|
1938
1948
|
const [selectedColumn, setSelectedColumn] = react.useState(null);
|
|
@@ -2689,7 +2699,7 @@ function DataTableViewOptions({ table, disabled,...props }) {
|
|
|
2689
2699
|
//#endregion
|
|
2690
2700
|
//#region src/components/auto-crud/auto-table-simple-filters.tsx
|
|
2691
2701
|
function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange }) {
|
|
2692
|
-
const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.
|
|
2702
|
+
const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()), [table]);
|
|
2693
2703
|
const queryStateOptions = table.options.meta?.queryStateOptions;
|
|
2694
2704
|
const [queryFilters, setQueryFilters] = useReadableFilters(columns, {
|
|
2695
2705
|
...queryStateOptions,
|
|
@@ -2709,9 +2719,10 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
2709
2719
|
react.useEffect(() => {
|
|
2710
2720
|
setMounted(true);
|
|
2711
2721
|
}, []);
|
|
2712
|
-
const
|
|
2713
|
-
|
|
2714
|
-
|
|
2722
|
+
const filterMap = react.useMemo(() => new Map(filters.map((f) => [f.id, f])), [filters]);
|
|
2723
|
+
const getFilterValue = react.useCallback((columnId) => {
|
|
2724
|
+
return filterMap.get(columnId)?.value;
|
|
2725
|
+
}, [filterMap]);
|
|
2715
2726
|
const updateFilter = react.useCallback((columnId, value) => {
|
|
2716
2727
|
const column = columns.find((c) => c.id === columnId);
|
|
2717
2728
|
if (!column) return;
|
|
@@ -2734,7 +2745,14 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
2734
2745
|
setFilters
|
|
2735
2746
|
]);
|
|
2736
2747
|
const hasFilters = filters.length > 0;
|
|
2737
|
-
const filterColumns = react.useMemo(() => columns.filter((col) =>
|
|
2748
|
+
const filterColumns = react.useMemo(() => columns.filter((col) => {
|
|
2749
|
+
const meta = col.columnDef.meta;
|
|
2750
|
+
const enableFilter = col.columnDef.enableColumnFilter !== false;
|
|
2751
|
+
if (!meta?.variant || !enableFilter) return false;
|
|
2752
|
+
const modes = meta.modes;
|
|
2753
|
+
if (modes && !modes.includes("simple")) return false;
|
|
2754
|
+
return true;
|
|
2755
|
+
}), [columns]);
|
|
2738
2756
|
const [expanded, setExpanded] = react.useState(false);
|
|
2739
2757
|
const [visibleCount, setVisibleCount] = react.useState(null);
|
|
2740
2758
|
const containerRef = react.useRef(null);
|
|
@@ -2874,7 +2892,7 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
2874
2892
|
}
|
|
2875
2893
|
function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
|
|
2876
2894
|
const [open, setOpen] = react.useState(false);
|
|
2877
|
-
const selectedValues = new Set(value);
|
|
2895
|
+
const selectedValues = react.useMemo(() => new Set(value), [value]);
|
|
2878
2896
|
const onItemSelect = (option, isSelected) => {
|
|
2879
2897
|
if (multiple) {
|
|
2880
2898
|
const newValues = new Set(selectedValues);
|
|
@@ -2905,6 +2923,12 @@ function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
|
|
|
2905
2923
|
tabIndex: 0,
|
|
2906
2924
|
className: "rounded-sm opacity-70 hover:opacity-100",
|
|
2907
2925
|
onClick: onReset,
|
|
2926
|
+
onKeyDown: (e) => {
|
|
2927
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2928
|
+
e.preventDefault();
|
|
2929
|
+
onReset(e);
|
|
2930
|
+
}
|
|
2931
|
+
},
|
|
2908
2932
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
|
|
2909
2933
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
|
|
2910
2934
|
title,
|
|
@@ -3012,6 +3036,12 @@ function SimpleSliderFilter({ title, range, unit, value, onChange }) {
|
|
|
3012
3036
|
tabIndex: 0,
|
|
3013
3037
|
className: "rounded-sm opacity-70 hover:opacity-100",
|
|
3014
3038
|
onClick: onReset,
|
|
3039
|
+
onKeyDown: (e) => {
|
|
3040
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3041
|
+
e.preventDefault();
|
|
3042
|
+
onReset(e);
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3015
3045
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
|
|
3016
3046
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
|
|
3017
3047
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: title }),
|
|
@@ -3135,6 +3165,12 @@ function SimpleDateFilter({ title, multiple, value, onChange }) {
|
|
|
3135
3165
|
tabIndex: 0,
|
|
3136
3166
|
className: "rounded-sm opacity-70 hover:opacity-100",
|
|
3137
3167
|
onClick: onReset,
|
|
3168
|
+
onKeyDown: (e) => {
|
|
3169
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3170
|
+
e.preventDefault();
|
|
3171
|
+
onReset(e);
|
|
3172
|
+
}
|
|
3173
|
+
},
|
|
3138
3174
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
|
|
3139
3175
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CalendarIcon, {}),
|
|
3140
3176
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: title }),
|
|
@@ -3747,33 +3783,47 @@ function inferFilterVariantByFieldName(fieldName) {
|
|
|
3747
3783
|
|
|
3748
3784
|
//#endregion
|
|
3749
3785
|
//#region src/lib/schema-bridge/zod-to-columns.tsx
|
|
3786
|
+
const parsedFieldCache = /* @__PURE__ */ new WeakMap();
|
|
3787
|
+
const baseTableSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
3750
3788
|
/**
|
|
3751
3789
|
* 解析 Zod 字段类型
|
|
3752
3790
|
*/
|
|
3753
3791
|
function parseZodField(schema) {
|
|
3792
|
+
const cached = parsedFieldCache.get(schema);
|
|
3793
|
+
if (cached) return cached;
|
|
3794
|
+
const finalize = (result) => {
|
|
3795
|
+
parsedFieldCache.set(schema, result);
|
|
3796
|
+
return result;
|
|
3797
|
+
};
|
|
3754
3798
|
const def = schema._zod?.def ?? schema._def;
|
|
3755
|
-
const testNull = schema.safeParse(null);
|
|
3756
|
-
const required = !schema.safeParse(void 0).success && !testNull.success;
|
|
3757
3799
|
const typeName = def?.typeName;
|
|
3758
|
-
|
|
3800
|
+
const isOptional = def?.type === "optional" || typeName === "ZodOptional";
|
|
3801
|
+
const isNullable = def?.type === "nullable" || typeName === "ZodNullable";
|
|
3802
|
+
if (isOptional || isNullable) {
|
|
3759
3803
|
const innerType = def?.innerType;
|
|
3760
|
-
if (innerType) return {
|
|
3804
|
+
if (innerType) return finalize({
|
|
3761
3805
|
...parseZodField(innerType),
|
|
3762
3806
|
required: false
|
|
3763
|
-
};
|
|
3807
|
+
});
|
|
3764
3808
|
}
|
|
3765
|
-
|
|
3809
|
+
let required;
|
|
3810
|
+
if (isOptional || isNullable) required = false;
|
|
3811
|
+
else {
|
|
3812
|
+
const testNull = schema.safeParse(null);
|
|
3813
|
+
required = !schema.safeParse(void 0).success && !testNull.success;
|
|
3814
|
+
}
|
|
3815
|
+
if (def?.type === "boolean") return finalize({
|
|
3766
3816
|
type: "boolean",
|
|
3767
3817
|
required
|
|
3768
|
-
};
|
|
3769
|
-
if (def?.type === "number") return {
|
|
3818
|
+
});
|
|
3819
|
+
if (def?.type === "number") return finalize({
|
|
3770
3820
|
type: "number",
|
|
3771
3821
|
required
|
|
3772
|
-
};
|
|
3773
|
-
if (def?.type === "date") return {
|
|
3822
|
+
});
|
|
3823
|
+
if (def?.type === "date") return finalize({
|
|
3774
3824
|
type: "date",
|
|
3775
3825
|
required
|
|
3776
|
-
};
|
|
3826
|
+
});
|
|
3777
3827
|
if (def?.type === "enum") {
|
|
3778
3828
|
let enumValues$1;
|
|
3779
3829
|
if (def?.entries) {
|
|
@@ -3784,20 +3834,20 @@ function parseZodField(schema) {
|
|
|
3784
3834
|
const values = def.values;
|
|
3785
3835
|
if (Array.isArray(values)) enumValues$1 = values.map(String);
|
|
3786
3836
|
}
|
|
3787
|
-
if (enumValues$1 && enumValues$1.length > 0) return {
|
|
3837
|
+
if (enumValues$1 && enumValues$1.length > 0) return finalize({
|
|
3788
3838
|
type: "enum",
|
|
3789
3839
|
required,
|
|
3790
3840
|
enumValues: enumValues$1
|
|
3791
|
-
};
|
|
3841
|
+
});
|
|
3792
3842
|
}
|
|
3793
|
-
if (def?.type === "string") return {
|
|
3843
|
+
if (def?.type === "string") return finalize({
|
|
3794
3844
|
type: "string",
|
|
3795
3845
|
required
|
|
3796
|
-
};
|
|
3797
|
-
if (typeName === "ZodBoolean") return {
|
|
3846
|
+
});
|
|
3847
|
+
if (typeName === "ZodBoolean") return finalize({
|
|
3798
3848
|
type: "boolean",
|
|
3799
3849
|
required
|
|
3800
|
-
};
|
|
3850
|
+
});
|
|
3801
3851
|
let enumValues;
|
|
3802
3852
|
if (def?.entries) {
|
|
3803
3853
|
const entries = def.entries;
|
|
@@ -3807,51 +3857,51 @@ function parseZodField(schema) {
|
|
|
3807
3857
|
const values = def.values;
|
|
3808
3858
|
if (Array.isArray(values)) enumValues = values.map(String);
|
|
3809
3859
|
}
|
|
3810
|
-
if (enumValues && Array.isArray(enumValues) && enumValues.length > 0) return {
|
|
3860
|
+
if (enumValues && Array.isArray(enumValues) && enumValues.length > 0) return finalize({
|
|
3811
3861
|
type: "enum",
|
|
3812
3862
|
required,
|
|
3813
3863
|
enumValues
|
|
3814
|
-
};
|
|
3815
|
-
if (typeName === "ZodNumber") return {
|
|
3864
|
+
});
|
|
3865
|
+
if (typeName === "ZodNumber") return finalize({
|
|
3816
3866
|
type: "number",
|
|
3817
3867
|
required
|
|
3818
|
-
};
|
|
3819
|
-
if (typeName === "ZodDate") return {
|
|
3868
|
+
});
|
|
3869
|
+
if (typeName === "ZodDate") return finalize({
|
|
3820
3870
|
type: "date",
|
|
3821
3871
|
required
|
|
3822
|
-
};
|
|
3823
|
-
if (typeName === "ZodArray") return {
|
|
3872
|
+
});
|
|
3873
|
+
if (typeName === "ZodArray") return finalize({
|
|
3824
3874
|
type: "array",
|
|
3825
3875
|
required
|
|
3826
|
-
};
|
|
3827
|
-
if (typeName === "ZodObject") return {
|
|
3876
|
+
});
|
|
3877
|
+
if (typeName === "ZodObject") return finalize({
|
|
3828
3878
|
type: "object",
|
|
3829
3879
|
required
|
|
3830
|
-
};
|
|
3880
|
+
});
|
|
3831
3881
|
const testString = schema.safeParse("test");
|
|
3832
3882
|
const testNumber = schema.safeParse(123);
|
|
3833
3883
|
const testBoolean = schema.safeParse(true);
|
|
3834
3884
|
const testDate = schema.safeParse(17040672e5);
|
|
3835
|
-
if (testBoolean.success && !testString.success && !testNumber.success) return {
|
|
3885
|
+
if (testBoolean.success && !testString.success && !testNumber.success) return finalize({
|
|
3836
3886
|
type: "boolean",
|
|
3837
3887
|
required
|
|
3838
|
-
};
|
|
3839
|
-
if (testDate.success && !testString.success) return {
|
|
3888
|
+
});
|
|
3889
|
+
if (testDate.success && !testString.success) return finalize({
|
|
3840
3890
|
type: "date",
|
|
3841
3891
|
required
|
|
3842
|
-
};
|
|
3843
|
-
if (testNumber.success && !testString.success) return {
|
|
3892
|
+
});
|
|
3893
|
+
if (testNumber.success && !testString.success) return finalize({
|
|
3844
3894
|
type: "number",
|
|
3845
3895
|
required
|
|
3846
|
-
};
|
|
3847
|
-
if (schema.safeParse([]).success && !testString.success) return {
|
|
3896
|
+
});
|
|
3897
|
+
if (schema.safeParse([]).success && !testString.success) return finalize({
|
|
3848
3898
|
type: "array",
|
|
3849
3899
|
required
|
|
3850
|
-
};
|
|
3851
|
-
return {
|
|
3900
|
+
});
|
|
3901
|
+
return finalize({
|
|
3852
3902
|
type: "string",
|
|
3853
3903
|
required
|
|
3854
|
-
};
|
|
3904
|
+
});
|
|
3855
3905
|
}
|
|
3856
3906
|
/**
|
|
3857
3907
|
* 渲染单元格内容
|
|
@@ -3893,9 +3943,14 @@ function renderCell(value, type) {
|
|
|
3893
3943
|
* 从 Zod Schema 创建 Table Schema (ColumnDef 数组)
|
|
3894
3944
|
*/
|
|
3895
3945
|
function createTableSchema(schema, options) {
|
|
3946
|
+
const { overrides, exclude = [] } = options ?? {};
|
|
3947
|
+
const canCache = !(!!overrides && Object.keys(overrides).length > 0) && exclude.length === 0;
|
|
3948
|
+
if (canCache) {
|
|
3949
|
+
const cached = baseTableSchemaCache.get(schema);
|
|
3950
|
+
if (cached) return [...cached];
|
|
3951
|
+
}
|
|
3896
3952
|
const shape = schema.shape;
|
|
3897
3953
|
const columns = [];
|
|
3898
|
-
const { overrides, exclude = [] } = options ?? {};
|
|
3899
3954
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
3900
3955
|
if (exclude.includes(key)) continue;
|
|
3901
3956
|
const override = overrides?.[key];
|
|
@@ -3930,6 +3985,7 @@ function createTableSchema(schema, options) {
|
|
|
3930
3985
|
...restOverride
|
|
3931
3986
|
});
|
|
3932
3987
|
}
|
|
3988
|
+
if (canCache) baseTableSchemaCache.set(schema, columns);
|
|
3933
3989
|
return columns;
|
|
3934
3990
|
}
|
|
3935
3991
|
/**
|
|
@@ -3958,8 +4014,7 @@ function createSelectColumn() {
|
|
|
3958
4014
|
/**
|
|
3959
4015
|
* 创建操作列
|
|
3960
4016
|
*/
|
|
3961
|
-
function createActionsColumn(
|
|
3962
|
-
const { onEdit, onDelete, onView, onCopy } = config;
|
|
4017
|
+
function createActionsColumn(items) {
|
|
3963
4018
|
return {
|
|
3964
4019
|
id: "actions",
|
|
3965
4020
|
cell: ({ row }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuTrigger, {
|
|
@@ -3970,29 +4025,14 @@ function createActionsColumn(config) {
|
|
|
3970
4025
|
className: "flex size-8 p-0 data-[state=open]:bg-muted",
|
|
3971
4026
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Ellipsis, { className: "size-4" })
|
|
3972
4027
|
})
|
|
3973
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4028
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuContent, {
|
|
3974
4029
|
align: "end",
|
|
3975
4030
|
className: "w-40",
|
|
3976
|
-
children: [
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
onEdit && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuItem, {
|
|
3982
|
-
onClick: () => onEdit(row.original),
|
|
3983
|
-
children: "编辑"
|
|
3984
|
-
}),
|
|
3985
|
-
onCopy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuItem, {
|
|
3986
|
-
onClick: () => onCopy(row.original),
|
|
3987
|
-
children: "复制"
|
|
3988
|
-
}),
|
|
3989
|
-
(onView || onEdit || onCopy) && onDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuSeparator, {}),
|
|
3990
|
-
onDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuItem, {
|
|
3991
|
-
className: "text-destructive",
|
|
3992
|
-
onClick: () => onDelete(row.original),
|
|
3993
|
-
children: "删除"
|
|
3994
|
-
})
|
|
3995
|
-
]
|
|
4031
|
+
children: items.map((item, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [item.separator && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuItem, {
|
|
4032
|
+
className: item.variant === "destructive" ? "text-destructive" : "",
|
|
4033
|
+
onClick: () => item.onClick(row.original),
|
|
4034
|
+
children: item.label
|
|
4035
|
+
})] }, i))
|
|
3996
4036
|
})] }),
|
|
3997
4037
|
size: 40
|
|
3998
4038
|
};
|
|
@@ -4446,6 +4486,13 @@ function useDataTable(props) {
|
|
|
4446
4486
|
filterableColumns,
|
|
4447
4487
|
enableAdvancedFilter
|
|
4448
4488
|
]);
|
|
4489
|
+
const getCoreRowModelFn = react.useMemo(() => (0, __tanstack_react_table.getCoreRowModel)(), []);
|
|
4490
|
+
const getFilteredRowModelFn = react.useMemo(() => (0, __tanstack_react_table.getFilteredRowModel)(), []);
|
|
4491
|
+
const getPaginationRowModelFn = react.useMemo(() => (0, __tanstack_react_table.getPaginationRowModel)(), []);
|
|
4492
|
+
const getSortedRowModelFn = react.useMemo(() => (0, __tanstack_react_table.getSortedRowModel)(), []);
|
|
4493
|
+
const getFacetedRowModelFn = react.useMemo(() => (0, __tanstack_react_table.getFacetedRowModel)(), []);
|
|
4494
|
+
const getFacetedUniqueValuesFn = react.useMemo(() => (0, __tanstack_react_table.getFacetedUniqueValues)(), []);
|
|
4495
|
+
const getFacetedMinMaxValuesFn = react.useMemo(() => (0, __tanstack_react_table.getFacetedMinMaxValues)(), []);
|
|
4449
4496
|
const table = (0, __tanstack_react_table.useReactTable)({
|
|
4450
4497
|
...tableProps,
|
|
4451
4498
|
columns,
|
|
@@ -4468,13 +4515,13 @@ function useDataTable(props) {
|
|
|
4468
4515
|
onSortingChange,
|
|
4469
4516
|
onColumnFiltersChange,
|
|
4470
4517
|
onColumnVisibilityChange: setColumnVisibility,
|
|
4471
|
-
getCoreRowModel:
|
|
4472
|
-
getFilteredRowModel:
|
|
4473
|
-
getPaginationRowModel:
|
|
4474
|
-
getSortedRowModel:
|
|
4475
|
-
getFacetedRowModel:
|
|
4476
|
-
getFacetedUniqueValues:
|
|
4477
|
-
getFacetedMinMaxValues:
|
|
4518
|
+
getCoreRowModel: getCoreRowModelFn,
|
|
4519
|
+
getFilteredRowModel: getFilteredRowModelFn,
|
|
4520
|
+
getPaginationRowModel: getPaginationRowModelFn,
|
|
4521
|
+
getSortedRowModel: getSortedRowModelFn,
|
|
4522
|
+
getFacetedRowModel: getFacetedRowModelFn,
|
|
4523
|
+
getFacetedUniqueValues: getFacetedUniqueValuesFn,
|
|
4524
|
+
getFacetedMinMaxValues: getFacetedMinMaxValuesFn,
|
|
4478
4525
|
manualPagination: true,
|
|
4479
4526
|
manualSorting: true,
|
|
4480
4527
|
manualFiltering: true,
|
|
@@ -4505,6 +4552,11 @@ function useDataTable(props) {
|
|
|
4505
4552
|
|
|
4506
4553
|
//#endregion
|
|
4507
4554
|
//#region src/components/auto-crud/auto-table.tsx
|
|
4555
|
+
const DEFAULT_MODES = [
|
|
4556
|
+
"simple",
|
|
4557
|
+
"advanced",
|
|
4558
|
+
"command"
|
|
4559
|
+
];
|
|
4508
4560
|
/** 过滤模式配置 */
|
|
4509
4561
|
const filterModeConfig = {
|
|
4510
4562
|
simple: {
|
|
@@ -4524,11 +4576,7 @@ const filterModeConfig = {
|
|
|
4524
4576
|
}
|
|
4525
4577
|
};
|
|
4526
4578
|
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, initialSorting, enableExport = true, onSelectedCountChange, getSelectedRows }) {
|
|
4527
|
-
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] :
|
|
4528
|
-
"simple",
|
|
4529
|
-
"advanced",
|
|
4530
|
-
"command"
|
|
4531
|
-
];
|
|
4579
|
+
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
4532
4580
|
const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
|
|
4533
4581
|
const showToggle = modes.length > 1;
|
|
4534
4582
|
const { table, shallow, debounceMs, throttleMs } = useDataTable({
|
|
@@ -4550,13 +4598,18 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4550
4598
|
]),
|
|
4551
4599
|
pageCount,
|
|
4552
4600
|
enableAdvancedFilter: true,
|
|
4553
|
-
initialState: {
|
|
4601
|
+
initialState: (0, react.useMemo)(() => ({
|
|
4554
4602
|
sorting: initialSorting,
|
|
4555
4603
|
columnPinning: pinnedColumns ?? {
|
|
4556
4604
|
left: enableRowSelection ? ["select"] : [],
|
|
4557
4605
|
right: actions ? ["actions"] : []
|
|
4558
4606
|
}
|
|
4559
|
-
},
|
|
4607
|
+
}), [
|
|
4608
|
+
initialSorting,
|
|
4609
|
+
pinnedColumns,
|
|
4610
|
+
enableRowSelection,
|
|
4611
|
+
actions
|
|
4612
|
+
]),
|
|
4560
4613
|
shallow: false,
|
|
4561
4614
|
clearOnDefault: true
|
|
4562
4615
|
});
|
|
@@ -4597,7 +4650,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4597
4650
|
})
|
|
4598
4651
|
})
|
|
4599
4652
|
})] }) : null;
|
|
4600
|
-
const
|
|
4653
|
+
const filtersContent = (0, react.useMemo)(() => {
|
|
4601
4654
|
switch (currentMode) {
|
|
4602
4655
|
case "simple": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTableSimpleFilters, {
|
|
4603
4656
|
table,
|
|
@@ -4617,7 +4670,13 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4617
4670
|
align: "start"
|
|
4618
4671
|
});
|
|
4619
4672
|
}
|
|
4620
|
-
}
|
|
4673
|
+
}, [
|
|
4674
|
+
currentMode,
|
|
4675
|
+
table,
|
|
4676
|
+
shallow,
|
|
4677
|
+
debounceMs,
|
|
4678
|
+
throttleMs
|
|
4679
|
+
]);
|
|
4621
4680
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4622
4681
|
className: "space-y-4",
|
|
4623
4682
|
children: [
|
|
@@ -4629,7 +4688,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4629
4688
|
children: [currentMode !== "simple" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableSortList, {
|
|
4630
4689
|
table,
|
|
4631
4690
|
align: "start"
|
|
4632
|
-
}),
|
|
4691
|
+
}), filtersContent]
|
|
4633
4692
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4634
4693
|
className: "flex items-center gap-2",
|
|
4635
4694
|
children: [
|
|
@@ -4861,10 +4920,462 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
4861
4920
|
}
|
|
4862
4921
|
const AutoForm = (0, react.forwardRef)(AutoFormInner);
|
|
4863
4922
|
|
|
4923
|
+
//#endregion
|
|
4924
|
+
//#region src/i18n/locale.ts
|
|
4925
|
+
const zhCN = {
|
|
4926
|
+
toolbar: {
|
|
4927
|
+
create: "新建",
|
|
4928
|
+
import: "导入",
|
|
4929
|
+
export: "导出",
|
|
4930
|
+
exportSelected: (count) => `导出(${count})`
|
|
4931
|
+
},
|
|
4932
|
+
rowActions: {
|
|
4933
|
+
view: "查看",
|
|
4934
|
+
edit: "编辑",
|
|
4935
|
+
copy: "复制",
|
|
4936
|
+
delete: "删除"
|
|
4937
|
+
},
|
|
4938
|
+
viewModal: { title: "详情" },
|
|
4939
|
+
boolean: {
|
|
4940
|
+
true: "是",
|
|
4941
|
+
false: "否"
|
|
4942
|
+
},
|
|
4943
|
+
formModal: {
|
|
4944
|
+
createTitle: "新建",
|
|
4945
|
+
editTitle: "编辑",
|
|
4946
|
+
cancel: "取消",
|
|
4947
|
+
create: "创建",
|
|
4948
|
+
save: "保存",
|
|
4949
|
+
submitting: "处理中..."
|
|
4950
|
+
},
|
|
4951
|
+
deleteModal: {
|
|
4952
|
+
title: "确认删除",
|
|
4953
|
+
description: "此操作无法撤销。确定要删除这条记录吗?",
|
|
4954
|
+
cancel: "取消",
|
|
4955
|
+
confirm: "确认删除",
|
|
4956
|
+
confirming: "删除中..."
|
|
4957
|
+
},
|
|
4958
|
+
importDialog: {
|
|
4959
|
+
title: "导入数据",
|
|
4960
|
+
uploadDescription: "上传 CSV 或 JSON 文件以批量导入数据",
|
|
4961
|
+
previewDescription: (count) => `已解析 ${count} 条数据,确认后开始导入`,
|
|
4962
|
+
importingDescription: "正在导入数据...",
|
|
4963
|
+
doneDescription: "导入完成",
|
|
4964
|
+
dragHint: "拖拽文件到此处,或点击选择文件",
|
|
4965
|
+
formatHint: "支持 CSV、JSON 格式",
|
|
4966
|
+
downloadTemplate: "下载 CSV 模板",
|
|
4967
|
+
moreColumns: (count) => `+${count} 列`,
|
|
4968
|
+
previewSummary: (rows, fields, format, previewLimit) => `共 ${rows} 条数据${previewLimit ? `(预览前 ${previewLimit} 条)` : ""},${fields} 个字段,格式: ${format.toUpperCase()}`,
|
|
4969
|
+
reselect: "重新选择",
|
|
4970
|
+
confirmImport: "确认导入",
|
|
4971
|
+
importingRows: (count) => `正在导入 ${count} 条数据...`,
|
|
4972
|
+
resultCreated: "新建",
|
|
4973
|
+
resultUpdated: "更新",
|
|
4974
|
+
resultSkipped: "跳过",
|
|
4975
|
+
resultFailed: "验证失败",
|
|
4976
|
+
failedRowHeader: "行号",
|
|
4977
|
+
failedErrorHeader: "错误",
|
|
4978
|
+
close: "关闭",
|
|
4979
|
+
continueImport: "继续导入",
|
|
4980
|
+
errorNoData: "文件中没有数据行",
|
|
4981
|
+
errorParseFailed: "文件解析失败",
|
|
4982
|
+
errorImportFailed: "导入失败"
|
|
4983
|
+
}
|
|
4984
|
+
};
|
|
4985
|
+
const enUS = {
|
|
4986
|
+
toolbar: {
|
|
4987
|
+
create: "New",
|
|
4988
|
+
import: "Import",
|
|
4989
|
+
export: "Export",
|
|
4990
|
+
exportSelected: (count) => `Export (${count})`
|
|
4991
|
+
},
|
|
4992
|
+
rowActions: {
|
|
4993
|
+
view: "View",
|
|
4994
|
+
edit: "Edit",
|
|
4995
|
+
copy: "Copy",
|
|
4996
|
+
delete: "Delete"
|
|
4997
|
+
},
|
|
4998
|
+
viewModal: { title: "Details" },
|
|
4999
|
+
boolean: {
|
|
5000
|
+
true: "Yes",
|
|
5001
|
+
false: "No"
|
|
5002
|
+
},
|
|
5003
|
+
formModal: {
|
|
5004
|
+
createTitle: "New",
|
|
5005
|
+
editTitle: "Edit",
|
|
5006
|
+
cancel: "Cancel",
|
|
5007
|
+
create: "Create",
|
|
5008
|
+
save: "Save",
|
|
5009
|
+
submitting: "Processing..."
|
|
5010
|
+
},
|
|
5011
|
+
deleteModal: {
|
|
5012
|
+
title: "Confirm Delete",
|
|
5013
|
+
description: "This action cannot be undone. Are you sure you want to delete this record?",
|
|
5014
|
+
cancel: "Cancel",
|
|
5015
|
+
confirm: "Delete",
|
|
5016
|
+
confirming: "Deleting..."
|
|
5017
|
+
},
|
|
5018
|
+
importDialog: {
|
|
5019
|
+
title: "Import Data",
|
|
5020
|
+
uploadDescription: "Upload a CSV or JSON file to bulk import data",
|
|
5021
|
+
previewDescription: (count) => `Parsed ${count} records, confirm to start importing`,
|
|
5022
|
+
importingDescription: "Importing data...",
|
|
5023
|
+
doneDescription: "Import complete",
|
|
5024
|
+
dragHint: "Drag file here, or click to select",
|
|
5025
|
+
formatHint: "Supports CSV, JSON formats",
|
|
5026
|
+
downloadTemplate: "Download CSV Template",
|
|
5027
|
+
moreColumns: (count) => `+${count} more`,
|
|
5028
|
+
previewSummary: (rows, fields, format, previewLimit) => `${rows} records${previewLimit ? ` (preview first ${previewLimit})` : ""}, ${fields} fields, format: ${format.toUpperCase()}`,
|
|
5029
|
+
reselect: "Reselect",
|
|
5030
|
+
confirmImport: "Confirm Import",
|
|
5031
|
+
importingRows: (count) => `Importing ${count} records...`,
|
|
5032
|
+
resultCreated: "Created",
|
|
5033
|
+
resultUpdated: "Updated",
|
|
5034
|
+
resultSkipped: "Skipped",
|
|
5035
|
+
resultFailed: "Failed",
|
|
5036
|
+
failedRowHeader: "Row",
|
|
5037
|
+
failedErrorHeader: "Error",
|
|
5038
|
+
close: "Close",
|
|
5039
|
+
continueImport: "Import More",
|
|
5040
|
+
errorNoData: "No data rows found in file",
|
|
5041
|
+
errorParseFailed: "Failed to parse file",
|
|
5042
|
+
errorImportFailed: "Import failed"
|
|
5043
|
+
}
|
|
5044
|
+
};
|
|
5045
|
+
const jaJP = {
|
|
5046
|
+
toolbar: {
|
|
5047
|
+
create: "新規作成",
|
|
5048
|
+
import: "インポート",
|
|
5049
|
+
export: "エクスポート",
|
|
5050
|
+
exportSelected: (count) => `エクスポート(${count})`
|
|
5051
|
+
},
|
|
5052
|
+
rowActions: {
|
|
5053
|
+
view: "詳細",
|
|
5054
|
+
edit: "編集",
|
|
5055
|
+
copy: "コピー",
|
|
5056
|
+
delete: "削除"
|
|
5057
|
+
},
|
|
5058
|
+
viewModal: { title: "詳細" },
|
|
5059
|
+
boolean: {
|
|
5060
|
+
true: "はい",
|
|
5061
|
+
false: "いいえ"
|
|
5062
|
+
},
|
|
5063
|
+
formModal: {
|
|
5064
|
+
createTitle: "新規作成",
|
|
5065
|
+
editTitle: "編集",
|
|
5066
|
+
cancel: "キャンセル",
|
|
5067
|
+
create: "作成",
|
|
5068
|
+
save: "保存",
|
|
5069
|
+
submitting: "処理中..."
|
|
5070
|
+
},
|
|
5071
|
+
deleteModal: {
|
|
5072
|
+
title: "削除の確認",
|
|
5073
|
+
description: "この操作は元に戻せません。このレコードを削除してもよろしいですか?",
|
|
5074
|
+
cancel: "キャンセル",
|
|
5075
|
+
confirm: "削除する",
|
|
5076
|
+
confirming: "削除中..."
|
|
5077
|
+
},
|
|
5078
|
+
importDialog: {
|
|
5079
|
+
title: "データのインポート",
|
|
5080
|
+
uploadDescription: "CSVまたはJSONファイルをアップロードして一括インポート",
|
|
5081
|
+
previewDescription: (count) => `${count}件のデータを解析しました。確認してインポートを開始します`,
|
|
5082
|
+
importingDescription: "データをインポート中...",
|
|
5083
|
+
doneDescription: "インポート完了",
|
|
5084
|
+
dragHint: "ファイルをここにドラッグするか、クリックして選択",
|
|
5085
|
+
formatHint: "CSV・JSON形式に対応",
|
|
5086
|
+
downloadTemplate: "CSVテンプレートをダウンロード",
|
|
5087
|
+
moreColumns: (count) => `+${count}列`,
|
|
5088
|
+
previewSummary: (rows, fields, format, previewLimit) => `${rows}件${previewLimit ? `(上位${previewLimit}件プレビュー)` : ""}、${fields}フィールド、形式: ${format.toUpperCase()}`,
|
|
5089
|
+
reselect: "再選択",
|
|
5090
|
+
confirmImport: "インポートを確認",
|
|
5091
|
+
importingRows: (count) => `${count}件をインポート中...`,
|
|
5092
|
+
resultCreated: "新規作成",
|
|
5093
|
+
resultUpdated: "更新",
|
|
5094
|
+
resultSkipped: "スキップ",
|
|
5095
|
+
resultFailed: "失敗",
|
|
5096
|
+
failedRowHeader: "行番号",
|
|
5097
|
+
failedErrorHeader: "エラー",
|
|
5098
|
+
close: "閉じる",
|
|
5099
|
+
continueImport: "続けてインポート",
|
|
5100
|
+
errorNoData: "ファイルにデータ行がありません",
|
|
5101
|
+
errorParseFailed: "ファイルの解析に失敗しました",
|
|
5102
|
+
errorImportFailed: "インポートに失敗しました"
|
|
5103
|
+
}
|
|
5104
|
+
};
|
|
5105
|
+
const koKR = {
|
|
5106
|
+
toolbar: {
|
|
5107
|
+
create: "새로 만들기",
|
|
5108
|
+
import: "가져오기",
|
|
5109
|
+
export: "내보내기",
|
|
5110
|
+
exportSelected: (count) => `내보내기(${count})`
|
|
5111
|
+
},
|
|
5112
|
+
rowActions: {
|
|
5113
|
+
view: "보기",
|
|
5114
|
+
edit: "편집",
|
|
5115
|
+
copy: "복사",
|
|
5116
|
+
delete: "삭제"
|
|
5117
|
+
},
|
|
5118
|
+
viewModal: { title: "상세 정보" },
|
|
5119
|
+
boolean: {
|
|
5120
|
+
true: "예",
|
|
5121
|
+
false: "아니요"
|
|
5122
|
+
},
|
|
5123
|
+
formModal: {
|
|
5124
|
+
createTitle: "새로 만들기",
|
|
5125
|
+
editTitle: "편집",
|
|
5126
|
+
cancel: "취소",
|
|
5127
|
+
create: "만들기",
|
|
5128
|
+
save: "저장",
|
|
5129
|
+
submitting: "처리 중..."
|
|
5130
|
+
},
|
|
5131
|
+
deleteModal: {
|
|
5132
|
+
title: "삭제 확인",
|
|
5133
|
+
description: "이 작업은 취소할 수 없습니다. 이 레코드를 삭제하시겠습니까?",
|
|
5134
|
+
cancel: "취소",
|
|
5135
|
+
confirm: "삭제",
|
|
5136
|
+
confirming: "삭제 중..."
|
|
5137
|
+
},
|
|
5138
|
+
importDialog: {
|
|
5139
|
+
title: "데이터 가져오기",
|
|
5140
|
+
uploadDescription: "CSV 또는 JSON 파일을 업로드하여 일괄 가져오기",
|
|
5141
|
+
previewDescription: (count) => `${count}개의 데이터를 분석했습니다. 확인 후 가져오기를 시작합니다`,
|
|
5142
|
+
importingDescription: "데이터를 가져오는 중...",
|
|
5143
|
+
doneDescription: "가져오기 완료",
|
|
5144
|
+
dragHint: "파일을 여기에 드래그하거나 클릭하여 선택",
|
|
5145
|
+
formatHint: "CSV, JSON 형식 지원",
|
|
5146
|
+
downloadTemplate: "CSV 템플릿 다운로드",
|
|
5147
|
+
moreColumns: (count) => `+${count}열 더`,
|
|
5148
|
+
previewSummary: (rows, fields, format, previewLimit) => `${rows}개 레코드${previewLimit ? ` (상위 ${previewLimit}개 미리보기)` : ""}, ${fields}개 필드, 형식: ${format.toUpperCase()}`,
|
|
5149
|
+
reselect: "다시 선택",
|
|
5150
|
+
confirmImport: "가져오기 확인",
|
|
5151
|
+
importingRows: (count) => `${count}개의 레코드를 가져오는 중...`,
|
|
5152
|
+
resultCreated: "생성됨",
|
|
5153
|
+
resultUpdated: "업데이트됨",
|
|
5154
|
+
resultSkipped: "건너뜀",
|
|
5155
|
+
resultFailed: "실패",
|
|
5156
|
+
failedRowHeader: "행 번호",
|
|
5157
|
+
failedErrorHeader: "오류",
|
|
5158
|
+
close: "닫기",
|
|
5159
|
+
continueImport: "계속 가져오기",
|
|
5160
|
+
errorNoData: "파일에 데이터 행이 없습니다",
|
|
5161
|
+
errorParseFailed: "파일 파싱에 실패했습니다",
|
|
5162
|
+
errorImportFailed: "가져오기에 실패했습니다"
|
|
5163
|
+
}
|
|
5164
|
+
};
|
|
5165
|
+
const frFR = {
|
|
5166
|
+
toolbar: {
|
|
5167
|
+
create: "Nouveau",
|
|
5168
|
+
import: "Importer",
|
|
5169
|
+
export: "Exporter",
|
|
5170
|
+
exportSelected: (count) => `Exporter (${count})`
|
|
5171
|
+
},
|
|
5172
|
+
rowActions: {
|
|
5173
|
+
view: "Voir",
|
|
5174
|
+
edit: "Modifier",
|
|
5175
|
+
copy: "Copier",
|
|
5176
|
+
delete: "Supprimer"
|
|
5177
|
+
},
|
|
5178
|
+
viewModal: { title: "Détails" },
|
|
5179
|
+
boolean: {
|
|
5180
|
+
true: "Oui",
|
|
5181
|
+
false: "Non"
|
|
5182
|
+
},
|
|
5183
|
+
formModal: {
|
|
5184
|
+
createTitle: "Nouveau",
|
|
5185
|
+
editTitle: "Modifier",
|
|
5186
|
+
cancel: "Annuler",
|
|
5187
|
+
create: "Créer",
|
|
5188
|
+
save: "Enregistrer",
|
|
5189
|
+
submitting: "En cours..."
|
|
5190
|
+
},
|
|
5191
|
+
deleteModal: {
|
|
5192
|
+
title: "Confirmer la suppression",
|
|
5193
|
+
description: "Cette action est irréversible. Voulez-vous vraiment supprimer cet enregistrement ?",
|
|
5194
|
+
cancel: "Annuler",
|
|
5195
|
+
confirm: "Supprimer",
|
|
5196
|
+
confirming: "Suppression..."
|
|
5197
|
+
},
|
|
5198
|
+
importDialog: {
|
|
5199
|
+
title: "Importer des données",
|
|
5200
|
+
uploadDescription: "Téléchargez un fichier CSV ou JSON pour importer en masse",
|
|
5201
|
+
previewDescription: (count) => `${count} enregistrements analysés, confirmez pour démarrer l'import`,
|
|
5202
|
+
importingDescription: "Importation en cours...",
|
|
5203
|
+
doneDescription: "Import terminé",
|
|
5204
|
+
dragHint: "Glissez un fichier ici ou cliquez pour sélectionner",
|
|
5205
|
+
formatHint: "Formats CSV et JSON acceptés",
|
|
5206
|
+
downloadTemplate: "Télécharger le modèle CSV",
|
|
5207
|
+
moreColumns: (count) => `+${count} colonnes`,
|
|
5208
|
+
previewSummary: (rows, fields, format, previewLimit) => `${rows} enregistrements${previewLimit ? ` (aperçu des ${previewLimit} premiers)` : ""}, ${fields} champs, format : ${format.toUpperCase()}`,
|
|
5209
|
+
reselect: "Resélectionner",
|
|
5210
|
+
confirmImport: "Confirmer l'import",
|
|
5211
|
+
importingRows: (count) => `Importation de ${count} enregistrements...`,
|
|
5212
|
+
resultCreated: "Créés",
|
|
5213
|
+
resultUpdated: "Mis à jour",
|
|
5214
|
+
resultSkipped: "Ignorés",
|
|
5215
|
+
resultFailed: "Échoués",
|
|
5216
|
+
failedRowHeader: "Ligne",
|
|
5217
|
+
failedErrorHeader: "Erreur",
|
|
5218
|
+
close: "Fermer",
|
|
5219
|
+
continueImport: "Continuer l'import",
|
|
5220
|
+
errorNoData: "Aucune ligne de données dans le fichier",
|
|
5221
|
+
errorParseFailed: "Échec de l'analyse du fichier",
|
|
5222
|
+
errorImportFailed: "Échec de l'importation"
|
|
5223
|
+
}
|
|
5224
|
+
};
|
|
5225
|
+
const deDE = {
|
|
5226
|
+
toolbar: {
|
|
5227
|
+
create: "Neu",
|
|
5228
|
+
import: "Importieren",
|
|
5229
|
+
export: "Exportieren",
|
|
5230
|
+
exportSelected: (count) => `Exportieren (${count})`
|
|
5231
|
+
},
|
|
5232
|
+
rowActions: {
|
|
5233
|
+
view: "Anzeigen",
|
|
5234
|
+
edit: "Bearbeiten",
|
|
5235
|
+
copy: "Kopieren",
|
|
5236
|
+
delete: "Löschen"
|
|
5237
|
+
},
|
|
5238
|
+
viewModal: { title: "Details" },
|
|
5239
|
+
boolean: {
|
|
5240
|
+
true: "Ja",
|
|
5241
|
+
false: "Nein"
|
|
5242
|
+
},
|
|
5243
|
+
formModal: {
|
|
5244
|
+
createTitle: "Neu",
|
|
5245
|
+
editTitle: "Bearbeiten",
|
|
5246
|
+
cancel: "Abbrechen",
|
|
5247
|
+
create: "Erstellen",
|
|
5248
|
+
save: "Speichern",
|
|
5249
|
+
submitting: "Wird verarbeitet..."
|
|
5250
|
+
},
|
|
5251
|
+
deleteModal: {
|
|
5252
|
+
title: "Löschen bestätigen",
|
|
5253
|
+
description: "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie diesen Datensatz wirklich löschen?",
|
|
5254
|
+
cancel: "Abbrechen",
|
|
5255
|
+
confirm: "Löschen",
|
|
5256
|
+
confirming: "Wird gelöscht..."
|
|
5257
|
+
},
|
|
5258
|
+
importDialog: {
|
|
5259
|
+
title: "Daten importieren",
|
|
5260
|
+
uploadDescription: "CSV- oder JSON-Datei hochladen, um Daten massenweise zu importieren",
|
|
5261
|
+
previewDescription: (count) => `${count} Datensätze analysiert, bestätigen Sie den Import`,
|
|
5262
|
+
importingDescription: "Daten werden importiert...",
|
|
5263
|
+
doneDescription: "Import abgeschlossen",
|
|
5264
|
+
dragHint: "Datei hierher ziehen oder klicken zum Auswählen",
|
|
5265
|
+
formatHint: "CSV- und JSON-Formate werden unterstützt",
|
|
5266
|
+
downloadTemplate: "CSV-Vorlage herunterladen",
|
|
5267
|
+
moreColumns: (count) => `+${count} Spalten`,
|
|
5268
|
+
previewSummary: (rows, fields, format, previewLimit) => `${rows} Datensätze${previewLimit ? ` (Vorschau der ersten ${previewLimit})` : ""}, ${fields} Felder, Format: ${format.toUpperCase()}`,
|
|
5269
|
+
reselect: "Neu auswählen",
|
|
5270
|
+
confirmImport: "Import bestätigen",
|
|
5271
|
+
importingRows: (count) => `${count} Datensätze werden importiert...`,
|
|
5272
|
+
resultCreated: "Erstellt",
|
|
5273
|
+
resultUpdated: "Aktualisiert",
|
|
5274
|
+
resultSkipped: "Übersprungen",
|
|
5275
|
+
resultFailed: "Fehlgeschlagen",
|
|
5276
|
+
failedRowHeader: "Zeile",
|
|
5277
|
+
failedErrorHeader: "Fehler",
|
|
5278
|
+
close: "Schließen",
|
|
5279
|
+
continueImport: "Weiteren Import starten",
|
|
5280
|
+
errorNoData: "Keine Datenzeilen in der Datei",
|
|
5281
|
+
errorParseFailed: "Datei konnte nicht analysiert werden",
|
|
5282
|
+
errorImportFailed: "Import fehlgeschlagen"
|
|
5283
|
+
}
|
|
5284
|
+
};
|
|
5285
|
+
const esES = {
|
|
5286
|
+
toolbar: {
|
|
5287
|
+
create: "Nuevo",
|
|
5288
|
+
import: "Importar",
|
|
5289
|
+
export: "Exportar",
|
|
5290
|
+
exportSelected: (count) => `Exportar (${count})`
|
|
5291
|
+
},
|
|
5292
|
+
rowActions: {
|
|
5293
|
+
view: "Ver",
|
|
5294
|
+
edit: "Editar",
|
|
5295
|
+
copy: "Copiar",
|
|
5296
|
+
delete: "Eliminar"
|
|
5297
|
+
},
|
|
5298
|
+
viewModal: { title: "Detalles" },
|
|
5299
|
+
boolean: {
|
|
5300
|
+
true: "Sí",
|
|
5301
|
+
false: "No"
|
|
5302
|
+
},
|
|
5303
|
+
formModal: {
|
|
5304
|
+
createTitle: "Nuevo",
|
|
5305
|
+
editTitle: "Editar",
|
|
5306
|
+
cancel: "Cancelar",
|
|
5307
|
+
create: "Crear",
|
|
5308
|
+
save: "Guardar",
|
|
5309
|
+
submitting: "Procesando..."
|
|
5310
|
+
},
|
|
5311
|
+
deleteModal: {
|
|
5312
|
+
title: "Confirmar eliminación",
|
|
5313
|
+
description: "Esta acción no se puede deshacer. ¿Está seguro de que desea eliminar este registro?",
|
|
5314
|
+
cancel: "Cancelar",
|
|
5315
|
+
confirm: "Eliminar",
|
|
5316
|
+
confirming: "Eliminando..."
|
|
5317
|
+
},
|
|
5318
|
+
importDialog: {
|
|
5319
|
+
title: "Importar datos",
|
|
5320
|
+
uploadDescription: "Suba un archivo CSV o JSON para importar datos de forma masiva",
|
|
5321
|
+
previewDescription: (count) => `Se analizaron ${count} registros, confirme para iniciar la importación`,
|
|
5322
|
+
importingDescription: "Importando datos...",
|
|
5323
|
+
doneDescription: "Importación completada",
|
|
5324
|
+
dragHint: "Arrastre el archivo aquí o haga clic para seleccionar",
|
|
5325
|
+
formatHint: "Formatos CSV y JSON compatibles",
|
|
5326
|
+
downloadTemplate: "Descargar plantilla CSV",
|
|
5327
|
+
moreColumns: (count) => `+${count} columnas`,
|
|
5328
|
+
previewSummary: (rows, fields, format, previewLimit) => `${rows} registros${previewLimit ? ` (vista previa de los primeros ${previewLimit})` : ""}, ${fields} campos, formato: ${format.toUpperCase()}`,
|
|
5329
|
+
reselect: "Volver a seleccionar",
|
|
5330
|
+
confirmImport: "Confirmar importación",
|
|
5331
|
+
importingRows: (count) => `Importando ${count} registros...`,
|
|
5332
|
+
resultCreated: "Creados",
|
|
5333
|
+
resultUpdated: "Actualizados",
|
|
5334
|
+
resultSkipped: "Omitidos",
|
|
5335
|
+
resultFailed: "Fallidos",
|
|
5336
|
+
failedRowHeader: "Fila",
|
|
5337
|
+
failedErrorHeader: "Error",
|
|
5338
|
+
close: "Cerrar",
|
|
5339
|
+
continueImport: "Continuar importando",
|
|
5340
|
+
errorNoData: "No se encontraron filas de datos en el archivo",
|
|
5341
|
+
errorParseFailed: "Error al analizar el archivo",
|
|
5342
|
+
errorImportFailed: "Error en la importación"
|
|
5343
|
+
}
|
|
5344
|
+
};
|
|
5345
|
+
const BUILTIN_LOCALES = {
|
|
5346
|
+
"zh-CN": zhCN,
|
|
5347
|
+
"en-US": enUS,
|
|
5348
|
+
"ja-JP": jaJP,
|
|
5349
|
+
"ko-KR": koKR,
|
|
5350
|
+
"fr-FR": frFR,
|
|
5351
|
+
"de-DE": deDE,
|
|
5352
|
+
"es-ES": esES
|
|
5353
|
+
};
|
|
5354
|
+
function deepMerge(target, source) {
|
|
5355
|
+
const result = { ...target };
|
|
5356
|
+
for (const key of Object.keys(source)) {
|
|
5357
|
+
const sv = source[key];
|
|
5358
|
+
const tv = target[key];
|
|
5359
|
+
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);
|
|
5360
|
+
else if (sv !== void 0) result[key] = sv;
|
|
5361
|
+
}
|
|
5362
|
+
return result;
|
|
5363
|
+
}
|
|
5364
|
+
/**
|
|
5365
|
+
* 解析 locale prop 为完整的 AutoCrudLocale
|
|
5366
|
+
* - string → 内置语言(fallback: zh-CN)
|
|
5367
|
+
* - object → 以 zh-CN 为基础深合并
|
|
5368
|
+
*/
|
|
5369
|
+
function resolveLocale(localeProp) {
|
|
5370
|
+
if (!localeProp) return zhCN;
|
|
5371
|
+
if (typeof localeProp === "string") return BUILTIN_LOCALES[localeProp] ?? zhCN;
|
|
5372
|
+
return deepMerge(zhCN, localeProp);
|
|
5373
|
+
}
|
|
5374
|
+
|
|
4864
5375
|
//#endregion
|
|
4865
5376
|
//#region src/components/auto-crud/crud-form-modal.tsx
|
|
4866
|
-
function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, title, labelAlign, labelWidth }) {
|
|
4867
|
-
const defaultTitle = mode === "create" ?
|
|
5377
|
+
function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, title, locale = zhCN.formModal, labelAlign, labelWidth }) {
|
|
5378
|
+
const defaultTitle = mode === "create" ? locale.createTitle : locale.editTitle;
|
|
4868
5379
|
const formRef = (0, react.useRef)(null);
|
|
4869
5380
|
const handleSubmit = async () => {
|
|
4870
5381
|
await formRef.current?.submit();
|
|
@@ -4880,12 +5391,12 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
|
|
|
4880
5391
|
type: "button",
|
|
4881
5392
|
variant: "outline",
|
|
4882
5393
|
onClick: () => onOpenChange(false),
|
|
4883
|
-
children:
|
|
5394
|
+
children: locale.cancel
|
|
4884
5395
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
4885
5396
|
type: "button",
|
|
4886
5397
|
onClick: handleSubmit,
|
|
4887
5398
|
disabled: loading,
|
|
4888
|
-
children: loading ?
|
|
5399
|
+
children: loading ? locale.submitting : mode === "create" ? locale.create : locale.save
|
|
4889
5400
|
})]
|
|
4890
5401
|
}),
|
|
4891
5402
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoForm, {
|
|
@@ -4905,7 +5416,7 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
|
|
|
4905
5416
|
|
|
4906
5417
|
//#endregion
|
|
4907
5418
|
//#region src/components/auto-crud/import-dialog.tsx
|
|
4908
|
-
function ImportDialog({ open, onOpenChange, onImport, columns = [], title =
|
|
5419
|
+
function ImportDialog({ open, onOpenChange, onImport, columns = [], title, locale = zhCN.importDialog }) {
|
|
4909
5420
|
const [step, setStep] = react.useState("upload");
|
|
4910
5421
|
const [parsedData, setParsedData] = react.useState(null);
|
|
4911
5422
|
const [error, setError] = react.useState(null);
|
|
@@ -4928,13 +5439,13 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
4928
5439
|
try {
|
|
4929
5440
|
const data = await parseImportFile(file);
|
|
4930
5441
|
if (data.rows.length === 0) {
|
|
4931
|
-
setError(
|
|
5442
|
+
setError(locale.errorNoData);
|
|
4932
5443
|
return;
|
|
4933
5444
|
}
|
|
4934
5445
|
setParsedData(data);
|
|
4935
5446
|
setStep("preview");
|
|
4936
5447
|
} catch (e) {
|
|
4937
|
-
setError(e instanceof Error ? e.message :
|
|
5448
|
+
setError(e instanceof Error ? e.message : locale.errorParseFailed);
|
|
4938
5449
|
}
|
|
4939
5450
|
}, []);
|
|
4940
5451
|
const handleDrop = react.useCallback((e) => {
|
|
@@ -4954,7 +5465,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
4954
5465
|
setResult(await onImport(parsedData.rows));
|
|
4955
5466
|
setStep("result");
|
|
4956
5467
|
} catch (e) {
|
|
4957
|
-
setError(e instanceof Error ? e.message :
|
|
5468
|
+
setError(e instanceof Error ? e.message : locale.errorImportFailed);
|
|
4958
5469
|
setStep("preview");
|
|
4959
5470
|
}
|
|
4960
5471
|
}, [parsedData, onImport]);
|
|
@@ -4974,11 +5485,11 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
4974
5485
|
onOpenChange: handleOpenChange,
|
|
4975
5486
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogContent, {
|
|
4976
5487
|
className: "sm:max-w-[600px]",
|
|
4977
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogTitle, { children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogDescription, { children: [
|
|
4978
|
-
step === "upload" &&
|
|
4979
|
-
step === "preview" &&
|
|
4980
|
-
step === "importing" &&
|
|
4981
|
-
step === "result" &&
|
|
5488
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogTitle, { children: title ?? locale.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogDescription, { children: [
|
|
5489
|
+
step === "upload" && locale.uploadDescription,
|
|
5490
|
+
step === "preview" && locale.previewDescription(parsedData?.rows.length ?? 0),
|
|
5491
|
+
step === "importing" && locale.importingDescription,
|
|
5492
|
+
step === "result" && locale.doneDescription
|
|
4982
5493
|
] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4983
5494
|
className: "space-y-4",
|
|
4984
5495
|
children: [
|
|
@@ -5009,11 +5520,11 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5009
5520
|
}),
|
|
5010
5521
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5011
5522
|
className: "text-sm text-muted-foreground",
|
|
5012
|
-
children:
|
|
5523
|
+
children: locale.dragHint
|
|
5013
5524
|
}),
|
|
5014
5525
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5015
5526
|
className: "text-xs text-muted-foreground/60",
|
|
5016
|
-
children:
|
|
5527
|
+
children: locale.formatHint
|
|
5017
5528
|
})
|
|
5018
5529
|
]
|
|
5019
5530
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
@@ -5028,7 +5539,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5028
5539
|
size: "sm",
|
|
5029
5540
|
className: "w-full",
|
|
5030
5541
|
onClick: handleDownloadTemplate,
|
|
5031
|
-
children:
|
|
5542
|
+
children: locale.downloadTemplate
|
|
5032
5543
|
})] }),
|
|
5033
5544
|
step === "preview" && parsedData && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5034
5545
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5048,13 +5559,9 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5048
5559
|
className: "px-3 py-2 text-left font-medium text-muted-foreground",
|
|
5049
5560
|
children: h
|
|
5050
5561
|
}, h)),
|
|
5051
|
-
parsedData.headers.length > 6 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5562
|
+
parsedData.headers.length > 6 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
5052
5563
|
className: "px-3 py-2 text-left font-medium text-muted-foreground",
|
|
5053
|
-
children:
|
|
5054
|
-
"+",
|
|
5055
|
-
parsedData.headers.length - 6,
|
|
5056
|
-
" 列"
|
|
5057
|
-
]
|
|
5564
|
+
children: locale.moreColumns(parsedData.headers.length - 6)
|
|
5058
5565
|
})
|
|
5059
5566
|
] })
|
|
5060
5567
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: parsedData.rows.slice(0, 10).map((row, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
|
|
@@ -5077,40 +5584,27 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5077
5584
|
})
|
|
5078
5585
|
})
|
|
5079
5586
|
}),
|
|
5080
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5587
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5081
5588
|
className: "text-sm text-muted-foreground",
|
|
5082
|
-
children:
|
|
5083
|
-
"共 ",
|
|
5084
|
-
parsedData.rows.length,
|
|
5085
|
-
" 条数据",
|
|
5086
|
-
parsedData.rows.length > 10 && "(预览前 10 条)",
|
|
5087
|
-
",",
|
|
5088
|
-
parsedData.headers.length,
|
|
5089
|
-
" 个字段 ,格式: ",
|
|
5090
|
-
parsedData.format.toUpperCase()
|
|
5091
|
-
]
|
|
5589
|
+
children: locale.previewSummary(parsedData.rows.length, parsedData.headers.length, parsedData.format, parsedData.rows.length > 10 ? 10 : void 0)
|
|
5092
5590
|
}),
|
|
5093
5591
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5094
5592
|
className: "flex gap-2 justify-end",
|
|
5095
5593
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
5096
5594
|
variant: "outline",
|
|
5097
5595
|
onClick: reset,
|
|
5098
|
-
children:
|
|
5596
|
+
children: locale.reselect
|
|
5099
5597
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
5100
5598
|
onClick: handleImport,
|
|
5101
|
-
children:
|
|
5599
|
+
children: locale.confirmImport
|
|
5102
5600
|
})]
|
|
5103
5601
|
})
|
|
5104
5602
|
] }),
|
|
5105
5603
|
step === "importing" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5106
5604
|
className: "flex flex-col items-center gap-4 py-8",
|
|
5107
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" }), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5605
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5108
5606
|
className: "text-sm text-muted-foreground",
|
|
5109
|
-
children:
|
|
5110
|
-
"正在导入 ",
|
|
5111
|
-
parsedData?.rows.length ?? 0,
|
|
5112
|
-
" 条数据..."
|
|
5113
|
-
]
|
|
5607
|
+
children: locale.importingRows(parsedData?.rows.length ?? 0)
|
|
5114
5608
|
})]
|
|
5115
5609
|
}),
|
|
5116
5610
|
step === "result" && result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5125,7 +5619,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5125
5619
|
children: result.success
|
|
5126
5620
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5127
5621
|
className: "text-xs text-green-600/80",
|
|
5128
|
-
children:
|
|
5622
|
+
children: locale.resultCreated
|
|
5129
5623
|
})]
|
|
5130
5624
|
}),
|
|
5131
5625
|
(result.updated ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5135,7 +5629,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5135
5629
|
children: result.updated
|
|
5136
5630
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5137
5631
|
className: "text-xs text-blue-600/80",
|
|
5138
|
-
children:
|
|
5632
|
+
children: locale.resultUpdated
|
|
5139
5633
|
})]
|
|
5140
5634
|
}),
|
|
5141
5635
|
result.skipped > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5145,7 +5639,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5145
5639
|
children: result.skipped
|
|
5146
5640
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5147
5641
|
className: "text-xs text-yellow-600/80",
|
|
5148
|
-
children:
|
|
5642
|
+
children: locale.resultSkipped
|
|
5149
5643
|
})]
|
|
5150
5644
|
}),
|
|
5151
5645
|
result.failed.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5155,7 +5649,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5155
5649
|
children: result.failed.length
|
|
5156
5650
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5157
5651
|
className: "text-xs text-red-600/80",
|
|
5158
|
-
children:
|
|
5652
|
+
children: locale.resultFailed
|
|
5159
5653
|
})]
|
|
5160
5654
|
})
|
|
5161
5655
|
]
|
|
@@ -5169,10 +5663,10 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5169
5663
|
className: "sticky top-0 bg-red-50 dark:bg-red-950/30",
|
|
5170
5664
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
5171
5665
|
className: "px-3 py-2 text-left font-medium text-red-600",
|
|
5172
|
-
children:
|
|
5666
|
+
children: locale.failedRowHeader
|
|
5173
5667
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
5174
5668
|
className: "px-3 py-2 text-left font-medium text-red-600",
|
|
5175
|
-
children:
|
|
5669
|
+
children: locale.failedErrorHeader
|
|
5176
5670
|
})] })
|
|
5177
5671
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: result.failed.map((f) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
|
|
5178
5672
|
className: "border-t border-red-100 dark:border-red-900",
|
|
@@ -5192,11 +5686,11 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5192
5686
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
5193
5687
|
variant: "outline",
|
|
5194
5688
|
onClick: () => handleOpenChange(false),
|
|
5195
|
-
children:
|
|
5689
|
+
children: locale.close
|
|
5196
5690
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
5197
5691
|
variant: "outline",
|
|
5198
5692
|
onClick: reset,
|
|
5199
|
-
children:
|
|
5693
|
+
children: locale.continueImport
|
|
5200
5694
|
})]
|
|
5201
5695
|
})] }),
|
|
5202
5696
|
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5218,26 +5712,51 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title = "导
|
|
|
5218
5712
|
function buildTableOverrides(fields, legacyOverrides) {
|
|
5219
5713
|
const result = { ...legacyOverrides };
|
|
5220
5714
|
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
5715
|
+
const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
|
|
5221
5716
|
let filterMeta;
|
|
5222
|
-
if (config.filter && config.filter
|
|
5223
|
-
|
|
5224
|
-
|
|
5717
|
+
if (config.filter && typeof config.filter === "object") {
|
|
5718
|
+
if (config.filter.enabled !== false && config.filter.hidden !== true) {
|
|
5719
|
+
const { enabled: _, hidden: __,...filterProps } = config.filter;
|
|
5720
|
+
if (Object.keys(filterProps).length > 0) filterMeta = filterProps;
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5723
|
+
if (config.filter === false) result[key] = {
|
|
5724
|
+
...result[key],
|
|
5725
|
+
enableColumnFilter: false
|
|
5726
|
+
};
|
|
5727
|
+
else if (config.filter && typeof config.filter === "object") {
|
|
5728
|
+
if (config.filter.enabled === false || config.filter.hidden === true) result[key] = {
|
|
5729
|
+
...result[key],
|
|
5730
|
+
enableColumnFilter: false
|
|
5731
|
+
};
|
|
5225
5732
|
}
|
|
5226
|
-
result[key] = {
|
|
5733
|
+
if (tableMeta || filterMeta) result[key] = {
|
|
5227
5734
|
...result[key],
|
|
5228
|
-
|
|
5229
|
-
...config.hidden && { hidden: true },
|
|
5230
|
-
...config.table,
|
|
5231
|
-
...filterMeta && { meta: {
|
|
5735
|
+
meta: {
|
|
5232
5736
|
...result[key]?.meta ?? {},
|
|
5233
|
-
...
|
|
5234
|
-
...filterMeta
|
|
5235
|
-
}
|
|
5737
|
+
...tableMeta ?? {},
|
|
5738
|
+
...filterMeta ?? {}
|
|
5739
|
+
}
|
|
5236
5740
|
};
|
|
5237
|
-
if (config.
|
|
5741
|
+
if (config.label) result[key] = {
|
|
5238
5742
|
...result[key],
|
|
5239
|
-
|
|
5743
|
+
label: config.label
|
|
5240
5744
|
};
|
|
5745
|
+
if (config.hidden) result[key] = {
|
|
5746
|
+
...result[key],
|
|
5747
|
+
hidden: true
|
|
5748
|
+
};
|
|
5749
|
+
if (config.table === false) result[key] = {
|
|
5750
|
+
...result[key],
|
|
5751
|
+
hidden: true
|
|
5752
|
+
};
|
|
5753
|
+
else if (config.table && typeof config.table === "object") {
|
|
5754
|
+
const { meta,...tableProps } = config.table;
|
|
5755
|
+
result[key] = {
|
|
5756
|
+
...result[key],
|
|
5757
|
+
...tableProps
|
|
5758
|
+
};
|
|
5759
|
+
}
|
|
5241
5760
|
}
|
|
5242
5761
|
return result;
|
|
5243
5762
|
}
|
|
@@ -5251,12 +5770,24 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
|
|
|
5251
5770
|
...result[field],
|
|
5252
5771
|
"x-hidden": true
|
|
5253
5772
|
};
|
|
5254
|
-
if (fields) for (const [key, config] of Object.entries(fields))
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5773
|
+
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
5774
|
+
if (config.label) result[key] = {
|
|
5775
|
+
...result[key],
|
|
5776
|
+
title: config.label
|
|
5777
|
+
};
|
|
5778
|
+
if (config.hidden) result[key] = {
|
|
5779
|
+
...result[key],
|
|
5780
|
+
"x-hidden": true
|
|
5781
|
+
};
|
|
5782
|
+
if (config.form === false) result[key] = {
|
|
5783
|
+
...result[key],
|
|
5784
|
+
"x-hidden": true
|
|
5785
|
+
};
|
|
5786
|
+
else if (config.form && typeof config.form === "object") result[key] = {
|
|
5787
|
+
...result[key],
|
|
5788
|
+
...config.form
|
|
5789
|
+
};
|
|
5790
|
+
}
|
|
5260
5791
|
return result;
|
|
5261
5792
|
}
|
|
5262
5793
|
/**
|
|
@@ -5265,7 +5796,9 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
|
|
|
5265
5796
|
function buildHiddenColumns(fields, legacyHidden, denyFields) {
|
|
5266
5797
|
const hidden = new Set([...legacyHidden ?? [], ...denyFields ?? []]);
|
|
5267
5798
|
if (fields) {
|
|
5268
|
-
for (const [key, config] of Object.entries(fields)) if (config.hidden
|
|
5799
|
+
for (const [key, config] of Object.entries(fields)) if (config.hidden) hidden.add(key);
|
|
5800
|
+
else if (config.table === false) hidden.add(key);
|
|
5801
|
+
else if (config.table && typeof config.table === "object" && config.table.hidden) hidden.add(key);
|
|
5269
5802
|
}
|
|
5270
5803
|
return Array.from(hidden);
|
|
5271
5804
|
}
|
|
@@ -5307,13 +5840,13 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
|
|
|
5307
5840
|
/**
|
|
5308
5841
|
* 渲染字段值
|
|
5309
5842
|
*/
|
|
5310
|
-
function renderFieldValue(value, type) {
|
|
5843
|
+
function renderFieldValue(value, type, booleanLocale) {
|
|
5311
5844
|
if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5312
5845
|
className: "text-muted-foreground",
|
|
5313
5846
|
children: "-"
|
|
5314
5847
|
});
|
|
5315
5848
|
switch (type) {
|
|
5316
|
-
case "boolean": return value ?
|
|
5849
|
+
case "boolean": return value ? booleanLocale.true : booleanLocale.false;
|
|
5317
5850
|
case "date": return formatDate(value);
|
|
5318
5851
|
case "enum": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
|
|
5319
5852
|
variant: "outline",
|
|
@@ -5336,7 +5869,7 @@ function renderFieldValue(value, type) {
|
|
|
5336
5869
|
/**
|
|
5337
5870
|
* ViewModal 组件 - 详情查看弹窗
|
|
5338
5871
|
*/
|
|
5339
|
-
function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, denyFields }) {
|
|
5872
|
+
function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, denyFields, locale }) {
|
|
5340
5873
|
if (!data) return null;
|
|
5341
5874
|
const shape = schema.shape;
|
|
5342
5875
|
const denySet = new Set(denyFields ?? []);
|
|
@@ -5356,7 +5889,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
|
|
|
5356
5889
|
children: label
|
|
5357
5890
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
5358
5891
|
className: "col-span-2 text-sm",
|
|
5359
|
-
children: renderFieldValue(value, parsed.type)
|
|
5892
|
+
children: renderFieldValue(value, parsed.type, locale.boolean)
|
|
5360
5893
|
})]
|
|
5361
5894
|
}, key);
|
|
5362
5895
|
})
|
|
@@ -5364,23 +5897,89 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
|
|
|
5364
5897
|
if (variant === "sheet") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Sheet, {
|
|
5365
5898
|
open,
|
|
5366
5899
|
onOpenChange,
|
|
5367
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.SheetContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.SheetHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.SheetTitle, { children:
|
|
5900
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.SheetContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.SheetHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.SheetTitle, { children: locale.viewModal.title }) }), content] })
|
|
5368
5901
|
});
|
|
5369
5902
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Dialog, {
|
|
5370
5903
|
open,
|
|
5371
5904
|
onOpenChange,
|
|
5372
5905
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogContent, {
|
|
5373
5906
|
className: "max-w-2xl max-h-[80vh] overflow-y-auto",
|
|
5374
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogTitle, { children:
|
|
5907
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogTitle, { children: locale.viewModal.title }) }), content]
|
|
5375
5908
|
})
|
|
5376
5909
|
});
|
|
5377
5910
|
}
|
|
5911
|
+
function resolveActions(items, defaults, rowActionsLocale) {
|
|
5912
|
+
const defaultItems = [
|
|
5913
|
+
{
|
|
5914
|
+
label: rowActionsLocale.view,
|
|
5915
|
+
onClick: defaults.openView
|
|
5916
|
+
},
|
|
5917
|
+
...defaults.openEdit ? [{
|
|
5918
|
+
label: rowActionsLocale.edit,
|
|
5919
|
+
onClick: defaults.openEdit
|
|
5920
|
+
}] : [],
|
|
5921
|
+
...defaults.copyRow ? [{
|
|
5922
|
+
label: rowActionsLocale.copy,
|
|
5923
|
+
onClick: defaults.copyRow
|
|
5924
|
+
}] : [],
|
|
5925
|
+
...defaults.openDelete ? [{
|
|
5926
|
+
label: rowActionsLocale.delete,
|
|
5927
|
+
onClick: defaults.openDelete,
|
|
5928
|
+
separator: true,
|
|
5929
|
+
variant: "destructive"
|
|
5930
|
+
}] : []
|
|
5931
|
+
];
|
|
5932
|
+
if (!items || items.length === 0) return defaultItems;
|
|
5933
|
+
if (!items.some((i) => i.type !== "custom")) {
|
|
5934
|
+
const startItems = items.filter((i) => i.type === "custom" && i.position === "start").map(({ label, onClick, separator, variant }) => ({
|
|
5935
|
+
label,
|
|
5936
|
+
onClick,
|
|
5937
|
+
separator,
|
|
5938
|
+
variant
|
|
5939
|
+
}));
|
|
5940
|
+
const endItems = items.filter((i) => i.type === "custom" && i.position !== "start").map(({ label, onClick, separator, variant }) => ({
|
|
5941
|
+
label,
|
|
5942
|
+
onClick,
|
|
5943
|
+
separator,
|
|
5944
|
+
variant
|
|
5945
|
+
}));
|
|
5946
|
+
return [
|
|
5947
|
+
...startItems,
|
|
5948
|
+
...defaultItems,
|
|
5949
|
+
...endItems
|
|
5950
|
+
];
|
|
5951
|
+
}
|
|
5952
|
+
const handlerMap = {
|
|
5953
|
+
view: defaults.openView,
|
|
5954
|
+
edit: defaults.openEdit,
|
|
5955
|
+
copy: defaults.copyRow,
|
|
5956
|
+
delete: defaults.openDelete
|
|
5957
|
+
};
|
|
5958
|
+
const variantMap = { delete: "destructive" };
|
|
5959
|
+
return items.flatMap((item) => {
|
|
5960
|
+
if (item.type === "custom") return [{
|
|
5961
|
+
label: item.label,
|
|
5962
|
+
onClick: item.onClick,
|
|
5963
|
+
separator: item.separator,
|
|
5964
|
+
variant: item.variant
|
|
5965
|
+
}];
|
|
5966
|
+
const handler = item.onClick ?? handlerMap[item.type];
|
|
5967
|
+
if (!handler) return [];
|
|
5968
|
+
return [{
|
|
5969
|
+
label: item.label ?? rowActionsLocale[item.type],
|
|
5970
|
+
onClick: handler,
|
|
5971
|
+
separator: item.separator,
|
|
5972
|
+
variant: variantMap[item.type]
|
|
5973
|
+
}];
|
|
5974
|
+
});
|
|
5975
|
+
}
|
|
5378
5976
|
/**
|
|
5379
5977
|
* AutoCrudTable 组件
|
|
5380
5978
|
*
|
|
5381
5979
|
* 高级 CRUD 表格组件,封装了完整的增删改查流程
|
|
5382
5980
|
*/
|
|
5383
|
-
function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, slots, permissions }) {
|
|
5981
|
+
function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, slots, permissions, actions: actionItems, locale: localeProp }) {
|
|
5982
|
+
const locale = resolveLocale(localeProp);
|
|
5384
5983
|
const can = {
|
|
5385
5984
|
create: permissions?.can?.create ?? true,
|
|
5386
5985
|
update: permissions?.can?.update ?? true,
|
|
@@ -5431,10 +6030,22 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
5431
6030
|
setExporting(false);
|
|
5432
6031
|
}
|
|
5433
6032
|
}, [selectedCount, handleExport]);
|
|
5434
|
-
const tableOverrides = buildTableOverrides(fields, tableConfig?.overrides);
|
|
5435
|
-
const formOverrides = buildFormOverrides(fields, formConfig?.overrides, denyFields)
|
|
5436
|
-
|
|
5437
|
-
|
|
6033
|
+
const tableOverrides = react.useMemo(() => buildTableOverrides(fields, tableConfig?.overrides), [fields, tableConfig?.overrides]);
|
|
6034
|
+
const formOverrides = react.useMemo(() => buildFormOverrides(fields, formConfig?.overrides, denyFields), [
|
|
6035
|
+
fields,
|
|
6036
|
+
formConfig?.overrides,
|
|
6037
|
+
denyFields
|
|
6038
|
+
]);
|
|
6039
|
+
const hiddenColumns = react.useMemo(() => buildHiddenColumns(fields, tableConfig?.hidden, denyFields), [
|
|
6040
|
+
fields,
|
|
6041
|
+
tableConfig?.hidden,
|
|
6042
|
+
denyFields
|
|
6043
|
+
]);
|
|
6044
|
+
const batchFields = react.useMemo(() => buildBatchUpdateFields(schema, tableConfig?.batchFields, fields), [
|
|
6045
|
+
schema,
|
|
6046
|
+
tableConfig?.batchFields,
|
|
6047
|
+
fields
|
|
6048
|
+
]);
|
|
5438
6049
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5439
6050
|
className: "space-y-4",
|
|
5440
6051
|
children: [
|
|
@@ -5461,18 +6072,18 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
5461
6072
|
variant: "outline",
|
|
5462
6073
|
size: "sm",
|
|
5463
6074
|
onClick: () => setImportOpen(true),
|
|
5464
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Download, { className: "mr-2 h-4 w-4" }),
|
|
6075
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Download, { className: "mr-2 h-4 w-4" }), locale.toolbar.import]
|
|
5465
6076
|
}),
|
|
5466
6077
|
canExport && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
|
|
5467
6078
|
variant: "outline",
|
|
5468
6079
|
size: "sm",
|
|
5469
6080
|
onClick: handleExportClick,
|
|
5470
6081
|
disabled: exporting || selectedCount === 0 && !resource.handlers.export,
|
|
5471
|
-
children: [exporting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Upload, { className: "mr-2 h-4 w-4" }), selectedCount > 0 ?
|
|
6082
|
+
children: [exporting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Upload, { className: "mr-2 h-4 w-4" }), selectedCount > 0 ? locale.toolbar.exportSelected(selectedCount) : locale.toolbar.export]
|
|
5472
6083
|
}),
|
|
5473
6084
|
can.create && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
5474
6085
|
onClick: resource.handlers.openCreate,
|
|
5475
|
-
children:
|
|
6086
|
+
children: locale.toolbar.create
|
|
5476
6087
|
})
|
|
5477
6088
|
]
|
|
5478
6089
|
})]
|
|
@@ -5484,12 +6095,12 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
5484
6095
|
overrides: tableOverrides,
|
|
5485
6096
|
exclude: hiddenColumns,
|
|
5486
6097
|
filterMode: tableConfig?.filterModes,
|
|
5487
|
-
actions: {
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
},
|
|
6098
|
+
actions: resolveActions(actionItems, {
|
|
6099
|
+
openView: resource.handlers.openView,
|
|
6100
|
+
openEdit: can.update ? resource.handlers.openEdit : void 0,
|
|
6101
|
+
copyRow: can.create ? resource.handlers.copyRow : void 0,
|
|
6102
|
+
openDelete: can.delete ? resource.handlers.openDelete : void 0
|
|
6103
|
+
}, locale.rowActions),
|
|
5493
6104
|
onDeleteSelected: can.delete ? resource.handlers.deleteMany : void 0,
|
|
5494
6105
|
onUpdateSelected: can.update ? resource.handlers.updateMany : void 0,
|
|
5495
6106
|
batchUpdateFields: can.update ? batchFields : void 0,
|
|
@@ -5510,7 +6121,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
5510
6121
|
},
|
|
5511
6122
|
loading: resource.mutations.isCreating || resource.mutations.isUpdating,
|
|
5512
6123
|
variant: resource.modal.variant,
|
|
5513
|
-
overrides: formOverrides
|
|
6124
|
+
overrides: formOverrides,
|
|
6125
|
+
locale: locale.formModal
|
|
5514
6126
|
}),
|
|
5515
6127
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewModal, {
|
|
5516
6128
|
open: resource.modal.viewOpen,
|
|
@@ -5519,22 +6131,24 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
5519
6131
|
data: resource.modal.selected,
|
|
5520
6132
|
schema,
|
|
5521
6133
|
fields,
|
|
5522
|
-
denyFields
|
|
6134
|
+
denyFields,
|
|
6135
|
+
locale
|
|
5523
6136
|
}),
|
|
5524
6137
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.AlertDialog, {
|
|
5525
6138
|
open: resource.modal.deleteOpen,
|
|
5526
6139
|
onOpenChange: (open) => !open && resource.handlers.closeModals(),
|
|
5527
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.AlertDialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.AlertDialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.AlertDialogTitle, { children:
|
|
6140
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.AlertDialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.AlertDialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.AlertDialogTitle, { children: locale.deleteModal.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.AlertDialogDescription, { children: locale.deleteModal.description })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.AlertDialogFooter, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.AlertDialogCancel, { children: locale.deleteModal.cancel }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.AlertDialogAction, {
|
|
5528
6141
|
onClick: resource.handlers.confirmDelete,
|
|
5529
6142
|
disabled: resource.mutations.isDeleting,
|
|
5530
|
-
children: resource.mutations.isDeleting ?
|
|
6143
|
+
children: resource.mutations.isDeleting ? locale.deleteModal.confirming : locale.deleteModal.confirm
|
|
5531
6144
|
})] })] })
|
|
5532
6145
|
}),
|
|
5533
6146
|
canImport && resource.handlers.import && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImportDialog, {
|
|
5534
6147
|
open: importOpen,
|
|
5535
6148
|
onOpenChange: setImportOpen,
|
|
5536
6149
|
onImport: resource.handlers.import,
|
|
5537
|
-
columns: importColumns
|
|
6150
|
+
columns: importColumns,
|
|
6151
|
+
locale: locale.importDialog
|
|
5538
6152
|
})
|
|
5539
6153
|
]
|
|
5540
6154
|
});
|
|
@@ -6166,6 +6780,8 @@ function DataTableToolbarFilter({ column }) {
|
|
|
6166
6780
|
const columnMeta = column.columnDef.meta;
|
|
6167
6781
|
return react.useCallback(() => {
|
|
6168
6782
|
if (!columnMeta?.variant) return null;
|
|
6783
|
+
const modes = columnMeta.modes;
|
|
6784
|
+
if (modes && !modes.includes("advanced")) return null;
|
|
6169
6785
|
switch (columnMeta.variant) {
|
|
6170
6786
|
case "text": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Input, {
|
|
6171
6787
|
placeholder: columnMeta.placeholder ?? columnMeta.label,
|
|
@@ -6287,6 +6903,10 @@ function modalReducer(state, action) {
|
|
|
6287
6903
|
function useAutoCrudResource({ router, schema, query: queryTransform, options = {} }) {
|
|
6288
6904
|
const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher } = options;
|
|
6289
6905
|
const toast = (0, react.useMemo)(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
|
|
6906
|
+
const hooksRef = (0, react.useRef)(hooks);
|
|
6907
|
+
(0, react.useEffect)(() => {
|
|
6908
|
+
hooksRef.current = hooks;
|
|
6909
|
+
}, [hooks]);
|
|
6290
6910
|
const columns = (0, react.useMemo)(() => createTableSchema(schema), [schema]);
|
|
6291
6911
|
const [urlParams] = useQueryStates({
|
|
6292
6912
|
page: parseAsInteger.withDefault(1),
|
|
@@ -6350,15 +6970,16 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
6350
6970
|
payload: variant
|
|
6351
6971
|
}), []);
|
|
6352
6972
|
const submitCreate = (0, react.useCallback)(async (values) => {
|
|
6973
|
+
const currentHooks = hooksRef.current;
|
|
6353
6974
|
try {
|
|
6354
|
-
if (
|
|
6355
|
-
const result = await
|
|
6975
|
+
if (currentHooks?.beforeCreate) {
|
|
6976
|
+
const result = await currentHooks.beforeCreate(values);
|
|
6356
6977
|
if (typeof result === "boolean") {
|
|
6357
6978
|
if (result) {
|
|
6358
6979
|
toast.success("创建成功");
|
|
6359
6980
|
closeModals();
|
|
6360
6981
|
listQuery.refetch();
|
|
6361
|
-
|
|
6982
|
+
currentHooks?.onSuccess?.("create", values);
|
|
6362
6983
|
}
|
|
6363
6984
|
return;
|
|
6364
6985
|
}
|
|
@@ -6369,36 +6990,36 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
6369
6990
|
toast.success("创建成功");
|
|
6370
6991
|
closeModals();
|
|
6371
6992
|
listQuery.refetch();
|
|
6372
|
-
|
|
6993
|
+
currentHooks?.onSuccess?.("create", data);
|
|
6373
6994
|
},
|
|
6374
6995
|
onError: (error) => {
|
|
6375
6996
|
const err = error;
|
|
6376
6997
|
toast.error(`创建失败: ${err.message}`);
|
|
6377
|
-
|
|
6998
|
+
currentHooks?.onError?.("create", err);
|
|
6378
6999
|
}
|
|
6379
7000
|
});
|
|
6380
7001
|
} catch (error) {
|
|
6381
7002
|
const err = error;
|
|
6382
7003
|
toast.error(`创建失败: ${err.message}`);
|
|
6383
|
-
|
|
7004
|
+
currentHooks?.onError?.("create", err);
|
|
6384
7005
|
}
|
|
6385
7006
|
}, [
|
|
6386
7007
|
createMutation,
|
|
6387
7008
|
closeModals,
|
|
6388
|
-
listQuery
|
|
6389
|
-
hooks
|
|
7009
|
+
listQuery
|
|
6390
7010
|
]);
|
|
6391
7011
|
const submitUpdate = (0, react.useCallback)(async (values) => {
|
|
7012
|
+
const currentHooks = hooksRef.current;
|
|
6392
7013
|
if (!modal.selected) return;
|
|
6393
7014
|
try {
|
|
6394
|
-
if (
|
|
6395
|
-
const result = await
|
|
7015
|
+
if (currentHooks?.beforeUpdate) {
|
|
7016
|
+
const result = await currentHooks.beforeUpdate(values, modal.selected);
|
|
6396
7017
|
if (typeof result === "boolean") {
|
|
6397
7018
|
if (result) {
|
|
6398
7019
|
toast.success("更新成功");
|
|
6399
7020
|
closeModals();
|
|
6400
7021
|
listQuery.refetch();
|
|
6401
|
-
|
|
7022
|
+
currentHooks?.onSuccess?.("update", values);
|
|
6402
7023
|
}
|
|
6403
7024
|
return;
|
|
6404
7025
|
}
|
|
@@ -6413,38 +7034,38 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
6413
7034
|
toast.success("更新成功");
|
|
6414
7035
|
closeModals();
|
|
6415
7036
|
listQuery.refetch();
|
|
6416
|
-
|
|
7037
|
+
currentHooks?.onSuccess?.("update", data);
|
|
6417
7038
|
},
|
|
6418
7039
|
onError: (error) => {
|
|
6419
7040
|
const err = error;
|
|
6420
7041
|
toast.error(`更新失败: ${err.message}`);
|
|
6421
|
-
|
|
7042
|
+
currentHooks?.onError?.("update", err);
|
|
6422
7043
|
}
|
|
6423
7044
|
});
|
|
6424
7045
|
} catch (error) {
|
|
6425
7046
|
const err = error;
|
|
6426
7047
|
toast.error(`更新失败: ${err.message}`);
|
|
6427
|
-
|
|
7048
|
+
currentHooks?.onError?.("update", err);
|
|
6428
7049
|
}
|
|
6429
7050
|
}, [
|
|
6430
7051
|
modal.selected,
|
|
6431
7052
|
idKey,
|
|
6432
7053
|
updateMutation,
|
|
6433
7054
|
closeModals,
|
|
6434
|
-
listQuery
|
|
6435
|
-
hooks
|
|
7055
|
+
listQuery
|
|
6436
7056
|
]);
|
|
6437
7057
|
const confirmDelete = (0, react.useCallback)(async () => {
|
|
7058
|
+
const currentHooks = hooksRef.current;
|
|
6438
7059
|
if (!modal.selected) return;
|
|
6439
7060
|
try {
|
|
6440
|
-
if (
|
|
6441
|
-
const result = await
|
|
7061
|
+
if (currentHooks?.beforeDelete) {
|
|
7062
|
+
const result = await currentHooks.beforeDelete(modal.selected);
|
|
6442
7063
|
if (typeof result === "boolean") {
|
|
6443
7064
|
if (result) {
|
|
6444
7065
|
toast.success("删除成功");
|
|
6445
7066
|
closeModals();
|
|
6446
7067
|
listQuery.refetch();
|
|
6447
|
-
|
|
7068
|
+
currentHooks?.onSuccess?.("delete", modal.selected);
|
|
6448
7069
|
}
|
|
6449
7070
|
return;
|
|
6450
7071
|
}
|
|
@@ -6455,28 +7076,28 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
6455
7076
|
toast.success("删除成功");
|
|
6456
7077
|
closeModals();
|
|
6457
7078
|
listQuery.refetch();
|
|
6458
|
-
|
|
7079
|
+
currentHooks?.onSuccess?.("delete", data);
|
|
6459
7080
|
},
|
|
6460
7081
|
onError: (error) => {
|
|
6461
7082
|
const err = error;
|
|
6462
7083
|
toast.error(`删除失败: ${err.message}`);
|
|
6463
|
-
|
|
7084
|
+
currentHooks?.onError?.("delete", err);
|
|
6464
7085
|
}
|
|
6465
7086
|
});
|
|
6466
7087
|
} catch (error) {
|
|
6467
7088
|
const err = error;
|
|
6468
7089
|
toast.error(`删除失败: ${err.message}`);
|
|
6469
|
-
|
|
7090
|
+
currentHooks?.onError?.("delete", err);
|
|
6470
7091
|
}
|
|
6471
7092
|
}, [
|
|
6472
7093
|
modal.selected,
|
|
6473
7094
|
idKey,
|
|
6474
7095
|
deleteMutation,
|
|
6475
7096
|
closeModals,
|
|
6476
|
-
listQuery
|
|
6477
|
-
hooks
|
|
7097
|
+
listQuery
|
|
6478
7098
|
]);
|
|
6479
7099
|
const deleteMany = (0, react.useCallback)((rows) => {
|
|
7100
|
+
const currentHooks = hooksRef.current;
|
|
6480
7101
|
if (!deleteManyMutation) {
|
|
6481
7102
|
toast.error("批量删除功能未启用");
|
|
6482
7103
|
return;
|
|
@@ -6486,21 +7107,21 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
6486
7107
|
onSuccess: (data) => {
|
|
6487
7108
|
toast.success(`成功删除 ${ids.length} 条记录`);
|
|
6488
7109
|
listQuery.refetch();
|
|
6489
|
-
|
|
7110
|
+
currentHooks?.onSuccess?.("delete", data);
|
|
6490
7111
|
},
|
|
6491
7112
|
onError: (error) => {
|
|
6492
7113
|
const err = error;
|
|
6493
7114
|
toast.error(`批量删除失败: ${err.message}`);
|
|
6494
|
-
|
|
7115
|
+
currentHooks?.onError?.("delete", err);
|
|
6495
7116
|
}
|
|
6496
7117
|
});
|
|
6497
7118
|
}, [
|
|
6498
7119
|
deleteManyMutation,
|
|
6499
7120
|
idKey,
|
|
6500
|
-
listQuery
|
|
6501
|
-
hooks
|
|
7121
|
+
listQuery
|
|
6502
7122
|
]);
|
|
6503
7123
|
const updateMany = (0, react.useCallback)((rows, data) => {
|
|
7124
|
+
const currentHooks = hooksRef.current;
|
|
6504
7125
|
if (!updateManyMutation) {
|
|
6505
7126
|
toast.error("批量更新功能未启用");
|
|
6506
7127
|
return;
|
|
@@ -6513,19 +7134,18 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
6513
7134
|
onSuccess: (result) => {
|
|
6514
7135
|
toast.success(`成功更新 ${ids.length} 条记录`);
|
|
6515
7136
|
listQuery.refetch();
|
|
6516
|
-
|
|
7137
|
+
currentHooks?.onSuccess?.("update", result);
|
|
6517
7138
|
},
|
|
6518
7139
|
onError: (error) => {
|
|
6519
7140
|
const err = error;
|
|
6520
7141
|
toast.error(`批量更新失败: ${err.message}`);
|
|
6521
|
-
|
|
7142
|
+
currentHooks?.onError?.("update", err);
|
|
6522
7143
|
}
|
|
6523
7144
|
});
|
|
6524
7145
|
}, [
|
|
6525
7146
|
updateManyMutation,
|
|
6526
7147
|
idKey,
|
|
6527
|
-
listQuery
|
|
6528
|
-
hooks
|
|
7148
|
+
listQuery
|
|
6529
7149
|
]);
|
|
6530
7150
|
const copyRow = (0, react.useCallback)((row) => {
|
|
6531
7151
|
dispatch({
|
|
@@ -6924,13 +7544,19 @@ exports.createSelectColumn = createSelectColumn;
|
|
|
6924
7544
|
exports.createTRPCDataSource = createTRPCDataSource;
|
|
6925
7545
|
exports.createTableSchema = createTableSchema;
|
|
6926
7546
|
exports.dataToCSV = dataToCSV;
|
|
7547
|
+
exports.deDE = deDE;
|
|
6927
7548
|
exports.downloadCSVTemplate = downloadCSVTemplate;
|
|
7549
|
+
exports.enUS = enUS;
|
|
7550
|
+
exports.esES = esES;
|
|
6928
7551
|
exports.exportAllToCSV = exportAllToCSV;
|
|
6929
7552
|
exports.exportTableToCSV = exportTableToCSV;
|
|
6930
7553
|
exports.formatDate = formatDate;
|
|
7554
|
+
exports.frFR = frFR;
|
|
6931
7555
|
exports.generateCSVTemplate = generateCSVTemplate;
|
|
6932
7556
|
exports.getUrlParams = getUrlParams;
|
|
6933
7557
|
exports.humanize = humanize;
|
|
7558
|
+
exports.jaJP = jaJP;
|
|
7559
|
+
exports.koKR = koKR;
|
|
6934
7560
|
exports.noopToastAdapter = noopToastAdapter;
|
|
6935
7561
|
exports.parseAsArrayOf = parseAsArrayOf;
|
|
6936
7562
|
exports.parseAsInteger = parseAsInteger;
|
|
@@ -6940,6 +7566,7 @@ exports.parseCSV = parseCSV;
|
|
|
6940
7566
|
exports.parseImportFile = parseImportFile;
|
|
6941
7567
|
exports.parseJSON = parseJSON;
|
|
6942
7568
|
exports.parseZodField = parseZodField;
|
|
7569
|
+
exports.resolveLocale = resolveLocale;
|
|
6943
7570
|
exports.setSearchParams = setSearchParams;
|
|
6944
7571
|
exports.useAutoCrudResource = useAutoCrudResource;
|
|
6945
7572
|
exports.useDataTable = useDataTable;
|
|
@@ -6947,4 +7574,5 @@ exports.useQueryState = useQueryState;
|
|
|
6947
7574
|
exports.useQueryStates = useQueryStates;
|
|
6948
7575
|
exports.useReadableFilters = useReadableFilters;
|
|
6949
7576
|
exports.useUrlState = useUrlState;
|
|
6950
|
-
exports.useUrlStates = useUrlStates;
|
|
7577
|
+
exports.useUrlStates = useUrlStates;
|
|
7578
|
+
exports.zhCN = zhCN;
|