@bit-sun/business-component 4.2.1-alpha.7-longya → 4.2.1-alpha.8
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/.claude/settings.local.json +8 -0
- package/dist/components/Functional/SearchSelect/pagination.d.ts +3 -3
- package/dist/index.esm.js +416 -953
- package/dist/index.js +415 -952
- package/docs/error-record.md +0 -9
- package/package.json +1 -1
- package/src/components/Business/AddSelectBusiness/index.tsx +8 -124
- package/src/components/Business/SearchSelect/BusinessUtils.tsx +12 -106
- package/src/components/Business/columnSettingTable/sulaSettingTable.tsx +1 -1
- package/src/components/Functional/AddSelect/helps.ts +0 -2
- package/src/components/Functional/AddSelect/index.less +0 -17
- package/src/components/Functional/AddSelect/index.tsx +9 -66
- package/src/components/Functional/SearchSelect/index.less +0 -16
- package/src/components/Functional/SearchSelect/index.md +2 -0
- package/src/components/Functional/SearchSelect/index.tsx +65 -77
- package/src/components/Functional/SearchSelect/pagination.ts +4 -4
- package/src/components/Solution/RuleComponent/ruleFiled.js +155 -176
- package/dist/components/Functional/QueryAutoComplete/buildOptions.d.ts +0 -3
- package/dist/components/Functional/QueryAutoComplete/buildOptions.test.d.ts +0 -1
- package/dist/components/Functional/QueryAutoComplete/index.d.ts +0 -23
- package/dist/components/Functional/SearchSelect/currentPageFilter.d.ts +0 -41
- package/docs/superpowers/plans/2026-10-12-IKJX29-sku-name-autocomplete.md +0 -489
- package/docs/superpowers/specs/2026-10-12-IKJX29-sku-name-autocomplete-design.md +0 -115
- package/src/components/Functional/QueryAutoComplete/buildOptions.test.ts +0 -22
- package/src/components/Functional/QueryAutoComplete/buildOptions.ts +0 -17
- package/src/components/Functional/QueryAutoComplete/index.tsx +0 -140
- package/src/components/Functional/SearchSelect/currentPageFilter.ts +0 -135
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export type QueryAutoCompleteRequestConfig = {
|
|
3
|
-
url: string;
|
|
4
|
-
method?: 'get' | 'GET' | 'post' | 'POST' | 'patch' | 'PATCH' | 'put' | 'PUT';
|
|
5
|
-
otherParams?: Record<string, any>;
|
|
6
|
-
mappingTextField?: string;
|
|
7
|
-
pageSize?: number;
|
|
8
|
-
debounceWait?: number;
|
|
9
|
-
/** 写入查询串的 like 参数名,默认 qp-skuName-like */
|
|
10
|
-
likeParamName?: string;
|
|
11
|
-
};
|
|
12
|
-
export type QueryAutoCompleteProps = {
|
|
13
|
-
value?: string;
|
|
14
|
-
onChange?: (value: string) => void;
|
|
15
|
-
onPressEnter?: () => void;
|
|
16
|
-
requestConfig: QueryAutoCompleteRequestConfig;
|
|
17
|
-
placeholder?: string;
|
|
18
|
-
allowClear?: boolean;
|
|
19
|
-
style?: React.CSSProperties;
|
|
20
|
-
disabled?: boolean;
|
|
21
|
-
};
|
|
22
|
-
declare const QueryAutoComplete: React.FC<QueryAutoCompleteProps>;
|
|
23
|
-
export default QueryAutoComplete;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export interface CurrentPageFilterColumn {
|
|
2
|
-
dataIndex?: string;
|
|
3
|
-
currentPageFilter?: boolean;
|
|
4
|
-
children?: CurrentPageFilterColumn[];
|
|
5
|
-
[key: string]: any;
|
|
6
|
-
}
|
|
7
|
-
export interface CurrentPageFilterOption {
|
|
8
|
-
text: string;
|
|
9
|
-
value: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* SKU 选择弹框「当前页列筛选」默认字段(合并 BusinessSearchSelect / AddSkuSelect 命名)。
|
|
13
|
-
* Functional 层按 dataIndex 命中自动开启,业务列不必再散标 currentPageFilter。
|
|
14
|
-
*/
|
|
15
|
-
export declare const SKU_CURRENT_PAGE_FILTER_DATA_INDEXES: string[];
|
|
16
|
-
/**
|
|
17
|
-
* 按 dataIndex 白名单为表格列打上 currentPageFilter。
|
|
18
|
-
*
|
|
19
|
-
* @param columns 原始表格列
|
|
20
|
-
* @param dataIndexes 需要开启当前页筛选的字段
|
|
21
|
-
* @returns 已标记的表格列
|
|
22
|
-
*/
|
|
23
|
-
export declare const markCurrentPageFilterByDataIndex: (columns?: CurrentPageFilterColumn[], dataIndexes?: string[]) => CurrentPageFilterColumn[];
|
|
24
|
-
/**
|
|
25
|
-
* 从当前页数据中提取指定字段的筛选项,过滤空值后按名称去重、排序。
|
|
26
|
-
*
|
|
27
|
-
* @param dataSource 当前页表格数据
|
|
28
|
-
* @param dataIndex 作为筛选值的数据字段
|
|
29
|
-
* @returns 当前页可用的筛选项
|
|
30
|
-
*/
|
|
31
|
-
export declare const getCurrentPageFilterOptions: (dataSource?: any[], dataIndex?: string) => CurrentPageFilterOption[];
|
|
32
|
-
/**
|
|
33
|
-
* 为声明了 currentPageFilter 的表格列注入当前页筛选能力。
|
|
34
|
-
*
|
|
35
|
-
* @param columns 原始表格列
|
|
36
|
-
* @param dataSource 当前页表格数据
|
|
37
|
-
* @param filteredValues 当前已选筛选值
|
|
38
|
-
* @param enabled 是否启用筛选;已选结果表仅移除内部配置,不启用筛选
|
|
39
|
-
* @returns 可直接传给 Ant Design Table 的表格列
|
|
40
|
-
*/
|
|
41
|
-
export declare const buildCurrentPageFilterColumns: (columns?: CurrentPageFilterColumn[], dataSource?: any[], filteredValues?: Record<string, any[] | null>, enabled?: boolean) => CurrentPageFilterColumn[];
|
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
# IKJX29 SKU名称 AutoComplete Implementation Plan
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
-
|
|
5
|
-
**Goal:** 在基础商品选择器 `AddSkuSelect` 中,把「SKU名称」从普通输入框改为自由录入型 AutoComplete(远程模糊联想,可选可输,选中只回填)。
|
|
6
|
-
|
|
7
|
-
**Architecture:** 抽出可复用的 `QueryAutoComplete`(antd `AutoComplete` + debounce 请求 + name 去重);在 `AddSelect.formItem` 增加 `autoComplete` 字段类型;`AddSkuSelect` 将 `qp-skuName-like` 接到该类型,联想接口与列表同源 `/items/sku/pager/v2`。
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** React 17、antd 4 `AutoComplete`、ahooks `useDebounceFn`、既有 `request` / `judgeIsRequestError`、`@umijs/test`(纯函数单测)。
|
|
10
|
-
|
|
11
|
-
**Spec:** `docs/superpowers/specs/2026-10-12-IKJX29-sku-name-autocomplete-design.md`
|
|
12
|
-
|
|
13
|
-
## Global Constraints
|
|
14
|
-
|
|
15
|
-
- 表单值始终是 **字符串**(名称关键字),禁止 `labelInValue` / skuId 对象
|
|
16
|
-
- 选中联想项 **只回填**,不自动触发 `onSearchTable`
|
|
17
|
-
- 无匹配仍可输入并点「查询」/回车查询
|
|
18
|
-
- 联想:`GET /items/sku/pager/v2`,`pageSize=50`,`currentPage=1`,默认条件 + `qp-skuName-like`
|
|
19
|
-
- debounce **300ms**;空串不请求;失败安静清空 options(不 `message.error`)
|
|
20
|
-
- 基线分支:`origin/master-4.0-tx`;功能分支建议:`feature/master-4.0-tx-24-<作者>-IKJX29-sku-name-autocomplete`
|
|
21
|
-
- Commit 仅在用户明确要求时执行;下列 Commit 步骤可跳过或攒批
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## File Structure
|
|
26
|
-
|
|
27
|
-
| 文件 | 职责 |
|
|
28
|
-
|------|------|
|
|
29
|
-
| `src/components/Functional/QueryAutoComplete/buildOptions.ts` | 纯函数:list → 去重 options |
|
|
30
|
-
| `src/components/Functional/QueryAutoComplete/buildOptions.test.ts` | 纯函数单测 |
|
|
31
|
-
| `src/components/Functional/QueryAutoComplete/index.tsx` | AutoComplete UI + debounce 请求 + 过期响应丢弃 |
|
|
32
|
-
| `src/components/Functional/AddSelect/index.tsx` | `formItem` 增加 `autoComplete` 分支 |
|
|
33
|
-
| `src/components/Business/AddSelectBusiness/index.tsx` | `AddSkuSelect` 的 `qp-skuName-like` 改配置 |
|
|
34
|
-
|
|
35
|
-
不新增对外 export(组件仅供 `AddSelect` 内部使用);若后续其它弹窗表单要用再导出。
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
### Task 1: `buildOptions` 纯函数 + 单测
|
|
40
|
-
|
|
41
|
-
**Files:**
|
|
42
|
-
- Create: `src/components/Functional/QueryAutoComplete/buildOptions.ts`
|
|
43
|
-
- Create: `src/components/Functional/QueryAutoComplete/buildOptions.test.ts`
|
|
44
|
-
|
|
45
|
-
**Interfaces:**
|
|
46
|
-
- Produces: `buildAutoCompleteOptions(list: Array<Record<string, any>>, mappingTextField?: string): { value: string }[]`
|
|
47
|
-
|
|
48
|
-
- [ ] **Step 1: Write the failing test**
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
// src/components/Functional/QueryAutoComplete/buildOptions.test.ts
|
|
52
|
-
import { buildAutoCompleteOptions } from './buildOptions';
|
|
53
|
-
|
|
54
|
-
describe('buildAutoCompleteOptions', () => {
|
|
55
|
-
it('maps name field and dedupes by text', () => {
|
|
56
|
-
const list = [
|
|
57
|
-
{ name: '战术臂章-鸡', skuCode: 'A' },
|
|
58
|
-
{ name: '战术臂章-鸡', skuCode: 'B' },
|
|
59
|
-
{ name: '战术臂章-狗', skuCode: 'C' },
|
|
60
|
-
{ name: '', skuCode: 'D' },
|
|
61
|
-
{ name: null, skuCode: 'E' },
|
|
62
|
-
];
|
|
63
|
-
expect(buildAutoCompleteOptions(list, 'name')).toEqual([
|
|
64
|
-
{ value: '战术臂章-鸡' },
|
|
65
|
-
{ value: '战术臂章-狗' },
|
|
66
|
-
]);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('returns empty for non-array', () => {
|
|
70
|
-
expect(buildAutoCompleteOptions(null as any)).toEqual([]);
|
|
71
|
-
expect(buildAutoCompleteOptions(undefined as any)).toEqual([]);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
- [ ] **Step 2: Run test to verify it fails**
|
|
77
|
-
|
|
78
|
-
Run: `npm test -- --testPathPattern=QueryAutoComplete/buildOptions --passWithNoTests 2>&1 | head -80`
|
|
79
|
-
(若项目无 jest 入口,用:`npx umi-test src/components/Functional/QueryAutoComplete/buildOptions.test.ts`)
|
|
80
|
-
Expected: FAIL(模块不存在或函数未定义)
|
|
81
|
-
|
|
82
|
-
- [ ] **Step 3: Write minimal implementation**
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
// src/components/Functional/QueryAutoComplete/buildOptions.ts
|
|
86
|
-
export function buildAutoCompleteOptions(
|
|
87
|
-
list: Array<Record<string, any>> | null | undefined,
|
|
88
|
-
mappingTextField = 'name',
|
|
89
|
-
): { value: string }[] {
|
|
90
|
-
if (!Array.isArray(list)) return [];
|
|
91
|
-
const seen = new Set<string>();
|
|
92
|
-
const options: { value: string }[] = [];
|
|
93
|
-
for (const item of list) {
|
|
94
|
-
const text = item?.[mappingTextField];
|
|
95
|
-
if (text == null) continue;
|
|
96
|
-
const value = String(text).trim();
|
|
97
|
-
if (!value || seen.has(value)) continue;
|
|
98
|
-
seen.add(value);
|
|
99
|
-
options.push({ value });
|
|
100
|
-
}
|
|
101
|
-
return options;
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
- [ ] **Step 4: Run test to verify it passes**
|
|
106
|
-
|
|
107
|
-
Run: 同 Step 2
|
|
108
|
-
Expected: PASS
|
|
109
|
-
|
|
110
|
-
- [ ] **Step 5: Commit**(仅当用户要求提交时)
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
git add src/components/Functional/QueryAutoComplete/buildOptions.ts \
|
|
114
|
-
src/components/Functional/QueryAutoComplete/buildOptions.test.ts
|
|
115
|
-
git commit -m "$(cat <<'EOF'
|
|
116
|
-
feat: IKJX29 add buildAutoCompleteOptions helper
|
|
117
|
-
|
|
118
|
-
EOF
|
|
119
|
-
)"
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
### Task 2: `QueryAutoComplete` 组件
|
|
125
|
-
|
|
126
|
-
**Files:**
|
|
127
|
-
- Create: `src/components/Functional/QueryAutoComplete/index.tsx`
|
|
128
|
-
- Consumes: `buildAutoCompleteOptions` from Task 1
|
|
129
|
-
|
|
130
|
-
**Interfaces:**
|
|
131
|
-
- Consumes: `buildAutoCompleteOptions(list, mappingTextField)`
|
|
132
|
-
- Produces: default export `QueryAutoComplete` with props:
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
export type QueryAutoCompleteRequestConfig = {
|
|
136
|
-
url: string;
|
|
137
|
-
method?: 'get' | 'GET';
|
|
138
|
-
otherParams?: Record<string, any>;
|
|
139
|
-
mappingTextField?: string; // default 'name'
|
|
140
|
-
pageSize?: number; // default 50
|
|
141
|
-
debounceWait?: number; // default 300
|
|
142
|
-
/** 写入查询串的 like 参数名,默认 qp-skuName-like */
|
|
143
|
-
likeParamName?: string;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export type QueryAutoCompleteProps = {
|
|
147
|
-
value?: string;
|
|
148
|
-
onChange?: (value: string) => void;
|
|
149
|
-
onPressEnter?: () => void;
|
|
150
|
-
requestConfig: QueryAutoCompleteRequestConfig;
|
|
151
|
-
placeholder?: string;
|
|
152
|
-
allowClear?: boolean;
|
|
153
|
-
style?: React.CSSProperties;
|
|
154
|
-
disabled?: boolean;
|
|
155
|
-
};
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
- [ ] **Step 1: Implement component**
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
// src/components/Functional/QueryAutoComplete/index.tsx
|
|
162
|
-
import React, { useRef, useState, useEffect } from 'react';
|
|
163
|
-
import { AutoComplete } from 'antd';
|
|
164
|
-
import { useDebounceFn } from 'ahooks';
|
|
165
|
-
import { stringify } from 'querystring';
|
|
166
|
-
import request from '@/utils/request';
|
|
167
|
-
import { judgeIsRequestError } from '@/utils/requestUtils';
|
|
168
|
-
import { buildAutoCompleteOptions } from './buildOptions';
|
|
169
|
-
|
|
170
|
-
export type QueryAutoCompleteRequestConfig = {
|
|
171
|
-
url: string;
|
|
172
|
-
method?: 'get' | 'GET';
|
|
173
|
-
otherParams?: Record<string, any>;
|
|
174
|
-
mappingTextField?: string;
|
|
175
|
-
pageSize?: number;
|
|
176
|
-
debounceWait?: number;
|
|
177
|
-
likeParamName?: string;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export type QueryAutoCompleteProps = {
|
|
181
|
-
value?: string;
|
|
182
|
-
onChange?: (value: string) => void;
|
|
183
|
-
onPressEnter?: () => void;
|
|
184
|
-
requestConfig: QueryAutoCompleteRequestConfig;
|
|
185
|
-
placeholder?: string;
|
|
186
|
-
allowClear?: boolean;
|
|
187
|
-
style?: React.CSSProperties;
|
|
188
|
-
disabled?: boolean;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const QueryAutoComplete: React.FC<QueryAutoCompleteProps> = ({
|
|
192
|
-
value,
|
|
193
|
-
onChange,
|
|
194
|
-
onPressEnter,
|
|
195
|
-
requestConfig,
|
|
196
|
-
placeholder = '请输入',
|
|
197
|
-
allowClear = true,
|
|
198
|
-
style,
|
|
199
|
-
disabled,
|
|
200
|
-
}) => {
|
|
201
|
-
const {
|
|
202
|
-
url,
|
|
203
|
-
otherParams = {},
|
|
204
|
-
mappingTextField = 'name',
|
|
205
|
-
pageSize = 50,
|
|
206
|
-
debounceWait = 300,
|
|
207
|
-
likeParamName = 'qp-skuName-like',
|
|
208
|
-
} = requestConfig || {};
|
|
209
|
-
|
|
210
|
-
const [options, setOptions] = useState<{ value: string }[]>([]);
|
|
211
|
-
const seqRef = useRef(0);
|
|
212
|
-
const mountedRef = useRef(true);
|
|
213
|
-
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
mountedRef.current = true;
|
|
216
|
-
return () => {
|
|
217
|
-
mountedRef.current = false;
|
|
218
|
-
seqRef.current += 1; // invalidate in-flight
|
|
219
|
-
};
|
|
220
|
-
}, []);
|
|
221
|
-
|
|
222
|
-
const fetchSuggest = (keyword: string) => {
|
|
223
|
-
const trimmed = (keyword || '').trim();
|
|
224
|
-
if (!url || !trimmed) {
|
|
225
|
-
setOptions([]);
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const seq = ++seqRef.current;
|
|
229
|
-
const queryParams = {
|
|
230
|
-
pageSize,
|
|
231
|
-
currentPage: 1,
|
|
232
|
-
...otherParams,
|
|
233
|
-
[likeParamName]: trimmed,
|
|
234
|
-
};
|
|
235
|
-
request
|
|
236
|
-
.get(`${url}${url.includes('?') ? '&' : '?'}${stringify(queryParams)}`)
|
|
237
|
-
.then((result: any) => {
|
|
238
|
-
if (!mountedRef.current || seq !== seqRef.current) return;
|
|
239
|
-
const body = result?.data;
|
|
240
|
-
if (judgeIsRequestError(body)) {
|
|
241
|
-
setOptions([]);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const res = body?.data;
|
|
245
|
-
const list = res?.list || res?.items || (Array.isArray(res) ? res : []);
|
|
246
|
-
setOptions(buildAutoCompleteOptions(list, mappingTextField));
|
|
247
|
-
})
|
|
248
|
-
.catch(() => {
|
|
249
|
-
if (!mountedRef.current || seq !== seqRef.current) return;
|
|
250
|
-
setOptions([]);
|
|
251
|
-
});
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const { run: debouncedFetch } = useDebounceFn(fetchSuggest, { wait: debounceWait });
|
|
255
|
-
|
|
256
|
-
const handleSearch = (text: string) => {
|
|
257
|
-
onChange?.(text);
|
|
258
|
-
debouncedFetch(text);
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const handleSelect = (text: string) => {
|
|
262
|
-
onChange?.(text);
|
|
263
|
-
// 不调用 onPressEnter / 不自动查表
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
return (
|
|
267
|
-
<AutoComplete
|
|
268
|
-
value={value}
|
|
269
|
-
options={options}
|
|
270
|
-
onSearch={handleSearch}
|
|
271
|
-
onSelect={handleSelect}
|
|
272
|
-
onChange={(next) => onChange?.(next ?? '')}
|
|
273
|
-
allowClear={allowClear}
|
|
274
|
-
disabled={disabled}
|
|
275
|
-
placeholder={placeholder}
|
|
276
|
-
style={{ width: '100%', ...style }}
|
|
277
|
-
onInputKeyDown={(e) => {
|
|
278
|
-
if (e.key === 'Enter' || (e as any).keyCode === 13) {
|
|
279
|
-
onPressEnter?.();
|
|
280
|
-
}
|
|
281
|
-
}}
|
|
282
|
-
/>
|
|
283
|
-
);
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
export default QueryAutoComplete;
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
注意:antd 4 的 `AutoComplete` 同时有 `onSearch` / `onChange`;以 Form.Item 注入的 `value`/`onChange` 为准。若联调发现双重触发,保留 `onChange` 更新值、`onSearch` 只负责 `debouncedFetch`(去掉 `handleSearch` 里的 `onChange`)。
|
|
290
|
-
|
|
291
|
-
- [ ] **Step 2: 本地类型/编译抽查**
|
|
292
|
-
|
|
293
|
-
Run: `npx tsc --noEmit -p tsconfig.json 2>&1 | head -40`(若仓无严格 tsc,可跳过,改在 dumi/`npm run build` 时验证)
|
|
294
|
-
Expected: 无与 `QueryAutoComplete` 相关的报错
|
|
295
|
-
|
|
296
|
-
- [ ] **Step 3: Commit**(仅当用户要求提交时)
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
git add src/components/Functional/QueryAutoComplete/index.tsx
|
|
300
|
-
git commit -m "$(cat <<'EOF'
|
|
301
|
-
feat: IKJX29 add QueryAutoComplete free-solo suggest input
|
|
302
|
-
|
|
303
|
-
EOF
|
|
304
|
-
)"
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
### Task 3: `AddSelect.formItem` 接入 `autoComplete`
|
|
310
|
-
|
|
311
|
-
**Files:**
|
|
312
|
-
- Modify: `src/components/Functional/AddSelect/index.tsx`
|
|
313
|
-
- import `QueryAutoComplete`(与 `QueryMutipleInput` 同级,从相对路径或 `@/components/Functional/QueryAutoComplete`)
|
|
314
|
-
- 在 `formItem` 的 `multipleQueryInput` 分支之后、默认 `Input` 之前插入 `autoComplete` 分支
|
|
315
|
-
|
|
316
|
-
**Interfaces:**
|
|
317
|
-
- Consumes: `QueryAutoComplete` from Task 2;`onSearchTable` 已有
|
|
318
|
-
|
|
319
|
-
- [ ] **Step 1: Add import**
|
|
320
|
-
|
|
321
|
-
在文件顶部 import 区增加(路径按仓内 alias 习惯;若 `QueryMutipleInput` 来自 `@/index`,可用相对路径避免循环依赖):
|
|
322
|
-
|
|
323
|
-
```ts
|
|
324
|
-
import QueryAutoComplete from '@/components/Functional/QueryAutoComplete';
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
若 `@/index` 循环依赖,改为:
|
|
328
|
-
|
|
329
|
-
```ts
|
|
330
|
-
import QueryAutoComplete from '../QueryAutoComplete';
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
- [ ] **Step 2: Add formItem branch**
|
|
334
|
-
|
|
335
|
-
插在 `multipleQueryInput` 分支之后:
|
|
336
|
-
|
|
337
|
-
```tsx
|
|
338
|
-
if (type === 'autoComplete' || field?.type === 'autoComplete') {
|
|
339
|
-
return (
|
|
340
|
-
<Form.Item name={name} label={label} key={name} {...restProps}>
|
|
341
|
-
<QueryAutoComplete
|
|
342
|
-
onPressEnter={onSearchTable}
|
|
343
|
-
{...(field?.props || {})}
|
|
344
|
-
/>
|
|
345
|
-
</Form.Item>
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
要求:`field.props` 必须含 `requestConfig`(由业务选择器配置)。
|
|
351
|
-
|
|
352
|
-
- [ ] **Step 3: Smoke-check 语法**
|
|
353
|
-
|
|
354
|
-
确认默认 `Input` 分支仍保留;其它 type 分支未改。
|
|
355
|
-
|
|
356
|
-
- [ ] **Step 4: Commit**(仅当用户要求提交时)
|
|
357
|
-
|
|
358
|
-
```bash
|
|
359
|
-
git add src/components/Functional/AddSelect/index.tsx
|
|
360
|
-
git commit -m "$(cat <<'EOF'
|
|
361
|
-
feat: IKJX29 support autoComplete search field in AddSelect
|
|
362
|
-
|
|
363
|
-
EOF
|
|
364
|
-
)"
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
---
|
|
368
|
-
|
|
369
|
-
### Task 4: `AddSkuSelect` 接线 `qp-skuName-like`
|
|
370
|
-
|
|
371
|
-
**Files:**
|
|
372
|
-
- Modify: `src/components/Business/AddSelectBusiness/index.tsx`(`AddSkuSelect` 内 `initialTableSearchForm`)
|
|
373
|
-
|
|
374
|
-
**Interfaces:**
|
|
375
|
-
- Consumes: `autoComplete` field type from Task 3
|
|
376
|
-
- `otherParams` 必须与该组件列表 `requestConfig.otherParams` 默认条件一致(含 `addOtherParams`)
|
|
377
|
-
|
|
378
|
-
- [ ] **Step 1: Replace SKU名称 field config**
|
|
379
|
-
|
|
380
|
-
将:
|
|
381
|
-
|
|
382
|
-
```ts
|
|
383
|
-
{ name: 'qp-skuName-like', label: 'SKU名称' },
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
替换为:
|
|
387
|
-
|
|
388
|
-
```ts
|
|
389
|
-
{
|
|
390
|
-
name: 'qp-skuName-like',
|
|
391
|
-
label: 'SKU名称',
|
|
392
|
-
type: 'autoComplete',
|
|
393
|
-
field: {
|
|
394
|
-
type: 'autoComplete',
|
|
395
|
-
props: {
|
|
396
|
-
requestConfig: {
|
|
397
|
-
url: '/items/sku/pager/v2',
|
|
398
|
-
otherParams: {
|
|
399
|
-
'qp-combination-eq': false,
|
|
400
|
-
'qp-approveStatus-eq': 1,
|
|
401
|
-
'qp-status-eq': 1,
|
|
402
|
-
...(parProps?.requestConfig?.addOtherParams || {}),
|
|
403
|
-
},
|
|
404
|
-
mappingTextField: 'name',
|
|
405
|
-
pageSize: 50,
|
|
406
|
-
debounceWait: 300,
|
|
407
|
-
likeParamName: 'qp-skuName-like',
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
注意:该对象在 `AddSkuSelect` 函数体内,已能访问 `parProps`。
|
|
415
|
-
|
|
416
|
-
- [ ] **Step 2: 对照列表 requestConfig**
|
|
417
|
-
|
|
418
|
-
打开同文件 `props.requestConfig.otherParams`,确认联想 `otherParams` 与列表默认条件一致(`qp-combination-eq` / `qp-approveStatus-eq` / `qp-status-eq` / `addOtherParams`)。若列表还有调用方常传的覆盖项,只对齐 `addOtherParams` 即可,不必复制整个 `parProps.requestConfig`(避免把 `filter`、`mappingValueField` 误塞进联想)。
|
|
419
|
-
|
|
420
|
-
- [ ] **Step 3: Commit**(仅当用户要求提交时)
|
|
421
|
-
|
|
422
|
-
```bash
|
|
423
|
-
git add src/components/Business/AddSelectBusiness/index.tsx
|
|
424
|
-
git commit -m "$(cat <<'EOF'
|
|
425
|
-
feat: IKJX29 wire AddSkuSelect SKU name to free-solo AutoComplete
|
|
426
|
-
|
|
427
|
-
EOF
|
|
428
|
-
)"
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
### Task 5: 手工验收(联调 / UAT 环境)
|
|
434
|
-
|
|
435
|
-
**Files:** 无代码改动
|
|
436
|
-
|
|
437
|
-
- [ ] **Step 1: 本地 link 或发 alpha 到业务仓**
|
|
438
|
-
|
|
439
|
-
任选其一:
|
|
440
|
-
- `business-component` build 后,在 `retail-front` 用 `yarn link` / 改 `package.json` 指本地 dist
|
|
441
|
-
- 或发 `4.2.x-alpha.x-IKJX29` 后 bump `retail-front` 依赖
|
|
442
|
-
|
|
443
|
-
- [ ] **Step 2: 打开零售售后 / 零售订单「选择商品」弹窗**
|
|
444
|
-
|
|
445
|
-
按 spec §7 验收:
|
|
446
|
-
|
|
447
|
-
1. 「SKU名称」可自由输入
|
|
448
|
-
2. 输入出现联想;点一项只回填,列表不变;点「查询」才刷新
|
|
449
|
-
3. 无结果仍可查询
|
|
450
|
-
4. 回车触发查询
|
|
451
|
-
5. 其它筛选项与确定 / 保存并继续不变
|
|
452
|
-
|
|
453
|
-
抓包确认联想请求形如:
|
|
454
|
-
|
|
455
|
-
```text
|
|
456
|
-
GET /items/sku/pager/v2?pageSize=50¤tPage=1&qp-combination-eq=false&qp-approveStatus-eq=1&qp-status-eq=1&qp-skuName-like=<关键字>
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
- [ ] **Step 3: 在工单 IKJX29 评论验收结果**(可选)
|
|
460
|
-
|
|
461
|
-
可贴设计口径:「SKU名称」改为自由录入型 AutoComplete(Combobox)…
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
## Spec coverage (self-review)
|
|
466
|
-
|
|
467
|
-
| Spec 项 | Task |
|
|
468
|
-
|---------|------|
|
|
469
|
-
| `autoComplete` 字段类型 | Task 3 |
|
|
470
|
-
| `QueryAutoComplete` + debounce 300ms | Task 2 |
|
|
471
|
-
| `/items/sku/pager/v2` + 默认条件 + `qp-skuName-like` | Task 2 + 4 |
|
|
472
|
-
| name 去重 | Task 1 |
|
|
473
|
-
| 选中只回填 | Task 2 `handleSelect` |
|
|
474
|
-
| 回车查表 | Task 3 `onPressEnter={onSearchTable}` |
|
|
475
|
-
| 失败安静 | Task 2 catch / judgeIsRequestError → `setOptions([])` |
|
|
476
|
-
| 卸载作废请求 | Task 2 `mountedRef` + `seqRef` |
|
|
477
|
-
| `AddSkuSelect` 接线 | Task 4 |
|
|
478
|
-
| 验收 | Task 5 |
|
|
479
|
-
| 不做 SPU→颜色尺码 | 无任务(显式不做) |
|
|
480
|
-
|
|
481
|
-
## Placeholder scan
|
|
482
|
-
|
|
483
|
-
无 TBD /「similar to Task N」占位。
|
|
484
|
-
|
|
485
|
-
## Type consistency
|
|
486
|
-
|
|
487
|
-
- `buildAutoCompleteOptions` → `{ value: string }[]` 与 antd 4 `options` 一致
|
|
488
|
-
- `likeParamName` / `mappingTextField` / `pageSize` / `debounceWait` 在 Task 2 与 Task 4 命名一致
|
|
489
|
-
- 表单字段名固定 `qp-skuName-like`
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# IKJX29:AddSkuSelect「SKU名称」自由录入型 AutoComplete
|
|
2
|
-
|
|
3
|
-
- **工单**:[IKJX29](https://e.gitee.com/bitsun_1/dashboard?issue=IKJX29)(铁血武汉 UAT / TX10040018)
|
|
4
|
-
- **迭代**:标品迭代 24
|
|
5
|
-
- **日期**:2026-10-12
|
|
6
|
-
- **仓**:`business-component`(`@bit-sun/business-component`)
|
|
7
|
-
|
|
8
|
-
## 1. 背景与目标
|
|
9
|
-
|
|
10
|
-
选择商品弹窗(`AddSkuSelect`)搜索条件里「SKU名称」当前是普通 `Input`,无法边输边联想。UAT 反馈难以通过产品简称快速定位。
|
|
11
|
-
|
|
12
|
-
**目标**:将「SKU名称」改为自由录入型 AutoComplete(Combobox):输入关键字远程模糊联想;可选提示项;无匹配时仍可原样输入并参与查询。
|
|
13
|
-
|
|
14
|
-
**专业口径(可贴工单)**:「SKU名称」改为自由录入型 AutoComplete(Combobox):远程模糊联想,可选可输,选中仅回填不自动查表。
|
|
15
|
-
|
|
16
|
-
## 2. 范围
|
|
17
|
-
|
|
18
|
-
### 做
|
|
19
|
-
|
|
20
|
-
- 在 `AddSelect` 查询表单中新增可复用字段类型 `autoComplete`(antd `AutoComplete`)
|
|
21
|
-
- `AddSkuSelect` 的 `qp-skuName-like` 改用该类型
|
|
22
|
-
- 联想请求:`GET /items/sku/pager/v2`,参数含现有默认条件 + `qp-skuName-like=<当前输入>`
|
|
23
|
-
|
|
24
|
-
### 不做
|
|
25
|
-
|
|
26
|
-
- 不改「先选 SPU 再选颜色尺码」两步流程(工单原文另一方向,另开需求)
|
|
27
|
-
- 不改 SKU 编码 / 款号(SPU) / 龙牙等其它筛选项
|
|
28
|
-
- 不强制必须从下拉选中
|
|
29
|
-
- 不改后端契约;表单值仍为名称字符串
|
|
30
|
-
|
|
31
|
-
## 3. 方案选型
|
|
32
|
-
|
|
33
|
-
| 方案 | 结论 |
|
|
34
|
-
|------|------|
|
|
35
|
-
| 1. `AddSelect` 新增 `autoComplete` + `AddSkuSelect` 接线 | **采用** |
|
|
36
|
-
| 2. 仅 `custom_filter_form_item` 塞自定义组件 | 不采用(难复用) |
|
|
37
|
-
| 3. 硬套 `BusinessSearchSelect` / 强制 Select | 不采用(与自由录入冲突) |
|
|
38
|
-
|
|
39
|
-
## 4. 组件与数据流
|
|
40
|
-
|
|
41
|
-
### 落点
|
|
42
|
-
|
|
43
|
-
| 层 | 改动 |
|
|
44
|
-
|----|------|
|
|
45
|
-
| `AddSelect` `formItem` | 新增 `type === 'autoComplete'` 分支 |
|
|
46
|
-
| 建议抽 `QueryAutoComplete` | debounce / options / 自由录入,避免逻辑堆在 `formItem` |
|
|
47
|
-
| `AddSkuSelect` | `qp-skuName-like` 配置为 `autoComplete` 并挂请求 |
|
|
48
|
-
|
|
49
|
-
### 数据流
|
|
50
|
-
|
|
51
|
-
```text
|
|
52
|
-
用户输入关键字
|
|
53
|
-
→ debounce(300ms)
|
|
54
|
-
→ GET /items/sku/pager/v2
|
|
55
|
-
pageSize:50
|
|
56
|
-
currentPage:1
|
|
57
|
-
+ AddSkuSelect 已有默认条件(qp-combination-eq / qp-approveStatus-eq / qp-status-eq 等)
|
|
58
|
-
+ qp-skuName-like=<当前输入>
|
|
59
|
-
→ 用返回 list 的 name 生成 options(前端按 name 去重)
|
|
60
|
-
→ 点选项 → form.setFieldsValue({ 'qp-skuName-like': name })
|
|
61
|
-
→ 点「查询」或回车 → 现有 onSearchTable(不自动因选中而查表)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### 值语义
|
|
65
|
-
|
|
66
|
-
- 表单值始终是 **字符串**(名称关键字),不是 skuId / `{ label, value }`
|
|
67
|
-
- 与现有 `qp-skuName-like` 提交一致
|
|
68
|
-
|
|
69
|
-
## 5. 交互与异常
|
|
70
|
-
|
|
71
|
-
| 场景 | 行为 |
|
|
72
|
-
|------|------|
|
|
73
|
-
| 空 / 清空 | 不发请求或清空 options;`allowClear` |
|
|
74
|
-
| 输入中 | debounce 后请求;输入框不锁死 |
|
|
75
|
-
| 有结果 | 下拉展示去重后的名称;点一项只回填 |
|
|
76
|
-
| 无结果 | 下拉空(无「强制选中」阻断);**不阻止**保留输入后点查询 |
|
|
77
|
-
| 回车 | 与现 Input 一致:触发 `onSearchTable` |
|
|
78
|
-
| 最短字数 | ≥1 才请求(空不发) |
|
|
79
|
-
| 请求失败 | 保留已输入文案;options 置空;**不**弹错误打断录入(安静失败) |
|
|
80
|
-
| 快速连敲 | 只采纳最后一次 debounce 对应响应 |
|
|
81
|
-
| 弹窗关闭 | 清理联想请求/状态,避免泄漏到下次打开 |
|
|
82
|
-
|
|
83
|
-
## 6. 影响面
|
|
84
|
-
|
|
85
|
-
| 项 | 说明 |
|
|
86
|
-
|----|------|
|
|
87
|
-
| 改动仓 | `business-component` |
|
|
88
|
-
| 消费方 | 升依赖后,所有 `AddSkuSelect` 自动获得新交互(零售售后选择商品、零售订单加商品、库存其它出入库等) |
|
|
89
|
-
| 后端 | 无改动 |
|
|
90
|
-
| 发版 | 发布 `@bit-sun/business-component` 后,业务仓 bump 版本验证 |
|
|
91
|
-
|
|
92
|
-
## 7. 验收
|
|
93
|
-
|
|
94
|
-
1. 「SKU名称」可自由输入任意关键字
|
|
95
|
-
2. 输入后出现联想;点一项只回填,列表不变;再点「查询」才刷新
|
|
96
|
-
3. 无联想结果时仍可保留输入并查询
|
|
97
|
-
4. 回车仍能触发查询
|
|
98
|
-
5. 其它筛选项与确定 / 保存并继续行为不变
|
|
99
|
-
|
|
100
|
-
## 8. 配置示意(实现时以代码为准)
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
{
|
|
104
|
-
name: 'qp-skuName-like',
|
|
105
|
-
label: 'SKU名称',
|
|
106
|
-
type: 'autoComplete',
|
|
107
|
-
field: {
|
|
108
|
-
type: 'autoComplete',
|
|
109
|
-
props: {
|
|
110
|
-
// url / otherParams / mappingTextField / debounce / pageSize 等
|
|
111
|
-
// 与 AddSkuSelect.requestConfig 默认条件对齐
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
}
|
|
115
|
-
```
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { buildAutoCompleteOptions } from './buildOptions';
|
|
2
|
-
|
|
3
|
-
describe('buildAutoCompleteOptions', () => {
|
|
4
|
-
it('maps name field and dedupes by text', () => {
|
|
5
|
-
const list = [
|
|
6
|
-
{ name: '战术臂章-鸡', skuCode: 'A' },
|
|
7
|
-
{ name: '战术臂章-鸡', skuCode: 'B' },
|
|
8
|
-
{ name: '战术臂章-狗', skuCode: 'C' },
|
|
9
|
-
{ name: '', skuCode: 'D' },
|
|
10
|
-
{ name: null, skuCode: 'E' },
|
|
11
|
-
];
|
|
12
|
-
expect(buildAutoCompleteOptions(list, 'name')).toEqual([
|
|
13
|
-
{ value: '战术臂章-鸡' },
|
|
14
|
-
{ value: '战术臂章-狗' },
|
|
15
|
-
]);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('returns empty for non-array', () => {
|
|
19
|
-
expect(buildAutoCompleteOptions(null as any)).toEqual([]);
|
|
20
|
-
expect(buildAutoCompleteOptions(undefined as any)).toEqual([]);
|
|
21
|
-
});
|
|
22
|
-
});
|