@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.
- package/README.md +42 -12
- package/dist/SchemaField.d.ts +1 -1
- package/dist/biz-select/BizSelect.d.ts +3 -0
- package/dist/biz-select/BizSelect.d.ts.map +1 -0
- package/dist/biz-select/BizSelect.js +87 -0
- package/dist/biz-select/BizSelect.js.map +1 -0
- package/dist/biz-select/BizSelectDisplay.d.ts +3 -0
- package/dist/biz-select/BizSelectDisplay.d.ts.map +1 -0
- package/dist/biz-select/BizSelectDisplay.js +58 -0
- package/dist/biz-select/BizSelectDisplay.js.map +1 -0
- package/dist/biz-select/cache.d.ts +16 -0
- package/dist/biz-select/cache.d.ts.map +1 -0
- package/dist/biz-select/cache.js +130 -0
- package/dist/biz-select/cache.js.map +1 -0
- package/dist/biz-select/compile.d.ts +25 -0
- package/dist/biz-select/compile.d.ts.map +1 -0
- package/dist/biz-select/compile.js +82 -0
- package/dist/biz-select/compile.js.map +1 -0
- package/dist/biz-select/factories.d.ts +23 -0
- package/dist/biz-select/factories.d.ts.map +1 -0
- package/dist/biz-select/factories.js +17 -0
- package/dist/biz-select/factories.js.map +1 -0
- package/dist/biz-select/format.d.ts +48 -0
- package/dist/biz-select/format.d.ts.map +1 -0
- package/dist/biz-select/format.js +110 -0
- package/dist/biz-select/format.js.map +1 -0
- package/dist/biz-select/index.d.ts +23 -0
- package/dist/biz-select/index.d.ts.map +1 -0
- package/dist/biz-select/index.js +22 -0
- package/dist/biz-select/index.js.map +1 -0
- package/dist/biz-select/key.d.ts +15 -0
- package/dist/biz-select/key.d.ts.map +1 -0
- package/dist/biz-select/key.js +69 -0
- package/dist/biz-select/key.js.map +1 -0
- package/dist/biz-select/normalize.d.ts +40 -0
- package/dist/biz-select/normalize.d.ts.map +1 -0
- package/dist/biz-select/normalize.js +46 -0
- package/dist/biz-select/normalize.js.map +1 -0
- package/dist/biz-select/registry.d.ts +15 -0
- package/dist/biz-select/registry.d.ts.map +1 -0
- package/dist/biz-select/registry.js +81 -0
- package/dist/biz-select/registry.js.map +1 -0
- package/dist/biz-select/schema.d.ts +5 -0
- package/dist/biz-select/schema.d.ts.map +1 -0
- package/dist/biz-select/schema.js +21 -0
- package/dist/biz-select/schema.js.map +1 -0
- package/dist/biz-select/source-id.d.ts +8 -0
- package/dist/biz-select/source-id.d.ts.map +1 -0
- package/dist/biz-select/source-id.js +10 -0
- package/dist/biz-select/source-id.js.map +1 -0
- package/dist/biz-select/types.d.ts +104 -0
- package/dist/biz-select/types.d.ts.map +1 -0
- package/dist/{app-select → biz-select}/types.js.map +1 -1
- package/dist/biz-select/useCandidates.d.ts +40 -0
- package/dist/biz-select/useCandidates.d.ts.map +1 -0
- package/dist/biz-select/useCandidates.js +292 -0
- package/dist/biz-select/useCandidates.js.map +1 -0
- package/dist/biz-select/useSelectedLabel.d.ts +18 -0
- package/dist/biz-select/useSelectedLabel.d.ts.map +1 -0
- package/dist/biz-select/useSelectedLabel.js +158 -0
- package/dist/biz-select/useSelectedLabel.js.map +1 -0
- package/dist/biz-select/useStableSourceKey.d.ts +3 -0
- package/dist/biz-select/useStableSourceKey.d.ts.map +1 -0
- package/dist/biz-select/useStableSourceKey.js +48 -0
- package/dist/biz-select/useStableSourceKey.js.map +1 -0
- package/dist/form/SchemaFormField.d.ts.map +1 -1
- package/dist/form/SchemaFormField.js +4 -3
- package/dist/form/SchemaFormField.js.map +1 -1
- package/dist/form/derive.d.ts +3 -8
- package/dist/form/derive.d.ts.map +1 -1
- package/dist/form/derive.js +28 -0
- package/dist/form/derive.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +6 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/app-select/AppSelect.d.ts +0 -3
- package/dist/app-select/AppSelect.d.ts.map +0 -1
- package/dist/app-select/AppSelect.js +0 -56
- package/dist/app-select/AppSelect.js.map +0 -1
- package/dist/app-select/AppSelectDisplay.d.ts +0 -3
- package/dist/app-select/AppSelectDisplay.d.ts.map +0 -1
- package/dist/app-select/AppSelectDisplay.js +0 -39
- package/dist/app-select/AppSelectDisplay.js.map +0 -1
- package/dist/app-select/cache.d.ts +0 -26
- package/dist/app-select/cache.d.ts.map +0 -1
- package/dist/app-select/cache.js +0 -82
- package/dist/app-select/cache.js.map +0 -1
- package/dist/app-select/hooks.d.ts +0 -56
- package/dist/app-select/hooks.d.ts.map +0 -1
- package/dist/app-select/hooks.js +0 -322
- package/dist/app-select/hooks.js.map +0 -1
- package/dist/app-select/index.d.ts +0 -7
- package/dist/app-select/index.d.ts.map +0 -1
- package/dist/app-select/index.js +0 -6
- package/dist/app-select/index.js.map +0 -1
- package/dist/app-select/register.d.ts +0 -2
- package/dist/app-select/register.d.ts.map +0 -1
- package/dist/app-select/register.js +0 -10
- package/dist/app-select/register.js.map +0 -1
- package/dist/app-select/sources.d.ts +0 -20
- package/dist/app-select/sources.d.ts.map +0 -1
- package/dist/app-select/sources.js +0 -59
- package/dist/app-select/sources.js.map +0 -1
- package/dist/app-select/types.d.ts +0 -68
- package/dist/app-select/types.d.ts.map +0 -1
- package/dist/app-select/utils.d.ts +0 -30
- package/dist/app-select/utils.d.ts.map +0 -1
- package/dist/app-select/utils.js +0 -76
- package/dist/app-select/utils.js.map +0 -1
- /package/dist/{app-select → biz-select}/types.js +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BizSelect 的类型契约。
|
|
3
|
+
*
|
|
4
|
+
* 对外只有一个「数据源」概念:能给出候选、能按编码解析名称。
|
|
5
|
+
* 具体是前端常量、一次拉全量还是关键词翻页,由源**提供了哪些能力**决定,
|
|
6
|
+
* 不再用 kind 之类的标签分类。
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
import type { SelectProps } from 'antd';
|
|
10
|
+
export type SelectCode = string | number;
|
|
11
|
+
/** 契约层的标准选项;后端字段换名在 adapter 层做,不污染组件 */
|
|
12
|
+
export interface BizOption<T = unknown> {
|
|
13
|
+
code: SelectCode;
|
|
14
|
+
name?: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** 原始接口记录,原样带回给业务回调 */
|
|
17
|
+
raw?: T;
|
|
18
|
+
}
|
|
19
|
+
/** 展示模式:编码 / 编码-名称 / 名称 */
|
|
20
|
+
export type DisplayMode = 'V' | 'V-L' | 'L';
|
|
21
|
+
/** 前端过滤时匹配哪些字段 */
|
|
22
|
+
export type SearchFields = 'code' | 'name' | 'both';
|
|
23
|
+
/** 值形态:标量编码,或自带名称的对象 */
|
|
24
|
+
export type ValueMode = 'primitive' | 'object';
|
|
25
|
+
/**
|
|
26
|
+
* 对象值里哪个键装编码、哪个装名称。
|
|
27
|
+
* 默认 `{ code: 'value', name: 'label' }`,兼容存量 `{ value, label }` 协议。
|
|
28
|
+
*/
|
|
29
|
+
export interface ValueFieldMap {
|
|
30
|
+
code?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
}
|
|
33
|
+
export type SelectParams = Record<string, unknown>;
|
|
34
|
+
/** 对象形态的值;具体键名由 valueFields 决定,所以只能约束到 Record */
|
|
35
|
+
export type BizObjectValue = Record<string, unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* 数据源契约。只有能力声明,不认识 URL、method、低代码控件配置。
|
|
38
|
+
*
|
|
39
|
+
* 行为推导:
|
|
40
|
+
* - 只有 `options` → 同步全集,前端过滤
|
|
41
|
+
* - 只有 `load` → 需要时加载一次,前端过滤
|
|
42
|
+
* - 只有 `search` → 后端过滤
|
|
43
|
+
* - `load` + `search` → 空关键词走 load 缓存,有关键词走后端
|
|
44
|
+
*/
|
|
45
|
+
export interface SelectSource<P extends SelectParams = SelectParams> {
|
|
46
|
+
/** 同步全集:纯前端常量,没有请求也没有 loading */
|
|
47
|
+
options?: BizOption[];
|
|
48
|
+
/** 异步全集:一次拉完,之后前端过滤 */
|
|
49
|
+
load?: (params: P, signal?: AbortSignal) => Promise<BizOption[]>;
|
|
50
|
+
/** 关键词搜索:分页接口固定取第一页 */
|
|
51
|
+
search?: (keyword: string, params: P, signal?: AbortSignal) => Promise<BizOption[]>;
|
|
52
|
+
/** 按编码精确查:远程回显,不能用模糊搜索冒充 */
|
|
53
|
+
resolve?: (code: SelectCode, params: P, signal?: AbortSignal) => Promise<BizOption | null>;
|
|
54
|
+
/** 缺任一参数就不发请求:用空条件查可能拿到别家数据 */
|
|
55
|
+
requiredParams?: string[];
|
|
56
|
+
/** 租户/权限隔离,参与缓存键 */
|
|
57
|
+
namespace?: () => string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 受控通道与搜索协议由组件接管,这些属性传进来要么被静默忽略、要么直接把控件弄坏:
|
|
61
|
+
* - `searchValue`:antd 的受控搜索框,传了会锁死用户输入(组件自己维护 keyword)
|
|
62
|
+
* - `showSearch` / `filterOption` / `optionFilterProp` / `onSearch`:搜索行为由源的能力决定
|
|
63
|
+
* - `optionLabelProp`:标签取哪个字段由 displayMode 决定
|
|
64
|
+
*/
|
|
65
|
+
type BaseAntProps = Omit<SelectProps, 'value' | 'defaultValue' | 'onChange' | 'options' | 'labelInValue' | 'mode' | 'onSearch' | 'searchValue' | 'showSearch' | 'filterOption' | 'optionFilterProp' | 'optionLabelProp'>;
|
|
66
|
+
/** 与 antd 无关的公共属性:可编辑与只读都吃这一份 */
|
|
67
|
+
interface CoreProps {
|
|
68
|
+
/** 注册名或内联源;schema 里只能是字符串 */
|
|
69
|
+
source?: string | SelectSource;
|
|
70
|
+
/** 传给 load/search/resolve,参与缓存键 */
|
|
71
|
+
params?: SelectParams;
|
|
72
|
+
/** 下拉与回显文本,默认 L */
|
|
73
|
+
displayMode?: DisplayMode;
|
|
74
|
+
/** 前端过滤匹配的字段,缺省随 displayMode 推导 */
|
|
75
|
+
searchFields?: SearchFields;
|
|
76
|
+
/** 详情已带回的名称,省掉一次 resolve */
|
|
77
|
+
seedOptions?: BizOption[];
|
|
78
|
+
/** 远程搜索防抖,默认 300ms */
|
|
79
|
+
debounceMs?: number;
|
|
80
|
+
/** 内联源需要跨实例共享缓存时显式指定 */
|
|
81
|
+
cacheKey?: string;
|
|
82
|
+
/** 门禁提示文案 */
|
|
83
|
+
blockedText?: ReactNode;
|
|
84
|
+
valueMode?: ValueMode;
|
|
85
|
+
valueFields?: ValueFieldMap;
|
|
86
|
+
}
|
|
87
|
+
interface PrimitiveModeProps {
|
|
88
|
+
valueMode?: 'primitive';
|
|
89
|
+
value?: SelectCode | null;
|
|
90
|
+
onChange?: (value: SelectCode | undefined, option?: BizOption) => void;
|
|
91
|
+
}
|
|
92
|
+
interface ObjectModeProps {
|
|
93
|
+
valueMode: 'object';
|
|
94
|
+
value?: BizObjectValue | null;
|
|
95
|
+
onChange?: (value: BizObjectValue | undefined, option?: BizOption) => void;
|
|
96
|
+
}
|
|
97
|
+
export type BizSelectProps = CoreProps & BaseAntProps & (PrimitiveModeProps | ObjectModeProps);
|
|
98
|
+
export interface BizSelectDisplayProps extends CoreProps {
|
|
99
|
+
value?: SelectCode | BizObjectValue | null;
|
|
100
|
+
/** 只读占位 */
|
|
101
|
+
emptyText?: ReactNode;
|
|
102
|
+
}
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/biz-select/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/biz-select/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { BizOption, SearchFields, SelectParams, SelectSource } from './types.js';
|
|
2
|
+
export interface UseCandidatesOptions {
|
|
3
|
+
/**
|
|
4
|
+
* 注册名或内联源。解析与来源键都在 Hook 内完成:
|
|
5
|
+
* 让调用方自己拼 sourceKey 就会绕开注册名的版本身份,组件和 Hook 表现不一致。
|
|
6
|
+
*/
|
|
7
|
+
source?: string | SelectSource;
|
|
8
|
+
/** 内联源需要跨实例共享缓存时显式指定;注册名自带稳定键 */
|
|
9
|
+
cacheKey?: string;
|
|
10
|
+
params?: SelectParams;
|
|
11
|
+
/** 前端过滤匹配哪些字段 */
|
|
12
|
+
searchFields: SearchFields;
|
|
13
|
+
debounceMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface UseCandidatesResult {
|
|
16
|
+
/** 解析后的来源键,和组件用的是同一个身份;要传给别的 Hook 时直接用它 */
|
|
17
|
+
sourceKey: string;
|
|
18
|
+
/** 当前该给下拉看的候选:本地过滤型已按 keyword 过滤 */
|
|
19
|
+
options: BizOption[];
|
|
20
|
+
/**
|
|
21
|
+
* 回显专用候选:优先取未过滤的全集。
|
|
22
|
+
* 已选中的项可能被关键词过滤掉,用它查名称才不会退回编码。
|
|
23
|
+
*/
|
|
24
|
+
echoOptions: BizOption[];
|
|
25
|
+
/** 当前关键词:搜索中不要往结果里补回已选项 */
|
|
26
|
+
keyword: string;
|
|
27
|
+
loading: boolean;
|
|
28
|
+
error: boolean;
|
|
29
|
+
/** 缺少必需参数,主动不查 */
|
|
30
|
+
blocked: boolean;
|
|
31
|
+
/** 展开下拉:`load` 型在这里才发第一次请求 */
|
|
32
|
+
open(): void;
|
|
33
|
+
/** 输入关键词:后端搜索型防抖请求;本地过滤型只更新关键字 */
|
|
34
|
+
search(keyword: string): void;
|
|
35
|
+
/** 收起下拉时清掉关键字:不发请求,避免下次打开看到上次的过滤结果 */
|
|
36
|
+
resetKeyword(): void;
|
|
37
|
+
reload(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare function useCandidates({ source: sourceProp, cacheKey, params, searchFields, debounceMs, }: UseCandidatesOptions): UseCandidatesResult;
|
|
40
|
+
//# sourceMappingURL=useCandidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCandidates.d.ts","sourceRoot":"","sources":["../../src/biz-select/useCandidates.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGnF,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,iBAAiB;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,IAAI,IAAI,IAAI,CAAC;IACb,kCAAkC;IAClC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,sCAAsC;IACtC,YAAY,IAAI,IAAI,CAAC;IACrB,MAAM,IAAI,IAAI,CAAC;CAChB;AAuBD,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,UAAgB,GACjB,EAAE,oBAAoB,GAAG,mBAAmB,CA0S5C"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 候选选项:加载、搜索、防抖、取消、参数失效。
|
|
3
|
+
*
|
|
4
|
+
* 关键约束(设计文档 §5.3):**effect / useCallback 的依赖里不出现源对象和其中的函数**。
|
|
5
|
+
* 源一律从 ref 读,依赖只保留 sourceKey / paramsKey 这类字符串。
|
|
6
|
+
* 违反这条的直接后果是:父组件每渲染一次,列表里每个单元格各发一次请求。
|
|
7
|
+
*/
|
|
8
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
9
|
+
import { dropCandidateCache, loadCandidatesWithCache } from './cache.js';
|
|
10
|
+
import { candidatesCacheKey, paramsKeyOf } from './key.js';
|
|
11
|
+
import { hasRequiredParams, normalizeSource } from './normalize.js';
|
|
12
|
+
import { resolveSelectSource } from './registry.js';
|
|
13
|
+
import { useStableSourceKey } from './useStableSourceKey.js';
|
|
14
|
+
import { filterOptions } from './format.js';
|
|
15
|
+
const EMPTY_OPTIONS = [];
|
|
16
|
+
export function useCandidates({ source: sourceProp, cacheKey, params, searchFields, debounceMs = 300, }) {
|
|
17
|
+
const source = resolveSelectSource(sourceProp);
|
|
18
|
+
// 订阅版本在这里:失效 / 重注册后键会变,已挂载的实例才会重新取数
|
|
19
|
+
const sourceKey = useStableSourceKey(sourceProp, cacheKey);
|
|
20
|
+
const capability = normalizeSource(source);
|
|
21
|
+
const { candidateMode, requiredParams, needsFetch } = capability;
|
|
22
|
+
const paramsKey = paramsKeyOf(params);
|
|
23
|
+
const blocked = !hasRequiredParams(requiredParams, params);
|
|
24
|
+
// 源与参数只走 ref:它们可以每帧变化,但绝不能成为 effect 依赖
|
|
25
|
+
const sourceRef = useRef(source);
|
|
26
|
+
sourceRef.current = source;
|
|
27
|
+
const paramsRef = useRef(params);
|
|
28
|
+
paramsRef.current = params;
|
|
29
|
+
const scopeRef = useRef('');
|
|
30
|
+
scopeRef.current = source?.namespace?.() ?? '';
|
|
31
|
+
// 当前作用域参与结果归属判断:只进缓存键不够,租户切换后旧候选仍会显示
|
|
32
|
+
const scope = scopeRef.current;
|
|
33
|
+
const sourceKeyRef = useRef(sourceKey);
|
|
34
|
+
sourceKeyRef.current = sourceKey;
|
|
35
|
+
const [loadState, setLoadState] = useState(null);
|
|
36
|
+
const [searchState, setSearchState] = useState(null);
|
|
37
|
+
const [keyword, setKeyword] = useState('');
|
|
38
|
+
// reload 在回调里要读当前关键词,不能进依赖(否则每次输入都换新函数)
|
|
39
|
+
const keywordRef = useRef('');
|
|
40
|
+
keywordRef.current = keyword;
|
|
41
|
+
// load 与 search 各记一份:共用会互相覆盖,一个结束就把另一个的「进行中」抹掉
|
|
42
|
+
const [loadLoading, setLoadLoading] = useState(false);
|
|
43
|
+
const [searchLoading, setSearchLoading] = useState(false);
|
|
44
|
+
const [error, setError] = useState(false);
|
|
45
|
+
/** 递增表示「需要拉一次候选」:展开下拉或手动重载 */
|
|
46
|
+
const [loadTick, setLoadTick] = useState(0);
|
|
47
|
+
/** 下拉是否展开:纯搜索型只在展开时才跟着源 / 条件变化重查 */
|
|
48
|
+
const openedRef = useRef(false);
|
|
49
|
+
const seqRef = useRef(0);
|
|
50
|
+
const abortRef = useRef(null);
|
|
51
|
+
const timerRef = useRef(null);
|
|
52
|
+
const cancelPending = useCallback(() => {
|
|
53
|
+
if (timerRef.current) {
|
|
54
|
+
clearTimeout(timerRef.current);
|
|
55
|
+
timerRef.current = null;
|
|
56
|
+
}
|
|
57
|
+
abortRef.current?.abort();
|
|
58
|
+
abortRef.current = null;
|
|
59
|
+
// 作废版本号才是正确性保障:abort 只是优化,源不认 signal 时仍会 resolve,
|
|
60
|
+
// 那时 seq 已经对不上,不会把旧结果写成 error / 空列表
|
|
61
|
+
seqRef.current += 1;
|
|
62
|
+
// 被作废的请求不再回来收尾,loading 得在这里结束:否则关掉下拉后就一直转圈
|
|
63
|
+
setSearchLoading(false);
|
|
64
|
+
}, []);
|
|
65
|
+
useEffect(() => cancelPending, [cancelPending]);
|
|
66
|
+
// 加载全集:只在展开下拉之后发生,依赖全是字符串,源从 ref 取
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
const current = sourceRef.current;
|
|
69
|
+
const load = current?.load;
|
|
70
|
+
// 上一次的请求被这次变更作废(cancelled)后不会自己收尾,提前返回时必须补关 loading
|
|
71
|
+
if (!loadTick || blocked || !load) {
|
|
72
|
+
setLoadLoading(false);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const key = candidatesCacheKey(scope, sourceKey, paramsKey);
|
|
76
|
+
let cancelled = false;
|
|
77
|
+
setLoadLoading(true);
|
|
78
|
+
setError(false);
|
|
79
|
+
loadCandidatesWithCache(key, () => load(paramsRef.current ?? {}, undefined))
|
|
80
|
+
.then((list) => {
|
|
81
|
+
if (!cancelled)
|
|
82
|
+
setLoadState({ sourceKey, scope, paramsKey, list });
|
|
83
|
+
})
|
|
84
|
+
.catch(() => {
|
|
85
|
+
if (!cancelled) {
|
|
86
|
+
setLoadState({ sourceKey, scope, paramsKey, list: EMPTY_OPTIONS });
|
|
87
|
+
setError(true);
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
.finally(() => {
|
|
91
|
+
if (!cancelled)
|
|
92
|
+
setLoadLoading(false);
|
|
93
|
+
});
|
|
94
|
+
return () => {
|
|
95
|
+
cancelled = true;
|
|
96
|
+
};
|
|
97
|
+
}, [loadTick, sourceKey, scope, paramsKey, blocked, candidateMode]);
|
|
98
|
+
// 身份变化(条件 / 来源 / 租户):取消在途请求,旧结果靠三元比对自动失效。
|
|
99
|
+
// scope 必须在这里:租户切了但 sourceKey、paramsKey 没变,在途请求照样得作废
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
cancelPending();
|
|
102
|
+
setKeyword('');
|
|
103
|
+
}, [paramsKey, sourceKey, scope, cancelPending]);
|
|
104
|
+
const runSearch = useCallback((keywordValue, key) => {
|
|
105
|
+
// 缺必需参数一律不查。open / reload 是公开入口(树、弹窗也会调),
|
|
106
|
+
// 光靠组件上的 disabled 挡不住,守卫必须收在真正发请求的这一处
|
|
107
|
+
if (blocked)
|
|
108
|
+
return;
|
|
109
|
+
const current = sourceRef.current;
|
|
110
|
+
const search = current?.search;
|
|
111
|
+
if (!search)
|
|
112
|
+
return;
|
|
113
|
+
const seq = seqRef.current + 1;
|
|
114
|
+
seqRef.current = seq;
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
abortRef.current = controller;
|
|
117
|
+
// 归属信息在发请求时定格:结果回来后要能证明它属于哪个源、哪个租户
|
|
118
|
+
const sourceKeyAtRequest = sourceKeyRef.current;
|
|
119
|
+
const scopeAtRequest = scopeRef.current;
|
|
120
|
+
setSearchLoading(true);
|
|
121
|
+
setError(false);
|
|
122
|
+
search(keywordValue, paramsRef.current ?? {}, controller.signal)
|
|
123
|
+
.then((list) => {
|
|
124
|
+
if (seq !== seqRef.current)
|
|
125
|
+
return;
|
|
126
|
+
setSearchState({
|
|
127
|
+
sourceKey: sourceKeyAtRequest,
|
|
128
|
+
scope: scopeAtRequest,
|
|
129
|
+
paramsKey: key,
|
|
130
|
+
keyword: keywordValue,
|
|
131
|
+
list,
|
|
132
|
+
});
|
|
133
|
+
})
|
|
134
|
+
.catch(() => {
|
|
135
|
+
if (seq !== seqRef.current)
|
|
136
|
+
return;
|
|
137
|
+
setSearchState({
|
|
138
|
+
sourceKey: sourceKeyAtRequest,
|
|
139
|
+
scope: scopeAtRequest,
|
|
140
|
+
paramsKey: key,
|
|
141
|
+
keyword: keywordValue,
|
|
142
|
+
list: EMPTY_OPTIONS,
|
|
143
|
+
});
|
|
144
|
+
setError(true);
|
|
145
|
+
})
|
|
146
|
+
.finally(() => {
|
|
147
|
+
if (seq === seqRef.current)
|
|
148
|
+
setSearchLoading(false);
|
|
149
|
+
});
|
|
150
|
+
}, [blocked]);
|
|
151
|
+
const search = useCallback((keywordValue) => {
|
|
152
|
+
setKeyword(keywordValue);
|
|
153
|
+
if (blocked || !needsFetch || candidateMode === 'load')
|
|
154
|
+
return;
|
|
155
|
+
if (candidateMode !== 'search' && candidateMode !== 'hybrid')
|
|
156
|
+
return;
|
|
157
|
+
cancelPending();
|
|
158
|
+
const key = paramsKeyOf(paramsRef.current);
|
|
159
|
+
timerRef.current = setTimeout(() => {
|
|
160
|
+
timerRef.current = null;
|
|
161
|
+
runSearch(keywordValue, key);
|
|
162
|
+
}, debounceMs);
|
|
163
|
+
}, [blocked, needsFetch, candidateMode, debounceMs, cancelPending, runSearch]);
|
|
164
|
+
const searchStateRef = useRef(null);
|
|
165
|
+
searchStateRef.current = searchState;
|
|
166
|
+
const open = useCallback(() => {
|
|
167
|
+
openedRef.current = true;
|
|
168
|
+
setLoadTick((tick) => tick + 1);
|
|
169
|
+
// 纯搜索型没有全集,展开时先取一次默认候选;已经取过就不重复请求
|
|
170
|
+
const current = sourceRef.current;
|
|
171
|
+
if (!current?.search || current.load)
|
|
172
|
+
return;
|
|
173
|
+
const key = paramsKeyOf(paramsRef.current);
|
|
174
|
+
const state = searchStateRef.current;
|
|
175
|
+
// 换了源或租户,旧结果不再代表当前范围,必须重查
|
|
176
|
+
if (!state ||
|
|
177
|
+
state.sourceKey !== sourceKeyRef.current ||
|
|
178
|
+
state.scope !== scopeRef.current ||
|
|
179
|
+
state.paramsKey !== key ||
|
|
180
|
+
state.keyword !== '') {
|
|
181
|
+
runSearch('', key);
|
|
182
|
+
}
|
|
183
|
+
}, [runSearch]);
|
|
184
|
+
/** 收起下拉:清关键字并作废在途搜索,避免关掉之后才回来的结果写进状态 */
|
|
185
|
+
const resetKeyword = useCallback(() => {
|
|
186
|
+
openedRef.current = false;
|
|
187
|
+
cancelPending();
|
|
188
|
+
setKeyword('');
|
|
189
|
+
}, [cancelPending]);
|
|
190
|
+
/** 手动重取:绕开缓存。有关键词就重跑搜索,否则重拉全集 */
|
|
191
|
+
const reload = useCallback(() => {
|
|
192
|
+
cancelPending();
|
|
193
|
+
const key = paramsKeyOf(paramsRef.current);
|
|
194
|
+
const current = sourceRef.current;
|
|
195
|
+
if (keywordRef.current && current?.search) {
|
|
196
|
+
runSearch(keywordRef.current, key);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (current?.load) {
|
|
200
|
+
// 先删缓存再 bump,否则 effect 跑起来又会命中同一份
|
|
201
|
+
dropCandidateCache(candidatesCacheKey(scopeRef.current, sourceKeyRef.current, key));
|
|
202
|
+
setLoadTick((tick) => tick + 1);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (current?.search)
|
|
206
|
+
runSearch('', key);
|
|
207
|
+
}, [cancelPending, runSearch]);
|
|
208
|
+
/**
|
|
209
|
+
* 纯搜索型没有 load effect 兜底:源失效、或父字段改了条件,而下拉还开着时,
|
|
210
|
+
* 旧候选会一直挂在列表里(可能是别的仓库的记录)。这里补一次重查。
|
|
211
|
+
* 带 load 的源由加载 effect 负责,不重复发请求。
|
|
212
|
+
*/
|
|
213
|
+
const identityRef = useRef({ sourceKey, paramsKey, scope });
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
const previous = identityRef.current;
|
|
216
|
+
identityRef.current = { sourceKey, paramsKey, scope };
|
|
217
|
+
if (!openedRef.current)
|
|
218
|
+
return;
|
|
219
|
+
if (previous.sourceKey === sourceKey &&
|
|
220
|
+
previous.paramsKey === paramsKey &&
|
|
221
|
+
previous.scope === scope) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const current = sourceRef.current;
|
|
225
|
+
if (!current?.search || current.load)
|
|
226
|
+
return;
|
|
227
|
+
runSearch('', paramsKeyOf(paramsRef.current));
|
|
228
|
+
}, [sourceKey, paramsKey, scope, runSearch]);
|
|
229
|
+
const loadValid = Boolean(loadState &&
|
|
230
|
+
loadState.sourceKey === sourceKey &&
|
|
231
|
+
loadState.scope === scope &&
|
|
232
|
+
loadState.paramsKey === paramsKey);
|
|
233
|
+
const searchValid = Boolean(searchState &&
|
|
234
|
+
searchState.sourceKey === sourceKey &&
|
|
235
|
+
searchState.scope === scope &&
|
|
236
|
+
searchState.paramsKey === paramsKey &&
|
|
237
|
+
searchState.keyword === keyword);
|
|
238
|
+
const options = useMemo(() => {
|
|
239
|
+
if (blocked || candidateMode === 'invalid' || candidateMode === 'none')
|
|
240
|
+
return EMPTY_OPTIONS;
|
|
241
|
+
// 同步全集也要过本地过滤:filterOption=false 已关掉 antd 自带过滤,
|
|
242
|
+
// 这里不处理的话静态源在输入框里根本筛不动
|
|
243
|
+
if (candidateMode === 'sync') {
|
|
244
|
+
return filterOptions(source?.options ?? EMPTY_OPTIONS, keyword, searchFields);
|
|
245
|
+
}
|
|
246
|
+
if (candidateMode === 'load') {
|
|
247
|
+
if (!loadValid || !loadState)
|
|
248
|
+
return EMPTY_OPTIONS;
|
|
249
|
+
// 全集已在前端,关键字只做本地过滤
|
|
250
|
+
return filterOptions(loadState.list, keyword, searchFields);
|
|
251
|
+
}
|
|
252
|
+
if (candidateMode === 'hybrid') {
|
|
253
|
+
if (keyword)
|
|
254
|
+
return searchValid && searchState ? searchState.list : EMPTY_OPTIONS;
|
|
255
|
+
return loadValid && loadState ? loadState.list : EMPTY_OPTIONS;
|
|
256
|
+
}
|
|
257
|
+
return searchValid && searchState ? searchState.list : EMPTY_OPTIONS;
|
|
258
|
+
}, [
|
|
259
|
+
blocked,
|
|
260
|
+
candidateMode,
|
|
261
|
+
source,
|
|
262
|
+
loadValid,
|
|
263
|
+
loadState,
|
|
264
|
+
searchValid,
|
|
265
|
+
searchState,
|
|
266
|
+
keyword,
|
|
267
|
+
searchFields,
|
|
268
|
+
]);
|
|
269
|
+
// 回显要用未过滤的候选:已选项可能正好被关键词筛掉
|
|
270
|
+
const echoOptions = useMemo(() => {
|
|
271
|
+
if (candidateMode === 'sync')
|
|
272
|
+
return source?.options ?? EMPTY_OPTIONS;
|
|
273
|
+
if (loadValid && loadState)
|
|
274
|
+
return loadState.list;
|
|
275
|
+
// 纯搜索型没有全集,只能用手上这批
|
|
276
|
+
return options;
|
|
277
|
+
}, [candidateMode, source, loadValid, loadState, options]);
|
|
278
|
+
return {
|
|
279
|
+
sourceKey,
|
|
280
|
+
options,
|
|
281
|
+
echoOptions,
|
|
282
|
+
keyword,
|
|
283
|
+
loading: loadLoading || searchLoading,
|
|
284
|
+
error,
|
|
285
|
+
blocked,
|
|
286
|
+
open,
|
|
287
|
+
search,
|
|
288
|
+
resetKeyword,
|
|
289
|
+
reload,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=useCandidates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCandidates.js","sourceRoot":"","sources":["../../src/biz-select/useCandidates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BizOption, SelectCode, SelectParams, SelectSource } from './types.js';
|
|
2
|
+
export type SelectedStatus = 'empty' | 'loading' | 'resolved' | 'missing' | 'blocked' | 'error';
|
|
3
|
+
export interface UseSelectedLabelOptions {
|
|
4
|
+
source?: SelectSource;
|
|
5
|
+
sourceKey: string;
|
|
6
|
+
code?: SelectCode;
|
|
7
|
+
/** 对象值里自带的名称 */
|
|
8
|
+
valueName?: string;
|
|
9
|
+
seedOptions?: BizOption[];
|
|
10
|
+
candidates?: BizOption[];
|
|
11
|
+
params?: SelectParams;
|
|
12
|
+
}
|
|
13
|
+
export interface UseSelectedLabelResult {
|
|
14
|
+
option: BizOption | null;
|
|
15
|
+
status: SelectedStatus;
|
|
16
|
+
}
|
|
17
|
+
export declare function useSelectedLabel({ source, sourceKey, code, valueName, seedOptions, candidates, params, }: UseSelectedLabelOptions): UseSelectedLabelResult;
|
|
18
|
+
//# sourceMappingURL=useSelectedLabel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSelectedLabel.d.ts","sourceRoot":"","sources":["../../src/biz-select/useSelectedLabel.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjF,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhG,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,gBAAgB;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,cAAc,CAAC;CACxB;AAyBD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,SAAS,EACT,IAAI,EACJ,SAAS,EACT,WAAW,EACX,UAAU,EACV,MAAM,GACP,EAAE,uBAAuB,GAAG,sBAAsB,CAgJlD"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 已选编码 → 名称的解析。
|
|
3
|
+
*
|
|
4
|
+
* 与关键词搜索是两条独立逻辑:搜索是模糊的,回显必须精确。
|
|
5
|
+
* 优先级(设计文档 §5.6):对象值自带的名称 → seedOptions → 已加载候选
|
|
6
|
+
* → resolve(code) → 原样显示编码并暴露状态。
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
9
|
+
import { resolveCodeWithCache } from './cache.js';
|
|
10
|
+
import { paramsKeyOf, resolveCacheKey } from './key.js';
|
|
11
|
+
import { hasRequiredParams, normalizeSource } from './normalize.js';
|
|
12
|
+
/** 编码比对统一走字符串:后端可能把 1 和 '1' 当同一个值 */
|
|
13
|
+
function sameCode(a, b) {
|
|
14
|
+
return a !== undefined && b !== undefined && String(a) === String(b);
|
|
15
|
+
}
|
|
16
|
+
function codeKeyOf(code) {
|
|
17
|
+
return code === undefined || code === null ? '' : `${typeof code}:${code}`;
|
|
18
|
+
}
|
|
19
|
+
function findOption(list, code) {
|
|
20
|
+
return list?.find((item) => item.code === code);
|
|
21
|
+
}
|
|
22
|
+
export function useSelectedLabel({ source, sourceKey, code, valueName, seedOptions, candidates, params, }) {
|
|
23
|
+
const capability = normalizeSource(source);
|
|
24
|
+
const { canResolve, requiredParams } = capability;
|
|
25
|
+
const paramsKey = paramsKeyOf(params);
|
|
26
|
+
const codeKey = codeKeyOf(code);
|
|
27
|
+
const scope = source?.namespace?.() ?? '';
|
|
28
|
+
const blocked = !hasRequiredParams(requiredParams, params);
|
|
29
|
+
const sourceRef = useRef(source);
|
|
30
|
+
sourceRef.current = source;
|
|
31
|
+
const paramsRef = useRef(params);
|
|
32
|
+
paramsRef.current = params;
|
|
33
|
+
const codeRef = useRef(code);
|
|
34
|
+
codeRef.current = code;
|
|
35
|
+
const [asyncResult, setAsyncResult] = useState(null);
|
|
36
|
+
const [loading, setLoading] = useState(false);
|
|
37
|
+
const seqRef = useRef(0);
|
|
38
|
+
/**
|
|
39
|
+
* 已选项的兜底记忆。
|
|
40
|
+
*
|
|
41
|
+
* 搜索型源的候选会随关键词整批换掉,重新展开拿到的也只是默认第一页,
|
|
42
|
+
* 已选项常常不在里面;没有 resolve 就只能退回编码。按
|
|
43
|
+
* 「来源 + 租户 + 条件 + 编码」记住最近一次命中的选项,条件一变自然失效。
|
|
44
|
+
*/
|
|
45
|
+
const memoryRef = useRef(null);
|
|
46
|
+
const memoryKey = `${sourceKey}|${scope}|${paramsKey}|${codeKey}`;
|
|
47
|
+
// 本地能查到就不发请求;命中结果是对象,这里只取布尔作为 effect 依赖
|
|
48
|
+
const seedHit = useMemo(() => (code === undefined ? undefined : findOption(seedOptions, code)), [seedOptions, code]);
|
|
49
|
+
const candidateHit = useMemo(() => (code === undefined ? undefined : findOption(candidates, code)), [candidates, code]);
|
|
50
|
+
const localHit = seedHit ?? candidateHit;
|
|
51
|
+
const hasLocal = Boolean(localHit);
|
|
52
|
+
// 对象值已经自带名称:展示层直接用,没有理由再发一次 resolve
|
|
53
|
+
const hasValueName = Boolean(valueName);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (code === undefined || hasLocal || hasValueName || !canResolve || blocked)
|
|
56
|
+
return;
|
|
57
|
+
const resolve = sourceRef.current?.resolve;
|
|
58
|
+
if (!resolve)
|
|
59
|
+
return;
|
|
60
|
+
const requested = codeRef.current;
|
|
61
|
+
const key = resolveCacheKey(scope, sourceKey, requested, paramsKey);
|
|
62
|
+
const seq = seqRef.current + 1;
|
|
63
|
+
seqRef.current = seq;
|
|
64
|
+
let cancelled = false;
|
|
65
|
+
setLoading(true);
|
|
66
|
+
// 不传 AbortSignal:这个 promise 是多个实例共享的,
|
|
67
|
+
// 单个实例卸载不能把别人的请求掐掉,只放弃自己的状态更新即可
|
|
68
|
+
resolveCodeWithCache(key, () => resolve(requested, paramsRef.current ?? {}))
|
|
69
|
+
.then((option) => {
|
|
70
|
+
if (cancelled || seq !== seqRef.current)
|
|
71
|
+
return;
|
|
72
|
+
// 接口返回了别的编码就别认:认错比认不出更糟,展示层会显示陌生名称。
|
|
73
|
+
// 数字/字符串同一编码(后端序列化常见)认下,但编码归一到值的类型:
|
|
74
|
+
// 否则选项 value 与 Select 的 value 对不上,标签等于白解析
|
|
75
|
+
const matched = option && sameCode(option.code, requested) ? { ...option, code: requested } : null;
|
|
76
|
+
setAsyncResult({ sourceKey, scope, paramsKey, codeKey, option: matched, failed: false });
|
|
77
|
+
})
|
|
78
|
+
.catch(() => {
|
|
79
|
+
if (cancelled || seq !== seqRef.current)
|
|
80
|
+
return;
|
|
81
|
+
setAsyncResult({
|
|
82
|
+
sourceKey,
|
|
83
|
+
scope,
|
|
84
|
+
paramsKey,
|
|
85
|
+
codeKey,
|
|
86
|
+
option: null,
|
|
87
|
+
failed: true,
|
|
88
|
+
});
|
|
89
|
+
})
|
|
90
|
+
.finally(() => {
|
|
91
|
+
if (!cancelled && seq === seqRef.current)
|
|
92
|
+
setLoading(false);
|
|
93
|
+
});
|
|
94
|
+
return () => {
|
|
95
|
+
cancelled = true;
|
|
96
|
+
};
|
|
97
|
+
// sourceKey / paramsKey / codeKey 都是字符串,源从 ref 取,函数引用不进依赖
|
|
98
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
99
|
+
}, [sourceKey, scope, paramsKey, codeKey, hasLocal, hasValueName, canResolve, blocked]);
|
|
100
|
+
return useMemo(() => {
|
|
101
|
+
if (code === undefined)
|
|
102
|
+
return { option: null, status: 'empty' };
|
|
103
|
+
// 命中即记账:下次候选被换掉时还能拿它兜底
|
|
104
|
+
const remember = (option) => {
|
|
105
|
+
memoryRef.current = { key: memoryKey, option };
|
|
106
|
+
return option;
|
|
107
|
+
};
|
|
108
|
+
const remembered = memoryRef.current;
|
|
109
|
+
const fallback = remembered?.key === memoryKey ? remembered.option : undefined;
|
|
110
|
+
// 1) 对象值自带的名称:详情已经给了,不必再查
|
|
111
|
+
if (valueName)
|
|
112
|
+
return { option: remember({ code, name: valueName }), status: 'resolved' };
|
|
113
|
+
// 2) 页面预置的候选
|
|
114
|
+
if (seedHit)
|
|
115
|
+
return { option: remember(seedHit), status: 'resolved' };
|
|
116
|
+
// 3) 已加载的候选
|
|
117
|
+
if (candidateHit)
|
|
118
|
+
return { option: remember(candidateHit), status: 'resolved' };
|
|
119
|
+
const asyncMatched = asyncResult &&
|
|
120
|
+
asyncResult.sourceKey === sourceKey &&
|
|
121
|
+
asyncResult.scope === scope &&
|
|
122
|
+
asyncResult.paramsKey === paramsKey &&
|
|
123
|
+
asyncResult.codeKey === codeKey;
|
|
124
|
+
if (asyncMatched && asyncResult) {
|
|
125
|
+
if (asyncResult.option)
|
|
126
|
+
return { option: remember(asyncResult.option), status: 'resolved' };
|
|
127
|
+
// 解析不到但之前见过这个编码:搜索型源的候选已被关键词换掉,别退回编码
|
|
128
|
+
if (fallback)
|
|
129
|
+
return { option: fallback, status: 'resolved' };
|
|
130
|
+
return { option: { code }, status: asyncResult.failed ? 'error' : 'missing' };
|
|
131
|
+
}
|
|
132
|
+
if (fallback)
|
|
133
|
+
return { option: fallback, status: 'resolved' };
|
|
134
|
+
if (!canResolve)
|
|
135
|
+
return { option: { code }, status: 'missing' };
|
|
136
|
+
// 用空条件查可能拿到别家数据,宁可不查
|
|
137
|
+
if (blocked)
|
|
138
|
+
return { option: { code }, status: 'blocked' };
|
|
139
|
+
if (loading)
|
|
140
|
+
return { option: { code }, status: 'loading' };
|
|
141
|
+
return { option: { code }, status: 'missing' };
|
|
142
|
+
}, [
|
|
143
|
+
code,
|
|
144
|
+
valueName,
|
|
145
|
+
seedHit,
|
|
146
|
+
candidateHit,
|
|
147
|
+
asyncResult,
|
|
148
|
+
memoryKey,
|
|
149
|
+
sourceKey,
|
|
150
|
+
scope,
|
|
151
|
+
paramsKey,
|
|
152
|
+
codeKey,
|
|
153
|
+
canResolve,
|
|
154
|
+
blocked,
|
|
155
|
+
loading,
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=useSelectedLabel.js.map
|