@wordrhyme/auto-crud 1.3.6 → 1.3.9
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 +61 -15
- package/dist/index.cjs +319 -65
- package/dist/index.d.cts +45 -19
- package/dist/index.d.ts +45 -19
- package/dist/index.js +319 -65
- package/package.json +6 -4
package/dist/index.cjs
CHANGED
|
@@ -4207,6 +4207,15 @@ function FormModal({ open, onOpenChange, title, description, children, variant =
|
|
|
4207
4207
|
|
|
4208
4208
|
//#endregion
|
|
4209
4209
|
//#region src/lib/schema-bridge/zod-to-formily.ts
|
|
4210
|
+
const DEFAULT_FORM_MANAGED_FIELDS = [
|
|
4211
|
+
"id",
|
|
4212
|
+
"createdAt",
|
|
4213
|
+
"updatedAt",
|
|
4214
|
+
"createdBy",
|
|
4215
|
+
"createdByType",
|
|
4216
|
+
"updatedBy",
|
|
4217
|
+
"updatedByType"
|
|
4218
|
+
];
|
|
4210
4219
|
/**
|
|
4211
4220
|
* 将字符串转为人类可读格式
|
|
4212
4221
|
*/
|
|
@@ -4289,16 +4298,12 @@ function createFormSchema(schema, options) {
|
|
|
4289
4298
|
};
|
|
4290
4299
|
}
|
|
4291
4300
|
/**
|
|
4292
|
-
* 为编辑模式创建 Form Schema
|
|
4301
|
+
* 为编辑模式创建 Form Schema(排除平台托管字段)
|
|
4293
4302
|
*/
|
|
4294
4303
|
function createEditFormSchema(schema, options) {
|
|
4295
4304
|
return createFormSchema(schema, {
|
|
4296
4305
|
...options,
|
|
4297
|
-
exclude: [
|
|
4298
|
-
"id",
|
|
4299
|
-
"createdAt",
|
|
4300
|
-
"updatedAt"
|
|
4301
|
-
]
|
|
4306
|
+
exclude: [...DEFAULT_FORM_MANAGED_FIELDS]
|
|
4302
4307
|
});
|
|
4303
4308
|
}
|
|
4304
4309
|
|
|
@@ -4360,6 +4365,7 @@ function normalizeDataSourceRegistration(registration) {
|
|
|
4360
4365
|
debounceMs: typeof registration.debounceMs === "number" && registration.debounceMs >= 0 ? registration.debounceMs : 300,
|
|
4361
4366
|
loadMore: registration.loadMore === true,
|
|
4362
4367
|
pageSize: typeof registration.pageSize === "number" && registration.pageSize > 0 ? registration.pageSize : 20,
|
|
4368
|
+
resolveFields: registration.resolveFields,
|
|
4363
4369
|
load: registration.load
|
|
4364
4370
|
};
|
|
4365
4371
|
}
|
|
@@ -4594,8 +4600,10 @@ function loadAutoCrudDataSource({ entry, field, registryVersion, search, sourceK
|
|
|
4594
4600
|
}
|
|
4595
4601
|
Promise.resolve(entry.load({
|
|
4596
4602
|
field: field.path.toString(),
|
|
4603
|
+
type: "form",
|
|
4597
4604
|
page,
|
|
4598
4605
|
pageSize: entry.loadMore ? entry.pageSize : void 0,
|
|
4606
|
+
query: searchValue,
|
|
4599
4607
|
search: searchValue,
|
|
4600
4608
|
values,
|
|
4601
4609
|
signal: controller?.signal
|
|
@@ -5632,6 +5640,15 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title, local
|
|
|
5632
5640
|
});
|
|
5633
5641
|
}
|
|
5634
5642
|
|
|
5643
|
+
//#endregion
|
|
5644
|
+
//#region src/lib/default-sorting.ts
|
|
5645
|
+
function getDefaultSortingForSchema(schema) {
|
|
5646
|
+
return Object.prototype.hasOwnProperty.call(schema.shape, "createdAt") ? [{
|
|
5647
|
+
id: "createdAt",
|
|
5648
|
+
desc: true
|
|
5649
|
+
}] : [];
|
|
5650
|
+
}
|
|
5651
|
+
|
|
5635
5652
|
//#endregion
|
|
5636
5653
|
//#region src/lib/crud-actions.ts
|
|
5637
5654
|
const entries = /* @__PURE__ */ new Map();
|
|
@@ -5781,6 +5798,29 @@ const crudActions = {
|
|
|
5781
5798
|
|
|
5782
5799
|
//#endregion
|
|
5783
5800
|
//#region src/components/auto-crud/auto-crud-table.tsx
|
|
5801
|
+
const DEFAULT_IMPORT_MANAGED_FIELDS = [
|
|
5802
|
+
"createdAt",
|
|
5803
|
+
"updatedAt",
|
|
5804
|
+
"createdBy",
|
|
5805
|
+
"createdByType",
|
|
5806
|
+
"updatedBy",
|
|
5807
|
+
"updatedByType"
|
|
5808
|
+
];
|
|
5809
|
+
const DEFAULT_HIDDEN_AUDIT_FIELDS = [
|
|
5810
|
+
"createdBy",
|
|
5811
|
+
"createdByType",
|
|
5812
|
+
"updatedBy",
|
|
5813
|
+
"updatedByType"
|
|
5814
|
+
];
|
|
5815
|
+
function isDefaultHiddenAuditField(key) {
|
|
5816
|
+
return DEFAULT_HIDDEN_AUDIT_FIELDS.includes(key);
|
|
5817
|
+
}
|
|
5818
|
+
function isFieldTableExplicitlyVisible(config) {
|
|
5819
|
+
return config?.hidden === false || config?.table !== false && typeof config?.table === "object" && config.table.hidden === false;
|
|
5820
|
+
}
|
|
5821
|
+
function isTableOverrideExplicitlyVisible(config) {
|
|
5822
|
+
return config?.hidden === false;
|
|
5823
|
+
}
|
|
5784
5824
|
let autoCrudToolbarResolver = null;
|
|
5785
5825
|
function setToolbarResolver(resolver) {
|
|
5786
5826
|
autoCrudToolbarResolver = resolver;
|
|
@@ -5852,8 +5892,8 @@ function haveSameRowSelection(left, right, idKey) {
|
|
|
5852
5892
|
if (leftIds.length === left.length && rightIds.length === right.length) return areStringArraysEqual(leftIds, rightIds);
|
|
5853
5893
|
return left.every((row, index$1) => row === right[index$1]);
|
|
5854
5894
|
}
|
|
5855
|
-
function
|
|
5856
|
-
return actions.find((action$1) => action$1.type ===
|
|
5895
|
+
function readToolbarAction(actions, type) {
|
|
5896
|
+
return actions.find((action$1) => action$1.type === type);
|
|
5857
5897
|
}
|
|
5858
5898
|
function resolveToolbarActionsWithResolver(targetId, ownerActions, context) {
|
|
5859
5899
|
const resolver = autoCrudToolbarResolver;
|
|
@@ -5927,6 +5967,47 @@ function shouldLoadDynamicFilterOptions(config) {
|
|
|
5927
5967
|
}
|
|
5928
5968
|
return normalizeDataSourceConfig(getFilterDataSource(config)) !== void 0;
|
|
5929
5969
|
}
|
|
5970
|
+
function isResolveValuePresent(value) {
|
|
5971
|
+
return value !== null && value !== void 0 && String(value).length > 0;
|
|
5972
|
+
}
|
|
5973
|
+
function getResolveValues(rows, field) {
|
|
5974
|
+
const values = /* @__PURE__ */ new Set();
|
|
5975
|
+
for (const row of rows) {
|
|
5976
|
+
const value = row[field];
|
|
5977
|
+
const items = Array.isArray(value) ? value : [value];
|
|
5978
|
+
for (const item of items) if (isResolveValuePresent(item)) values.add(String(item));
|
|
5979
|
+
}
|
|
5980
|
+
return Array.from(values);
|
|
5981
|
+
}
|
|
5982
|
+
function normalizeResolveCacheValue(value) {
|
|
5983
|
+
if (value instanceof Date) return value.toISOString();
|
|
5984
|
+
if (Array.isArray(value)) return value.map(normalizeResolveCacheValue);
|
|
5985
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map((key) => [key, normalizeResolveCacheValue(value[key])]));
|
|
5986
|
+
return value === void 0 ? { __autoCrudUndefined: true } : value;
|
|
5987
|
+
}
|
|
5988
|
+
function resolveRecordSignature(record) {
|
|
5989
|
+
return JSON.stringify(Object.keys(record).sort().map((key) => [key, normalizeResolveCacheValue(record[key])]));
|
|
5990
|
+
}
|
|
5991
|
+
function resolveRecordCacheKey(sourceKey, field, record) {
|
|
5992
|
+
return `${sourceKey}\u0000${field}\u0000${String(record[field])}\u0000${resolveRecordSignature(record)}`;
|
|
5993
|
+
}
|
|
5994
|
+
function mergeFieldOptions(current, incoming) {
|
|
5995
|
+
if ((!current || current.length === 0) && (!incoming || incoming.length === 0)) return;
|
|
5996
|
+
return mergeFilterOptions(current, incoming ?? []);
|
|
5997
|
+
}
|
|
5998
|
+
function mergeOptionsByField(left, right) {
|
|
5999
|
+
const keys = new Set([...Object.keys(left ?? {}), ...Object.keys(right ?? {})]);
|
|
6000
|
+
return Object.fromEntries(Array.from(keys).flatMap((key) => {
|
|
6001
|
+
const options = mergeFieldOptions(left?.[key], right?.[key]);
|
|
6002
|
+
return options ? [[key, options]] : [];
|
|
6003
|
+
}));
|
|
6004
|
+
}
|
|
6005
|
+
function shouldResolveOptions(field, config, hiddenColumns) {
|
|
6006
|
+
if (!config) return false;
|
|
6007
|
+
if (hiddenColumns.has(field)) return false;
|
|
6008
|
+
if (normalizeFieldOptions(config.enum)) return false;
|
|
6009
|
+
return normalizeDataSourceConfig(config.dataSource) !== void 0;
|
|
6010
|
+
}
|
|
5930
6011
|
function useRegistryVersion(subscribe) {
|
|
5931
6012
|
const [version$1, setVersion] = react.useState(0);
|
|
5932
6013
|
react.useEffect(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
|
|
@@ -6048,8 +6129,10 @@ function useDynamicFilterOptions(fields) {
|
|
|
6048
6129
|
const timer = setTimeout(() => {
|
|
6049
6130
|
Promise.resolve(entry.load({
|
|
6050
6131
|
field,
|
|
6132
|
+
type: "filter",
|
|
6051
6133
|
page,
|
|
6052
6134
|
pageSize: entry.loadMore ? entry.pageSize : void 0,
|
|
6135
|
+
query: search,
|
|
6053
6136
|
search,
|
|
6054
6137
|
values: {},
|
|
6055
6138
|
signal: controller?.signal
|
|
@@ -6142,6 +6225,117 @@ function useDynamicFilterOptions(fields) {
|
|
|
6142
6225
|
setSearchValue
|
|
6143
6226
|
};
|
|
6144
6227
|
}
|
|
6228
|
+
function rowHasResolveValue(row, field, value) {
|
|
6229
|
+
const rowValue = row[field];
|
|
6230
|
+
return (Array.isArray(rowValue) ? rowValue : [rowValue]).some((item) => item !== null && item !== void 0 && String(item) === value);
|
|
6231
|
+
}
|
|
6232
|
+
function resolveExtraFields(entry, field) {
|
|
6233
|
+
const configured = typeof entry.resolveFields === "function" ? entry.resolveFields({ field }) : entry.resolveFields;
|
|
6234
|
+
return Array.from(new Set((configured ?? []).filter((key) => typeof key === "string" && key.length > 0)));
|
|
6235
|
+
}
|
|
6236
|
+
function buildResolveRecords(rows, field, values, extraFields) {
|
|
6237
|
+
return values.map((value) => {
|
|
6238
|
+
const row = rows.find((candidate) => rowHasResolveValue(candidate, field, value));
|
|
6239
|
+
const record = { [field]: value };
|
|
6240
|
+
for (const extraField of extraFields) if (row && Object.prototype.hasOwnProperty.call(row, extraField)) record[extraField] = row[extraField];
|
|
6241
|
+
return record;
|
|
6242
|
+
});
|
|
6243
|
+
}
|
|
6244
|
+
function useDynamicResolveOptions(fields, rows, hiddenColumns) {
|
|
6245
|
+
const registryVersion = useRegistryVersion(dataSources.subscribe);
|
|
6246
|
+
const [optionsByField, setOptionsByField] = react.useState({});
|
|
6247
|
+
const cacheRef = react.useRef(/* @__PURE__ */ new Map());
|
|
6248
|
+
const requestVersionRef = react.useRef(0);
|
|
6249
|
+
const sourceEntries = react.useMemo(() => {
|
|
6250
|
+
if (!fields || rows.length === 0) return [];
|
|
6251
|
+
const hiddenSet = new Set(hiddenColumns);
|
|
6252
|
+
return Object.entries(fields).flatMap(([field, config]) => {
|
|
6253
|
+
if (!shouldResolveOptions(field, config, hiddenSet)) return [];
|
|
6254
|
+
const source = normalizeDataSourceConfig(config.dataSource);
|
|
6255
|
+
const values = getResolveValues(rows, field);
|
|
6256
|
+
return source && values.length > 0 ? [{
|
|
6257
|
+
field,
|
|
6258
|
+
values,
|
|
6259
|
+
source
|
|
6260
|
+
}] : [];
|
|
6261
|
+
});
|
|
6262
|
+
}, [
|
|
6263
|
+
fields,
|
|
6264
|
+
hiddenColumns,
|
|
6265
|
+
rows
|
|
6266
|
+
]);
|
|
6267
|
+
react.useEffect(() => {
|
|
6268
|
+
const requestVersion = requestVersionRef.current + 1;
|
|
6269
|
+
requestVersionRef.current = requestVersion;
|
|
6270
|
+
if (sourceEntries.length === 0) {
|
|
6271
|
+
setOptionsByField({});
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
let cancelled = false;
|
|
6275
|
+
const activeFields = new Set(sourceEntries.map(({ field }) => field));
|
|
6276
|
+
const readCachedOptions = (entry, records) => mergeFilterOptions(void 0, records.flatMap((record) => {
|
|
6277
|
+
const option = cacheRef.current.get(resolveRecordCacheKey(entry.source.key, entry.field, record));
|
|
6278
|
+
return option ? [option] : [];
|
|
6279
|
+
}));
|
|
6280
|
+
const recordsByField = new Map(sourceEntries.flatMap((sourceEntry) => {
|
|
6281
|
+
const entry = dataSources.get(sourceEntry.source.key);
|
|
6282
|
+
if (!entry) return [];
|
|
6283
|
+
return [[sourceEntry.field, buildResolveRecords(rows, sourceEntry.field, sourceEntry.values, resolveExtraFields(entry, sourceEntry.field))]];
|
|
6284
|
+
}));
|
|
6285
|
+
const readSourceEntryCachedOptions = (entry) => {
|
|
6286
|
+
return readCachedOptions(entry, recordsByField.get(entry.field) ?? []);
|
|
6287
|
+
};
|
|
6288
|
+
const findOptionForRecord = (options, field, record) => {
|
|
6289
|
+
const value = String(record[field]);
|
|
6290
|
+
return options.find((option) => option.value === value);
|
|
6291
|
+
};
|
|
6292
|
+
const missingRecordsFor = (entry, records) => records.filter((record) => !cacheRef.current.has(resolveRecordCacheKey(entry.source.key, entry.field, record)));
|
|
6293
|
+
setOptionsByField((current) => {
|
|
6294
|
+
const next = {};
|
|
6295
|
+
for (const entry of sourceEntries) {
|
|
6296
|
+
const cached = readSourceEntryCachedOptions(entry);
|
|
6297
|
+
if (cached.length > 0) next[entry.field] = cached;
|
|
6298
|
+
}
|
|
6299
|
+
for (const [field, options] of Object.entries(current)) if (activeFields.has(field) && !next[field]) next[field] = options;
|
|
6300
|
+
return next;
|
|
6301
|
+
});
|
|
6302
|
+
for (const sourceEntry of sourceEntries) {
|
|
6303
|
+
const entry = dataSources.get(sourceEntry.source.key);
|
|
6304
|
+
if (!entry) continue;
|
|
6305
|
+
const missingRecords = missingRecordsFor(sourceEntry, recordsByField.get(sourceEntry.field) ?? []);
|
|
6306
|
+
if (missingRecords.length === 0) continue;
|
|
6307
|
+
Promise.resolve(entry.load({
|
|
6308
|
+
field: sourceEntry.field,
|
|
6309
|
+
type: "resolve",
|
|
6310
|
+
values: missingRecords
|
|
6311
|
+
})).then((result) => {
|
|
6312
|
+
if (cancelled || requestVersionRef.current !== requestVersion) return;
|
|
6313
|
+
const options = normalizeOptions(result);
|
|
6314
|
+
for (const record of missingRecords) {
|
|
6315
|
+
const option = findOptionForRecord(options, sourceEntry.field, record);
|
|
6316
|
+
if (!option) continue;
|
|
6317
|
+
cacheRef.current.set(resolveRecordCacheKey(sourceEntry.source.key, sourceEntry.field, record), option);
|
|
6318
|
+
}
|
|
6319
|
+
const cached = readSourceEntryCachedOptions(sourceEntry);
|
|
6320
|
+
setOptionsByField((current) => ({
|
|
6321
|
+
...current,
|
|
6322
|
+
[sourceEntry.field]: cached
|
|
6323
|
+
}));
|
|
6324
|
+
}, (error) => {
|
|
6325
|
+
if (cancelled || requestVersionRef.current !== requestVersion) return;
|
|
6326
|
+
console.warn(`[AutoCrud] Failed to resolve data source "${sourceEntry.source.key}".`, error);
|
|
6327
|
+
});
|
|
6328
|
+
}
|
|
6329
|
+
return () => {
|
|
6330
|
+
cancelled = true;
|
|
6331
|
+
};
|
|
6332
|
+
}, [
|
|
6333
|
+
registryVersion,
|
|
6334
|
+
rows,
|
|
6335
|
+
sourceEntries
|
|
6336
|
+
]);
|
|
6337
|
+
return { optionsByField };
|
|
6338
|
+
}
|
|
6145
6339
|
function getOptionLabel(value, options) {
|
|
6146
6340
|
const stringValue = String(value);
|
|
6147
6341
|
return options?.find((option) => option.value === stringValue)?.label ?? stringValue;
|
|
@@ -6150,7 +6344,7 @@ function getOptionLabel(value, options) {
|
|
|
6150
6344
|
* 从统一配置生成表格 overrides
|
|
6151
6345
|
* 当 filter 配置存在时,合并到 meta 中(不影响列隐藏)
|
|
6152
6346
|
*/
|
|
6153
|
-
function buildTableOverrides(fields, legacyOverrides, dynamicFilterState) {
|
|
6347
|
+
function buildTableOverrides(fields, legacyOverrides, dynamicFilterState, dynamicResolveState) {
|
|
6154
6348
|
const result = { ...legacyOverrides };
|
|
6155
6349
|
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
6156
6350
|
const fieldOptions = normalizeFieldOptions(config.enum);
|
|
@@ -6158,7 +6352,8 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState) {
|
|
|
6158
6352
|
const filterDataSourceRegistered = dynamicFilterState?.registeredByField[key];
|
|
6159
6353
|
const filterDataSourceSearchable = dynamicFilterState?.searchableByField[key];
|
|
6160
6354
|
const currentDynamicOptions = !fieldOptions && filterDataSourceRegistered ? toTableOptions(dynamicFilterState?.optionsByField[key] ?? []) : void 0;
|
|
6161
|
-
const
|
|
6355
|
+
const mergedDynamicOptions = mergeFieldOptions(!fieldOptions && filterDataSourceRegistered ? dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? [] : void 0, !fieldOptions ? dynamicResolveState?.optionsByField[key] : void 0);
|
|
6356
|
+
const dynamicOptions = !fieldOptions && mergedDynamicOptions ? toTableOptions(mergedDynamicOptions) : void 0;
|
|
6162
6357
|
const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
|
|
6163
6358
|
const fieldEnumMeta = tableOptions ? {
|
|
6164
6359
|
options: tableOptions,
|
|
@@ -6166,18 +6361,20 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState) {
|
|
|
6166
6361
|
} : void 0;
|
|
6167
6362
|
const fieldDataSourceMeta = dynamicOptions ? {
|
|
6168
6363
|
options: dynamicOptions,
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6364
|
+
...filterDataSourceRegistered ? {
|
|
6365
|
+
autoCrudFilterOptions: currentDynamicOptions ?? dynamicOptions,
|
|
6366
|
+
autoCrudFilterHasMore: dynamicFilterState?.hasMoreByField[key] ?? false,
|
|
6367
|
+
autoCrudFilterLoading: dynamicFilterState?.loadingByField[key] ?? false,
|
|
6368
|
+
autoCrudFilterOnPopupScroll: (event) => {
|
|
6369
|
+
if (!isNearPopupScrollBottom(event.currentTarget)) return;
|
|
6370
|
+
dynamicFilterState?.loadMore(key);
|
|
6371
|
+
},
|
|
6372
|
+
variant: "multiSelect",
|
|
6373
|
+
...filterDataSourceSearchable ? {
|
|
6374
|
+
autoCrudFilterSearchValue: dynamicFilterState?.searchValues[key] ?? "",
|
|
6375
|
+
autoCrudFilterOnSearch: (value) => dynamicFilterState?.setSearchValue(key, value),
|
|
6376
|
+
autoCrudFilterShouldFilter: false
|
|
6377
|
+
} : void 0
|
|
6181
6378
|
} : void 0
|
|
6182
6379
|
} : void 0;
|
|
6183
6380
|
let filterMeta;
|
|
@@ -6232,10 +6429,18 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState) {
|
|
|
6232
6429
|
/**
|
|
6233
6430
|
* 从统一配置生成隐藏列列表
|
|
6234
6431
|
*/
|
|
6235
|
-
function buildHiddenColumns(fields, legacyHidden, denyFields) {
|
|
6236
|
-
const
|
|
6237
|
-
|
|
6238
|
-
|
|
6432
|
+
function buildHiddenColumns(fields, legacyHidden, denyFields, legacyOverrides) {
|
|
6433
|
+
const legacyHiddenSet = new Set(legacyHidden ?? []);
|
|
6434
|
+
const denySet = new Set(denyFields ?? []);
|
|
6435
|
+
const hidden = new Set([
|
|
6436
|
+
...DEFAULT_HIDDEN_AUDIT_FIELDS,
|
|
6437
|
+
...legacyHiddenSet,
|
|
6438
|
+
...denySet
|
|
6439
|
+
]);
|
|
6440
|
+
for (const key of DEFAULT_HIDDEN_AUDIT_FIELDS) if (!legacyHiddenSet.has(key) && !denySet.has(key) && isTableOverrideExplicitlyVisible(legacyOverrides?.[key])) hidden.delete(key);
|
|
6441
|
+
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
6442
|
+
if (isDefaultHiddenAuditField(key) && !legacyHiddenSet.has(key) && !denySet.has(key) && isFieldTableExplicitlyVisible(config)) hidden.delete(key);
|
|
6443
|
+
if (config.hidden) hidden.add(key);
|
|
6239
6444
|
else if (config.table === false) hidden.add(key);
|
|
6240
6445
|
else if (config.table && typeof config.table === "object" && config.table.hidden) hidden.add(key);
|
|
6241
6446
|
}
|
|
@@ -6339,7 +6544,10 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
|
|
|
6339
6544
|
className: "grid gap-4 py-4",
|
|
6340
6545
|
children: Object.entries(shape).filter(([key]) => {
|
|
6341
6546
|
if (denySet.has(key)) return false;
|
|
6342
|
-
|
|
6547
|
+
const config = fieldConfig?.[key];
|
|
6548
|
+
if (config?.hidden) return false;
|
|
6549
|
+
if (isDefaultHiddenAuditField(key) && !isFieldTableExplicitlyVisible(config)) return false;
|
|
6550
|
+
return true;
|
|
6343
6551
|
}).map(([key, fieldSchema]) => {
|
|
6344
6552
|
const parsed = parseZodField(fieldSchema);
|
|
6345
6553
|
const label = fieldConfig?.[key]?.label ?? humanize(key);
|
|
@@ -6495,6 +6703,14 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6495
6703
|
const [exporting, setExporting] = react.useState(false);
|
|
6496
6704
|
const getSelectedRowsRef = react.useRef(null);
|
|
6497
6705
|
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
6706
|
+
const hiddenColumns = react.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields, tableConfig?.overrides), [
|
|
6707
|
+
resolvedFields,
|
|
6708
|
+
tableConfig?.hidden,
|
|
6709
|
+
denyFields,
|
|
6710
|
+
tableConfig?.overrides
|
|
6711
|
+
]);
|
|
6712
|
+
const dynamicResolveOptions = useDynamicResolveOptions(resolvedFields, resource.tableData.data, hiddenColumns);
|
|
6713
|
+
const dynamicOptionsByField = react.useMemo(() => mergeOptionsByField(dynamicFilterOptions.labelOptionsByField, dynamicResolveOptions.optionsByField), [dynamicFilterOptions.labelOptionsByField, dynamicResolveOptions.optionsByField]);
|
|
6498
6714
|
const resourceIdKey = resource.idKey ?? "id";
|
|
6499
6715
|
const actionRegistryVersion = useCrudActionsVersion();
|
|
6500
6716
|
const unifiedActions = react.useMemo(() => {
|
|
@@ -6528,31 +6744,13 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6528
6744
|
const handleSelectedRowsChange = react.useCallback((rows) => {
|
|
6529
6745
|
setSelectedRows((currentRows) => haveSameRowSelection(currentRows, rows, resourceIdKey) ? currentRows : rows);
|
|
6530
6746
|
}, [resourceIdKey]);
|
|
6531
|
-
const
|
|
6532
|
-
const
|
|
6533
|
-
const
|
|
6534
|
-
|
|
6535
|
-
idKey: resourceIdKey,
|
|
6536
|
-
rowIds,
|
|
6537
|
-
selectedRowIds,
|
|
6538
|
-
selectedCount,
|
|
6539
|
-
isRefreshing: resource.tableData.isFetching,
|
|
6540
|
-
...resource.handlers.refresh ? { refresh: resource.handlers.refresh } : {},
|
|
6541
|
-
...toolbarOpenCreate ? { openCreate: toolbarOpenCreate } : {}
|
|
6542
|
-
}), [
|
|
6543
|
-
id,
|
|
6544
|
-
resourceIdKey,
|
|
6545
|
-
rowIds,
|
|
6546
|
-
selectedRowIds,
|
|
6547
|
-
selectedCount,
|
|
6548
|
-
resource.handlers.refresh,
|
|
6549
|
-
resource.tableData.isFetching,
|
|
6550
|
-
toolbarOpenCreate
|
|
6551
|
-
]);
|
|
6552
|
-
const resolvedToolbarActions = resolveToolbarActionsWithResolver(id, registryToolbarActions, toolbarContext);
|
|
6747
|
+
const ownerRefreshAction = react.useMemo(() => readToolbarAction(ownerToolbarActions, "refresh"), [ownerToolbarActions]);
|
|
6748
|
+
const ownerImportAction = react.useMemo(() => readToolbarAction(ownerToolbarActions, "import"), [ownerToolbarActions]);
|
|
6749
|
+
const ownerExportAction = react.useMemo(() => readToolbarAction(ownerToolbarActions, "export"), [ownerToolbarActions]);
|
|
6750
|
+
const ownerCreateAction = react.useMemo(() => readToolbarAction(ownerToolbarActions, "create"), [ownerToolbarActions]);
|
|
6553
6751
|
const importColumns = react.useMemo(() => {
|
|
6554
6752
|
const shape = resolvedSchema.shape;
|
|
6555
|
-
const denySet = new Set(denyFields ?? []);
|
|
6753
|
+
const denySet = new Set([...DEFAULT_IMPORT_MANAGED_FIELDS, ...denyFields ?? []]);
|
|
6556
6754
|
return Object.keys(shape).filter((key) => !denySet.has(key));
|
|
6557
6755
|
}, [resolvedSchema, denyFields]);
|
|
6558
6756
|
const handleExport = react.useCallback(async (mode) => {
|
|
@@ -6586,21 +6784,75 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6586
6784
|
setExporting(false);
|
|
6587
6785
|
}
|
|
6588
6786
|
}, [selectedCount, handleExport]);
|
|
6589
|
-
const
|
|
6787
|
+
const toolbarRefresh = react.useMemo(() => {
|
|
6788
|
+
const refresh = ownerRefreshAction?.onClick ?? resource.handlers.refresh;
|
|
6789
|
+
if (!refresh) return void 0;
|
|
6790
|
+
return async () => refresh();
|
|
6791
|
+
}, [ownerRefreshAction?.onClick, resource.handlers.refresh]);
|
|
6792
|
+
const toolbarOpenImport = react.useMemo(() => {
|
|
6793
|
+
if (!canImport) return void 0;
|
|
6794
|
+
return ownerImportAction?.onClick ?? (() => setImportOpen(true));
|
|
6795
|
+
}, [canImport, ownerImportAction?.onClick]);
|
|
6796
|
+
const toolbarExportData = react.useMemo(() => {
|
|
6797
|
+
if (!canExport) return void 0;
|
|
6798
|
+
const exportData = ownerExportAction?.onClick ?? handleExportClick;
|
|
6799
|
+
return async () => exportData();
|
|
6800
|
+
}, [
|
|
6801
|
+
canExport,
|
|
6802
|
+
handleExportClick,
|
|
6803
|
+
ownerExportAction?.onClick
|
|
6804
|
+
]);
|
|
6805
|
+
const toolbarOpenCreate = react.useMemo(() => {
|
|
6806
|
+
if (!can.create) return void 0;
|
|
6807
|
+
return ownerCreateAction?.onClick ?? onCreate ?? resource.handlers.openCreate;
|
|
6808
|
+
}, [
|
|
6809
|
+
can.create,
|
|
6810
|
+
onCreate,
|
|
6811
|
+
ownerCreateAction?.onClick,
|
|
6812
|
+
resource.handlers.openCreate
|
|
6813
|
+
]);
|
|
6814
|
+
const toolbarContext = react.useMemo(() => ({
|
|
6815
|
+
crudId: id ?? "",
|
|
6816
|
+
idKey: resourceIdKey,
|
|
6817
|
+
rowIds,
|
|
6818
|
+
selectedRowIds,
|
|
6819
|
+
selectedCount,
|
|
6820
|
+
isRefreshing: resource.tableData.isFetching,
|
|
6821
|
+
isExporting: exporting,
|
|
6822
|
+
...toolbarRefresh ? { refresh: toolbarRefresh } : {},
|
|
6823
|
+
...toolbarOpenImport ? { openImport: toolbarOpenImport } : {},
|
|
6824
|
+
...toolbarExportData ? { exportData: toolbarExportData } : {},
|
|
6825
|
+
...toolbarOpenCreate ? { openCreate: toolbarOpenCreate } : {}
|
|
6826
|
+
}), [
|
|
6827
|
+
id,
|
|
6828
|
+
resourceIdKey,
|
|
6829
|
+
rowIds,
|
|
6830
|
+
selectedRowIds,
|
|
6831
|
+
selectedCount,
|
|
6832
|
+
resource.tableData.isFetching,
|
|
6833
|
+
exporting,
|
|
6834
|
+
toolbarRefresh,
|
|
6835
|
+
toolbarOpenImport,
|
|
6836
|
+
toolbarExportData,
|
|
6837
|
+
toolbarOpenCreate
|
|
6838
|
+
]);
|
|
6839
|
+
const resolvedToolbarActions = resolveToolbarActionsWithResolver(id, registryToolbarActions, toolbarContext);
|
|
6840
|
+
const tableOverrides = react.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides, dynamicFilterOptions, dynamicResolveOptions), [
|
|
6590
6841
|
resolvedFields,
|
|
6591
6842
|
tableConfig?.overrides,
|
|
6592
|
-
dynamicFilterOptions
|
|
6843
|
+
dynamicFilterOptions,
|
|
6844
|
+
dynamicResolveOptions
|
|
6845
|
+
]);
|
|
6846
|
+
const defaultSort = react.useMemo(() => resource.defaultSort ?? tableConfig?.defaultSort ?? getDefaultSortingForSchema(resolvedSchema), [
|
|
6847
|
+
resource.defaultSort,
|
|
6848
|
+
resolvedSchema,
|
|
6849
|
+
tableConfig?.defaultSort
|
|
6593
6850
|
]);
|
|
6594
6851
|
const formOverrides = react.useMemo(() => buildFormOverrides(resolvedFields, formConfig?.overrides, denyFields), [
|
|
6595
6852
|
resolvedFields,
|
|
6596
6853
|
formConfig?.overrides,
|
|
6597
6854
|
denyFields
|
|
6598
6855
|
]);
|
|
6599
|
-
const hiddenColumns = react.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields), [
|
|
6600
|
-
resolvedFields,
|
|
6601
|
-
tableConfig?.hidden,
|
|
6602
|
-
denyFields
|
|
6603
|
-
]);
|
|
6604
6856
|
const searchConfig = react.useMemo(() => {
|
|
6605
6857
|
const tableSearch = tableConfig?.search;
|
|
6606
6858
|
if (tableSearch === false) return false;
|
|
@@ -6661,7 +6913,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6661
6913
|
children: (() => {
|
|
6662
6914
|
const renderBuiltinButton = (type, overrides) => {
|
|
6663
6915
|
if (type === "refresh") {
|
|
6664
|
-
const onRefresh = overrides?.onClick ??
|
|
6916
|
+
const onRefresh = overrides?.onClick ?? toolbarRefresh;
|
|
6665
6917
|
const label = overrides?.label ?? locale.toolbar.refresh;
|
|
6666
6918
|
if (!onRefresh) return null;
|
|
6667
6919
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Button, {
|
|
@@ -6677,18 +6929,18 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6677
6929
|
if (type === "import" && canImport) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Button, {
|
|
6678
6930
|
variant: "outline",
|
|
6679
6931
|
size: "sm",
|
|
6680
|
-
onClick: overrides?.onClick ??
|
|
6932
|
+
onClick: overrides?.onClick ?? toolbarOpenImport,
|
|
6681
6933
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Download, { className: "mr-2 h-4 w-4" }), overrides?.label ?? locale.toolbar.import]
|
|
6682
6934
|
}, "import");
|
|
6683
6935
|
if (type === "export" && canExport) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Button, {
|
|
6684
6936
|
variant: "outline",
|
|
6685
6937
|
size: "sm",
|
|
6686
|
-
onClick: overrides?.onClick ??
|
|
6938
|
+
onClick: overrides?.onClick ?? toolbarExportData,
|
|
6687
6939
|
disabled: exporting || selectedCount === 0 && !resource.handlers.export,
|
|
6688
6940
|
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" }), overrides?.label ?? (selectedCount > 0 ? locale.toolbar.exportSelected(selectedCount) : locale.toolbar.export)]
|
|
6689
6941
|
}, "export");
|
|
6690
6942
|
if (type === "create" && can.create) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Button, {
|
|
6691
|
-
onClick: overrides?.onClick ??
|
|
6943
|
+
onClick: overrides?.onClick ?? toolbarOpenCreate,
|
|
6692
6944
|
children: overrides?.label ?? locale.toolbar.create
|
|
6693
6945
|
}, "create");
|
|
6694
6946
|
return null;
|
|
@@ -6747,7 +6999,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6747
6999
|
deleteConfirmation: locale.bulkDeleteModal,
|
|
6748
7000
|
enableExport: canExport,
|
|
6749
7001
|
showDefaultExport: false,
|
|
6750
|
-
initialSorting:
|
|
7002
|
+
initialSorting: defaultSort,
|
|
6751
7003
|
onSelectedRowsChange: handleSelectedRowsChange,
|
|
6752
7004
|
getSelectedRows: getSelectedRowsRef
|
|
6753
7005
|
}),
|
|
@@ -6776,7 +7028,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6776
7028
|
data: resource.modal.selected,
|
|
6777
7029
|
schema: resolvedSchema,
|
|
6778
7030
|
fields: resolvedFields,
|
|
6779
|
-
dynamicOptions:
|
|
7031
|
+
dynamicOptions: dynamicOptionsByField,
|
|
6780
7032
|
denyFields,
|
|
6781
7033
|
locale
|
|
6782
7034
|
}),
|
|
@@ -7549,7 +7801,7 @@ function modalReducer(state, action$1) {
|
|
|
7549
7801
|
* Hook 主函数
|
|
7550
7802
|
*/
|
|
7551
7803
|
function useAutoCrudResource({ router, schema, query: queryTransform, options = {} }) {
|
|
7552
|
-
const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher } = options;
|
|
7804
|
+
const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher, defaultSort: defaultSortOption } = options;
|
|
7553
7805
|
const toast = (0, react.useMemo)(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
|
|
7554
7806
|
const hooksRef = (0, react.useRef)(hooks);
|
|
7555
7807
|
(0, react.useEffect)(() => {
|
|
@@ -7557,12 +7809,13 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7557
7809
|
}, [hooks]);
|
|
7558
7810
|
const metadata = (router.meta?.useQuery(void 0, { staleTime: 3e4 }))?.data;
|
|
7559
7811
|
const resolvedSchema = (0, react.useMemo)(() => mergeMetadataSchema(schema, metadata?.schema), [schema, metadata?.schema]);
|
|
7812
|
+
const defaultSorting = (0, react.useMemo)(() => defaultSortOption === false ? [] : defaultSortOption ?? getDefaultSortingForSchema(resolvedSchema), [defaultSortOption, resolvedSchema]);
|
|
7560
7813
|
const metadataFields = (0, react.useMemo)(() => readMetadataFields(metadata?.fields), [metadata?.fields]);
|
|
7561
7814
|
const columns = (0, react.useMemo)(() => createTableSchema(resolvedSchema), [resolvedSchema]);
|
|
7562
7815
|
const [urlParams] = useQueryStates({
|
|
7563
7816
|
page: parseAsInteger.withDefault(1),
|
|
7564
7817
|
perPage: parseAsInteger.withDefault(10),
|
|
7565
|
-
sort: getSortingStateParser().withDefault(
|
|
7818
|
+
sort: getSortingStateParser().withDefault(defaultSorting),
|
|
7566
7819
|
joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
|
|
7567
7820
|
search: parseAsString.withDefault("")
|
|
7568
7821
|
}, { shallow: false });
|
|
@@ -7850,6 +8103,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7850
8103
|
},
|
|
7851
8104
|
schema: resolvedSchema,
|
|
7852
8105
|
fields: metadataFields,
|
|
8106
|
+
defaultSort: defaultSorting,
|
|
7853
8107
|
modal,
|
|
7854
8108
|
mutations: {
|
|
7855
8109
|
isCreating: createMutation.isPending,
|