@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/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(排除 id, createdAt, updatedAt)
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 readCreateAction(actions) {
5856
- return actions.find((action$1) => action$1.type === "create");
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 dynamicOptions = !fieldOptions && filterDataSourceRegistered ? toTableOptions(dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? []) : void 0;
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
- autoCrudFilterOptions: currentDynamicOptions ?? dynamicOptions,
6170
- autoCrudFilterHasMore: dynamicFilterState?.hasMoreByField[key] ?? false,
6171
- autoCrudFilterLoading: dynamicFilterState?.loadingByField[key] ?? false,
6172
- autoCrudFilterOnPopupScroll: (event) => {
6173
- if (!isNearPopupScrollBottom(event.currentTarget)) return;
6174
- dynamicFilterState?.loadMore(key);
6175
- },
6176
- variant: "multiSelect",
6177
- ...filterDataSourceSearchable ? {
6178
- autoCrudFilterSearchValue: dynamicFilterState?.searchValues[key] ?? "",
6179
- autoCrudFilterOnSearch: (value) => dynamicFilterState?.setSearchValue(key, value),
6180
- autoCrudFilterShouldFilter: false
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 hidden = new Set([...legacyHidden ?? [], ...denyFields ?? []]);
6237
- if (fields) {
6238
- for (const [key, config] of Object.entries(fields)) if (config.hidden) hidden.add(key);
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
- return !(fieldConfig?.[key])?.hidden;
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 ownerCreateAction = react.useMemo(() => readCreateAction(ownerToolbarActions), [ownerToolbarActions]);
6532
- const toolbarOpenCreate = can.create ? ownerCreateAction?.onClick ?? onCreate ?? resource.handlers.openCreate : void 0;
6533
- const toolbarContext = react.useMemo(() => ({
6534
- crudId: id ?? "",
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 tableOverrides = react.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides, dynamicFilterOptions), [
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 ?? resource.handlers.refresh;
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 ?? (() => setImportOpen(true)),
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 ?? handleExportClick,
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 ?? onCreate ?? resource.handlers.openCreate,
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: tableConfig?.defaultSort,
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: dynamicFilterOptions.optionsByField,
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,