@hzab/list-render 1.10.0-beta1 → 1.10.0-beta2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/list-render",
3
- "version": "1.10.0-beta1",
3
+ "version": "1.10.0-beta2",
4
4
  "description": "",
5
5
  "main": "src",
6
6
  "scripts": {
@@ -1,2 +1,4 @@
1
1
  // 设置url query参数时默认key名称
2
2
  export const URL_PARAM_NAME = "defaultSearchParams";
3
+
4
+ export const CLEAR_LIST = ["Input", "Select", "DatePicker.RangePicker", "DatePicker"];
@@ -1,11 +1,16 @@
1
1
  import _ from "lodash";
2
2
  import { getFieldMap } from "./utils";
3
- import { isRunStr, handleSchemaStrVal, isScopeKey as formIsScopeKey, getScopeKeyVal } from '@hzab/form-render/src/common/schema-handler';
3
+ import {
4
+ isRunStr,
5
+ handleSchemaStrVal,
6
+ isScopeKey as formIsScopeKey,
7
+ getScopeKeyVal,
8
+ } from "@hzab/form-render/src/common/schema-handler";
4
9
  import { URL_PARAM_NAME } from "./constant";
5
10
 
6
11
  // ===================== 类型定义 =====================
7
12
 
8
- type AntdDateComponent = 'DatePicker' | 'DatePicker.RangePicker';
13
+ type AntdDateComponent = "DatePicker" | "DatePicker.RangePicker";
9
14
 
10
15
  interface XComponentProps {
11
16
  isSplitTimes?: boolean;
@@ -17,13 +22,13 @@ interface XComponentProps {
17
22
  interface FormilyField {
18
23
  type?: string;
19
24
  title?: string;
20
- 'x-decorator'?: string;
21
- 'x-component'?: string;
22
- 'x-validator'?: unknown[];
23
- 'x-component-props'?: XComponentProps;
24
- 'x-decorator-props'?: Record<string, unknown>;
25
- 'x-designable-id'?: string;
26
- 'x-index'?: number;
25
+ "x-decorator"?: string;
26
+ "x-component"?: string;
27
+ "x-validator"?: unknown[];
28
+ "x-component-props"?: XComponentProps;
29
+ "x-decorator-props"?: Record<string, unknown>;
30
+ "x-designable-id"?: string;
31
+ "x-index"?: number;
27
32
  [k: string]: unknown;
28
33
  }
29
34
 
@@ -269,7 +274,6 @@ export const handleHoc = (source, cb, opt) => {
269
274
 
270
275
  let res = null;
271
276
  if (isRunStr(source)) {
272
-
273
277
  _source = handleSchemaStrVal(source, schemaScope);
274
278
  }
275
279
  if (formIsScopeKey(source, schemaScope)) {
@@ -354,6 +358,22 @@ export const getValByScope = (strKey, schemaScope = {}) => {
354
358
  }
355
359
  };
356
360
 
361
+ function removeEmptyValues(obj) {
362
+ return _.pickBy(obj, (value) => {
363
+ // 排除 undefined 和 null
364
+ if (value == null) return false;
365
+
366
+ // 排除空字符串
367
+ if (_.isString(value) && value.trim() === "") return false;
368
+
369
+ // 排除空数组
370
+ if (_.isArray(value) && value.length === 0) return false;
371
+
372
+ // 保留其他值(包括 0、false、非空对象等)
373
+ return true;
374
+ });
375
+ }
376
+
357
377
  /**
358
378
  * 将对象序列化后写入 URL query 中
359
379
  * @param obj 要存储的对象(保持原始类型)
@@ -367,7 +387,10 @@ export function setURLObjectQueryParam<T extends object>(obj: T, paramName = URL
367
387
  const [origin, hash = ""] = href.split("#");
368
388
  const [path, queryString = ""] = hash.split("?");
369
389
  const searchParams = new URLSearchParams(queryString);
370
- const encoded = encodeURIComponent(JSON.stringify(obj));
390
+ // 指定的空值不设置
391
+ const copyObj = _.cloneDeep(removeEmptyValues(obj));
392
+
393
+ const encoded = encodeURIComponent(JSON.stringify(copyObj));
371
394
 
372
395
  searchParams.set(paramName, encoded);
373
396
 
@@ -380,11 +403,11 @@ export function setURLObjectQueryParam<T extends object>(obj: T, paramName = URL
380
403
  }
381
404
 
382
405
  /**
383
- * 从当前URL中删除指定的查询参数(不刷新页面)
384
- * 专门处理哈希路由中的查询参数
385
- * @param key 要删除的查询参数名称
406
+ * 从哈希路由的查询参数中,删除某个“对象型参数”下的指定属性(不刷新页面)
407
+ * @param parentKey 父级对象参数名
408
+ * @param childKey 要删除的对象属性名
386
409
  */
387
- export function removeURLObjectQueryParam(key: string): void {
410
+ export function removeURLObjectQueryParam(parentKey: string, childKey: string, reset?: boolean): void {
388
411
  if (typeof window === "undefined") return;
389
412
 
390
413
  try {
@@ -393,21 +416,52 @@ export function setURLObjectQueryParam<T extends object>(obj: T, paramName = URL
393
416
  const [hashPath, queryString = ""] = hash.split("?");
394
417
  const searchParams = new URLSearchParams(queryString);
395
418
 
396
- searchParams.delete(key);
419
+ const raw = searchParams.get(parentKey);
420
+ if (raw == null) return;
421
+
422
+ // URLSearchParams.get 已自动做了百分号解码,但是可能会遇到编码两次的情况,因此这里再尝试解码一次
423
+ let obj: unknown;
424
+ try {
425
+ obj = JSON.parse(decodeURIComponent(raw));
426
+ } catch {
427
+ // 若不是合法 JSON,就不处理该参数
428
+ console.error(`[removeURLObjectQueryParam] 序列化URL参数错误`);
429
+ return;
430
+ }
397
431
 
398
- // 重建哈希部分
399
- let newHash = hashPath;
400
- if (searchParams.toString()) {
401
- newHash += `?${searchParams.toString()}`;
432
+ // 如果是重置,直接清空整个parentKey
433
+ if (reset && parentKey) searchParams.delete(parentKey);
434
+ else {
435
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
436
+ const record = obj as Record<string, unknown>;
437
+ // 删除空值key
438
+ if (childKey || record[childKey] === "" || record[childKey] === undefined || record[childKey] === null)
439
+ delete record[childKey];
440
+
441
+ // 如果对象被删空,则移除整个 parentKey;否则写回
442
+ if (Object.keys(record).length === 0) {
443
+ searchParams.delete(parentKey);
444
+ } else {
445
+ searchParams.set(parentKey, encodeURIComponent(JSON.stringify(record)));
446
+ }
447
+ } else {
448
+ // parentKey 的值不是对象,安全起见不处理
449
+ return;
450
+ }
402
451
  }
403
452
 
404
- const newUrl = `${origin}${newHash ? `#${newHash}` : ''}`;
453
+ // 重建哈希部分
454
+ let newHash = hashPath;
455
+ const qs = searchParams.toString();
456
+ if (qs) newHash += `?${qs}`;
405
457
 
458
+ const newUrl = `${origin}${newHash ? `#${newHash}` : ""}`;
406
459
  window.history.replaceState({}, "", newUrl);
407
460
  } catch (e) {
408
461
  console.error(`[removeURLObjectQueryParam] 删除失败:`, e);
409
462
  }
410
463
  }
464
+
411
465
  /**
412
466
  * 从 URL 中解析序列化的对象
413
467
  * @param paramName query 参数名
@@ -433,14 +487,13 @@ export function getURLObjectQueryParam<T = any>(paramName = URL_PARAM_NAME): T |
433
487
  }
434
488
  }
435
489
 
436
-
437
490
  // ===================== 辅助函数 =====================
438
491
 
439
492
  const pickFormilyProperties = (schema: FormilyV1Schema): Record<string, FormilyField> =>
440
- (schema?.schema?.properties ?? {});
493
+ schema?.schema?.properties ?? {};
441
494
 
442
495
  const isAntdDateComponent = (comp: unknown, whitelist: readonly string[]) =>
443
- typeof comp === 'string' && whitelist.includes(comp);
496
+ typeof comp === "string" && whitelist.includes(comp);
444
497
 
445
498
  const filterKeys = (props: Record<string, FormilyField>, filters: readonly string[]) => {
446
499
  const filterSet = new Set(filters);
@@ -448,14 +501,10 @@ const filterKeys = (props: Record<string, FormilyField>, filters: readonly strin
448
501
  };
449
502
 
450
503
  const matchSplitFlag = (field: FormilyField, splitFlag: string, expected: unknown) =>
451
- field?.['x-component-props']?.[splitFlag] === expected;
504
+ field?.["x-component-props"]?.[splitFlag] === expected;
452
505
 
453
506
  /** 两个值都缺失则返回 undefined;否则返回二元组 */
454
- const getRangeTupleOrUndefined = (
455
- urlObj: URLQueryObject,
456
- startKey: string,
457
- endKey: string
458
- ): [any, any] | undefined => {
507
+ const getRangeTupleOrUndefined = (urlObj: URLQueryObject, startKey: string, endKey: string): [any, any] | undefined => {
459
508
  const obj = (urlObj ?? {}) as Record<string, any>;
460
509
  const hasStart = Object.prototype.hasOwnProperty.call(obj, startKey);
461
510
  const hasEnd = Object.prototype.hasOwnProperty.call(obj, endKey);
@@ -472,14 +521,14 @@ export function extractSplitDateRanges(
472
521
  filters: string[],
473
522
  formilySchema: FormilyV1Schema,
474
523
  urlObj: URLQueryObject,
475
- options: ExtractOptions = {}
524
+ options: ExtractOptions = {},
476
525
  ): Record<string, any> {
477
526
  const {
478
- splitFlag = 'isSplitTimes',
527
+ splitFlag = "isSplitTimes",
479
528
  splitFlagExpectedValue = true,
480
- defaultStartKey = 'startTime',
481
- defaultEndKey = 'endTime',
482
- antdDateComponents = ['DatePicker', 'DatePicker.RangePicker'],
529
+ defaultStartKey = "startTime",
530
+ defaultEndKey = "endTime",
531
+ antdDateComponents = ["DatePicker", "DatePicker.RangePicker"],
483
532
  removeMatchedKeys = true,
484
533
  } = options;
485
534
 
@@ -494,10 +543,10 @@ export function extractSplitDateRanges(
494
543
 
495
544
  for (const [fieldKey, field] of candidates) {
496
545
  // 未命中则直接跳过本次循环
497
- if (!isAntdDateComponent(field?.['x-component'], antdDateComponents)) continue;
546
+ if (!isAntdDateComponent(field?.["x-component"], antdDateComponents)) continue;
498
547
  if (!matchSplitFlag(field, splitFlag, splitFlagExpectedValue)) continue;
499
548
 
500
- const props = field?.['x-component-props'] ?? {};
549
+ const props = field?.["x-component-props"] ?? {};
501
550
  const startKey = (props?.startKey as string) || defaultStartKey;
502
551
  const endKey = (props?.endKey as string) || defaultEndKey;
503
552
 
@@ -15,8 +15,13 @@ import FormModal from "./FormModal";
15
15
  import DetailModal from "./DetailModal";
16
16
 
17
17
  import { objToFormData } from "./common/utils";
18
- import { setURLObjectQueryParam, getURLObjectQueryParam, removeURLObjectQueryParam, extractSplitDateRanges } from "./common/handleQuerySchema";
19
- import { URL_PARAM_NAME } from "./common/constant";
18
+ import {
19
+ setURLObjectQueryParam,
20
+ getURLObjectQueryParam,
21
+ removeURLObjectQueryParam,
22
+ extractSplitDateRanges,
23
+ } from "./common/handleQuerySchema";
24
+ import { CLEAR_LIST, URL_PARAM_NAME } from "./common/constant";
20
25
 
21
26
  import "./index.less";
22
27
 
@@ -54,7 +59,7 @@ const ListRender = forwardRef(function (props, parentRef) {
54
59
  /**
55
60
  * 自定义设置url query对象参数的key
56
61
  * */
57
- appendUrlQueryKey = URL_PARAM_NAME
62
+ appendUrlQueryKey = URL_PARAM_NAME,
58
63
  } = props;
59
64
  const pageSizeOptions = props.paginationConf?.pageSizeOptions || pageSizeOptionMap[props.layout] || [10, 20, 50, 100];
60
65
  // const [pageSizeOptions, setPageSizeOptions] = useState(
@@ -119,11 +124,11 @@ const ListRender = forwardRef(function (props, parentRef) {
119
124
  if (queryRef.current && queryRef.current?.formRef?.current.formRender) {
120
125
  const extractValues = extractSplitDateRanges(filters, schema, getUrlQuery);
121
126
 
122
- queryRef.current.formRef.current.formRender?.setValues(extractValues)
127
+ queryRef.current.formRef.current.formRender?.setValues(extractValues);
123
128
 
124
- formQueryRef.current = _.cloneDeep(getUrlQuery)
129
+ formQueryRef.current = _.cloneDeep(getUrlQuery);
125
130
  }
126
- })
131
+ });
127
132
 
128
133
  !props.closeAutoRequest && getList({ ...(modelQueryRef.current || {}), ...getUrlQuery });
129
134
  }, []);
@@ -224,7 +229,7 @@ const ListRender = forwardRef(function (props, parentRef) {
224
229
 
225
230
  if (appendUrlQuery) {
226
231
  const getUrlQuery = getURLObjectQueryParam(appendUrlQueryKey);
227
- setURLObjectQueryParam({ ...getUrlQuery, ...paginationQueryRef.current }, appendUrlQueryKey)
232
+ setURLObjectQueryParam({ ...getUrlQuery, ...paginationQueryRef.current }, appendUrlQueryKey);
228
233
  }
229
234
  getList();
230
235
  }
@@ -249,7 +254,25 @@ const ListRender = forwardRef(function (props, parentRef) {
249
254
  }
250
255
 
251
256
  function handleFormReset() {
252
- appendUrlQuery && removeURLObjectQueryParam(appendUrlQueryKey)
257
+ appendUrlQuery && removeURLObjectQueryParam(appendUrlQueryKey, "", true);
258
+ }
259
+
260
+ function handleFieldValueChange(filed, form) {
261
+ // props?.onFieldValueChange?.(filed, form);
262
+
263
+ if (!appendUrlQuery) return;
264
+
265
+ try {
266
+ const { componentType, value, path } = filed;
267
+ if (CLEAR_LIST.includes(componentType)) {
268
+ if (value === "" || value?.length === 0 || value === undefined) {
269
+ const key = path.entire;
270
+ key && removeURLObjectQueryParam(appendUrlQueryKey, key);
271
+ }
272
+ }
273
+ } catch (e) {
274
+ console.error("url参数清除失败", e);
275
+ }
253
276
  }
254
277
 
255
278
  function forceUpdate() {
@@ -409,6 +432,7 @@ const ListRender = forwardRef(function (props, parentRef) {
409
432
  onReset={handleFormReset}
410
433
  schemaScope={props.schemaScope}
411
434
  components={props.components}
435
+ onFieldValueChange={handleFieldValueChange}
412
436
  i18n={i18n}
413
437
  />
414
438
  ) : (
@@ -7,6 +7,7 @@ import FormRender from "@hzab/form-render";
7
7
  import { handleQuerySchema } from "../common/handleQuerySchema";
8
8
 
9
9
  import "./index.less";
10
+ import { onFieldValueChange } from "@formily/core";
10
11
 
11
12
  function QueryRender(props, parentRef) {
12
13
  const [schema, setSchema] = useState({});
@@ -58,7 +59,6 @@ function QueryRender(props, parentRef) {
58
59
  props.onSearch && props.onSearch(_.cloneDeep(formRef?.current?.formRender?.values), "queryRender", true);
59
60
  }
60
61
 
61
-
62
62
  return (
63
63
  <div className="query-render">
64
64
  {Object.keys(schema?.schema?.properties || {}).length > 0 ? (
@@ -69,6 +69,7 @@ function QueryRender(props, parentRef) {
69
69
  schema={schema}
70
70
  schemaScope={{ scenario: "query", ...(props.schemaScope || {}) }}
71
71
  initialValues={props.queryFormInitialValues}
72
+ onFieldValueChange={props.onFieldValueChange}
72
73
  Slots={() => {
73
74
  return (
74
75
  <div className="query-operation">