best-lowcode-runtime 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,41 @@
1
+ // src/lowcode/adapter.ts
2
+ import dayjs from "dayjs";
3
+ function normalizeBestValues(values, normalize = defaultNormalize) {
4
+ return Object.fromEntries(
5
+ Object.entries(values).map(([field, value]) => [
6
+ field,
7
+ normalizeNested(value, normalize, field)
8
+ ])
9
+ );
10
+ }
11
+ function normalizeNested(value, normalize, field) {
12
+ if (Array.isArray(value)) return value.map((item) => normalizeNested(item, normalize, field));
13
+ if (dayjs.isDayjs(value) || value instanceof Date) return value;
14
+ if (value && typeof value === "object") {
15
+ return Object.fromEntries(
16
+ Object.entries(value).map(([key, item]) => [key, normalizeNested(item, normalize, key)])
17
+ );
18
+ }
19
+ return normalize(value, field);
20
+ }
21
+ function defaultNormalize(value) {
22
+ if (typeof value === "string") return value.trim() === "" ? void 0 : value.trim();
23
+ return value;
24
+ }
25
+ function createBestCrudAdapter(options = {}) {
26
+ const normalize = options.normalize ?? defaultNormalize;
27
+ return {
28
+ fromList: options.fromList,
29
+ fromDetail: options.fromDetail,
30
+ toCreatePayload: (values) => options.toCreatePayload?.(normalizeBestValues(values, normalize)) ?? normalizeBestValues(values, normalize),
31
+ toUpdatePayload: (values, record) => options.toUpdatePayload?.(normalizeBestValues(values, normalize), record) ?? normalizeBestValues(values, normalize)
32
+ };
33
+ }
34
+
1
35
  // src/lowcode/BestCrudPage.tsx
2
36
  import { Button as Button4, Modal as Modal2, message } from "antd";
3
- import dayjs3 from "dayjs";
4
- import { useCallback, useEffect as useEffect3, useMemo as useMemo3, useRef, useState as useState3 } from "react";
37
+ import dayjs5 from "dayjs";
38
+ import { useCallback, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo5, useRef as useRef3, useState as useState3 } from "react";
5
39
 
6
40
  // src/runtime/BestProvider.tsx
7
41
  import { ConfigProvider } from "antd";
