@tianyr/schema-antd 0.1.0 → 0.1.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.
Files changed (116) hide show
  1. package/README.md +42 -12
  2. package/dist/SchemaField.d.ts +1 -1
  3. package/dist/biz-select/BizSelect.d.ts +3 -0
  4. package/dist/biz-select/BizSelect.d.ts.map +1 -0
  5. package/dist/biz-select/BizSelect.js +87 -0
  6. package/dist/biz-select/BizSelect.js.map +1 -0
  7. package/dist/biz-select/BizSelectDisplay.d.ts +3 -0
  8. package/dist/biz-select/BizSelectDisplay.d.ts.map +1 -0
  9. package/dist/biz-select/BizSelectDisplay.js +58 -0
  10. package/dist/biz-select/BizSelectDisplay.js.map +1 -0
  11. package/dist/biz-select/cache.d.ts +16 -0
  12. package/dist/biz-select/cache.d.ts.map +1 -0
  13. package/dist/biz-select/cache.js +130 -0
  14. package/dist/biz-select/cache.js.map +1 -0
  15. package/dist/biz-select/compile.d.ts +25 -0
  16. package/dist/biz-select/compile.d.ts.map +1 -0
  17. package/dist/biz-select/compile.js +82 -0
  18. package/dist/biz-select/compile.js.map +1 -0
  19. package/dist/biz-select/factories.d.ts +23 -0
  20. package/dist/biz-select/factories.d.ts.map +1 -0
  21. package/dist/biz-select/factories.js +17 -0
  22. package/dist/biz-select/factories.js.map +1 -0
  23. package/dist/biz-select/format.d.ts +48 -0
  24. package/dist/biz-select/format.d.ts.map +1 -0
  25. package/dist/biz-select/format.js +110 -0
  26. package/dist/biz-select/format.js.map +1 -0
  27. package/dist/biz-select/index.d.ts +23 -0
  28. package/dist/biz-select/index.d.ts.map +1 -0
  29. package/dist/biz-select/index.js +22 -0
  30. package/dist/biz-select/index.js.map +1 -0
  31. package/dist/biz-select/key.d.ts +15 -0
  32. package/dist/biz-select/key.d.ts.map +1 -0
  33. package/dist/biz-select/key.js +69 -0
  34. package/dist/biz-select/key.js.map +1 -0
  35. package/dist/biz-select/normalize.d.ts +40 -0
  36. package/dist/biz-select/normalize.d.ts.map +1 -0
  37. package/dist/biz-select/normalize.js +46 -0
  38. package/dist/biz-select/normalize.js.map +1 -0
  39. package/dist/biz-select/registry.d.ts +15 -0
  40. package/dist/biz-select/registry.d.ts.map +1 -0
  41. package/dist/biz-select/registry.js +81 -0
  42. package/dist/biz-select/registry.js.map +1 -0
  43. package/dist/biz-select/schema.d.ts +5 -0
  44. package/dist/biz-select/schema.d.ts.map +1 -0
  45. package/dist/biz-select/schema.js +21 -0
  46. package/dist/biz-select/schema.js.map +1 -0
  47. package/dist/biz-select/source-id.d.ts +8 -0
  48. package/dist/biz-select/source-id.d.ts.map +1 -0
  49. package/dist/biz-select/source-id.js +10 -0
  50. package/dist/biz-select/source-id.js.map +1 -0
  51. package/dist/biz-select/types.d.ts +104 -0
  52. package/dist/biz-select/types.d.ts.map +1 -0
  53. package/dist/{app-select → biz-select}/types.js.map +1 -1
  54. package/dist/biz-select/useCandidates.d.ts +40 -0
  55. package/dist/biz-select/useCandidates.d.ts.map +1 -0
  56. package/dist/biz-select/useCandidates.js +292 -0
  57. package/dist/biz-select/useCandidates.js.map +1 -0
  58. package/dist/biz-select/useSelectedLabel.d.ts +18 -0
  59. package/dist/biz-select/useSelectedLabel.d.ts.map +1 -0
  60. package/dist/biz-select/useSelectedLabel.js +158 -0
  61. package/dist/biz-select/useSelectedLabel.js.map +1 -0
  62. package/dist/biz-select/useStableSourceKey.d.ts +3 -0
  63. package/dist/biz-select/useStableSourceKey.d.ts.map +1 -0
  64. package/dist/biz-select/useStableSourceKey.js +48 -0
  65. package/dist/biz-select/useStableSourceKey.js.map +1 -0
  66. package/dist/form/SchemaFormField.d.ts.map +1 -1
  67. package/dist/form/SchemaFormField.js +4 -3
  68. package/dist/form/SchemaFormField.js.map +1 -1
  69. package/dist/form/derive.d.ts +3 -8
  70. package/dist/form/derive.d.ts.map +1 -1
  71. package/dist/form/derive.js +28 -0
  72. package/dist/form/derive.js.map +1 -1
  73. package/dist/index.d.ts +1 -1
  74. package/dist/index.js +2 -2
  75. package/dist/index.js.map +1 -1
  76. package/dist/registry.d.ts +6 -1
  77. package/dist/registry.d.ts.map +1 -1
  78. package/dist/registry.js.map +1 -1
  79. package/dist/types.d.ts +7 -2
  80. package/dist/types.d.ts.map +1 -1
  81. package/package.json +1 -1
  82. package/dist/app-select/AppSelect.d.ts +0 -3
  83. package/dist/app-select/AppSelect.d.ts.map +0 -1
  84. package/dist/app-select/AppSelect.js +0 -56
  85. package/dist/app-select/AppSelect.js.map +0 -1
  86. package/dist/app-select/AppSelectDisplay.d.ts +0 -3
  87. package/dist/app-select/AppSelectDisplay.d.ts.map +0 -1
  88. package/dist/app-select/AppSelectDisplay.js +0 -39
  89. package/dist/app-select/AppSelectDisplay.js.map +0 -1
  90. package/dist/app-select/cache.d.ts +0 -26
  91. package/dist/app-select/cache.d.ts.map +0 -1
  92. package/dist/app-select/cache.js +0 -82
  93. package/dist/app-select/cache.js.map +0 -1
  94. package/dist/app-select/hooks.d.ts +0 -56
  95. package/dist/app-select/hooks.d.ts.map +0 -1
  96. package/dist/app-select/hooks.js +0 -322
  97. package/dist/app-select/hooks.js.map +0 -1
  98. package/dist/app-select/index.d.ts +0 -7
  99. package/dist/app-select/index.d.ts.map +0 -1
  100. package/dist/app-select/index.js +0 -6
  101. package/dist/app-select/index.js.map +0 -1
  102. package/dist/app-select/register.d.ts +0 -2
  103. package/dist/app-select/register.d.ts.map +0 -1
  104. package/dist/app-select/register.js +0 -10
  105. package/dist/app-select/register.js.map +0 -1
  106. package/dist/app-select/sources.d.ts +0 -20
  107. package/dist/app-select/sources.d.ts.map +0 -1
  108. package/dist/app-select/sources.js +0 -59
  109. package/dist/app-select/sources.js.map +0 -1
  110. package/dist/app-select/types.d.ts +0 -68
  111. package/dist/app-select/types.d.ts.map +0 -1
  112. package/dist/app-select/utils.d.ts +0 -30
  113. package/dist/app-select/utils.d.ts.map +0 -1
  114. package/dist/app-select/utils.js +0 -76
  115. package/dist/app-select/utils.js.map +0 -1
  116. /package/dist/{app-select → biz-select}/types.js +0 -0
