@wordrhyme/auto-crud 1.1.2 → 1.1.3
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.cjs +206 -45
- package/dist/index.d.cts +51 -20
- package/dist/index.d.ts +35 -4
- package/dist/index.js +206 -46
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -59,6 +59,8 @@ let __pixpilot_formily_shadcn = require("@pixpilot/formily-shadcn");
|
|
|
59
59
|
__pixpilot_formily_shadcn = __toESM(__pixpilot_formily_shadcn);
|
|
60
60
|
let __formily_react = require("@formily/react");
|
|
61
61
|
__formily_react = __toESM(__formily_react);
|
|
62
|
+
let __pixpilot_shadcn_ui = require("@pixpilot/shadcn-ui");
|
|
63
|
+
__pixpilot_shadcn_ui = __toESM(__pixpilot_shadcn_ui);
|
|
62
64
|
let sonner = require("sonner");
|
|
63
65
|
sonner = __toESM(sonner);
|
|
64
66
|
|
|
@@ -2705,8 +2707,17 @@ function DataTableViewOptions({ table, disabled,...props }) {
|
|
|
2705
2707
|
|
|
2706
2708
|
//#endregion
|
|
2707
2709
|
//#region src/components/auto-crud/auto-table-simple-filters.tsx
|
|
2708
|
-
function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange }) {
|
|
2709
|
-
const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()),
|
|
2710
|
+
function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange, leading }) {
|
|
2711
|
+
const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()).sort((a, b) => {
|
|
2712
|
+
const readOrder = (column) => {
|
|
2713
|
+
const metaIndex = column.columnDef.meta?.index;
|
|
2714
|
+
const columnIndex = column.columnDef.index;
|
|
2715
|
+
return typeof metaIndex === "number" ? metaIndex : typeof columnIndex === "number" ? columnIndex : 1e4;
|
|
2716
|
+
};
|
|
2717
|
+
const orderDiff = readOrder(a) - readOrder(b);
|
|
2718
|
+
if (orderDiff !== 0) return orderDiff;
|
|
2719
|
+
return a.getIndex() - b.getIndex();
|
|
2720
|
+
}), [table]);
|
|
2710
2721
|
const queryStateOptions = table.options.meta?.queryStateOptions;
|
|
2711
2722
|
const [queryFilters, setQueryFilters] = useReadableFilters(columns, {
|
|
2712
2723
|
...queryStateOptions,
|
|
@@ -2823,8 +2834,9 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
2823
2834
|
if (!mounted) return null;
|
|
2824
2835
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2825
2836
|
ref: containerRef,
|
|
2826
|
-
className: "flex flex-wrap items-center gap-2 min-w-0",
|
|
2837
|
+
className: "flex flex-1 flex-wrap items-center gap-2 min-w-0",
|
|
2827
2838
|
children: [
|
|
2839
|
+
leading,
|
|
2828
2840
|
filterColumns.map((column, index) => {
|
|
2829
2841
|
const meta = column.columnDef.meta;
|
|
2830
2842
|
const value = getFilterValue(column.id);
|
|
@@ -3957,7 +3969,7 @@ function createTableSchema(schema, options) {
|
|
|
3957
3969
|
if (cached) return [...cached];
|
|
3958
3970
|
}
|
|
3959
3971
|
const shape = schema.shape;
|
|
3960
|
-
const
|
|
3972
|
+
const columnEntries = [];
|
|
3961
3973
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
3962
3974
|
if (exclude.includes(key)) continue;
|
|
3963
3975
|
const override = overrides?.[key];
|
|
@@ -3978,20 +3990,31 @@ function createTableSchema(schema, options) {
|
|
|
3978
3990
|
options: override?.meta?.options ?? autoOptions
|
|
3979
3991
|
};
|
|
3980
3992
|
const { meta: _ignoredMeta,...restOverride } = override ?? {};
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
+
const sourceIndex = columnEntries.length;
|
|
3994
|
+
const order = typeof restOverride.index === "number" ? restOverride.index : typeof meta.index === "number" ? meta.index : 1e4 + sourceIndex;
|
|
3995
|
+
columnEntries.push({
|
|
3996
|
+
order,
|
|
3997
|
+
sourceIndex,
|
|
3998
|
+
column: {
|
|
3999
|
+
id: key,
|
|
4000
|
+
accessorKey: key,
|
|
4001
|
+
header: ({ column }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableColumnHeader, {
|
|
4002
|
+
column,
|
|
4003
|
+
label
|
|
4004
|
+
}),
|
|
4005
|
+
cell: ({ row }) => renderCell(row.getValue(key), parsed.type),
|
|
4006
|
+
enableColumnFilter: true,
|
|
4007
|
+
enableSorting: true,
|
|
4008
|
+
meta,
|
|
4009
|
+
...restOverride
|
|
4010
|
+
}
|
|
3993
4011
|
});
|
|
3994
4012
|
}
|
|
4013
|
+
const columns = columnEntries.sort((a, b) => {
|
|
4014
|
+
const orderDiff = a.order - b.order;
|
|
4015
|
+
if (orderDiff !== 0) return orderDiff;
|
|
4016
|
+
return a.sourceIndex - b.sourceIndex;
|
|
4017
|
+
}).map((entry) => entry.column);
|
|
3995
4018
|
if (canCache) baseTableSchemaCache.set(schema, columns);
|
|
3996
4019
|
return columns;
|
|
3997
4020
|
}
|
|
@@ -4663,10 +4686,19 @@ const filterModeConfig = {
|
|
|
4663
4686
|
tooltip: "Linear-style command filters"
|
|
4664
4687
|
}
|
|
4665
4688
|
};
|
|
4666
|
-
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
|
|
4689
|
+
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
|
|
4667
4690
|
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
4668
4691
|
const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
|
|
4669
4692
|
const showToggle = modes.length > 1;
|
|
4693
|
+
const [searchValue, setSearchValue] = useUrlState("search", parseAsString.withDefault(""), {
|
|
4694
|
+
shallow: false,
|
|
4695
|
+
clearOnDefault: true
|
|
4696
|
+
});
|
|
4697
|
+
const [, setSearchPage] = useUrlState("page", parseAsInteger.withDefault(1), {
|
|
4698
|
+
shallow: false,
|
|
4699
|
+
clearOnDefault: true
|
|
4700
|
+
});
|
|
4701
|
+
const searchConfig = search && typeof search === "object" ? search : {};
|
|
4670
4702
|
const { table, shallow, debounceMs, throttleMs } = useDataTable({
|
|
4671
4703
|
data,
|
|
4672
4704
|
columns: (0, react.useMemo)(() => {
|
|
@@ -4738,11 +4770,21 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4738
4770
|
})
|
|
4739
4771
|
})
|
|
4740
4772
|
})] }) : null;
|
|
4773
|
+
const searchInput = search ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Input, {
|
|
4774
|
+
value: searchValue,
|
|
4775
|
+
onChange: (event) => {
|
|
4776
|
+
setSearchValue(event.target.value);
|
|
4777
|
+
setSearchPage(1);
|
|
4778
|
+
},
|
|
4779
|
+
placeholder: searchConfig.placeholder ?? "搜索",
|
|
4780
|
+
className: "h-8 w-48 shrink-0"
|
|
4781
|
+
}) : null;
|
|
4741
4782
|
const filtersContent = (0, react.useMemo)(() => {
|
|
4742
4783
|
switch (currentMode) {
|
|
4743
4784
|
case "simple": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTableSimpleFilters, {
|
|
4744
4785
|
table,
|
|
4745
|
-
shallow
|
|
4786
|
+
shallow,
|
|
4787
|
+
leading: searchInput
|
|
4746
4788
|
});
|
|
4747
4789
|
case "command": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableFilterMenu, {
|
|
4748
4790
|
table,
|
|
@@ -4762,6 +4804,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4762
4804
|
currentMode,
|
|
4763
4805
|
table,
|
|
4764
4806
|
shallow,
|
|
4807
|
+
searchInput,
|
|
4765
4808
|
debounceMs,
|
|
4766
4809
|
throttleMs
|
|
4767
4810
|
]);
|
|
@@ -4773,10 +4816,14 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4773
4816
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4774
4817
|
className: "flex flex-1 items-start gap-2 min-h-[40px]",
|
|
4775
4818
|
"data-filter-parent": true,
|
|
4776
|
-
children: [
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4819
|
+
children: [
|
|
4820
|
+
currentMode !== "simple" && searchInput,
|
|
4821
|
+
currentMode !== "simple" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableSortList, {
|
|
4822
|
+
table,
|
|
4823
|
+
align: "start"
|
|
4824
|
+
}),
|
|
4825
|
+
filtersContent
|
|
4826
|
+
]
|
|
4780
4827
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4781
4828
|
className: "flex items-center gap-2",
|
|
4782
4829
|
children: [
|
|
@@ -4965,10 +5012,44 @@ function createEditFormSchema(schema, options) {
|
|
|
4965
5012
|
|
|
4966
5013
|
//#endregion
|
|
4967
5014
|
//#region src/components/auto-crud/auto-form.tsx
|
|
5015
|
+
const COMBOBOX_LIST_CLASS = "[&_[cmdk-list]]:max-h-[360px]";
|
|
4968
5016
|
const FormilySwitch = (0, __formily_react.connect)(__pixpilot_shadcn.Switch, (0, __formily_react.mapProps)({
|
|
4969
5017
|
value: "checked",
|
|
4970
5018
|
onInput: "onCheckedChange"
|
|
4971
5019
|
}));
|
|
5020
|
+
const FormilyCombobox = (0, __formily_react.connect)(AutoCrudCombobox, (0, __formily_react.mapProps)({ dataSource: "options" }));
|
|
5021
|
+
function AutoCrudCombobox({ options = [], filter, className,...props }) {
|
|
5022
|
+
const resolvedFilter = (0, react.useMemo)(() => {
|
|
5023
|
+
if (typeof filter === "function") return filter;
|
|
5024
|
+
return createComboboxFilter(options);
|
|
5025
|
+
}, [filter, options]);
|
|
5026
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn_ui.Combobox, {
|
|
5027
|
+
...props,
|
|
5028
|
+
options,
|
|
5029
|
+
filter: resolvedFilter,
|
|
5030
|
+
className: (0, __pixpilot_shadcn.cn)(COMBOBOX_LIST_CLASS, className)
|
|
5031
|
+
});
|
|
5032
|
+
}
|
|
5033
|
+
function createComboboxFilter(options) {
|
|
5034
|
+
const searchTextByValue = new Map(options.map((option) => [option.value, collectSearchText(option.value, option.label, option.searchText, option.keywords).toLocaleLowerCase()]));
|
|
5035
|
+
return (value, search, keywords) => {
|
|
5036
|
+
const query = search.trim().toLocaleLowerCase();
|
|
5037
|
+
if (!query) return 1;
|
|
5038
|
+
const searchText = collectSearchText(searchTextByValue.get(value), keywords).toLocaleLowerCase();
|
|
5039
|
+
if (searchText === query) return 2;
|
|
5040
|
+
return searchText.includes(query) ? 1 : 0;
|
|
5041
|
+
};
|
|
5042
|
+
}
|
|
5043
|
+
function collectSearchText(...parts) {
|
|
5044
|
+
const texts = [];
|
|
5045
|
+
for (const part of parts) {
|
|
5046
|
+
if (!part) continue;
|
|
5047
|
+
if (Array.isArray(part)) texts.push(...part.map(String));
|
|
5048
|
+
else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
|
|
5049
|
+
else texts.push(String(part));
|
|
5050
|
+
}
|
|
5051
|
+
return texts.join(" ");
|
|
5052
|
+
}
|
|
4972
5053
|
function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
|
|
4973
5054
|
const form = (0, react.useMemo)(() => (0, __formily_core.createForm)({ initialValues }), [JSON.stringify(initialValues)]);
|
|
4974
5055
|
const formSchema = (0, react.useMemo)(() => createEditFormSchema(zodSchema, {
|
|
@@ -4993,10 +5074,16 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
4993
5074
|
form,
|
|
4994
5075
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_formily_shadcn.JsonSchemaField, {
|
|
4995
5076
|
schema: formSchema,
|
|
4996
|
-
components: { fields: {
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5077
|
+
components: { fields: {
|
|
5078
|
+
Combobox: {
|
|
5079
|
+
component: FormilyCombobox,
|
|
5080
|
+
decorator: "FormItem"
|
|
5081
|
+
},
|
|
5082
|
+
Switch: {
|
|
5083
|
+
component: FormilySwitch,
|
|
5084
|
+
decorator: "FormItem"
|
|
5085
|
+
}
|
|
5086
|
+
} }
|
|
5000
5087
|
}), showSubmitButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5001
5088
|
className: "flex justify-end gap-2 mt-6",
|
|
5002
5089
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
|
|
@@ -6088,6 +6175,11 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
|
|
|
6088
6175
|
*/
|
|
6089
6176
|
function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbarActions, locale: localeProp, onCreate }) {
|
|
6090
6177
|
const locale = resolveLocale(localeProp);
|
|
6178
|
+
const resolvedSchema = resource.schema ?? schema;
|
|
6179
|
+
const resolvedFields = react.useMemo(() => ({
|
|
6180
|
+
...fields,
|
|
6181
|
+
...resource.fields
|
|
6182
|
+
}), [fields, resource.fields]);
|
|
6091
6183
|
const can = {
|
|
6092
6184
|
create: permissions?.can?.create ?? true,
|
|
6093
6185
|
update: permissions?.can?.update ?? true,
|
|
@@ -6103,10 +6195,10 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6103
6195
|
const [exporting, setExporting] = react.useState(false);
|
|
6104
6196
|
const getSelectedRowsRef = react.useRef(null);
|
|
6105
6197
|
const importColumns = react.useMemo(() => {
|
|
6106
|
-
const shape =
|
|
6198
|
+
const shape = resolvedSchema.shape;
|
|
6107
6199
|
const denySet = new Set(denyFields ?? []);
|
|
6108
6200
|
return Object.keys(shape).filter((key) => !denySet.has(key));
|
|
6109
|
-
}, [
|
|
6201
|
+
}, [resolvedSchema, denyFields]);
|
|
6110
6202
|
const handleExport = react.useCallback(async (mode) => {
|
|
6111
6203
|
const filename = title ?? "export";
|
|
6112
6204
|
const excludeColumns = denyFields;
|
|
@@ -6138,21 +6230,34 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6138
6230
|
setExporting(false);
|
|
6139
6231
|
}
|
|
6140
6232
|
}, [selectedCount, handleExport]);
|
|
6141
|
-
const tableOverrides = react.useMemo(() => buildTableOverrides(
|
|
6142
|
-
const formOverrides = react.useMemo(() => buildFormOverrides(
|
|
6143
|
-
|
|
6233
|
+
const tableOverrides = react.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides), [resolvedFields, tableConfig?.overrides]);
|
|
6234
|
+
const formOverrides = react.useMemo(() => buildFormOverrides(resolvedFields, formConfig?.overrides, denyFields), [
|
|
6235
|
+
resolvedFields,
|
|
6144
6236
|
formConfig?.overrides,
|
|
6145
6237
|
denyFields
|
|
6146
6238
|
]);
|
|
6147
|
-
const hiddenColumns = react.useMemo(() => buildHiddenColumns(
|
|
6148
|
-
|
|
6239
|
+
const hiddenColumns = react.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields), [
|
|
6240
|
+
resolvedFields,
|
|
6149
6241
|
tableConfig?.hidden,
|
|
6150
6242
|
denyFields
|
|
6151
6243
|
]);
|
|
6152
|
-
const
|
|
6153
|
-
|
|
6244
|
+
const searchConfig = react.useMemo(() => {
|
|
6245
|
+
const tableSearch = tableConfig?.search;
|
|
6246
|
+
if (tableSearch === false) return false;
|
|
6247
|
+
if (tableSearch && typeof tableSearch === "object") return tableSearch;
|
|
6248
|
+
return Object.values(resolvedFields).some((field) => field?.search === true) ? true : false;
|
|
6249
|
+
}, [resolvedFields, tableConfig?.search]);
|
|
6250
|
+
const batchFields = react.useMemo(() => buildBatchUpdateFields(resolvedSchema, tableConfig?.batchFields, resolvedFields), [
|
|
6251
|
+
resolvedSchema,
|
|
6154
6252
|
tableConfig?.batchFields,
|
|
6155
|
-
|
|
6253
|
+
resolvedFields
|
|
6254
|
+
]);
|
|
6255
|
+
const formInitialValues = react.useMemo(() => {
|
|
6256
|
+
return resource.modal.createOpen ? resource.modal.copySource ?? void 0 : resource.modal.selected ?? void 0;
|
|
6257
|
+
}, [
|
|
6258
|
+
resource.modal.createOpen,
|
|
6259
|
+
resource.modal.copySource,
|
|
6260
|
+
resource.modal.selected
|
|
6156
6261
|
]);
|
|
6157
6262
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6158
6263
|
className: "space-y-4",
|
|
@@ -6233,11 +6338,12 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6233
6338
|
}),
|
|
6234
6339
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTable, {
|
|
6235
6340
|
data: resource.tableData.data,
|
|
6236
|
-
schema,
|
|
6341
|
+
schema: resolvedSchema,
|
|
6237
6342
|
pageCount: resource.tableData.pageCount,
|
|
6238
6343
|
overrides: tableOverrides,
|
|
6239
6344
|
exclude: hiddenColumns,
|
|
6240
6345
|
filterMode: tableConfig?.filterModes,
|
|
6346
|
+
search: searchConfig,
|
|
6241
6347
|
actions: resolveActions(actionItems, {
|
|
6242
6348
|
openView: resource.handlers.openView,
|
|
6243
6349
|
openEdit: can.update ? resource.handlers.openEdit : void 0,
|
|
@@ -6258,8 +6364,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6258
6364
|
open: resource.modal.createOpen || resource.modal.editOpen,
|
|
6259
6365
|
onOpenChange: (open) => !open && resource.handlers.closeModals(),
|
|
6260
6366
|
mode: resource.modal.createOpen ? "create" : "edit",
|
|
6261
|
-
schema,
|
|
6262
|
-
initialValues:
|
|
6367
|
+
schema: resolvedSchema,
|
|
6368
|
+
initialValues: formInitialValues,
|
|
6263
6369
|
onSubmit: (values) => {
|
|
6264
6370
|
if (resource.modal.createOpen) resource.handlers.submitCreate(values);
|
|
6265
6371
|
else resource.handlers.submitUpdate(values);
|
|
@@ -6276,8 +6382,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6276
6382
|
onOpenChange: (open) => !open && resource.handlers.closeModals(),
|
|
6277
6383
|
variant: resource.modal.variant,
|
|
6278
6384
|
data: resource.modal.selected,
|
|
6279
|
-
schema,
|
|
6280
|
-
fields,
|
|
6385
|
+
schema: resolvedSchema,
|
|
6386
|
+
fields: resolvedFields,
|
|
6281
6387
|
denyFields,
|
|
6282
6388
|
locale
|
|
6283
6389
|
}),
|
|
@@ -6996,6 +7102,53 @@ const noopToastAdapter = {
|
|
|
6996
7102
|
info: () => {},
|
|
6997
7103
|
warning: () => {}
|
|
6998
7104
|
};
|
|
7105
|
+
function isRecord(value) {
|
|
7106
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7107
|
+
}
|
|
7108
|
+
function isJsonSchemaObject(value) {
|
|
7109
|
+
return isRecord(value) && isRecord(value.properties);
|
|
7110
|
+
}
|
|
7111
|
+
function readJsonSchemaType(property) {
|
|
7112
|
+
return Array.isArray(property.type) ? property.type.find((item) => item !== "null") : property.type;
|
|
7113
|
+
}
|
|
7114
|
+
function jsonPropertyToZod(property, required) {
|
|
7115
|
+
const enumValues = property.enum?.filter((value) => typeof value === "string");
|
|
7116
|
+
let schema;
|
|
7117
|
+
if (enumValues && enumValues.length > 0) schema = zod.z.enum(enumValues);
|
|
7118
|
+
else switch (readJsonSchemaType(property)) {
|
|
7119
|
+
case "number":
|
|
7120
|
+
case "integer":
|
|
7121
|
+
schema = zod.z.number();
|
|
7122
|
+
break;
|
|
7123
|
+
case "boolean":
|
|
7124
|
+
schema = zod.z.boolean();
|
|
7125
|
+
break;
|
|
7126
|
+
case "array":
|
|
7127
|
+
schema = zod.z.array(zod.z.unknown());
|
|
7128
|
+
break;
|
|
7129
|
+
case "object":
|
|
7130
|
+
schema = zod.z.record(zod.z.string(), zod.z.unknown());
|
|
7131
|
+
break;
|
|
7132
|
+
case "string":
|
|
7133
|
+
schema = property.format === "date" || property.format === "date-time" ? zod.z.coerce.date() : zod.z.string();
|
|
7134
|
+
break;
|
|
7135
|
+
default: schema = zod.z.unknown();
|
|
7136
|
+
}
|
|
7137
|
+
return required ? schema : schema.optional();
|
|
7138
|
+
}
|
|
7139
|
+
function mergeMetadataSchema(baseSchema, metadataSchema) {
|
|
7140
|
+
if (!isJsonSchemaObject(metadataSchema)) return baseSchema;
|
|
7141
|
+
const required = new Set(Array.isArray(metadataSchema.required) ? metadataSchema.required.filter((field) => typeof field === "string") : []);
|
|
7142
|
+
const extensionShape = {};
|
|
7143
|
+
for (const [field, property] of Object.entries(metadataSchema.properties ?? {})) {
|
|
7144
|
+
if (field in baseSchema.shape) continue;
|
|
7145
|
+
extensionShape[field] = jsonPropertyToZod(property, required.has(field));
|
|
7146
|
+
}
|
|
7147
|
+
return Object.keys(extensionShape).length > 0 ? baseSchema.extend(extensionShape) : baseSchema;
|
|
7148
|
+
}
|
|
7149
|
+
function readMetadataFields(fields) {
|
|
7150
|
+
return isRecord(fields) ? fields : void 0;
|
|
7151
|
+
}
|
|
6999
7152
|
/**
|
|
7000
7153
|
* Modal 状态 Reducer
|
|
7001
7154
|
*/
|
|
@@ -7054,12 +7207,16 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7054
7207
|
(0, react.useEffect)(() => {
|
|
7055
7208
|
hooksRef.current = hooks;
|
|
7056
7209
|
}, [hooks]);
|
|
7057
|
-
const
|
|
7210
|
+
const metadata = (router.meta?.useQuery(void 0, { staleTime: 3e4 }))?.data;
|
|
7211
|
+
const resolvedSchema = (0, react.useMemo)(() => mergeMetadataSchema(schema, metadata?.schema), [schema, metadata?.schema]);
|
|
7212
|
+
const metadataFields = (0, react.useMemo)(() => readMetadataFields(metadata?.fields), [metadata?.fields]);
|
|
7213
|
+
const columns = (0, react.useMemo)(() => createTableSchema(resolvedSchema), [resolvedSchema]);
|
|
7058
7214
|
const [urlParams] = useQueryStates({
|
|
7059
7215
|
page: parseAsInteger.withDefault(1),
|
|
7060
7216
|
perPage: parseAsInteger.withDefault(10),
|
|
7061
7217
|
sort: getSortingStateParser().withDefault([]),
|
|
7062
|
-
joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and")
|
|
7218
|
+
joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
|
|
7219
|
+
search: parseAsString.withDefault("")
|
|
7063
7220
|
}, { shallow: false });
|
|
7064
7221
|
const [filters] = useReadableFilters(columns, { shallow: false });
|
|
7065
7222
|
const queryInput = (0, react.useMemo)(() => {
|
|
@@ -7068,7 +7225,8 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7068
7225
|
perPage: urlParams.perPage,
|
|
7069
7226
|
sort: urlParams.sort,
|
|
7070
7227
|
filters,
|
|
7071
|
-
joinOperator: urlParams.joinOperator
|
|
7228
|
+
joinOperator: urlParams.joinOperator,
|
|
7229
|
+
...urlParams.search.trim() ? { search: urlParams.search.trim() } : {}
|
|
7072
7230
|
};
|
|
7073
7231
|
return queryTransform ? queryTransform(autoParams) : autoParams;
|
|
7074
7232
|
}, [
|
|
@@ -7080,6 +7238,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7080
7238
|
placeholderData: keepPreviousData,
|
|
7081
7239
|
staleTime: 0
|
|
7082
7240
|
});
|
|
7241
|
+
const tableRows = listQuery.data?.data ?? [];
|
|
7083
7242
|
const createMutation = router.create.useMutation();
|
|
7084
7243
|
const updateMutation = router.update.useMutation();
|
|
7085
7244
|
const deleteMutation = router.delete.useMutation();
|
|
@@ -7332,11 +7491,13 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7332
7491
|
const canExport = !!(exportFetcher || exportQuery);
|
|
7333
7492
|
return {
|
|
7334
7493
|
tableData: {
|
|
7335
|
-
data:
|
|
7494
|
+
data: tableRows,
|
|
7336
7495
|
pageCount: listQuery.data?.pageCount ?? 0,
|
|
7337
7496
|
isLoading: listQuery.isLoading,
|
|
7338
7497
|
isFetching: listQuery.isFetching
|
|
7339
7498
|
},
|
|
7499
|
+
schema: resolvedSchema,
|
|
7500
|
+
fields: metadataFields,
|
|
7340
7501
|
modal,
|
|
7341
7502
|
mutations: {
|
|
7342
7503
|
isCreating: createMutation.isPending,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime3 from "react/jsx-runtime";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import * as _tanstack_react_table0 from "@tanstack/react-table";
|
|
4
4
|
import { Column, ColumnDef, ColumnSort, Row, RowData, Table, TableOptions, TableState } from "@tanstack/react-table";
|
|
@@ -125,6 +125,10 @@ interface UseAutoCrudResourceReturn<TSchema extends z.ZodObject<z.ZodRawShape>,
|
|
|
125
125
|
/** 是否正在获取数据(用于显示微妙的加载指示器) */
|
|
126
126
|
isFetching: boolean;
|
|
127
127
|
};
|
|
128
|
+
/** Optional schema override from host metadata. */
|
|
129
|
+
schema?: z.ZodObject<z.ZodRawShape>;
|
|
130
|
+
/** Optional field config override from host metadata. */
|
|
131
|
+
fields?: Fields;
|
|
128
132
|
modal: ModalState<TListItem>;
|
|
129
133
|
mutations: {
|
|
130
134
|
isCreating: boolean;
|
|
@@ -182,6 +186,10 @@ interface CrudRouter {
|
|
|
182
186
|
export?: {
|
|
183
187
|
useQuery: (input?: any, opts?: any) => any;
|
|
184
188
|
};
|
|
189
|
+
/** 元数据路由(query,由 createCrudRouter 自动生成) */
|
|
190
|
+
meta?: {
|
|
191
|
+
useQuery: (input?: any, opts?: any) => any;
|
|
192
|
+
};
|
|
185
193
|
}
|
|
186
194
|
/**
|
|
187
195
|
* 自动生成的查询参数(由内部 URL 状态管理生成)
|
|
@@ -193,6 +201,7 @@ interface AutoQueryParams {
|
|
|
193
201
|
id: string;
|
|
194
202
|
desc: boolean;
|
|
195
203
|
}>;
|
|
204
|
+
search?: string;
|
|
196
205
|
filters: any[];
|
|
197
206
|
joinOperator: "and" | "or";
|
|
198
207
|
}
|
|
@@ -352,7 +361,7 @@ declare function AutoTableActionBar<TData>({
|
|
|
352
361
|
enableDelete,
|
|
353
362
|
extraActions,
|
|
354
363
|
actions
|
|
355
|
-
}: AutoTableActionBarProps<TData>):
|
|
364
|
+
}: AutoTableActionBarProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
356
365
|
//#endregion
|
|
357
366
|
//#region src/lib/schema-bridge/types.d.ts
|
|
358
367
|
/**
|
|
@@ -379,6 +388,7 @@ interface ParsedZodField {
|
|
|
379
388
|
type ColumnOverrides<T> = Partial<{ [K in keyof T]: Partial<ColumnDef<T, unknown>> & {
|
|
380
389
|
hidden?: boolean;
|
|
381
390
|
label?: string;
|
|
391
|
+
index?: number;
|
|
382
392
|
} }>;
|
|
383
393
|
/**
|
|
384
394
|
* Formily Schema 覆盖选项
|
|
@@ -473,6 +483,10 @@ interface AutoTableProps<T extends z.ZodObject<z.ZodRawShape>> {
|
|
|
473
483
|
* 默认: ["simple", "advanced", "command"] (全部显示)
|
|
474
484
|
*/
|
|
475
485
|
filterMode?: FilterMode | FilterMode[];
|
|
486
|
+
/** 全局搜索框 */
|
|
487
|
+
search?: boolean | {
|
|
488
|
+
placeholder?: string;
|
|
489
|
+
};
|
|
476
490
|
/** 批量删除回调 */
|
|
477
491
|
onDeleteSelected?: (rows: z.infer<T>[]) => void;
|
|
478
492
|
/** 批量更新回调 */
|
|
@@ -504,6 +518,7 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
504
518
|
actions,
|
|
505
519
|
pinnedColumns,
|
|
506
520
|
filterMode,
|
|
521
|
+
search,
|
|
507
522
|
onDeleteSelected,
|
|
508
523
|
onUpdateSelected,
|
|
509
524
|
batchUpdateFields,
|
|
@@ -514,7 +529,7 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
514
529
|
showDefaultExport,
|
|
515
530
|
onSelectedCountChange,
|
|
516
531
|
getSelectedRows
|
|
517
|
-
}: AutoTableProps<T>):
|
|
532
|
+
}: AutoTableProps<T>): react_jsx_runtime3.JSX.Element;
|
|
518
533
|
//#endregion
|
|
519
534
|
//#region src/i18n/locale.d.ts
|
|
520
535
|
/**
|
|
@@ -629,6 +644,8 @@ interface FilterConfig {
|
|
|
629
644
|
unit?: string;
|
|
630
645
|
/** 过滤器占位符 */
|
|
631
646
|
placeholder?: string;
|
|
647
|
+
/** 排序权重,数值越小越靠前 */
|
|
648
|
+
index?: number;
|
|
632
649
|
/** 是否在筛选栏中隐藏(隐藏筛选但不影响表格列显示) */
|
|
633
650
|
hidden?: boolean;
|
|
634
651
|
/** 控制在哪些筛选模式下显示(未设置则在所有模式显示) */
|
|
@@ -643,6 +660,8 @@ interface Field {
|
|
|
643
660
|
label?: string;
|
|
644
661
|
/** 是否隐藏(表格和表单都隐藏) */
|
|
645
662
|
hidden?: boolean;
|
|
663
|
+
/** 是否参与 AutoCrud 全局搜索 */
|
|
664
|
+
search?: boolean;
|
|
646
665
|
/**
|
|
647
666
|
* 筛选器独立配置
|
|
648
667
|
* - FilterConfig: 详细配置
|
|
@@ -658,6 +677,8 @@ interface Field {
|
|
|
658
677
|
table?: {
|
|
659
678
|
/** 是否在表格中隐藏 */
|
|
660
679
|
hidden?: boolean;
|
|
680
|
+
/** 排序权重,数值越小越靠前 */
|
|
681
|
+
index?: number;
|
|
661
682
|
/** 筛选器配置 */
|
|
662
683
|
meta?: Record<string, unknown>;
|
|
663
684
|
/** 其他列配置 */
|
|
@@ -734,6 +755,8 @@ type ToolbarActionConfig = ToolbarActionItem[] | ((defaults: ToolbarBuiltinActio
|
|
|
734
755
|
* AutoCrudTable Props 接口
|
|
735
756
|
*/
|
|
736
757
|
interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
758
|
+
/** Public CRUD id. Used by hosts to document the extension target. */
|
|
759
|
+
id?: string;
|
|
737
760
|
/** 页面标题 */
|
|
738
761
|
title?: string;
|
|
739
762
|
/** 页面描述 */
|
|
@@ -759,6 +782,10 @@ interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
759
782
|
* - 数组: 第一个为默认值,显示切换按钮
|
|
760
783
|
*/
|
|
761
784
|
filterModes?: FilterMode | FilterMode[];
|
|
785
|
+
/** 全局搜索框;默认在存在 search: true 字段时显示 */
|
|
786
|
+
search?: boolean | {
|
|
787
|
+
placeholder?: string;
|
|
788
|
+
};
|
|
762
789
|
/**
|
|
763
790
|
* 批量更新字段配置
|
|
764
791
|
* - 只需传字段名数组,options 自动从 schema enum 推导
|
|
@@ -847,7 +874,7 @@ declare function AutoCrudTable<TSchema extends z.ZodObject<z.ZodRawShape>>({
|
|
|
847
874
|
toolbarActions,
|
|
848
875
|
locale: localeProp,
|
|
849
876
|
onCreate
|
|
850
|
-
}: AutoCrudTableProps<TSchema>):
|
|
877
|
+
}: AutoCrudTableProps<TSchema>): react_jsx_runtime3.JSX.Element;
|
|
851
878
|
//#endregion
|
|
852
879
|
//#region src/components/auto-crud/auto-form.d.ts
|
|
853
880
|
interface AutoFormProps<T extends z.ZodObject<z.ZodRawShape>> {
|
|
@@ -879,7 +906,7 @@ declare function AutoFormInner<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
879
906
|
labelAlign,
|
|
880
907
|
labelWidth,
|
|
881
908
|
showSubmitButton
|
|
882
|
-
}: AutoFormProps<T>, ref: React.Ref<AutoFormRef>):
|
|
909
|
+
}: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime3.JSX.Element;
|
|
883
910
|
declare const AutoForm: <T extends z.ZodObject<z.ZodRawShape>>(props: AutoFormProps<T> & {
|
|
884
911
|
ref?: React.Ref<AutoFormRef>;
|
|
885
912
|
}) => ReturnType<typeof AutoFormInner>;
|
|
@@ -1093,12 +1120,12 @@ declare const filterItemSchema: z.ZodObject<{
|
|
|
1093
1120
|
variant: z.ZodEnum<{
|
|
1094
1121
|
number: "number";
|
|
1095
1122
|
boolean: "boolean";
|
|
1096
|
-
date: "date";
|
|
1097
1123
|
text: "text";
|
|
1124
|
+
range: "range";
|
|
1125
|
+
date: "date";
|
|
1126
|
+
dateRange: "dateRange";
|
|
1098
1127
|
select: "select";
|
|
1099
1128
|
multiSelect: "multiSelect";
|
|
1100
|
-
dateRange: "dateRange";
|
|
1101
|
-
range: "range";
|
|
1102
1129
|
}>;
|
|
1103
1130
|
operator: z.ZodEnum<{
|
|
1104
1131
|
iLike: "iLike";
|
|
@@ -1128,6 +1155,7 @@ declare module "@tanstack/react-table" {
|
|
|
1128
1155
|
}
|
|
1129
1156
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
1130
1157
|
label?: string;
|
|
1158
|
+
index?: number;
|
|
1131
1159
|
placeholder?: string;
|
|
1132
1160
|
variant?: FilterVariant$1;
|
|
1133
1161
|
options?: Option[];
|
|
@@ -1171,13 +1199,15 @@ interface AutoTableSimpleFiltersProps<TData> {
|
|
|
1171
1199
|
shallow?: boolean;
|
|
1172
1200
|
filters?: ExtendedColumnFilter<TData>[];
|
|
1173
1201
|
onFiltersChange?: (filters: ExtendedColumnFilter<TData>[]) => void;
|
|
1202
|
+
leading?: React$1.ReactNode;
|
|
1174
1203
|
}
|
|
1175
1204
|
declare function AutoTableSimpleFilters<TData>({
|
|
1176
1205
|
table,
|
|
1177
1206
|
shallow,
|
|
1178
1207
|
filters: externalFilters,
|
|
1179
|
-
onFiltersChange
|
|
1180
|
-
|
|
1208
|
+
onFiltersChange,
|
|
1209
|
+
leading
|
|
1210
|
+
}: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime3.JSX.Element | null;
|
|
1181
1211
|
//#endregion
|
|
1182
1212
|
//#region src/components/auto-crud/form-modal.d.ts
|
|
1183
1213
|
type ModalVariant = "dialog" | "sheet";
|
|
@@ -1219,7 +1249,7 @@ declare function CrudFormModal<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1219
1249
|
labelAlign,
|
|
1220
1250
|
labelWidth,
|
|
1221
1251
|
className
|
|
1222
|
-
}: CrudFormModalProps<T>):
|
|
1252
|
+
}: CrudFormModalProps<T>): react_jsx_runtime3.JSX.Element;
|
|
1223
1253
|
//#endregion
|
|
1224
1254
|
//#region src/components/auto-crud/import-dialog.d.ts
|
|
1225
1255
|
interface ImportDialogProps {
|
|
@@ -1241,7 +1271,7 @@ declare function ImportDialog({
|
|
|
1241
1271
|
columns,
|
|
1242
1272
|
title,
|
|
1243
1273
|
locale
|
|
1244
|
-
}: ImportDialogProps):
|
|
1274
|
+
}: ImportDialogProps): react_jsx_runtime3.JSX.Element;
|
|
1245
1275
|
//#endregion
|
|
1246
1276
|
//#region src/components/auto-crud/export-dialog.d.ts
|
|
1247
1277
|
type ExportMode = "selected" | "filtered";
|
|
@@ -1261,7 +1291,7 @@ declare function ExportDialog({
|
|
|
1261
1291
|
selectedCount,
|
|
1262
1292
|
onExport,
|
|
1263
1293
|
canExportFiltered
|
|
1264
|
-
}: ExportDialogProps):
|
|
1294
|
+
}: ExportDialogProps): react_jsx_runtime3.JSX.Element;
|
|
1265
1295
|
//#endregion
|
|
1266
1296
|
//#region src/components/data-table/data-table.d.ts
|
|
1267
1297
|
interface DataTableProps<TData> extends React$1.ComponentProps<"div"> {
|
|
@@ -1274,7 +1304,7 @@ declare function DataTable<TData>({
|
|
|
1274
1304
|
children,
|
|
1275
1305
|
className,
|
|
1276
1306
|
...props
|
|
1277
|
-
}: DataTableProps<TData>):
|
|
1307
|
+
}: DataTableProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1278
1308
|
//#endregion
|
|
1279
1309
|
//#region src/components/data-table/data-table-advanced-toolbar.d.ts
|
|
1280
1310
|
interface DataTableAdvancedToolbarProps<TData> extends React$1.ComponentProps<"div"> {
|
|
@@ -1285,7 +1315,7 @@ declare function DataTableAdvancedToolbar<TData>({
|
|
|
1285
1315
|
children,
|
|
1286
1316
|
className,
|
|
1287
1317
|
...props
|
|
1288
|
-
}: DataTableAdvancedToolbarProps<TData>):
|
|
1318
|
+
}: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1289
1319
|
//#endregion
|
|
1290
1320
|
//#region src/components/data-table/data-table-column-header.d.ts
|
|
1291
1321
|
interface DataTableColumnHeaderProps<TData, TValue> extends React.ComponentProps<typeof DropdownMenuTrigger> {
|
|
@@ -1297,7 +1327,7 @@ declare function DataTableColumnHeader<TData, TValue>({
|
|
|
1297
1327
|
label,
|
|
1298
1328
|
className,
|
|
1299
1329
|
...props
|
|
1300
|
-
}: DataTableColumnHeaderProps<TData, TValue>):
|
|
1330
|
+
}: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime3.JSX.Element;
|
|
1301
1331
|
//#endregion
|
|
1302
1332
|
//#region src/components/data-table/data-table-faceted-filter.d.ts
|
|
1303
1333
|
interface DataTableFacetedFilterProps<TData, TValue> {
|
|
@@ -1311,7 +1341,7 @@ declare function DataTableFacetedFilter<TData, TValue>({
|
|
|
1311
1341
|
title,
|
|
1312
1342
|
options,
|
|
1313
1343
|
multiple
|
|
1314
|
-
}: DataTableFacetedFilterProps<TData, TValue>):
|
|
1344
|
+
}: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime3.JSX.Element;
|
|
1315
1345
|
//#endregion
|
|
1316
1346
|
//#region src/components/data-table/data-table-pagination.d.ts
|
|
1317
1347
|
interface DataTablePaginationProps<TData> extends React.ComponentProps<"div"> {
|
|
@@ -1323,7 +1353,7 @@ declare function DataTablePagination<TData>({
|
|
|
1323
1353
|
pageSizeOptions,
|
|
1324
1354
|
className,
|
|
1325
1355
|
...props
|
|
1326
|
-
}: DataTablePaginationProps<TData>):
|
|
1356
|
+
}: DataTablePaginationProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1327
1357
|
//#endregion
|
|
1328
1358
|
//#region src/components/data-table/data-table-toolbar.d.ts
|
|
1329
1359
|
interface DataTableToolbarProps<TData> extends React$1.ComponentProps<"div"> {
|
|
@@ -1334,7 +1364,7 @@ declare function DataTableToolbar<TData>({
|
|
|
1334
1364
|
children,
|
|
1335
1365
|
className,
|
|
1336
1366
|
...props
|
|
1337
|
-
}: DataTableToolbarProps<TData>):
|
|
1367
|
+
}: DataTableToolbarProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1338
1368
|
//#endregion
|
|
1339
1369
|
//#region src/components/data-table/data-table-view-options.d.ts
|
|
1340
1370
|
interface DataTableViewOptionsProps<TData> extends React$1.ComponentProps<typeof PopoverContent> {
|
|
@@ -1345,7 +1375,7 @@ declare function DataTableViewOptions<TData>({
|
|
|
1345
1375
|
table,
|
|
1346
1376
|
disabled,
|
|
1347
1377
|
...props
|
|
1348
|
-
}: DataTableViewOptionsProps<TData>):
|
|
1378
|
+
}: DataTableViewOptionsProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1349
1379
|
//#endregion
|
|
1350
1380
|
//#region src/hooks/use-data-table.d.ts
|
|
1351
1381
|
interface UseDataTableProps<TData> extends Omit<TableOptions<TData>, "state" | "pageCount" | "getCoreRowModel" | "manualFiltering" | "manualPagination" | "manualSorting">, Required<Pick<TableOptions<TData>, "pageCount">> {
|
|
@@ -1512,6 +1542,7 @@ interface ListParams {
|
|
|
1512
1542
|
id: string;
|
|
1513
1543
|
desc: boolean;
|
|
1514
1544
|
}>;
|
|
1545
|
+
search?: string;
|
|
1515
1546
|
filters?: Array<{
|
|
1516
1547
|
id: string;
|
|
1517
1548
|
value: any;
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,10 @@ interface UseAutoCrudResourceReturn<TSchema extends z.ZodObject<z.ZodRawShape>,
|
|
|
125
125
|
/** 是否正在获取数据(用于显示微妙的加载指示器) */
|
|
126
126
|
isFetching: boolean;
|
|
127
127
|
};
|
|
128
|
+
/** Optional schema override from host metadata. */
|
|
129
|
+
schema?: z.ZodObject<z.ZodRawShape>;
|
|
130
|
+
/** Optional field config override from host metadata. */
|
|
131
|
+
fields?: Fields;
|
|
128
132
|
modal: ModalState<TListItem>;
|
|
129
133
|
mutations: {
|
|
130
134
|
isCreating: boolean;
|
|
@@ -182,6 +186,10 @@ interface CrudRouter {
|
|
|
182
186
|
export?: {
|
|
183
187
|
useQuery: (input?: any, opts?: any) => any;
|
|
184
188
|
};
|
|
189
|
+
/** 元数据路由(query,由 createCrudRouter 自动生成) */
|
|
190
|
+
meta?: {
|
|
191
|
+
useQuery: (input?: any, opts?: any) => any;
|
|
192
|
+
};
|
|
185
193
|
}
|
|
186
194
|
/**
|
|
187
195
|
* 自动生成的查询参数(由内部 URL 状态管理生成)
|
|
@@ -193,6 +201,7 @@ interface AutoQueryParams {
|
|
|
193
201
|
id: string;
|
|
194
202
|
desc: boolean;
|
|
195
203
|
}>;
|
|
204
|
+
search?: string;
|
|
196
205
|
filters: any[];
|
|
197
206
|
joinOperator: "and" | "or";
|
|
198
207
|
}
|
|
@@ -379,6 +388,7 @@ interface ParsedZodField {
|
|
|
379
388
|
type ColumnOverrides<T> = Partial<{ [K in keyof T]: Partial<ColumnDef<T, unknown>> & {
|
|
380
389
|
hidden?: boolean;
|
|
381
390
|
label?: string;
|
|
391
|
+
index?: number;
|
|
382
392
|
} }>;
|
|
383
393
|
/**
|
|
384
394
|
* Formily Schema 覆盖选项
|
|
@@ -473,6 +483,10 @@ interface AutoTableProps<T extends z.ZodObject<z.ZodRawShape>> {
|
|
|
473
483
|
* 默认: ["simple", "advanced", "command"] (全部显示)
|
|
474
484
|
*/
|
|
475
485
|
filterMode?: FilterMode | FilterMode[];
|
|
486
|
+
/** 全局搜索框 */
|
|
487
|
+
search?: boolean | {
|
|
488
|
+
placeholder?: string;
|
|
489
|
+
};
|
|
476
490
|
/** 批量删除回调 */
|
|
477
491
|
onDeleteSelected?: (rows: z.infer<T>[]) => void;
|
|
478
492
|
/** 批量更新回调 */
|
|
@@ -504,6 +518,7 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
504
518
|
actions,
|
|
505
519
|
pinnedColumns,
|
|
506
520
|
filterMode,
|
|
521
|
+
search,
|
|
507
522
|
onDeleteSelected,
|
|
508
523
|
onUpdateSelected,
|
|
509
524
|
batchUpdateFields,
|
|
@@ -629,6 +644,8 @@ interface FilterConfig {
|
|
|
629
644
|
unit?: string;
|
|
630
645
|
/** 过滤器占位符 */
|
|
631
646
|
placeholder?: string;
|
|
647
|
+
/** 排序权重,数值越小越靠前 */
|
|
648
|
+
index?: number;
|
|
632
649
|
/** 是否在筛选栏中隐藏(隐藏筛选但不影响表格列显示) */
|
|
633
650
|
hidden?: boolean;
|
|
634
651
|
/** 控制在哪些筛选模式下显示(未设置则在所有模式显示) */
|
|
@@ -643,6 +660,8 @@ interface Field {
|
|
|
643
660
|
label?: string;
|
|
644
661
|
/** 是否隐藏(表格和表单都隐藏) */
|
|
645
662
|
hidden?: boolean;
|
|
663
|
+
/** 是否参与 AutoCrud 全局搜索 */
|
|
664
|
+
search?: boolean;
|
|
646
665
|
/**
|
|
647
666
|
* 筛选器独立配置
|
|
648
667
|
* - FilterConfig: 详细配置
|
|
@@ -658,6 +677,8 @@ interface Field {
|
|
|
658
677
|
table?: {
|
|
659
678
|
/** 是否在表格中隐藏 */
|
|
660
679
|
hidden?: boolean;
|
|
680
|
+
/** 排序权重,数值越小越靠前 */
|
|
681
|
+
index?: number;
|
|
661
682
|
/** 筛选器配置 */
|
|
662
683
|
meta?: Record<string, unknown>;
|
|
663
684
|
/** 其他列配置 */
|
|
@@ -734,6 +755,8 @@ type ToolbarActionConfig = ToolbarActionItem[] | ((defaults: ToolbarBuiltinActio
|
|
|
734
755
|
* AutoCrudTable Props 接口
|
|
735
756
|
*/
|
|
736
757
|
interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
758
|
+
/** Public CRUD id. Used by hosts to document the extension target. */
|
|
759
|
+
id?: string;
|
|
737
760
|
/** 页面标题 */
|
|
738
761
|
title?: string;
|
|
739
762
|
/** 页面描述 */
|
|
@@ -759,6 +782,10 @@ interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
759
782
|
* - 数组: 第一个为默认值,显示切换按钮
|
|
760
783
|
*/
|
|
761
784
|
filterModes?: FilterMode | FilterMode[];
|
|
785
|
+
/** 全局搜索框;默认在存在 search: true 字段时显示 */
|
|
786
|
+
search?: boolean | {
|
|
787
|
+
placeholder?: string;
|
|
788
|
+
};
|
|
762
789
|
/**
|
|
763
790
|
* 批量更新字段配置
|
|
764
791
|
* - 只需传字段名数组,options 自动从 schema enum 推导
|
|
@@ -1093,12 +1120,12 @@ declare const filterItemSchema: z.ZodObject<{
|
|
|
1093
1120
|
variant: z.ZodEnum<{
|
|
1094
1121
|
number: "number";
|
|
1095
1122
|
boolean: "boolean";
|
|
1096
|
-
date: "date";
|
|
1097
1123
|
text: "text";
|
|
1124
|
+
range: "range";
|
|
1125
|
+
date: "date";
|
|
1126
|
+
dateRange: "dateRange";
|
|
1098
1127
|
select: "select";
|
|
1099
1128
|
multiSelect: "multiSelect";
|
|
1100
|
-
dateRange: "dateRange";
|
|
1101
|
-
range: "range";
|
|
1102
1129
|
}>;
|
|
1103
1130
|
operator: z.ZodEnum<{
|
|
1104
1131
|
iLike: "iLike";
|
|
@@ -1128,6 +1155,7 @@ declare module "@tanstack/react-table" {
|
|
|
1128
1155
|
}
|
|
1129
1156
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
1130
1157
|
label?: string;
|
|
1158
|
+
index?: number;
|
|
1131
1159
|
placeholder?: string;
|
|
1132
1160
|
variant?: FilterVariant$1;
|
|
1133
1161
|
options?: Option[];
|
|
@@ -1171,12 +1199,14 @@ interface AutoTableSimpleFiltersProps<TData> {
|
|
|
1171
1199
|
shallow?: boolean;
|
|
1172
1200
|
filters?: ExtendedColumnFilter<TData>[];
|
|
1173
1201
|
onFiltersChange?: (filters: ExtendedColumnFilter<TData>[]) => void;
|
|
1202
|
+
leading?: React$1.ReactNode;
|
|
1174
1203
|
}
|
|
1175
1204
|
declare function AutoTableSimpleFilters<TData>({
|
|
1176
1205
|
table,
|
|
1177
1206
|
shallow,
|
|
1178
1207
|
filters: externalFilters,
|
|
1179
|
-
onFiltersChange
|
|
1208
|
+
onFiltersChange,
|
|
1209
|
+
leading
|
|
1180
1210
|
}: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime2.JSX.Element | null;
|
|
1181
1211
|
//#endregion
|
|
1182
1212
|
//#region src/components/auto-crud/form-modal.d.ts
|
|
@@ -1512,6 +1542,7 @@ interface ListParams {
|
|
|
1512
1542
|
id: string;
|
|
1513
1543
|
desc: boolean;
|
|
1514
1544
|
}>;
|
|
1545
|
+
search?: string;
|
|
1515
1546
|
filters?: Array<{
|
|
1516
1547
|
id: string;
|
|
1517
1548
|
value: any;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from "react";
|
|
3
3
|
import { ArrowDownUp, BadgeCheck, CalendarIcon, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, CommandIcon, Download, Ellipsis, EyeOff, FileDown, FileSpreadsheetIcon, GripVertical, ListFilter, ListFilterIcon, Loader2, PlusCircle, Settings2, Text, Trash2, Upload, X, XCircle } from "lucide-react";
|
|
4
4
|
import { flexRender, getCoreRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
|
5
|
-
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Calendar, Checkbox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Slider, Switch } from "@pixpilot/shadcn";
|
|
5
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Calendar, Checkbox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Slider, Switch, cn as cn$1 } from "@pixpilot/shadcn";
|
|
6
6
|
import { clsx } from "clsx";
|
|
7
7
|
import { twMerge } from "tailwind-merge";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -18,6 +18,7 @@ import { z } from "zod";
|
|
|
18
18
|
import { createForm } from "@formily/core";
|
|
19
19
|
import { Form, JsonSchemaField } from "@pixpilot/formily-shadcn";
|
|
20
20
|
import { connect, mapProps } from "@formily/react";
|
|
21
|
+
import { Combobox } from "@pixpilot/shadcn-ui";
|
|
21
22
|
import { toast } from "sonner";
|
|
22
23
|
|
|
23
24
|
//#region src/lib/utils.ts
|
|
@@ -2663,8 +2664,17 @@ function DataTableViewOptions({ table, disabled,...props }) {
|
|
|
2663
2664
|
|
|
2664
2665
|
//#endregion
|
|
2665
2666
|
//#region src/components/auto-crud/auto-table-simple-filters.tsx
|
|
2666
|
-
function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange }) {
|
|
2667
|
-
const columns = React.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()),
|
|
2667
|
+
function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange, leading }) {
|
|
2668
|
+
const columns = React.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()).sort((a, b) => {
|
|
2669
|
+
const readOrder = (column) => {
|
|
2670
|
+
const metaIndex = column.columnDef.meta?.index;
|
|
2671
|
+
const columnIndex = column.columnDef.index;
|
|
2672
|
+
return typeof metaIndex === "number" ? metaIndex : typeof columnIndex === "number" ? columnIndex : 1e4;
|
|
2673
|
+
};
|
|
2674
|
+
const orderDiff = readOrder(a) - readOrder(b);
|
|
2675
|
+
if (orderDiff !== 0) return orderDiff;
|
|
2676
|
+
return a.getIndex() - b.getIndex();
|
|
2677
|
+
}), [table]);
|
|
2668
2678
|
const queryStateOptions = table.options.meta?.queryStateOptions;
|
|
2669
2679
|
const [queryFilters, setQueryFilters] = useReadableFilters(columns, {
|
|
2670
2680
|
...queryStateOptions,
|
|
@@ -2781,8 +2791,9 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
2781
2791
|
if (!mounted) return null;
|
|
2782
2792
|
return /* @__PURE__ */ jsxs("div", {
|
|
2783
2793
|
ref: containerRef,
|
|
2784
|
-
className: "flex flex-wrap items-center gap-2 min-w-0",
|
|
2794
|
+
className: "flex flex-1 flex-wrap items-center gap-2 min-w-0",
|
|
2785
2795
|
children: [
|
|
2796
|
+
leading,
|
|
2786
2797
|
filterColumns.map((column, index) => {
|
|
2787
2798
|
const meta = column.columnDef.meta;
|
|
2788
2799
|
const value = getFilterValue(column.id);
|
|
@@ -3915,7 +3926,7 @@ function createTableSchema(schema, options) {
|
|
|
3915
3926
|
if (cached) return [...cached];
|
|
3916
3927
|
}
|
|
3917
3928
|
const shape = schema.shape;
|
|
3918
|
-
const
|
|
3929
|
+
const columnEntries = [];
|
|
3919
3930
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
3920
3931
|
if (exclude.includes(key)) continue;
|
|
3921
3932
|
const override = overrides?.[key];
|
|
@@ -3936,20 +3947,31 @@ function createTableSchema(schema, options) {
|
|
|
3936
3947
|
options: override?.meta?.options ?? autoOptions
|
|
3937
3948
|
};
|
|
3938
3949
|
const { meta: _ignoredMeta,...restOverride } = override ?? {};
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3950
|
+
const sourceIndex = columnEntries.length;
|
|
3951
|
+
const order = typeof restOverride.index === "number" ? restOverride.index : typeof meta.index === "number" ? meta.index : 1e4 + sourceIndex;
|
|
3952
|
+
columnEntries.push({
|
|
3953
|
+
order,
|
|
3954
|
+
sourceIndex,
|
|
3955
|
+
column: {
|
|
3956
|
+
id: key,
|
|
3957
|
+
accessorKey: key,
|
|
3958
|
+
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, {
|
|
3959
|
+
column,
|
|
3960
|
+
label
|
|
3961
|
+
}),
|
|
3962
|
+
cell: ({ row }) => renderCell(row.getValue(key), parsed.type),
|
|
3963
|
+
enableColumnFilter: true,
|
|
3964
|
+
enableSorting: true,
|
|
3965
|
+
meta,
|
|
3966
|
+
...restOverride
|
|
3967
|
+
}
|
|
3951
3968
|
});
|
|
3952
3969
|
}
|
|
3970
|
+
const columns = columnEntries.sort((a, b) => {
|
|
3971
|
+
const orderDiff = a.order - b.order;
|
|
3972
|
+
if (orderDiff !== 0) return orderDiff;
|
|
3973
|
+
return a.sourceIndex - b.sourceIndex;
|
|
3974
|
+
}).map((entry) => entry.column);
|
|
3953
3975
|
if (canCache) baseTableSchemaCache.set(schema, columns);
|
|
3954
3976
|
return columns;
|
|
3955
3977
|
}
|
|
@@ -4621,10 +4643,19 @@ const filterModeConfig = {
|
|
|
4621
4643
|
tooltip: "Linear-style command filters"
|
|
4622
4644
|
}
|
|
4623
4645
|
};
|
|
4624
|
-
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
|
|
4646
|
+
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
|
|
4625
4647
|
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
4626
4648
|
const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
|
|
4627
4649
|
const showToggle = modes.length > 1;
|
|
4650
|
+
const [searchValue, setSearchValue] = useUrlState("search", parseAsString.withDefault(""), {
|
|
4651
|
+
shallow: false,
|
|
4652
|
+
clearOnDefault: true
|
|
4653
|
+
});
|
|
4654
|
+
const [, setSearchPage] = useUrlState("page", parseAsInteger.withDefault(1), {
|
|
4655
|
+
shallow: false,
|
|
4656
|
+
clearOnDefault: true
|
|
4657
|
+
});
|
|
4658
|
+
const searchConfig = search && typeof search === "object" ? search : {};
|
|
4628
4659
|
const { table, shallow, debounceMs, throttleMs } = useDataTable({
|
|
4629
4660
|
data,
|
|
4630
4661
|
columns: useMemo(() => {
|
|
@@ -4696,11 +4727,21 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4696
4727
|
})
|
|
4697
4728
|
})
|
|
4698
4729
|
})] }) : null;
|
|
4730
|
+
const searchInput = search ? /* @__PURE__ */ jsx(Input, {
|
|
4731
|
+
value: searchValue,
|
|
4732
|
+
onChange: (event) => {
|
|
4733
|
+
setSearchValue(event.target.value);
|
|
4734
|
+
setSearchPage(1);
|
|
4735
|
+
},
|
|
4736
|
+
placeholder: searchConfig.placeholder ?? "搜索",
|
|
4737
|
+
className: "h-8 w-48 shrink-0"
|
|
4738
|
+
}) : null;
|
|
4699
4739
|
const filtersContent = useMemo(() => {
|
|
4700
4740
|
switch (currentMode) {
|
|
4701
4741
|
case "simple": return /* @__PURE__ */ jsx(AutoTableSimpleFilters, {
|
|
4702
4742
|
table,
|
|
4703
|
-
shallow
|
|
4743
|
+
shallow,
|
|
4744
|
+
leading: searchInput
|
|
4704
4745
|
});
|
|
4705
4746
|
case "command": return /* @__PURE__ */ jsx(DataTableFilterMenu, {
|
|
4706
4747
|
table,
|
|
@@ -4720,6 +4761,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4720
4761
|
currentMode,
|
|
4721
4762
|
table,
|
|
4722
4763
|
shallow,
|
|
4764
|
+
searchInput,
|
|
4723
4765
|
debounceMs,
|
|
4724
4766
|
throttleMs
|
|
4725
4767
|
]);
|
|
@@ -4731,10 +4773,14 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
4731
4773
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
4732
4774
|
className: "flex flex-1 items-start gap-2 min-h-[40px]",
|
|
4733
4775
|
"data-filter-parent": true,
|
|
4734
|
-
children: [
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4776
|
+
children: [
|
|
4777
|
+
currentMode !== "simple" && searchInput,
|
|
4778
|
+
currentMode !== "simple" && /* @__PURE__ */ jsx(DataTableSortList, {
|
|
4779
|
+
table,
|
|
4780
|
+
align: "start"
|
|
4781
|
+
}),
|
|
4782
|
+
filtersContent
|
|
4783
|
+
]
|
|
4738
4784
|
}), /* @__PURE__ */ jsxs("div", {
|
|
4739
4785
|
className: "flex items-center gap-2",
|
|
4740
4786
|
children: [
|
|
@@ -4923,10 +4969,44 @@ function createEditFormSchema(schema, options) {
|
|
|
4923
4969
|
|
|
4924
4970
|
//#endregion
|
|
4925
4971
|
//#region src/components/auto-crud/auto-form.tsx
|
|
4972
|
+
const COMBOBOX_LIST_CLASS = "[&_[cmdk-list]]:max-h-[360px]";
|
|
4926
4973
|
const FormilySwitch = connect(Switch, mapProps({
|
|
4927
4974
|
value: "checked",
|
|
4928
4975
|
onInput: "onCheckedChange"
|
|
4929
4976
|
}));
|
|
4977
|
+
const FormilyCombobox = connect(AutoCrudCombobox, mapProps({ dataSource: "options" }));
|
|
4978
|
+
function AutoCrudCombobox({ options = [], filter, className,...props }) {
|
|
4979
|
+
const resolvedFilter = useMemo(() => {
|
|
4980
|
+
if (typeof filter === "function") return filter;
|
|
4981
|
+
return createComboboxFilter(options);
|
|
4982
|
+
}, [filter, options]);
|
|
4983
|
+
return /* @__PURE__ */ jsx(Combobox, {
|
|
4984
|
+
...props,
|
|
4985
|
+
options,
|
|
4986
|
+
filter: resolvedFilter,
|
|
4987
|
+
className: cn$1(COMBOBOX_LIST_CLASS, className)
|
|
4988
|
+
});
|
|
4989
|
+
}
|
|
4990
|
+
function createComboboxFilter(options) {
|
|
4991
|
+
const searchTextByValue = new Map(options.map((option) => [option.value, collectSearchText(option.value, option.label, option.searchText, option.keywords).toLocaleLowerCase()]));
|
|
4992
|
+
return (value, search, keywords) => {
|
|
4993
|
+
const query = search.trim().toLocaleLowerCase();
|
|
4994
|
+
if (!query) return 1;
|
|
4995
|
+
const searchText = collectSearchText(searchTextByValue.get(value), keywords).toLocaleLowerCase();
|
|
4996
|
+
if (searchText === query) return 2;
|
|
4997
|
+
return searchText.includes(query) ? 1 : 0;
|
|
4998
|
+
};
|
|
4999
|
+
}
|
|
5000
|
+
function collectSearchText(...parts) {
|
|
5001
|
+
const texts = [];
|
|
5002
|
+
for (const part of parts) {
|
|
5003
|
+
if (!part) continue;
|
|
5004
|
+
if (Array.isArray(part)) texts.push(...part.map(String));
|
|
5005
|
+
else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
|
|
5006
|
+
else texts.push(String(part));
|
|
5007
|
+
}
|
|
5008
|
+
return texts.join(" ");
|
|
5009
|
+
}
|
|
4930
5010
|
function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
|
|
4931
5011
|
const form = useMemo(() => createForm({ initialValues }), [JSON.stringify(initialValues)]);
|
|
4932
5012
|
const formSchema = useMemo(() => createEditFormSchema(zodSchema, {
|
|
@@ -4951,10 +5031,16 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
4951
5031
|
form,
|
|
4952
5032
|
children: [/* @__PURE__ */ jsx(JsonSchemaField, {
|
|
4953
5033
|
schema: formSchema,
|
|
4954
|
-
components: { fields: {
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
5034
|
+
components: { fields: {
|
|
5035
|
+
Combobox: {
|
|
5036
|
+
component: FormilyCombobox,
|
|
5037
|
+
decorator: "FormItem"
|
|
5038
|
+
},
|
|
5039
|
+
Switch: {
|
|
5040
|
+
component: FormilySwitch,
|
|
5041
|
+
decorator: "FormItem"
|
|
5042
|
+
}
|
|
5043
|
+
} }
|
|
4958
5044
|
}), showSubmitButton && /* @__PURE__ */ jsx("div", {
|
|
4959
5045
|
className: "flex justify-end gap-2 mt-6",
|
|
4960
5046
|
children: /* @__PURE__ */ jsx(Button, {
|
|
@@ -6046,6 +6132,11 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
|
|
|
6046
6132
|
*/
|
|
6047
6133
|
function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbarActions, locale: localeProp, onCreate }) {
|
|
6048
6134
|
const locale = resolveLocale(localeProp);
|
|
6135
|
+
const resolvedSchema = resource.schema ?? schema;
|
|
6136
|
+
const resolvedFields = React.useMemo(() => ({
|
|
6137
|
+
...fields,
|
|
6138
|
+
...resource.fields
|
|
6139
|
+
}), [fields, resource.fields]);
|
|
6049
6140
|
const can = {
|
|
6050
6141
|
create: permissions?.can?.create ?? true,
|
|
6051
6142
|
update: permissions?.can?.update ?? true,
|
|
@@ -6061,10 +6152,10 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6061
6152
|
const [exporting, setExporting] = React.useState(false);
|
|
6062
6153
|
const getSelectedRowsRef = React.useRef(null);
|
|
6063
6154
|
const importColumns = React.useMemo(() => {
|
|
6064
|
-
const shape =
|
|
6155
|
+
const shape = resolvedSchema.shape;
|
|
6065
6156
|
const denySet = new Set(denyFields ?? []);
|
|
6066
6157
|
return Object.keys(shape).filter((key) => !denySet.has(key));
|
|
6067
|
-
}, [
|
|
6158
|
+
}, [resolvedSchema, denyFields]);
|
|
6068
6159
|
const handleExport = React.useCallback(async (mode) => {
|
|
6069
6160
|
const filename = title ?? "export";
|
|
6070
6161
|
const excludeColumns = denyFields;
|
|
@@ -6096,21 +6187,34 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6096
6187
|
setExporting(false);
|
|
6097
6188
|
}
|
|
6098
6189
|
}, [selectedCount, handleExport]);
|
|
6099
|
-
const tableOverrides = React.useMemo(() => buildTableOverrides(
|
|
6100
|
-
const formOverrides = React.useMemo(() => buildFormOverrides(
|
|
6101
|
-
|
|
6190
|
+
const tableOverrides = React.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides), [resolvedFields, tableConfig?.overrides]);
|
|
6191
|
+
const formOverrides = React.useMemo(() => buildFormOverrides(resolvedFields, formConfig?.overrides, denyFields), [
|
|
6192
|
+
resolvedFields,
|
|
6102
6193
|
formConfig?.overrides,
|
|
6103
6194
|
denyFields
|
|
6104
6195
|
]);
|
|
6105
|
-
const hiddenColumns = React.useMemo(() => buildHiddenColumns(
|
|
6106
|
-
|
|
6196
|
+
const hiddenColumns = React.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields), [
|
|
6197
|
+
resolvedFields,
|
|
6107
6198
|
tableConfig?.hidden,
|
|
6108
6199
|
denyFields
|
|
6109
6200
|
]);
|
|
6110
|
-
const
|
|
6111
|
-
|
|
6201
|
+
const searchConfig = React.useMemo(() => {
|
|
6202
|
+
const tableSearch = tableConfig?.search;
|
|
6203
|
+
if (tableSearch === false) return false;
|
|
6204
|
+
if (tableSearch && typeof tableSearch === "object") return tableSearch;
|
|
6205
|
+
return Object.values(resolvedFields).some((field) => field?.search === true) ? true : false;
|
|
6206
|
+
}, [resolvedFields, tableConfig?.search]);
|
|
6207
|
+
const batchFields = React.useMemo(() => buildBatchUpdateFields(resolvedSchema, tableConfig?.batchFields, resolvedFields), [
|
|
6208
|
+
resolvedSchema,
|
|
6112
6209
|
tableConfig?.batchFields,
|
|
6113
|
-
|
|
6210
|
+
resolvedFields
|
|
6211
|
+
]);
|
|
6212
|
+
const formInitialValues = React.useMemo(() => {
|
|
6213
|
+
return resource.modal.createOpen ? resource.modal.copySource ?? void 0 : resource.modal.selected ?? void 0;
|
|
6214
|
+
}, [
|
|
6215
|
+
resource.modal.createOpen,
|
|
6216
|
+
resource.modal.copySource,
|
|
6217
|
+
resource.modal.selected
|
|
6114
6218
|
]);
|
|
6115
6219
|
return /* @__PURE__ */ jsxs("div", {
|
|
6116
6220
|
className: "space-y-4",
|
|
@@ -6191,11 +6295,12 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6191
6295
|
}),
|
|
6192
6296
|
/* @__PURE__ */ jsx(AutoTable, {
|
|
6193
6297
|
data: resource.tableData.data,
|
|
6194
|
-
schema,
|
|
6298
|
+
schema: resolvedSchema,
|
|
6195
6299
|
pageCount: resource.tableData.pageCount,
|
|
6196
6300
|
overrides: tableOverrides,
|
|
6197
6301
|
exclude: hiddenColumns,
|
|
6198
6302
|
filterMode: tableConfig?.filterModes,
|
|
6303
|
+
search: searchConfig,
|
|
6199
6304
|
actions: resolveActions(actionItems, {
|
|
6200
6305
|
openView: resource.handlers.openView,
|
|
6201
6306
|
openEdit: can.update ? resource.handlers.openEdit : void 0,
|
|
@@ -6216,8 +6321,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6216
6321
|
open: resource.modal.createOpen || resource.modal.editOpen,
|
|
6217
6322
|
onOpenChange: (open) => !open && resource.handlers.closeModals(),
|
|
6218
6323
|
mode: resource.modal.createOpen ? "create" : "edit",
|
|
6219
|
-
schema,
|
|
6220
|
-
initialValues:
|
|
6324
|
+
schema: resolvedSchema,
|
|
6325
|
+
initialValues: formInitialValues,
|
|
6221
6326
|
onSubmit: (values) => {
|
|
6222
6327
|
if (resource.modal.createOpen) resource.handlers.submitCreate(values);
|
|
6223
6328
|
else resource.handlers.submitUpdate(values);
|
|
@@ -6234,8 +6339,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6234
6339
|
onOpenChange: (open) => !open && resource.handlers.closeModals(),
|
|
6235
6340
|
variant: resource.modal.variant,
|
|
6236
6341
|
data: resource.modal.selected,
|
|
6237
|
-
schema,
|
|
6238
|
-
fields,
|
|
6342
|
+
schema: resolvedSchema,
|
|
6343
|
+
fields: resolvedFields,
|
|
6239
6344
|
denyFields,
|
|
6240
6345
|
locale
|
|
6241
6346
|
}),
|
|
@@ -6954,6 +7059,53 @@ const noopToastAdapter = {
|
|
|
6954
7059
|
info: () => {},
|
|
6955
7060
|
warning: () => {}
|
|
6956
7061
|
};
|
|
7062
|
+
function isRecord(value) {
|
|
7063
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7064
|
+
}
|
|
7065
|
+
function isJsonSchemaObject(value) {
|
|
7066
|
+
return isRecord(value) && isRecord(value.properties);
|
|
7067
|
+
}
|
|
7068
|
+
function readJsonSchemaType(property) {
|
|
7069
|
+
return Array.isArray(property.type) ? property.type.find((item) => item !== "null") : property.type;
|
|
7070
|
+
}
|
|
7071
|
+
function jsonPropertyToZod(property, required) {
|
|
7072
|
+
const enumValues = property.enum?.filter((value) => typeof value === "string");
|
|
7073
|
+
let schema;
|
|
7074
|
+
if (enumValues && enumValues.length > 0) schema = z.enum(enumValues);
|
|
7075
|
+
else switch (readJsonSchemaType(property)) {
|
|
7076
|
+
case "number":
|
|
7077
|
+
case "integer":
|
|
7078
|
+
schema = z.number();
|
|
7079
|
+
break;
|
|
7080
|
+
case "boolean":
|
|
7081
|
+
schema = z.boolean();
|
|
7082
|
+
break;
|
|
7083
|
+
case "array":
|
|
7084
|
+
schema = z.array(z.unknown());
|
|
7085
|
+
break;
|
|
7086
|
+
case "object":
|
|
7087
|
+
schema = z.record(z.string(), z.unknown());
|
|
7088
|
+
break;
|
|
7089
|
+
case "string":
|
|
7090
|
+
schema = property.format === "date" || property.format === "date-time" ? z.coerce.date() : z.string();
|
|
7091
|
+
break;
|
|
7092
|
+
default: schema = z.unknown();
|
|
7093
|
+
}
|
|
7094
|
+
return required ? schema : schema.optional();
|
|
7095
|
+
}
|
|
7096
|
+
function mergeMetadataSchema(baseSchema, metadataSchema) {
|
|
7097
|
+
if (!isJsonSchemaObject(metadataSchema)) return baseSchema;
|
|
7098
|
+
const required = new Set(Array.isArray(metadataSchema.required) ? metadataSchema.required.filter((field) => typeof field === "string") : []);
|
|
7099
|
+
const extensionShape = {};
|
|
7100
|
+
for (const [field, property] of Object.entries(metadataSchema.properties ?? {})) {
|
|
7101
|
+
if (field in baseSchema.shape) continue;
|
|
7102
|
+
extensionShape[field] = jsonPropertyToZod(property, required.has(field));
|
|
7103
|
+
}
|
|
7104
|
+
return Object.keys(extensionShape).length > 0 ? baseSchema.extend(extensionShape) : baseSchema;
|
|
7105
|
+
}
|
|
7106
|
+
function readMetadataFields(fields) {
|
|
7107
|
+
return isRecord(fields) ? fields : void 0;
|
|
7108
|
+
}
|
|
6957
7109
|
/**
|
|
6958
7110
|
* Modal 状态 Reducer
|
|
6959
7111
|
*/
|
|
@@ -7012,12 +7164,16 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7012
7164
|
useEffect(() => {
|
|
7013
7165
|
hooksRef.current = hooks;
|
|
7014
7166
|
}, [hooks]);
|
|
7015
|
-
const
|
|
7167
|
+
const metadata = (router.meta?.useQuery(void 0, { staleTime: 3e4 }))?.data;
|
|
7168
|
+
const resolvedSchema = useMemo(() => mergeMetadataSchema(schema, metadata?.schema), [schema, metadata?.schema]);
|
|
7169
|
+
const metadataFields = useMemo(() => readMetadataFields(metadata?.fields), [metadata?.fields]);
|
|
7170
|
+
const columns = useMemo(() => createTableSchema(resolvedSchema), [resolvedSchema]);
|
|
7016
7171
|
const [urlParams] = useQueryStates({
|
|
7017
7172
|
page: parseAsInteger.withDefault(1),
|
|
7018
7173
|
perPage: parseAsInteger.withDefault(10),
|
|
7019
7174
|
sort: getSortingStateParser().withDefault([]),
|
|
7020
|
-
joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and")
|
|
7175
|
+
joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
|
|
7176
|
+
search: parseAsString.withDefault("")
|
|
7021
7177
|
}, { shallow: false });
|
|
7022
7178
|
const [filters] = useReadableFilters(columns, { shallow: false });
|
|
7023
7179
|
const queryInput = useMemo(() => {
|
|
@@ -7026,7 +7182,8 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7026
7182
|
perPage: urlParams.perPage,
|
|
7027
7183
|
sort: urlParams.sort,
|
|
7028
7184
|
filters,
|
|
7029
|
-
joinOperator: urlParams.joinOperator
|
|
7185
|
+
joinOperator: urlParams.joinOperator,
|
|
7186
|
+
...urlParams.search.trim() ? { search: urlParams.search.trim() } : {}
|
|
7030
7187
|
};
|
|
7031
7188
|
return queryTransform ? queryTransform(autoParams) : autoParams;
|
|
7032
7189
|
}, [
|
|
@@ -7038,6 +7195,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7038
7195
|
placeholderData: keepPreviousData,
|
|
7039
7196
|
staleTime: 0
|
|
7040
7197
|
});
|
|
7198
|
+
const tableRows = listQuery.data?.data ?? [];
|
|
7041
7199
|
const createMutation = router.create.useMutation();
|
|
7042
7200
|
const updateMutation = router.update.useMutation();
|
|
7043
7201
|
const deleteMutation = router.delete.useMutation();
|
|
@@ -7290,11 +7448,13 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7290
7448
|
const canExport = !!(exportFetcher || exportQuery);
|
|
7291
7449
|
return {
|
|
7292
7450
|
tableData: {
|
|
7293
|
-
data:
|
|
7451
|
+
data: tableRows,
|
|
7294
7452
|
pageCount: listQuery.data?.pageCount ?? 0,
|
|
7295
7453
|
isLoading: listQuery.isLoading,
|
|
7296
7454
|
isFetching: listQuery.isFetching
|
|
7297
7455
|
},
|
|
7456
|
+
schema: resolvedSchema,
|
|
7457
|
+
fields: metadataFields,
|
|
7298
7458
|
modal,
|
|
7299
7459
|
mutations: {
|
|
7300
7460
|
isCreating: createMutation.isPending,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordrhyme/auto-crud",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.3",
|
|
5
5
|
"description": "Schema-first CRUD components with auto-generated tables and forms",
|
|
6
6
|
"author": "wordrhyme",
|
|
7
7
|
"license": "MIT",
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"tailwind-merge": "^3.5.0",
|
|
67
67
|
"vaul": "^1.1.2",
|
|
68
68
|
"@pixpilot/formily-shadcn": "1.11.20",
|
|
69
|
-
"@pixpilot/shadcn": "1.
|
|
70
|
-
"@pixpilot/shadcn
|
|
69
|
+
"@pixpilot/shadcn-ui": "1.21.1",
|
|
70
|
+
"@pixpilot/shadcn": "1.2.7"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@tanstack/react-query": "^5.90.15",
|
|
@@ -82,11 +82,11 @@
|
|
|
82
82
|
"react-dom": "19.2.4",
|
|
83
83
|
"tsdown": "^0.15.12",
|
|
84
84
|
"typescript": "^5.9.3",
|
|
85
|
-
"@internal/prettier-config": "0.0.1",
|
|
86
|
-
"@internal/tsconfig": "0.1.0",
|
|
87
85
|
"@internal/eslint-config": "0.3.0",
|
|
88
|
-
"@internal/
|
|
89
|
-
"@internal/tsdown-config": "0.1.0"
|
|
86
|
+
"@internal/tsconfig": "0.1.0",
|
|
87
|
+
"@internal/tsdown-config": "0.1.0",
|
|
88
|
+
"@internal/prettier-config": "0.0.1",
|
|
89
|
+
"@internal/vitest-config": "0.1.0"
|
|
90
90
|
},
|
|
91
91
|
"prettier": "@internal/prettier-config",
|
|
92
92
|
"scripts": {
|