@@ -141,22 +175,105 @@ function BestFilePreview({ files, onDownload }) {
141
175
 
142
176
  // src/ui/BestForm.tsx
143
177
  import { Button as Button2, DatePicker, Form, Input as Input2, InputNumber, Select, Space } from "antd";
144
- import { useEffect, useState as useState2 } from "react";
178
+ import dayjs2 from "dayjs";
179
+ import { useEffect, useMemo as useMemo3, useRef, useState as useState2 } from "react";
145
180
  import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
181
+ function toNamePath(path) {
182
+ return path === void 0 ? [] : Array.isArray(path) ? path : [path];
183
+ }
184
+ function isNamePath(value) {
185
+ return Array.isArray(value);
186
+ }
187
+ function isRecord(value) {
188
+ if (!value || typeof value !== "object" || Array.isArray(value) || value instanceof Date) {
189
+ return false;
190
+ }
191
+ if (dayjs2.isDayjs(value)) return false;
192
+ const prototype = Object.getPrototypeOf(value);
193
+ return prototype === Object.prototype || prototype === null;
194
+ }
195
+ function appendNamePath(path, ...segments) {
196
+ return [...toNamePath(path), ...segments];
197
+ }
198
+ function getConditionFieldValue(values, field) {
199
+ if (Object.hasOwn(values, field)) return values[field];
200
+ return getPathValue(values, field.split("."));
201
+ }
202
+ function getScopedValues(values, itemPath) {
203
+ const itemValues = getPathValue(values, itemPath);
204
+ return isRecord(itemValues) ? { ...values, ...itemValues } : values;
205
+ }
206
+ function isFieldVisible(field, values, mode) {
207
+ return !field.hidden && (!field.visibleWhen || evaluateCondition(field.visibleWhen, values, mode));
208
+ }
209
+ function resolveFieldDisabled(field, values, mode) {
210
+ return field.disabled || Boolean(field.disabledWhen && evaluateCondition(field.disabledWhen, values, mode));
211
+ }
212
+ function toDayjsValue(value) {
213
+ if (dayjs2.isDayjs(value)) return value;
214
+ if (value instanceof Date) return dayjs2(value);
215
+ if (typeof value === "number") {
216
+ const normalized = Math.abs(value) >= 1e12 ? value : value * 1e3;
217
+ const parsed = dayjs2(normalized);
218
+ return parsed.isValid() ? parsed : null;
219
+ }
220
+ if (typeof value === "string") {
221
+ const trimmed = value.trim();
222
+ if (!trimmed) return null;
223
+ const parsed = dayjs2(trimmed);
224
+ return parsed.isValid() ? parsed : null;
225
+ }
226
+ return null;
227
+ }
228
+ function toDatePickerValue(value) {
229
+ return toDayjsValue(value);
230
+ }
231
+ function toDateRangePickerValue(value) {
232
+ if (!Array.isArray(value)) return null;
233
+ const nextValue = [toDayjsValue(value[0]), toDayjsValue(value[1])];
234
+ return nextValue[0] || nextValue[1] ? nextValue : null;
235
+ }
236
+ function serializeDatePickerValue(_, dateString) {
237
+ return dateString || void 0;
238
+ }
239
+ function serializeDateRangePickerValue(_, dateStrings) {
240
+ return dateStrings[0] || dateStrings[1] ? dateStrings : void 0;
241
+ }
242
+ function fieldSignature(field) {
243
+ return {
244
+ field: field.field,
245
+ component: field.component,
246
+ defaultValue: field.defaultValue,
247
+ remoteService: field.remoteService,
248
+ searchField: field.searchField,
249
+ labelField: field.labelField,
250
+ valueField: field.valueField,
251
+ debounceMs: field.debounceMs,
252
+ pageSize: field.pageSize,
253
+ itemFields: field.itemFields?.map((itemField) => fieldSignature(itemField))
254
+ };
255
+ }
146
256
  function FormControl({
147
257
  field,
148
258
  values,
259
+ scopeValues,
149
260
  mode = "create",
150
261
  form,
262
+ namePath,
263
+ value,
264
+ onChange,
151
265
  ...controlProps
152
266
  }) {
153
267
  const registry = useBestRegistry();
268
+ const renderValues = scopeValues ?? values;
154
269
  switch (field.component) {
155
270
  case "select":
156
271
  return /* @__PURE__ */ jsx5(
157
272
  Select,
158
273
  {
159
274
  ...controlProps,
275
+ value,
276
+ onChange,
160
277
  disabled: field.disabled,
161
278
  options: field.options,
162
279
  placeholder: field.placeholder
@@ -167,10 +284,13 @@ function FormControl({
167
284
  RemoteSelect,
168
285
  {
169
286
  field,
170
- value: values[field.field],
287
+ value: value !== void 0 ? value : getPathValue(values, namePath ?? field.field),
171
288
  disabled: field.disabled,
172
289
  placeholder: field.placeholder,
173
- onChange: (value) => form.setFieldValue(field.field, value),
290
+ onChange: (nextValue) => {
291
+ if (onChange) onChange(nextValue);
292
+ else form.setFieldValue(namePath ?? field.field, nextValue);
293
+ },
174
294
  registry
175
295
  }
176
296
  );
@@ -179,18 +299,31 @@ function FormControl({
179
299
  InputNumber,
180
300
  {
181
301
  ...controlProps,
302
+ value,
303
+ onChange,
182
304
  disabled: field.disabled,
183
305
  placeholder: field.placeholder,
184
306
  style: { width: "100%" }
185
307
  }
186
308
  );
187
309
  case "date":
188
- return /* @__PURE__ */ jsx5(DatePicker, { ...controlProps, disabled: field.disabled, style: { width: "100%" } });
310
+ return /* @__PURE__ */ jsx5(
311
+ DatePicker,
312
+ {
313
+ ...controlProps,
314
+ value: toDatePickerValue(value),
315
+ onChange: (date, dateString) => onChange?.(serializeDatePickerValue(date, dateString)),
316
+ disabled: field.disabled,
317
+ style: { width: "100%" }
318
+ }
319
+ );
189
320
  case "dateRange":
190
321
  return /* @__PURE__ */ jsx5(
191
322
  DatePicker.RangePicker,
192
323
  {
193
324
  ...controlProps,
325
+ value: toDateRangePickerValue(value),
326
+ onChange: (dates, dateStrings) => onChange?.(serializeDateRangePickerValue(dates, dateStrings)),
194
327
  disabled: field.disabled,
195
328
  style: { width: "100%" }
196
329
  }
@@ -200,6 +333,8 @@ function FormControl({
200
333
  Input2.TextArea,
201
334
  {
202
335
  ...controlProps,
336
+ value,
337
+ onChange,
203
338
  disabled: field.disabled,
204
339
  placeholder: field.placeholder
205
340
  }
@@ -207,48 +342,135 @@ function FormControl({
207
342
  case "slot":
208
343
  return /* @__PURE__ */ jsx5(Fragment, { children: field.render?.({
209
344
  field: field.field,
210
- values,
211
- value: values[field.field],
345
+ values: renderValues,
346
+ value: value !== void 0 ? value : getPathValue(values, namePath ?? field.field),
212
347
  mode,
213
348
  disabled: field.disabled,
214
- setValue: (name, value) => form.setFieldValue(name, value)
349
+ setValue: (name, nextValue) => form.setFieldValue(
350
+ namePath ? isNamePath(name) ? name : [...toNamePath(namePath).slice(0, -1), name] : name,
351
+ nextValue
352
+ )
215
353
  }) });
216
354
  case "repeatable":
217
- return /* @__PURE__ */ jsx5(Form.List, { name: field.field, children: (items, { add, remove }) => /* @__PURE__ */ jsxs2(Fragment, { children: [
218
- items.map((item) => /* @__PURE__ */ jsxs2(Space, { align: "start", children: [
219
- field.itemFields?.map((itemField) => /* @__PURE__ */ jsx5(
220
- Form.Item,
221
- {
222
- label: itemField.label,
223
- name: [item.name, itemField.field],
224
- rules: toAntRules(itemField.rules),
225
- children: /* @__PURE__ */ jsx5(FormControl, { field: itemField, form, mode, values })
226
- },
227
- itemField.field
228
- )),
229
- /* @__PURE__ */ jsx5(
230
- Button2,
355
+ return /* @__PURE__ */ jsx5(
356
+ RepeatableField,
357
+ {
358
+ field,
359
+ form,
360
+ mode,
361
+ namePath,
362
+ values
363
+ }
364
+ );
365
+ default:
366
+ return /* @__PURE__ */ jsx5(
367
+ Input2,
368
+ {
369
+ ...controlProps,
370
+ value,
371
+ onChange,
372
+ disabled: field.disabled,
373
+ placeholder: field.placeholder
374
+ }
375
+ );
376
+ }
377
+ }
378
+ function RepeatableField({
379
+ field,
380
+ form,
381
+ mode,
382
+ namePath,
383
+ values
384
+ }) {
385
+ const listName = namePath ?? field.field;
386
+ return /* @__PURE__ */ jsx5(Form.List, { name: listName, children: (items, { add, remove }) => /* @__PURE__ */ jsxs2(Fragment, { children: [
387
+ items.map((item) => /* @__PURE__ */ jsx5(
388
+ RepeatableFieldRow,
389
+ {
390
+ field,
391
+ form,
392
+ itemCount: items.length,
393
+ itemName: item.name,
394
+ itemKey: item.key,
395
+ listName,
396
+ mode,
397
+ remove,
398
+ values
399
+ },
400
+ item.key
401
+ )),
402
+ /* @__PURE__ */ jsx5(
403
+ Button2,
404
+ {
405
+ disabled: field.maxItems !== void 0 && items.length >= field.maxItems,
406
+ onClick: () => add(resolveInitialValues(field.itemFields ?? [], void 0)),
407
+ type: "dashed",
408
+ children: "\u65B0\u589E\u4E00\u9879"
409
+ }
410
+ )
411
+ ] }) });
412
+ }
413
+ function RepeatableFieldRow({
414
+ field,
415
+ form,
416
+ itemCount,
417
+ itemName,
418
+ itemKey,
419
+ listName,
420
+ mode,
421
+ remove,
422
+ values
423
+ }) {
424
+ const itemPath = appendNamePath(listName, itemName);
425
+ const itemValues = getPathValue(values, itemPath);
426
+ const scopeValues = getScopedValues(values, itemPath);
427
+ useEffect(() => {
428
+ field.itemFields?.forEach((itemField) => {
429
+ if (!itemField.clearWhenHidden) return;
430
+ const nextPath = appendNamePath(itemPath, itemField.field);
431
+ if (!isFieldVisible(itemField, scopeValues, mode) && getPathValue(values, nextPath) !== void 0) {
432
+ form.setFieldValue(nextPath, void 0);
433
+ }
434
+ });
435
+ }, [field.itemFields, form, itemPath, mode, scopeValues, values]);
436
+ return /* @__PURE__ */ jsxs2(Space, { align: "start", children: [
437
+ field.itemFields?.map((itemField) => {
438
+ if (!isFieldVisible(itemField, scopeValues, mode)) return null;
439
+ const nextPath = appendNamePath(itemPath, itemField.field);
440
+ return /* @__PURE__ */ jsx5(
441
+ Form.Item,
442
+ {
443
+ label: itemField.label,
444
+ name: [itemName, itemField.field],
445
+ rules: toAntRules(itemField.rules),
446
+ children: /* @__PURE__ */ jsx5(
447
+ FormControl,
231
448
  {
232
- disabled: items.length <= (field.minItems ?? 0),
233
- onClick: () => remove(item.name),
234
- type: "link",
235
- children: "\u5220\u9664"
449
+ field: {
450
+ ...itemField,
451
+ disabled: resolveFieldDisabled(itemField, scopeValues, mode)
452
+ },
453
+ form,
454
+ mode,
455
+ namePath: nextPath,
456
+ scopeValues,
457
+ values: isRecord(itemValues) ? { ...values, ...itemValues } : values
236
458
  }
237
459
  )
238
- ] }, item.key)),
239
- /* @__PURE__ */ jsx5(
240
- Button2,
241
- {
242
- disabled: Boolean(field.maxItems && items.length >= field.maxItems),
243
- onClick: () => add(),
244
- type: "dashed",
245
- children: "\u65B0\u589E\u4E00\u9879"
246
- }
247
- )
248
- ] }) });
249
- default:
250
- return /* @__PURE__ */ jsx5(Input2, { ...controlProps, disabled: field.disabled, placeholder: field.placeholder });
251
- }
460
+ },
461
+ itemField.field
462
+ );
463
+ }),
464
+ /* @__PURE__ */ jsx5(
465
+ Button2,
466
+ {
467
+ disabled: itemCount <= (field.minItems ?? 0),
468
+ onClick: () => remove(itemName),
469
+ type: "link",
470
+ children: "\u5220\u9664"
471
+ }
472
+ )
473
+ ] }, itemKey);
252
474
  }
253
475
  function FormFields({ fields, form, initialValues, mode = "create" }) {
254
476
  const values = Form.useWatch([], { form, preserve: true }) ?? initialValues ?? {};
@@ -290,7 +512,7 @@ function toAntRules(rules) {
290
512
  type: rule.type === "string" ? void 0 : rule.type,
291
513
  min: rule.min ?? rule.minLength,
292
514
  max: rule.max ?? rule.maxLength,
293
- pattern: rule.pattern ? new RegExp(rule.pattern) : void 0,
515
+ pattern: safeRegExp(rule.pattern),
294
516
  message: rule.message,
295
517
  validator: rule.type === "integer" ? async (_, value) => {
296
518
  if (value === void 0 || value === null || value === "") return;
@@ -298,6 +520,14 @@ function toAntRules(rules) {
298
520
  } : void 0
299
521
  }));
300
522
  }
523
+ function safeRegExp(pattern) {
524
+ if (!pattern) return void 0;
525
+ try {
526
+ return new RegExp(pattern);
527
+ } catch {
528
+ return void 0;
529
+ }
530
+ }
301
531
  function RemoteSelect({
302
532
  field,
303
533
  value,
@@ -306,39 +536,105 @@ function RemoteSelect({
306
536
  onChange,
307
537
  registry
308
538
  }) {
309
- const [options, setOptions] = useState2([]);
539
+ const [keywordOptions, setKeywordOptions] = useState2([]);
540
+ const [selectedOptions, setSelectedOptions] = useState2([]);
310
541
  const [loading, setLoading] = useState2(false);
311
542
  const [keyword, setKeyword] = useState2("");
543
+ const keywordRequestId = useRef(0);
544
+ const selectedRequestId = useRef(0);
312
545
  const service = field.remoteService ? registry.services[field.remoteService] : void 0;
313
546
  const labelField = field.labelField ?? "label";
314
547
  const valueField = field.valueField ?? "value";
315
548
  const searchField = field.searchField ?? "keyword";
316
549
  const debounceMs = field.debounceMs ?? 300;
550
+ const pageSize = field.pageSize ?? 20;
551
+ const parseOptions = (response) => {
552
+ const raw = Array.isArray(response) ? response : response?.items ?? response?.list ?? [];
553
+ return raw.flatMap((item) => {
554
+ if (!item || typeof item !== "object") return [];
555
+ const record = item;
556
+ const optionValue = record[valueField];
557
+ if (typeof optionValue !== "string" && typeof optionValue !== "number") return [];
558
+ return [
559
+ {
560
+ label: String(record[labelField] ?? optionValue ?? ""),
561
+ value: optionValue,
562
+ disabled: Boolean(record.disabled)
563
+ }
564
+ ];
565
+ });
566
+ };
567
+ const mergeOptions = (base, extra) => {
568
+ const merged = new Map(base.map((item) => [String(item.value), item]));
569
+ extra.forEach((item) => {
570
+ merged.set(String(item.value), item);
571
+ });
572
+ return [...merged.values()];
573
+ };
574
+ const options = useMemo3(
575
+ () => mergeOptions(keywordOptions, selectedOptions),
576
+ [keywordOptions, selectedOptions]
577
+ );
317
578
  useEffect(() => {
318
- if (!service) return;
579
+ const currentRequestId = ++keywordRequestId.current;
580
+ if (!service) {
581
+ setKeywordOptions([]);
582
+ setLoading(false);
583
+ return;
584
+ }
319
585
  const controller = new AbortController();
586
+ const isCurrent = () => currentRequestId === keywordRequestId.current && !controller.signal.aborted;
320
587
  const timer = window.setTimeout(async () => {
321
588
  setLoading(true);
322
589
  try {
323
590
  const response = await service(
324
- { [searchField]: keyword, page: 1, pageSize: field.pageSize ?? 20 },
591
+ { [searchField]: keyword, page: 1, pageSize },
325
592
  { signal: controller.signal }
326
593
  );
327
- const raw = Array.isArray(response) ? response : response?.items ?? response?.list ?? [];
328
- setOptions(
329
- raw.filter((item) => !!item && typeof item === "object").map((item) => ({ label: String(item[labelField] ?? ""), value: item[valueField] }))
330
- );
594
+ if (isCurrent()) setKeywordOptions(parseOptions(response));
331
595
  } catch {
332
- setOptions([]);
596
+ if (isCurrent()) setKeywordOptions([]);
333
597
  } finally {
334
- if (!controller.signal.aborted) setLoading(false);
598
+ if (isCurrent()) setLoading(false);
335
599
  }
336
600
  }, debounceMs);
337
601
  return () => {
338
602
  window.clearTimeout(timer);
339
603
  controller.abort();
340
604
  };
341
- }, [debounceMs, field.pageSize, keyword, labelField, searchField, service, valueField]);
605
+ }, [debounceMs, keyword, labelField, pageSize, searchField, service, valueField]);
606
+ useEffect(() => {
607
+ const currentRequestId = ++selectedRequestId.current;
608
+ if (!service) {
609
+ setSelectedOptions([]);
610
+ return;
611
+ }
612
+ if (value === void 0 || value === null || value === "") {
613
+ setSelectedOptions([]);
614
+ return;
615
+ }
616
+ if (typeof value !== "string" && typeof value !== "number") {
617
+ setSelectedOptions([]);
618
+ return;
619
+ }
620
+ const controller = new AbortController();
621
+ const isCurrent = () => currentRequestId === selectedRequestId.current && !controller.signal.aborted;
622
+ const fallbackOption = {
623
+ label: String(value),
624
+ value
625
+ };
626
+ setSelectedOptions([fallbackOption]);
627
+ void service({ [valueField]: value, page: 1, pageSize }, { signal: controller.signal }).then((response) => {
628
+ if (!isCurrent()) return;
629
+ const resolvedOptions = parseOptions(response);
630
+ if (resolvedOptions.length) {
631
+ setSelectedOptions(mergeOptions([fallbackOption], resolvedOptions));
632
+ }
633
+ }).catch(() => void 0);
634
+ return () => {
635
+ controller.abort();
636
+ };
637
+ }, [labelField, pageSize, service, value, valueField]);
342
638
  return /* @__PURE__ */ jsx5(
343
639
  Select,
344
640
  {
@@ -363,6 +659,39 @@ function BestForm({
363
659
  mode,
364
660
  onSubmit,
365
661
  onCancel
662
+ }) {
663
+ const resolvedInitialValues = useMemo3(
664
+ () => resolveInitialValues(fields, initialValues),
665
+ [fields, initialValues]
666
+ );
667
+ const formSignature = useMemo3(
668
+ () => JSON.stringify([mode, fields.map((field) => fieldSignature(field)), resolvedInitialValues]),
669
+ [fields, mode, resolvedInitialValues]
670
+ );
671
+ return /* @__PURE__ */ jsx5(
672
+ BestFormBody,
673
+ {
674
+ extra,
675
+ fields,
676
+ initialValues: resolvedInitialValues,
677
+ loading,
678
+ mode,
679
+ onCancel,
680
+ onSubmit,
681
+ submitText
682
+ },
683
+ formSignature
684
+ );
685
+ }
686
+ function BestFormBody({
687
+ fields,
688
+ initialValues,
689
+ loading,
690
+ submitText = "\u63D0\u4EA4",
691
+ extra,
692
+ mode,
693
+ onSubmit,
694
+ onCancel
366
695
  }) {
367
696
  const [form] = Form.useForm();
368
697
  return /* @__PURE__ */ jsxs2(Form, { form, initialValues, layout: "vertical", onFinish: onSubmit, children: [
@@ -374,12 +703,49 @@ function BestForm({
374
703
  ] })
375
704
  ] });
376
705
  }
706
+ function getDefaultValues(fields) {
707
+ return Object.fromEntries(
708
+ fields.filter((field) => field.defaultValue !== void 0).map((field) => [field.field, field.defaultValue])
709
+ );
710
+ }
711
+ function resolveInitialValues(fields, initialValues) {
712
+ const values = mergeDefinedValues(getDefaultValues(fields), initialValues);
713
+ const resolvedValues = { ...values };
714
+ fields.forEach((field) => {
715
+ if (field.component !== "repeatable" || !field.itemFields) return;
716
+ const items = Array.isArray(resolvedValues[field.field]) ? resolvedValues[field.field] : [];
717
+ resolvedValues[field.field] = items.map(
718
+ (item) => resolveInitialValues(field.itemFields ?? [], isRecord(item) ? item : {})
719
+ );
720
+ });
721
+ return resolvedValues;
722
+ }
723
+ function mergeDefinedValues(baseValues, nextValues) {
724
+ const mergedValues = { ...baseValues };
725
+ Object.entries(nextValues ?? {}).forEach(([key, value]) => {
726
+ if (value === void 0) return;
727
+ const currentValue = mergedValues[key];
728
+ if (isRecord(currentValue) && isRecord(value)) {
729
+ mergedValues[key] = mergeDefinedValues(currentValue, value);
730
+ return;
731
+ }
732
+ mergedValues[key] = value;
733
+ });
734
+ return mergedValues;
735
+ }
736
+ function getPathValue(values, path) {
737
+ const parts = Array.isArray(path) ? path : [path];
738
+ return parts.reduce((current, part) => {
739
+ if (current && typeof current === "object") return current[part];
740
+ return void 0;
741
+ }, values);
742
+ }
377
743
  function evaluateCondition(condition, values, mode) {
378
744
  switch (condition.operator) {
379
745
  case "equals":
380
- return values[condition.field] === condition.value;
746
+ return getConditionFieldValue(values, condition.field) === condition.value;
381
747
  case "notEmpty":
382
- return values[condition.field] !== void 0 && values[condition.field] !== null && values[condition.field] !== "";
748
+ return getConditionFieldValue(values, condition.field) !== void 0 && getConditionFieldValue(values, condition.field) !== null && getConditionFieldValue(values, condition.field) !== "";
383
749
  case "modeEquals":
384
750
  return mode === condition.value;
385
751
  case "and":
@@ -430,17 +796,36 @@ function BestModal({ children, open, title, onClose }) {
430
796
 
431
797
  // src/ui/BestSearch.tsx
432
798
  import { Button as Button3, Col, DatePicker as DatePicker2, Form as Form2, Input as Input3, InputNumber as InputNumber2, Row, Select as Select2 } from "antd";
433
- import dayjs from "dayjs";
434
- import { useEffect as useEffect2 } from "react";
799
+ import dayjs3 from "dayjs";
800
+ import { useEffect as useEffect2, useMemo as useMemo4, useRef as useRef2 } from "react";
435
801
  import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
436
- function FieldControl({ field }) {
802
+ function searchFormSignature(fields, initialValues) {
803
+ return JSON.stringify([
804
+ fields.map((field) => ({
805
+ field: field.field,
806
+ component: field.component,
807
+ defaultValue: field.defaultValue,
808
+ remoteService: field.remoteService,
809
+ searchField: field.searchField,
810
+ labelField: field.labelField,
811
+ valueField: field.valueField,
812
+ debounceMs: field.debounceMs,
813
+ pageSize: field.pageSize
814
+ })),
815
+ initialValues
816
+ ]);
817
+ }
818
+ function FieldControl({ field, value, onChange, ...controlProps }) {
437
819
  const registry = useBestRegistry();
438
820
  switch (field.component) {
439
821
  case "select":
440
822
  return /* @__PURE__ */ jsx8(
441
823
  Select2,
442
824
  {
825
+ ...controlProps,
443
826
  allowClear: true,
827
+ value,
828
+ onChange,
444
829
  disabled: field.disabled,
445
830
  options: field.options,
446
831
  placeholder: field.placeholder
@@ -450,6 +835,9 @@ function FieldControl({ field }) {
450
835
  return /* @__PURE__ */ jsx8(
451
836
  InputNumber2,
452
837
  {
838
+ ...controlProps,
839
+ value,
840
+ onChange,
453
841
  disabled: field.disabled,
454
842
  placeholder: field.placeholder,
455
843
  style: { width: "100%" }
@@ -460,8 +848,10 @@ function FieldControl({ field }) {
460
848
  RemoteSelect,
461
849
  {
462
850
  field,
463
- value: void 0,
464
- onChange: () => void 0,
851
+ value,
852
+ onChange: onChange ?? (() => void 0),
853
+ disabled: field.disabled,
854
+ placeholder: field.placeholder,
465
855
  registry
466
856
  }
467
857
  );
@@ -469,8 +859,11 @@ function FieldControl({ field }) {
469
859
  return /* @__PURE__ */ jsx8(
470
860
  DatePicker2,
471
861
  {
862
+ ...controlProps,
863
+ value: toDatePickerValue(value),
864
+ onChange: (date, dateString) => onChange?.(serializeDatePickerValue(date, dateString)),
472
865
  disabled: field.disabled,
473
- disabledDate: (current) => field.maxDate === "today" && current.isAfter(dayjs(), "day"),
866
+ disabledDate: (current) => field.maxDate === "today" && current.isAfter(dayjs3(), "day"),
474
867
  placeholder: field.placeholder,
475
868
  style: { width: "100%" }
476
869
  }
@@ -479,38 +872,108 @@ function FieldControl({ field }) {
479
872
  return /* @__PURE__ */ jsx8(
480
873
  DatePicker2.RangePicker,
481
874
  {
875
+ ...controlProps,
876
+ value: toDateRangePickerValue(value),
877
+ onChange: (dates, dateStrings) => onChange?.(serializeDateRangePickerValue(dates, dateStrings)),
482
878
  disabled: field.disabled,
483
- disabledDate: (current) => field.maxDate === "today" && current.isAfter(dayjs(), "day"),
879
+ disabledDate: (current) => field.maxDate === "today" && current.isAfter(dayjs3(), "day"),
484
880
  style: { width: "100%" }
485
881
  }
486
882
  );
487
883
  case "textarea":
488
- return /* @__PURE__ */ jsx8(Input3.TextArea, { disabled: field.disabled, placeholder: field.placeholder });
884
+ return /* @__PURE__ */ jsx8(
885
+ Input3.TextArea,
886
+ {
887
+ ...controlProps,
888
+ value,
889
+ onChange,
890
+ disabled: field.disabled,
891
+ placeholder: field.placeholder
892
+ }
893
+ );
489
894
  default:
490
- return /* @__PURE__ */ jsx8(Input3, { allowClear: true, disabled: field.disabled, placeholder: field.placeholder });
895
+ return /* @__PURE__ */ jsx8(
896
+ Input3,
897
+ {
898
+ ...controlProps,
899
+ allowClear: true,
900
+ value,
901
+ onChange,
902
+ disabled: field.disabled,
903
+ placeholder: field.placeholder
904
+ }
905
+ );
491
906
  }
492
907
  }
493
- function syncValues(form, value) {
494
- form.setFieldsValue(value ?? {});
908
+ function syncValues(form, value, fields) {
909
+ form.setFieldsValue(Object.fromEntries(fields.map((field) => [field.field, void 0])));
910
+ form.setFieldsValue(value);
495
911
  }
496
912
  function BestSearch({
497
913
  fields,
498
914
  value,
915
+ defaultValues,
499
916
  loading,
500
917
  submitText = "\u67E5\u8BE2",
501
918
  extra,
502
919
  onChange,
503
920
  onSearch,
504
921
  onReset
922
+ }) {
923
+ const initialValues = useMemo4(
924
+ () => mergeDefinedValues(getDefaultValues(fields), defaultValues),
925
+ [fields, defaultValues]
926
+ );
927
+ return /* @__PURE__ */ jsx8(
928
+ BestSearchBody,
929
+ {
930
+ extra,
931
+ fields,
932
+ initialValues,
933
+ loading,
934
+ onChange,
935
+ onReset,
936
+ onSearch,
937
+ submitText,
938
+ value
939
+ },
940
+ searchFormSignature(fields, initialValues)
941
+ );
942
+ }
943
+ function BestSearchBody({
944
+ fields,
945
+ value,
946
+ initialValues,
947
+ loading,
948
+ submitText,
949
+ extra,
950
+ onChange,
951
+ onSearch,
952
+ onReset
505
953
  }) {
506
954
  const [form] = Form2.useForm();
955
+ const valueSignatureRef = useRef2(void 0);
956
+ const controlledRef = useRef2(false);
507
957
  useEffect2(() => {
508
- syncValues(form, value);
509
- }, [form, value]);
958
+ if (value === void 0) {
959
+ if (controlledRef.current) {
960
+ syncValues(form, {}, fields);
961
+ valueSignatureRef.current = JSON.stringify({});
962
+ controlledRef.current = false;
963
+ }
964
+ return;
965
+ }
966
+ controlledRef.current = true;
967
+ const signature = JSON.stringify(value);
968
+ if (valueSignatureRef.current === signature) return;
969
+ syncValues(form, value, fields);
970
+ valueSignatureRef.current = signature;
971
+ }, [fields, form, value]);
510
972
  return /* @__PURE__ */ jsx8(
511
973
  Form2,
512
974
  {
513
975
  form,
976
+ initialValues,
514
977
  layout: "vertical",
515
978
  onFinish: (values) => onSearch(values),
516
979
  onValuesChange: (_, values) => onChange?.(values),
@@ -523,7 +986,8 @@ function BestSearch({
523
986
  Button3,
524
987
  {
525
988
  onClick: () => {
526
- form.resetFields();
989
+ syncValues(form, {}, fields);
990
+ valueSignatureRef.current = JSON.stringify({});
527
991
  onChange?.({});
528
992
  onReset?.();
529
993
  },
@@ -601,9 +1065,9 @@ function BestTable({
601
1065
  }
602
1066
 
603
1067
  // src/lowcode/runtime.ts
604
- import dayjs2 from "dayjs";
1068
+ import dayjs4 from "dayjs";
605
1069
  import utc from "dayjs/plugin/utc.js";
606
- dayjs2.extend(utc);
1070
+ dayjs4.extend(utc);
607
1071
  function toBestListQuery(params, sort) {
608
1072
  const { current, pageSize, ...filters } = params;
609
1073
  const normalizedSort = Object.fromEntries(
@@ -636,7 +1100,7 @@ function formatCrudValue(value, format) {
636
1100
  const numericValue = typeof value === "number" ? value : Number(value.trim());
637
1101
  timestamp = Number.isFinite(numericValue) && numericValue !== 0 ? Math.abs(numericValue) >= 1e12 ? numericValue : numericValue * 1e3 : value;
638
1102
  }
639
- const date = dayjs2.utc(timestamp);
1103
+ const date = dayjs4.utc(timestamp);
640
1104
  if (!date.isValid()) return String(value);
641
1105
  return date.format(format === "date" ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss");
642
1106
  }
@@ -753,6 +1217,13 @@ function validateFields(fields, path, registry, diagnostics) {
753
1217
  "field.repeatable",
754
1218
  "repeatable \u7EC4\u4EF6\u81F3\u5C11\u9700\u8981\u4E00\u4E2A itemFields \u5B57\u6BB5"
755
1219
  );
1220
+ if (field.itemFields?.length)
1221
+ validateFields(
1222
+ field.itemFields,
1223
+ `${fieldPath}/itemFields`,
1224
+ registry,
1225
+ diagnostics
1226
+ );
756
1227
  if (field.minItems !== void 0 && field.maxItems !== void 0 && field.minItems > field.maxItems)
757
1228
  push(diagnostics, fieldPath, "field.repeatable.range", "minItems \u4E0D\u80FD\u5927\u4E8E maxItems");
758
1229
  if (field.slot && registry && !registry.slots[field.slot])
@@ -809,6 +1280,22 @@ function validateCrudPageSchema(schema, registry) {
809
1280
  `\u672A\u6CE8\u518C\u670D\u52A1\uFF1A${schema.dataSource.detail}`
810
1281
  );
811
1282
  }
1283
+ if (schema.dataSource?.create && registry && !registry.services[schema.dataSource.create]) {
1284
+ push(
1285
+ diagnostics,
1286
+ "/dataSource/create",
1287
+ "registry.service",
1288
+ `\u672A\u6CE8\u518C\u670D\u52A1\uFF1A${schema.dataSource.create}`
1289
+ );
1290
+ }
1291
+ if (schema.dataSource?.update && registry && !registry.services[schema.dataSource.update]) {
1292
+ push(
1293
+ diagnostics,
1294
+ "/dataSource/update",
1295
+ "registry.service",
1296
+ `\u672A\u6CE8\u518C\u670D\u52A1\uFF1A${schema.dataSource.update}`
1297
+ );
1298
+ }
812
1299
  if (schema.dataSource?.remove && registry && !registry.services[schema.dataSource.remove]) {
813
1300
  push(
814
1301
  diagnostics,
@@ -884,8 +1371,10 @@ ${message2}`);
884
1371
 
885
1372
  // src/lowcode/BestCrudPage.tsx
886
1373
  import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
887
- function toFieldDefinition(field, options, slots) {
1374
+ function toFieldDefinition(field, dictionaries, slots) {
888
1375
  const slot = field.slot;
1376
+ const options = field.dict ? dictionaries[field.dict] ?? [] : [];
1377
+ const rules = field.rules ?? [];
889
1378
  return {
890
1379
  field: field.field,
891
1380
  label: field.label,
@@ -903,25 +1392,22 @@ function toFieldDefinition(field, options, slots) {
903
1392
  hidden: field.hidden,
904
1393
  maxDate: field.maxDate,
905
1394
  span: field.span,
1395
+ defaultValue: field.defaultValue,
906
1396
  visibleWhen: field.visibleWhen,
907
1397
  disabledWhen: field.disabledWhen,
908
1398
  clearWhenHidden: field.clearWhenHidden,
909
- itemFields: field.itemFields?.map((item) => toFieldDefinition(item, [], slots)),
1399
+ itemFields: field.itemFields?.map(
1400
+ (item) => toFieldDefinition(item, dictionaries, slots)
1401
+ ),
910
1402
  minItems: field.minItems,
911
1403
  maxItems: field.maxItems,
912
1404
  render: slot ? (context) => slots[slot]?.(context) : void 0,
913
- rules: field.required ? [{ required: true, message: `\u8BF7\u586B\u5199${field.label}` }, ...field.rules ?? []] : field.rules
1405
+ rules: field.required && !rules.some((rule) => rule.required) ? [{ required: true, message: `\u8BF7\u586B\u5199${field.label}` }, ...rules] : rules
914
1406
  };
915
1407
  }
916
1408
  function useFields(fields = []) {
917
1409
  const registry = useBestRegistry();
918
- return fields.map(
919
- (field) => toFieldDefinition(
920
- field,
921
- field.dict ? registry.dictionaries[field.dict] ?? [] : [],
922
- registry.slots
923
- )
924
- );
1410
+ return fields.map((field) => toFieldDefinition(field, registry.dictionaries, registry.slots));
925
1411
  }
926
1412
  function toDetailField(field, dictionary, slots) {
927
1413
  const slot = field.slot;
@@ -937,6 +1423,7 @@ function toDetailField(field, dictionary, slots) {
937
1423
  };
938
1424
  }
939
1425
  function toTableColumn(column, dictionary, slots) {
1426
+ const slot = column.slot;
940
1427
  const valueEnum = column.dict ? Object.fromEntries(
941
1428
  (dictionary[column.dict] ?? []).map((item) => [String(item.value), { text: item.label }])
942
1429
  ) : void 0;
@@ -946,7 +1433,12 @@ function toTableColumn(column, dictionary, slots) {
946
1433
  title: column.title,
947
1434
  width: column.width,
948
1435
  valueEnum,
949
- render: (value, record) => column.slot ? slots[column.slot]?.({ field: column.field, record, value }) ?? "-" : formatCrudValue(value, column.format)
1436
+ renderText: column.format && !slot && !column.dict ? (value) => formatCrudValue(value, column.format) : void 0,
1437
+ render: slot ? (_value, record) => slots[slot]?.({
1438
+ field: column.field,
1439
+ record,
1440
+ value: record[column.field]
1441
+ }) ?? "-" : void 0
950
1442
  };
951
1443
  }
952
1444
  function toProTableSearchColumn(field, dictionary) {
@@ -974,7 +1466,7 @@ function toProTableSearchColumn(field, dictionary) {
974
1466
  initialValue: field.defaultValue,
975
1467
  fieldProps: {
976
1468
  ...options ? { options } : {},
977
- ...field.maxDate === "today" ? { disabledDate: (current) => current.isAfter(dayjs3(), "day") } : {}
1469
+ ...field.maxDate === "today" ? { disabledDate: (current) => current.isAfter(dayjs5(), "day") } : {}
978
1470
  }
979
1471
  };
980
1472
  }
@@ -995,20 +1487,41 @@ function BestCrudPage({ adapter, className, schema }) {
995
1487
  const registeredListService = useBestListService(schema.dataSource.list);
996
1488
  if (!registeredListService) throw new Error(`\u672A\u6CE8\u518C\u5217\u8868\u670D\u52A1\uFF1A${schema.dataSource.list}`);
997
1489
  const listService = useCallback(
998
- async (query, options) => {
999
- const response = await registeredListService(query, options);
1490
+ async (query2, options) => {
1491
+ const response = await registeredListService(query2, options);
1000
1492
  return adapter?.fromList ? { ...response, items: adapter.fromList(response.items) } : response;
1001
1493
  },
1002
1494
  [adapter, registeredListService]
1003
1495
  );
1004
1496
  const searchFields = useFields(schema.search);
1005
- const useBestSearch = schema.searchMode === "bestSearch";
1497
+ const useBestSearch = schema.searchMode === "bestSearch" || schema.search?.some((field) => field.component === "remoteSelect");
1006
1498
  const formFields = useFields(schema.form);
1007
- const actionRef = useRef(void 0);
1008
- const pageRequestRef = useRef(void 0);
1009
- const queryRef = useRef({});
1499
+ const actionRef = useRef3(void 0);
1500
+ const searchDefaultValues = useMemo5(() => getDefaultValues(searchFields), [searchFields]);
1501
+ const searchFieldNames = useMemo5(
1502
+ () => new Set(searchFields.map((field) => field.field)),
1503
+ [searchFields]
1504
+ );
1505
+ const searchSchemaSignature = useMemo5(
1506
+ () => JSON.stringify(
1507
+ searchFields.map((field) => ({
1508
+ field: field.field,
1509
+ component: field.component,
1510
+ defaultValue: field.defaultValue
1511
+ }))
1512
+ ),
1513
+ [searchFields]
1514
+ );
1515
+ const [query, setQuery] = useState3(searchDefaultValues);
1516
+ const queryRef = useRef3(searchDefaultValues);
1517
+ const queryStateRef = useRef3({
1518
+ schemaId: schema.id,
1519
+ searchSchemaSignature,
1520
+ userTouched: false
1521
+ });
1010
1522
  const [drawer, setDrawer] = useState3({ mode: "closed" });
1011
1523
  const [submitting, setSubmitting] = useState3(false);
1524
+ const submittingRef = useRef3(false);
1012
1525
  const handleAction = useCallback(
1013
1526
  async (action, record) => {
1014
1527
  if (action.access && !registry.access(action.access)) return;
@@ -1038,7 +1551,10 @@ function BestCrudPage({ adapter, className, schema }) {
1038
1551
  if (action.effect === "openCreate") setDrawer({ mode: "create" });
1039
1552
  if (action.effect === "remove" && schema.dataSource.remove) {
1040
1553
  const removeService = registry.services[schema.dataSource.remove];
1041
- if (!removeService) return;
1554
+ if (!removeService) {
1555
+ message.error(`\u672A\u6CE8\u518C\u670D\u52A1\uFF1A${schema.dataSource.remove}`);
1556
+ return;
1557
+ }
1042
1558
  try {
1043
1559
  const deleted = await removeCrudRecord(
1044
1560
  record ?? {},
@@ -1055,17 +1571,22 @@ function BestCrudPage({ adapter, className, schema }) {
1055
1571
  }
1056
1572
  }
1057
1573
  if (action.effect === "runAction" && action.action) {
1574
+ const runAction = registry.actions[action.action];
1575
+ if (!runAction) {
1576
+ message.error(`\u672A\u6CE8\u518C\u52A8\u4F5C\uFF1A${action.action}`);
1577
+ return;
1578
+ }
1058
1579
  try {
1059
- await registry.actions[action.action]?.({ record });
1580
+ await runAction({ record });
1060
1581
  actionRef.current?.reload();
1061
1582
  } catch (error) {
1062
1583
  message.error(errorMessage(error, `${action.label}\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`));
1063
1584
  }
1064
1585
  }
1065
1586
  },
1066
- [registry]
1587
+ [adapter, registry, schema]
1067
1588
  );
1068
- const columns = useMemo3(() => {
1589
+ const columns = useMemo5(() => {
1069
1590
  const proTableSearchFields = useBestSearch ? [] : schema.search ?? [];
1070
1591
  const searchColumns = new Map(
1071
1592
  proTableSearchFields.map((field) => [
@@ -1097,31 +1618,57 @@ function BestCrudPage({ adapter, className, schema }) {
1097
1618
  schema.table.columns,
1098
1619
  useBestSearch
1099
1620
  ]);
1100
- const request = useMemo3(() => {
1101
- pageRequestRef.current?.abort();
1102
- const pageRequest = createLatestPageRequest(
1621
+ const pageRequest = useMemo5(
1622
+ () => createLatestPageRequest(
1103
1623
  listService,
1104
1624
  (error) => message.error(errorMessage(error, `${schema.title}\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`))
1625
+ ),
1626
+ [listService, schema.title]
1627
+ );
1628
+ const request = useCallback(
1629
+ (params, sort) => pageRequest.request(
1630
+ toBestListQuery({ ...params, ...useBestSearch ? queryRef.current : {} }, sort)
1631
+ ),
1632
+ [pageRequest, useBestSearch]
1633
+ );
1634
+ useLayoutEffect(() => {
1635
+ const previous = queryStateRef.current;
1636
+ if (previous.schemaId === schema.id && previous.searchSchemaSignature === searchSchemaSignature)
1637
+ return;
1638
+ const shouldUseDefaults = previous.schemaId !== schema.id || !previous.userTouched;
1639
+ const nextQuery = shouldUseDefaults ? searchDefaultValues : Object.fromEntries(
1640
+ Object.entries(queryRef.current).filter(([field]) => searchFieldNames.has(field))
1105
1641
  );
1106
- pageRequestRef.current = pageRequest;
1107
- return (params, sort) => pageRequest.request(toBestListQuery({ ...params, ...queryRef.current }, sort));
1108
- }, [listService, schema.title]);
1109
- useEffect3(() => () => pageRequestRef.current?.abort(), []);
1642
+ queryRef.current = nextQuery;
1643
+ setQuery(nextQuery);
1644
+ queryStateRef.current = {
1645
+ schemaId: schema.id,
1646
+ searchSchemaSignature,
1647
+ userTouched: shouldUseDefaults ? false : previous.userTouched
1648
+ };
1649
+ }, [schema.id, searchDefaultValues, searchFieldNames, searchSchemaSignature]);
1650
+ useEffect3(() => () => pageRequest.abort(), [pageRequest]);
1110
1651
  const rowKey = schema.table.rowKey;
1111
1652
  const submitForm = useCallback(
1112
1653
  async (values) => {
1113
- if (submitting) return;
1654
+ if (submittingRef.current) return;
1114
1655
  const serviceKey = drawer.mode === "edit" ? schema.dataSource.update : schema.dataSource.create;
1115
1656
  if (!serviceKey) {
1116
1657
  message.error(`${drawer.mode === "edit" ? "\u66F4\u65B0" : "\u521B\u5EFA"}\u670D\u52A1\u672A\u914D\u7F6E`);
1117
1658
  return;
1118
1659
  }
1660
+ const service = registry.services[serviceKey];
1661
+ if (!service) {
1662
+ message.error(`\u672A\u6CE8\u518C\u670D\u52A1\uFF1A${serviceKey}`);
1663
+ return;
1664
+ }
1119
1665
  const record = drawer.mode === "closed" ? void 0 : drawer.record;
1666
+ submittingRef.current = true;
1120
1667
  setSubmitting(true);
1121
1668
  try {
1122
1669
  const mergedValues = { ...record, ...values };
1123
1670
  const payload = drawer.mode === "edit" ? adapter?.toUpdatePayload?.(mergedValues, record) ?? mergedValues : adapter?.toCreatePayload?.(mergedValues) ?? mergedValues;
1124
- await registry.services[serviceKey]?.(payload);
1671
+ await service(payload);
1125
1672
  message.success("\u4FDD\u5B58\u6210\u529F");
1126
1673
  setDrawer({ mode: "closed" });
1127
1674
  actionRef.current?.reload();
@@ -1129,21 +1676,28 @@ function BestCrudPage({ adapter, className, schema }) {
1129
1676
  message.error(errorMessage(error, "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"));
1130
1677
  } finally {
1131
1678
  setSubmitting(false);
1679
+ submittingRef.current = false;
1132
1680
  }
1133
1681
  },
1134
- [adapter, drawer, registry.services, schema.dataSource.create, schema.dataSource.update, submitting]
1682
+ [adapter, drawer, registry.services, schema.dataSource.create, schema.dataSource.update]
1135
1683
  );
1136
1684
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1137
1685
  useBestSearch && searchFields.length ? /* @__PURE__ */ jsx11(
1138
1686
  BestSearch,
1139
1687
  {
1140
1688
  fields: searchFields,
1689
+ value: query,
1690
+ defaultValues: searchDefaultValues,
1141
1691
  onReset: () => {
1142
1692
  queryRef.current = {};
1693
+ queryStateRef.current = { ...queryStateRef.current, userTouched: true };
1694
+ setQuery({});
1143
1695
  actionRef.current?.reload();
1144
1696
  },
1145
1697
  onSearch: (values) => {
1146
1698
  queryRef.current = values;
1699
+ queryStateRef.current = { ...queryStateRef.current, userTouched: true };
1700
+ setQuery(values);
1147
1701
  actionRef.current?.reload();
1148
1702
  }
1149
1703
  }
@@ -1218,35 +1772,6 @@ function BestCrudPage({ adapter, className, schema }) {
1218
1772
  );
1219
1773
  }
1220
1774
  }
1221
-
1222
- // src/lowcode/adapter.ts
1223
- function normalizeBestValues(values, normalize = defaultNormalize) {
1224
- return Object.fromEntries(
1225
- Object.entries(values).map(([field, value]) => [field, normalizeNested(value, normalize, field)])
1226
- );
1227
- }
1228
- function normalizeNested(value, normalize, field) {
1229
- if (Array.isArray(value)) return value.map((item) => normalizeNested(item, normalize, field));
1230
- if (value && typeof value === "object" && !(value instanceof Date)) {
1231
- return Object.fromEntries(
1232
- Object.entries(value).map(([key, item]) => [key, normalizeNested(item, normalize, key)])
1233
- );
1234
- }
1235
- return normalize(value, field);
1236
- }
1237
- function defaultNormalize(value) {
1238
- if (typeof value === "string") return value.trim() === "" ? void 0 : value.trim();
1239
- return value;
1240
- }
1241
- function createBestCrudAdapter(options = {}) {
1242
- const normalize = options.normalize ?? defaultNormalize;
1243
- return {
1244
- fromList: options.fromList,
1245
- fromDetail: options.fromDetail,
1246
- toCreatePayload: (values) => options.toCreatePayload?.(normalizeBestValues(values, normalize)) ?? normalizeBestValues(values, normalize),
1247
- toUpdatePayload: (values, record) => options.toUpdatePayload?.(normalizeBestValues(values, normalize), record) ?? normalizeBestValues(values, normalize)
1248
- };
1249
- }
1250
1775
  export {
1251
1776
  BestBatchInput,
1252
1777
  BestCrudPage,