package/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # schema-antd
2
2
 
3
- 基于 antd v5 的 Schema 驱动表单与可编辑表格。一份扁平的字段配置,同时用于普通表单、
3
+ 基于 antd v5 的 Schema 驱动表单、可编辑表格与业务下拉。一份扁平的字段配置,同时用于普通表单、
4
4
  单活动行编辑表格和全行常驻编辑表格,字段的显隐、禁用、校验、选项和联动都由配置描述。
5
5
 
6
6
  - **不内置 antd**:`antd` / `react` 全部是 peer 依赖,产物只有 ESM 与类型声明
7
7
  - **一份绑定层**:普通表单和两种表格的行复用同一个字段绑定,联动语义完全一致
8
8
  - **可编辑列三态**:`true` 编辑、`'read'` 只读但跟随本行 Form(能看到联动带出的值)、`false` 直接读行记录
9
+ - **业务下拉**:`BizSelect` 把候选来源、编码回显、级联接入收进一个组件,值默认存编码
9
10
 
10
11
  ## 安装
11
12
 
@@ -117,26 +118,53 @@ const columns: EditableColumn<OrderRow>[] = [
117
118
  />
118
119
  ```
119
120
 
120
- ## AppSelect
121
+ ## BizSelect
121
122
 
122
- 带搜索与回显分离的远程选择器。候选源在应用启动时注册一次:
123
+ 业务下拉:候选从哪来、只有一个编码时名称怎么还原、切换上级后旧候选怎么办。
124
+ 值默认是标量编码(`valueMode="object"` 可存对象),字段类型由使用方显式注册。
123
125
 
124
126
  ```ts
125
- import { registerAppSelectSource } from '@tianyr/schema-antd'
126
-
127
- registerAppSelectSource('product', {
128
- kind: 'remote',
129
- search: (keyword, params, signal) => api.searchProduct(keyword, params, signal),
130
- resolve: (id, params, signal) => api.getProduct(id, params, signal),
131
- requiredParams: ['companyId'], // 缺任一参数就不发请求
127
+ import { defineSelectSources, loaded, remote, staticList } from '@tianyr/schema-antd'
128
+
129
+ defineSelectSources({
130
+ province: staticList(PROVINCES), // 前端常量,不发请求
131
+ currency: loaded(api.listCurrency), // 一次拉全量,之后前端过滤
132
+ material: remote({ // 关键词交给后端,另配编码精确回显
133
+ search: (keyword, params, signal) => api.pageMaterial({ keyword, ...params }, signal),
134
+ resolve: (code, params, signal) => api.getMaterial(code, params, signal),
135
+ requiredParams: ['warehouseCode'], // 缺参数不发请求,避免查到别家数据
136
+ }),
132
137
  })
133
138
  ```
134
139
 
135
140
  ```tsx
136
- { type: 'AppSelect', name: 'productId', label: '物料', props: { source: 'product', queryParams: { companyId } } }
141
+ // 独立使用:受控,value / onChange 必配
142
+ <BizSelect source="currency" value={currency} onChange={setCurrency} />
143
+
144
+ // 只读:同一套名称解析,不挂 Select
145
+ <BizSelectDisplay source="material" params={{ warehouseCode }} value={materialCode} />
146
+ ```
147
+
148
+ 在 schema 里用,级联一行配完(先调一次 `registerBizSelectField()` 注册类型):
149
+
150
+ ```tsx
151
+ {
152
+ type: 'BizSelect',
153
+ name: 'materialCode',
154
+ label: '物料',
155
+ source: 'material',
156
+ paramsFrom: { warehouseCode: 'warehouseCode' }, // 参数名 ← 字段名
157
+ blockedText: '请先选择仓库',
158
+ }
137
159
  ```
138
160
 
139
- 页面局部接口不必注册,直接把源对象传给 `dataSource`。
161
+ 约定:
162
+
163
+ - 行为由源的能力推导:`options` 前端常量、`load` 一次拉全量后前端过滤、`search` 后端搜索,三者可组合。
164
+ - `resolve(code)` 专用于按编码精确回显,不要用模糊搜索冒充。
165
+ - `requiredParams` 不齐时进入 `blocked`:候选为空、不发请求。
166
+ - `invalidateSelectSource(name)` 让已挂载的实例重新取数;`useCandidates` 可把同一份候选给树、弹窗复用。
167
+ - 内联源要跨实例共享缓存,就传导入的函数或显式 `cacheKey`;内联箭头每次渲染都换键,缓存命中不了。
140
168
 
141
169
  ## 其他导出
142
170
 
@@ -154,6 +182,8 @@ registerAppSelectSource('product', {
154
182
  不影响功能,只是少几条开发提示。
155
183
  - `virtual` 不开放:虚拟滚动会卸载离屏行,全行常驻表格的行 Form 会随之销毁、输入丢失。
156
184
 
185
+ 版本变更见 [CHANGELOG.md](./CHANGELOG.md)。
186
+
157
187
  ## License
158
188
 
159
189
  MIT
@@ -2,7 +2,7 @@ import type { AnyFieldSchema, FieldMode } from './types.js';
2
2
  export interface SchemaFieldProps<TValue = unknown> {
3
3
  schema: AnyFieldSchema<TValue>;
4
4
  value?: TValue;
5
- /** 统一为 (value, ...meta);Select/AppSelect 的 meta[0] 即选中项 */
5
+ /** 统一为 (value, ...meta);Select/BizSelect 的 meta[0] 即选中项 */
6
6
  onChange?: (value: TValue, ...meta: unknown[]) => void;
7
7
  /** 高于 schema.mode */
8
8
  mode?: FieldMode;
@@ -0,0 +1,3 @@
1
+ import type { BizSelectProps } from './types.js';
2
+ export declare function BizSelect(props: BizSelectProps): import("react").JSX.Element;
3
+ //# sourceMappingURL=BizSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BizSelect.d.ts","sourceRoot":"","sources":["../../src/biz-select/BizSelect.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAa,cAAc,EAAyB,MAAM,SAAS,CAAC;AAIhF,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAmH9C"}
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * BizSelect:业务下拉的可编辑态。
4
+ *
5
+ * 渲染层只吃「候选列表 + 已选名称 + 展示模式」,不认识 load / search / resolve。
6
+ * 所有取数与解析都在 hook 里完成。
7
+ */
8
+ import { useEffect, useMemo } from 'react';
9
+ import { Select } from 'antd';
10
+ import { normalizeSource } from './normalize.js';
11
+ import { resolveSelectSource } from './registry.js';
12
+ import { useCandidates } from './useCandidates.js';
13
+ import { useSelectedLabel } from './useSelectedLabel.js';
14
+ import { buildObjectValue, defaultSearchFields, readCode, readObjectName, toAntOption, } from './format.js';
15
+ const DEFAULT_BLOCKED_TEXT = '请先选择上级';
16
+ export function BizSelect(props) {
17
+ const { source, params, displayMode = 'L', searchFields, seedOptions, debounceMs, cacheKey, blockedText = DEFAULT_BLOCKED_TEXT, valueMode = 'primitive', valueFields, value, onChange, disabled, placeholder, ...rest } = props;
18
+ const sourceObj = resolveSelectSource(source);
19
+ const fields = searchFields ?? defaultSearchFields(displayMode);
20
+ const code = readCode(value, valueMode, valueFields);
21
+ const valueName = readObjectName(value, valueMode, valueFields);
22
+ // 源直接交给 Hook:解析与来源键(含版本订阅)都在里面,组件不再自己算一份
23
+ const candidates = useCandidates({
24
+ source,
25
+ cacheKey,
26
+ params,
27
+ searchFields: fields,
28
+ debounceMs,
29
+ });
30
+ const { sourceKey } = candidates;
31
+ const selected = useSelectedLabel({
32
+ source: sourceObj,
33
+ sourceKey,
34
+ code,
35
+ valueName,
36
+ seedOptions,
37
+ // 回显查的是未过滤全集,否则输入关键词后已选项会被筛掉、名称退回编码
38
+ candidates: candidates.echoOptions,
39
+ params,
40
+ });
41
+ // 只有 load 型需要为了回显提前拉一次全集:它没有 resolve,名称只能从全集里找。
42
+ // 对象值自带名称、seed 已命中时不用拉。
43
+ const hasKnownName = Boolean(valueName) || Boolean(seedOptions?.some((item) => item.code === code));
44
+ const needEchoLoad = code !== undefined && !hasKnownName && normalizeSource(sourceObj).candidateMode === 'load';
45
+ useEffect(() => {
46
+ if (needEchoLoad)
47
+ candidates.open();
48
+ }, [needEchoLoad, sourceKey, candidates.open]);
49
+ // 搜索中不要补回已选项:否则结果里会混进一条不匹配的记录。
50
+ // 关闭下拉后 keyword 归零,已选项会重新补回,闭合态照常显示名称。
51
+ // 取数失败时也不补:补上一条会让列表非空,antd 就不渲染 notFoundContent,
52
+ // 重试入口跟着被藏起来,用户只看到孤零零一条已选项。
53
+ const searching = candidates.keyword.trim().length > 0;
54
+ const antOptions = useMemo(() => {
55
+ const list = candidates.options.map((item) => toAntOption(item, displayMode));
56
+ if (searching || candidates.error)
57
+ return list;
58
+ const selectedOption = selected.option;
59
+ if (selectedOption && !list.some((item) => item.value === selectedOption.code)) {
60
+ list.unshift(toAntOption(selectedOption, displayMode));
61
+ }
62
+ return list;
63
+ }, [candidates.options, candidates.error, displayMode, selected.option, searching]);
64
+ const handleChange = (next, antOption) => {
65
+ const picked = antOption?.raw ??
66
+ antOptions.find((item) => item.value === next)?.raw;
67
+ const emit = onChange;
68
+ if (valueMode === 'object') {
69
+ emit?.(next === undefined || next === null ? undefined : buildObjectValue(next, picked, valueFields), picked);
70
+ return;
71
+ }
72
+ emit?.(next ?? undefined, picked);
73
+ };
74
+ return (_jsx(Select, { ...rest,
75
+ // 候选由数据源决定,一律关掉 antd 自带过滤:本地过滤已在 hook 里做
76
+ filterOption: false, showSearch: true, options: antOptions, value: code, loading: candidates.loading || selected.status === 'loading' || rest.loading, disabled: disabled || candidates.blocked, placeholder: candidates.blocked ? blockedText : placeholder, notFoundContent: candidates.blocked ? (blockedText) : candidates.error ? (
77
+ // 失败得能重试:静默给一个空列表等于让用户以为「确实没有」
78
+ _jsx("a", { onClick: () => candidates.reload(), children: "\u52A0\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" })) : (rest.notFoundContent), onSearch: candidates.search, onChange: handleChange, onDropdownVisibleChange: (opened) => {
79
+ // 只有展开时才取默认候选,避免挂载即发请求
80
+ if (opened)
81
+ candidates.open();
82
+ else
83
+ candidates.resetKeyword();
84
+ rest.onDropdownVisibleChange?.(opened);
85
+ } }));
86
+ }
87
+ //# sourceMappingURL=BizSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BizSelect.js","sourceRoot":"","sources":["../../src/biz-select/BizSelect.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { BizSelectDisplayProps } from './types.js';
2
+ export declare function BizSelectDisplay(props: BizSelectDisplayProps): import("react").JSX.Element;
3
+ //# sourceMappingURL=BizSelectDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BizSelectDisplay.d.ts","sourceRoot":"","sources":["../../src/biz-select/BizSelectDisplay.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAUrD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BAmD5D"}
@@ -0,0 +1,58 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * BizSelect 的只读态。
4
+ *
5
+ * 复用同一套名称解析,但不挂载 Select:只读列表里成百上千个单元格
6
+ * 不该为了显示一个名称付出一个交互组件的代价。
7
+ */
8
+ import { useEffect } from 'react';
9
+ import { EMPTY, defaultSearchFields, formatLabel, readCode, readObjectName } from './format.js';
10
+ import { normalizeSource } from './normalize.js';
11
+ import { resolveSelectSource } from './registry.js';
12
+ import { useCandidates } from './useCandidates.js';
13
+ import { useSelectedLabel } from './useSelectedLabel.js';
14
+ /** 状态说明挂在 title 上:用户不能把「没查到」误认为「业务值为空」 */
15
+ const STATUS_TITLE = {
16
+ loading: '正在解析名称',
17
+ missing: '未找到对应名称,显示原始编码',
18
+ blocked: '缺少必需参数,未解析名称',
19
+ error: '名称解析失败,显示原始编码',
20
+ };
21
+ export function BizSelectDisplay(props) {
22
+ const { source, params, displayMode = 'L', seedOptions, cacheKey, emptyText = EMPTY, valueMode = 'primitive', valueFields, value, } = props;
23
+ const sourceObj = resolveSelectSource(source);
24
+ const code = readCode(value, valueMode, valueFields);
25
+ const valueName = readObjectName(value, valueMode, valueFields);
26
+ // 只有 load 型需要为回显拉一次全集:它没有 resolve,名称只能从全集里找
27
+ const candidates = useCandidates({
28
+ source,
29
+ cacheKey,
30
+ params,
31
+ searchFields: defaultSearchFields(displayMode),
32
+ });
33
+ const { sourceKey } = candidates;
34
+ // 对象值自带名称、seed 已命中时不用为了回显拉全集
35
+ const hasKnownName = Boolean(valueName) || Boolean(seedOptions?.some((item) => item.code === code));
36
+ const needEchoLoad = code !== undefined && !hasKnownName && normalizeSource(sourceObj).candidateMode === 'load';
37
+ useEffect(() => {
38
+ if (needEchoLoad)
39
+ candidates.open();
40
+ }, [needEchoLoad, sourceKey, candidates.open]);
41
+ const { option, status } = useSelectedLabel({
42
+ source: sourceObj,
43
+ sourceKey,
44
+ code,
45
+ valueName,
46
+ seedOptions,
47
+ candidates: candidates.echoOptions,
48
+ params,
49
+ });
50
+ if (status === 'empty' || !option)
51
+ return _jsx(_Fragment, { children: emptyText });
52
+ // 只返回内容本身:外层 schema-field-read 由 SchemaField 统一包,
53
+ // 这里再套一层会得到双层 span。title 只在解析异常时挂,正常态不产生额外节点。
54
+ const text = formatLabel(option, displayMode);
55
+ const title = STATUS_TITLE[status];
56
+ return title ? _jsx("span", { title: title, children: text }) : _jsx(_Fragment, { children: text });
57
+ }
58
+ //# sourceMappingURL=BizSelectDisplay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BizSelectDisplay.js","sourceRoot":"","sources":["../../src/biz-select/BizSelectDisplay.tsx"],"names":[],"mappings":";AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAItD,0CAA0C;AAC1C,MAAM,YAAY,GAA4C;IAC5D,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,gBAAgB;IACzB,OAAO,EAAE,cAAc;IACvB,KAAK,EAAE,eAAe;CACvB,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,MAAM,EACJ,MAAM,EACN,MAAM,EACN,WAAW,GAAG,GAAG,EACjB,WAAW,EACX,QAAQ,EACR,SAAS,GAAG,KAAK,EACjB,SAAS,GAAG,WAAW,EACvB,WAAW,EACX,KAAK,GACN,GAAG,KAAK,CAAC;IAEV,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAEhE,4CAA4C;IAC5C,MAAM,UAAU,GAAG,aAAa,CAAC;QAC/B,MAAM;QACN,QAAQ;QACR,MAAM;QACN,YAAY,EAAE,mBAAmB,CAAC,WAAW,CAAC;KAC/C,CAAC,CAAC;IACH,MAAM,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC;IACjC,6BAA6B;IAC7B,MAAM,YAAY,GAChB,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;IACjF,MAAM,YAAY,GAChB,IAAI,KAAK,SAAS,IAAI,CAAC,YAAY,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC,aAAa,KAAK,MAAM,CAAC;IAC7F,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,YAAY;YAAE,UAAU,CAAC,IAAI,EAAE,CAAC;IACtC,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAE/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;QAC1C,MAAM,EAAE,SAAS;QACjB,SAAS;QACT,IAAI;QACJ,SAAS;QACT,WAAW;QACX,UAAU,EAAE,UAAU,CAAC,WAAW;QAClC,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,MAAM,KAAK,OAAO,IAAI,CAAC,MAAM;QAAE,OAAO,4BAAG,SAAS,GAAI,CAAC;IAE3D,kDAAkD;IAClD,8CAA8C;IAC9C,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,CAAC,CAAC,eAAM,KAAK,EAAE,KAAK,YAAG,IAAI,GAAQ,CAAC,CAAC,CAAC,4BAAG,IAAI,GAAI,CAAC;AACjE,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 候选与解析的共享缓存。
3
+ *
4
+ * 表格式页面里同一列的几十个单元格会各调一次源,因此在**来源键**这一层共享:
5
+ * 同键并发只发一次请求,失败不缓存。
6
+ * 调用方各自的取消只影响自己的状态更新,不打断共享中的请求。
7
+ */
8
+ import type { BizOption } from './types.js';
9
+ export declare function loadCandidatesWithCache(key: string, loader: () => Promise<BizOption[]>): Promise<BizOption[]>;
10
+ /** 丢掉单条候选缓存:reload() 要真正重取,不能又命中同一份 */
11
+ export declare function dropCandidateCache(key: string): void;
12
+ export declare function resolveCodeWithCache(key: string, loader: () => Promise<BizOption | null>): Promise<BizOption | null>;
13
+ export declare function clearSelectCacheOfSource(sourceKey: string): void;
14
+ export declare function clearSelectCache(): void;
15
+ export declare function getSelectCacheSize(): number;
16
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/biz-select/cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AA2FzC,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,GACjC,OAAO,CAAC,SAAS,EAAE,CAAC,CA2BtB;AAED,uCAAuC;AACvC,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GACtC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAgB3B;AAOD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAQhE;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
@@ -0,0 +1,130 @@
1
+ /** 命中后的存活时间 */
2
+ const HIT_TTL = 5 * 60 * 1000;
3
+ /** 空结果的存活时间:别把「暂时查不到」当成长久不存在 */
4
+ const MISS_TTL = 30 * 1000;
5
+ /**
6
+ * 条目上限。过期只在读到同一个 key 时才清理,没有上限的话,
7
+ * 大量编码 × 参数组合会一直堆着。这里按插入顺序淘汰(FIFO),够用即可。
8
+ */
9
+ const MAX_ENTRIES = 500;
10
+ /**
11
+ * 缓存代次:clear 只清 Map,已经在途的请求稍后回来照样会写回旧数据。
12
+ * 每次 clear 递增一代,写回时比对代次就能丢掉上一代的结果。
13
+ */
14
+ let generation = 0;
15
+ const candidateCache = new Map();
16
+ const resolveCache = new Map();
17
+ function take(store, key) {
18
+ const hit = store.get(key);
19
+ if (!hit)
20
+ return undefined;
21
+ if (hit.expiresAt <= Date.now()) {
22
+ store.delete(key);
23
+ return undefined;
24
+ }
25
+ return hit.promise;
26
+ }
27
+ function evict(store) {
28
+ while (store.size > MAX_ENTRIES) {
29
+ const oldest = store.keys().next();
30
+ if (oldest.done)
31
+ return;
32
+ store.delete(oldest.value);
33
+ }
34
+ }
35
+ /** 请求发出后缓存被清过:这份结果属于上一代 */
36
+ function staleGeneration(startedGeneration) {
37
+ return startedGeneration !== generation;
38
+ }
39
+ /** 首次登记:只占位,不覆盖同键已有的请求(并发调用共享同一份 promise) */
40
+ function register(store, key, promise, ttl, startedGeneration) {
41
+ if (staleGeneration(startedGeneration) || store.has(key))
42
+ return;
43
+ store.set(key, { promise, expiresAt: Date.now() + ttl });
44
+ evict(store);
45
+ }
46
+ /**
47
+ * 结果写回:条目必须仍属于这次请求。
48
+ *
49
+ * 「条目不存在」也算失效——reload 换过一轮、新请求又失败了,此时写回去
50
+ * 就是让旧数据复活。所以不能只看 `current && current.promise !== owner`。
51
+ */
52
+ function settle(store, key, owner, value, ttl, startedGeneration) {
53
+ if (staleGeneration(startedGeneration))
54
+ return;
55
+ const current = store.get(key);
56
+ if (!current || current.promise !== owner)
57
+ return;
58
+ store.set(key, { promise: Promise.resolve(value), expiresAt: Date.now() + ttl });
59
+ }
60
+ /** 失败清场:只删自己那一条,别误删已经接手的新请求 */
61
+ function dropOwn(store, key, owner) {
62
+ if (store.get(key)?.promise === owner)
63
+ store.delete(key);
64
+ }
65
+ export function loadCandidatesWithCache(key, loader) {
66
+ const cached = take(candidateCache, key);
67
+ if (cached)
68
+ return cached;
69
+ const startedGeneration = generation;
70
+ const promise = Promise.resolve()
71
+ .then(loader)
72
+ .then((list) => {
73
+ const options = list ?? [];
74
+ // 空结果只短暂记住
75
+ settle(candidateCache, key, promise, options, options.length ? HIT_TTL : MISS_TTL, startedGeneration);
76
+ return options;
77
+ })
78
+ .catch((error) => {
79
+ // 失败不缓存,下一次仍可重试
80
+ dropOwn(candidateCache, key, promise);
81
+ throw error;
82
+ });
83
+ register(candidateCache, key, promise, HIT_TTL, startedGeneration);
84
+ return promise;
85
+ }
86
+ /** 丢掉单条候选缓存:reload() 要真正重取,不能又命中同一份 */
87
+ export function dropCandidateCache(key) {
88
+ candidateCache.delete(key);
89
+ }
90
+ export function resolveCodeWithCache(key, loader) {
91
+ const cached = take(resolveCache, key);
92
+ if (cached)
93
+ return cached;
94
+ const startedGeneration = generation;
95
+ const promise = loader()
96
+ .then((option) => {
97
+ settle(resolveCache, key, promise, option, option ? HIT_TTL : MISS_TTL, startedGeneration);
98
+ return option;
99
+ })
100
+ .catch((error) => {
101
+ dropOwn(resolveCache, key, promise);
102
+ throw error;
103
+ });
104
+ register(resolveCache, key, promise, HIT_TTL, startedGeneration);
105
+ return promise;
106
+ }
107
+ /** 来源键在缓存键里固定占第二段,因此用两侧分隔符即可精确匹配 */
108
+ function matchesSource(key, sourceKey) {
109
+ return key.startsWith(`${sourceKey}|`) || key.includes(`|${sourceKey}|`);
110
+ }
111
+ export function clearSelectCacheOfSource(sourceKey) {
112
+ generation += 1;
113
+ for (const key of candidateCache.keys()) {
114
+ if (matchesSource(key, sourceKey))
115
+ candidateCache.delete(key);
116
+ }
117
+ for (const key of resolveCache.keys()) {
118
+ if (matchesSource(key, sourceKey))
119
+ resolveCache.delete(key);
120
+ }
121
+ }
122
+ export function clearSelectCache() {
123
+ generation += 1;
124
+ candidateCache.clear();
125
+ resolveCache.clear();
126
+ }
127
+ export function getSelectCacheSize() {
128
+ return candidateCache.size + resolveCache.size;
129
+ }
130
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/biz-select/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { AnyFormFieldSchema, NamePath } from '../types.js';
2
+ import type { BizOption, DisplayMode, SearchFields, ValueFieldMap, ValueMode } from './types.js';
3
+ /** BizSelect 在 schema 里额外认的专有属性 */
4
+ export interface BizSelectSchemaProps {
5
+ /** 数据源注册名;schema 里不允许写函数 */
6
+ source?: string;
7
+ /** 参数名 ← 表单字段名:`{ provinceCode: 'provinceCode' }` */
8
+ paramsFrom?: Record<string, NamePath>;
9
+ valueMode?: ValueMode;
10
+ valueFields?: ValueFieldMap;
11
+ displayMode?: DisplayMode;
12
+ searchFields?: SearchFields;
13
+ seedOptions?: BizOption[];
14
+ /** 默认 true:父值变化时清空本字段 */
15
+ clearOnParent?: boolean;
16
+ blockedText?: unknown;
17
+ debounceMs?: number;
18
+ }
19
+ /**
20
+ * 归一化:专有属性下沉 + 级联编译。
21
+ *
22
+ * 必须是纯函数——调用方按 schema 对象缓存结果,每次新建对象会让下游 memo 与 effect 全部失效。
23
+ */
24
+ export declare function normalizeBizSelectSchema<T extends AnyFormFieldSchema>(schema: T): T;
25
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/biz-select/compile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,kBAAkB,EAIlB,QAAQ,EAET,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,aAAa,EACb,SAAS,EACV,MAAM,SAAS,CAAC;AAEjB,mCAAmC;AACnC,MAAM,WAAW,oBAAoB;IACnC,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1B,yBAAyB;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAwDD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,kBAAkB,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CA0BnF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * schema 侧的纯逻辑:专有属性下沉 + `paramsFrom` 编译。
3
+ *
4
+ * 与 `schema.tsx`(负责注册、需要 antd/React)分开,是为了让这部分能被单测直接加载。
5
+ */
6
+ import { dedupeNamePaths } from '../form/namePath.js';
7
+ /** 需要下沉到 props 的专有属性:组件只认 props */
8
+ const SUNK_KEYS = [
9
+ 'source',
10
+ 'valueMode',
11
+ 'valueFields',
12
+ 'displayMode',
13
+ 'searchFields',
14
+ 'seedOptions',
15
+ 'blockedText',
16
+ 'debounceMs',
17
+ ];
18
+ /**
19
+ * 把 `paramsFrom` 编译成已有的两套机制:
20
+ * - `dynamic`:订阅父字段,把值映射成 `params` 传给组件
21
+ * - `linkage`:父值变化时清空本字段
22
+ */
23
+ function compileParamsFrom(schema, entries) {
24
+ const deps = dedupeNamePaths(entries.map(([, path]) => path));
25
+ const base = schema.dynamic;
26
+ const inheritedProps = base?.props;
27
+ // 静态固定条件(如 props.params: { enabled: true })必须保留,否则查询会放宽
28
+ const staticParams = (schema.props?.params ?? undefined);
29
+ const dynamic = {
30
+ ...base,
31
+ // 与调用方自己声明的依赖取并集去重
32
+ dependencies: dedupeNamePaths([...(base?.dependencies ?? []), ...deps]),
33
+ props: (ctx) => {
34
+ const inherited = typeof inheritedProps === 'function' ? inheritedProps(ctx) : (inheritedProps ?? {});
35
+ const inheritedParams = inherited.params;
36
+ const mapped = Object.fromEntries(entries.map(([param, path]) => [param, ctx.get(path)]));
37
+ // 合并顺序:静态 props.params < dynamic.props().params < paramsFrom 映射。
38
+ // 越靠后的越"当前",同名时覆盖前面的
39
+ return {
40
+ ...inherited,
41
+ params: { ...staticParams, ...inheritedParams, ...mapped },
42
+ };
43
+ },
44
+ };
45
+ // clearOnParent=false 是「快照语义」:父变不清空,也不额外下发联动规则
46
+ const linkage = schema.clearOnParent === false
47
+ ? schema.linkage
48
+ : [...(schema.linkage ?? []), { dependencies: deps, handler: (ctx) => ctx.clear(schema.name) }];
49
+ return { dynamic, linkage };
50
+ }
51
+ /**
52
+ * 归一化:专有属性下沉 + 级联编译。
53
+ *
54
+ * 必须是纯函数——调用方按 schema 对象缓存结果,每次新建对象会让下游 memo 与 effect 全部失效。
55
+ */
56
+ export function normalizeBizSelectSchema(schema) {
57
+ const ext = schema;
58
+ if (ext.type !== 'BizSelect')
59
+ return schema;
60
+ const sunk = {};
61
+ for (const key of SUNK_KEYS) {
62
+ if (ext[key] !== undefined)
63
+ sunk[key] = ext[key];
64
+ }
65
+ const entries = Object.entries(ext.paramsFrom ?? {});
66
+ // 没有可下沉的属性和可编译的级联时保持同一引用,避免无意义的新对象
67
+ if (!entries.length && !Object.keys(sunk).length)
68
+ return schema;
69
+ const next = { ...schema, props: { ...schema.props, ...sunk } };
70
+ if (entries.length) {
71
+ const { dynamic, linkage } = compileParamsFrom(ext, entries);
72
+ next.dynamic = dynamic;
73
+ if (linkage)
74
+ next.linkage = linkage;
75
+ }
76
+ // 已经编译进 props / dynamic / linkage 的属性从顶层摘掉,保证归一化幂等
77
+ for (const key of [...SUNK_KEYS, 'paramsFrom', 'clearOnParent']) {
78
+ delete next[key];
79
+ }
80
+ return next;
81
+ }
82
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/biz-select/compile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAmCnD,mCAAmC;AACnC,MAAM,SAAS,GAAG;IAChB,QAAQ;IACR,WAAW;IACX,aAAa;IACb,aAAa;IACb,cAAc;IACd,aAAa;IACb,aAAa;IACb,YAAY;CACJ,CAAC;AAEX;;;;GAIG;AACH,SAAS,iBAAiB,CACxB,MAA8C,EAC9C,OAA6B;IAE7B,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC;IAC5B,MAAM,cAAc,GAAG,IAAI,EAAE,KAAK,CAAC;IACnC,wDAAwD;IACxD,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,SAAS,CAA6B,CAAC;IAErF,MAAM,OAAO,GAAkB;QAC7B,GAAG,IAAI;QACP,mBAAmB;QACnB,YAAY,EAAE,eAAe,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACvE,KAAK,EAAE,CAAC,GAAgB,EAAE,EAAE;YAC1B,MAAM,SAAS,GACb,OAAO,cAAc,KAAK,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;YACtF,MAAM,eAAe,GAAI,SAAuC,CAAC,MAAM,CAAC;YACxE,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1F,iEAAiE;YACjE,qBAAqB;YACrB,OAAO;gBACL,GAAG,SAAS;gBACZ,MAAM,EAAE,EAAE,GAAG,YAAY,EAAE,GAAG,eAAe,EAAE,GAAG,MAAM,EAAE;aAC3D,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,+CAA+C;IAC/C,MAAM,OAAO,GACX,MAAM,CAAC,aAAa,KAAK,KAAK;QAC5B,CAAC,CAAC,MAAM,CAAC,OAAO;QAChB,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEpG,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAA+B,MAAS;IAC9E,MAAM,GAAG,GAAG,MAA2D,CAAC;IACxE,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IAE5C,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAyB,CAAC;IAC7E,mCAAmC;IACnC,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAEhE,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,EAAE,EAA6B,CAAC;IAE3F,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACtC,CAAC;IAED,mDAAmD;IACnD,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,eAAe,CAAC,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,IAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 三个工厂:只做形状收敛,不注册、不缓存、不发请求。
3
+ *
4
+ * 之所以保留工厂而不是让调用方直接写 `{ load, search }`:
5
+ * 工厂承载了「这个源属于哪种能力组合」的语义,写法越短,越不容易写出
6
+ * `resolve` 用模糊搜索冒充精确查这类错误。
7
+ */
8
+ import type { BizOption, SelectCode, SelectSource, SelectParams } from './types.js';
9
+ /** ① 纯前端常量:同步可用,没有请求也没有 loading */
10
+ export declare function staticList<T = unknown>(list: ReadonlyArray<BizOption<T>>): SelectSource;
11
+ /**
12
+ * ② 有限远程枚举:一次拉全量,之后由前端过滤。
13
+ * 传导入的函数(如 `api.listCurrency`)而不是内联箭头,才能跨实例共享缓存。
14
+ */
15
+ export declare function loaded<P extends SelectParams = SelectParams>(load: (params: P, signal?: AbortSignal) => Promise<BizOption[]>, extra?: Pick<SelectSource<P>, 'requiredParams' | 'namespace'>): SelectSource<P>;
16
+ /** ③ 无限远程:关键词搜索 + 按编码回显 */
17
+ export declare function remote<P extends SelectParams = SelectParams>(config: {
18
+ search: (keyword: string, params: P, signal?: AbortSignal) => Promise<BizOption[]>;
19
+ resolve?: (code: SelectCode, params: P, signal?: AbortSignal) => Promise<BizOption | null>;
20
+ requiredParams?: string[];
21
+ namespace?: () => string;
22
+ }): SelectSource<P>;
23
+ //# sourceMappingURL=factories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../src/biz-select/factories.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjF,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAEvF;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAC1D,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC,EAC/D,KAAK,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,gBAAgB,GAAG,WAAW,CAAC,GAC5D,YAAY,CAAC,CAAC,CAAC,CAEjB;AAED,2BAA2B;AAC3B,wBAAgB,MAAM,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,MAAM,EAAE;IACpE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACnF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC3F,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;CAC1B,GAAG,YAAY,CAAC,CAAC,CAAC,CAGlB"}
@@ -0,0 +1,17 @@
1
+ /** ① 纯前端常量:同步可用,没有请求也没有 loading */
2
+ export function staticList(list) {
3
+ return { options: [...list] };
4
+ }
5
+ /**
6
+ * ② 有限远程枚举:一次拉全量,之后由前端过滤。
7
+ * 传导入的函数(如 `api.listCurrency`)而不是内联箭头,才能跨实例共享缓存。
8
+ */
9
+ export function loaded(load, extra) {
10
+ return { load, ...extra };
11
+ }
12
+ /** ③ 无限远程:关键词搜索 + 按编码回显 */
13
+ export function remote(config) {
14
+ const { search, resolve, requiredParams, namespace } = config;
15
+ return { search, resolve, requiredParams, namespace };
16
+ }
17
+ //# sourceMappingURL=factories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.js","sourceRoot":"","sources":["../../src/biz-select/factories.ts"],"names":[],"mappings":"AASA,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAc,IAAiC;IACvE,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CACpB,IAA+D,EAC/D,KAA6D;IAE7D,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,MAAM,CAAwC,MAK7D;IACC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAC9D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;AACxD,CAAC"}