@xfe-repo/web-components 1.6.2 → 1.7.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/package.json +6 -5
- package/skills/xfe-web-components/GUIDE.md +78 -0
- package/skills/xfe-web-components/SKILL.md +78 -0
- package/skills/xfe-web-components/TEMPLATE.md +170 -0
- package/skills/xfe-web-components/references/ApiService.md +136 -0
- package/skills/xfe-web-components/references/CInput.md +196 -0
- package/skills/xfe-web-components/references/CTable.md +274 -0
- package/skills/xfe-web-components/references/CVideo.md +94 -0
- package/skills/xfe-web-components/references/Clock.md +53 -0
- package/skills/xfe-web-components/references/ConfigProvider.md +198 -0
- package/skills/xfe-web-components/references/Countdown.md +109 -0
- package/skills/xfe-web-components/references/Counter.md +75 -0
- package/skills/xfe-web-components/references/Currency.md +112 -0
- package/skills/xfe-web-components/references/EffectAddressCascade.md +110 -0
- package/skills/xfe-web-components/references/EffectBrandSelect.md +231 -0
- package/skills/xfe-web-components/references/EffectBrandTransfer.md +143 -0
- package/skills/xfe-web-components/references/EffectCategoryCascade.md +227 -0
- package/skills/xfe-web-components/references/EffectFileUpload.md +348 -0
- package/skills/xfe-web-components/references/EffectLabelSelect.md +222 -0
- package/skills/xfe-web-components/references/EffectMerchantSelect.md +183 -0
- package/skills/xfe-web-components/references/EffectReservoirSelect.md +178 -0
- package/skills/xfe-web-components/references/EffectScopeSelect.md +220 -0
- package/skills/xfe-web-components/references/EffectSeriesSelect.md +204 -0
- package/skills/xfe-web-components/references/EffectSeriesSelectV2.md +154 -0
- package/skills/xfe-web-components/references/EffectSkuRecognize.md +149 -0
- package/skills/xfe-web-components/references/EffectSkuSelect.md +251 -0
- package/skills/xfe-web-components/references/EffectSkuTable.md +184 -0
- package/skills/xfe-web-components/references/EffectSpuSelect.md +189 -0
- package/skills/xfe-web-components/references/EffectStaffSelect.md +150 -0
- package/skills/xfe-web-components/references/EffectWarehouseSelect.md +183 -0
- package/skills/xfe-web-components/references/EffectWithFilePanel.md +205 -0
- package/skills/xfe-web-components/references/FileUpload.md +126 -0
- package/skills/xfe-web-components/references/Iconfont.md +31 -0
- package/skills/xfe-web-components/references/Loading.md +68 -0
- package/skills/xfe-web-components/references/MultiWindow.md +145 -0
- package/skills/xfe-web-components/references/OSSImage.md +230 -0
- package/skills/xfe-web-components/references/PrivacyField.md +90 -0
- package/skills/xfe-web-components/references/QRCode.md +148 -0
- package/skills/xfe-web-components/references/RichTextEditor.md +119 -0
- package/skills/xfe-web-components/references/SearchForm.md +270 -0
- package/skills/xfe-web-components/references/SearchList.md +128 -0
- package/skills/xfe-web-components/references/WithModal.md +328 -0
- package/skills/xfe-web-components/references/WithPanel.md +307 -0
- package/skills/xfe-web-components/references/commonFn.md +254 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# EffectFileUpload
|
|
2
|
+
|
|
3
|
+
> 文件上传组件,支持通过 Ref 手动触发上传、批量上传管理(Group 子组件),数据通过 `apiService.uploadUploadFile` 上传。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectFileUpload, transformUploadFile2SimpleFile, transformSimpleFile2UploadFile } from '@xfe-repo/web-components'
|
|
9
|
+
import type { SimpleFileData, UploadResult, EffectFileUploadRef } from '@xfe-repo/web-components'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 基本用法
|
|
13
|
+
|
|
14
|
+
在 Form.Item 中使用,value/onChange 由 Form 自动注入:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { useRef } from 'react'
|
|
18
|
+
import { Form, Button, message } from 'antd'
|
|
19
|
+
import { EffectFileUpload } from '@xfe-repo/web-components'
|
|
20
|
+
import type { EffectFileUploadRef } from '@xfe-repo/web-components'
|
|
21
|
+
|
|
22
|
+
function ProductForm() {
|
|
23
|
+
const [form] = Form.useForm()
|
|
24
|
+
const uploadRef = useRef<EffectFileUploadRef>(null)
|
|
25
|
+
|
|
26
|
+
const handleSubmit = async () => {
|
|
27
|
+
// 1. 先触发文件上传
|
|
28
|
+
const uploadResult = await uploadRef.current!.handleUpload()
|
|
29
|
+
if (!uploadResult.result) {
|
|
30
|
+
message.error(uploadResult.msg)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
// 2. 再校验并提交表单
|
|
34
|
+
const values = await form.validateFields()
|
|
35
|
+
await saveProduct(values)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Form form={form}>
|
|
40
|
+
<Form.Item name="images" label="商品图片" rules={[{ required: true }]}>
|
|
41
|
+
<EffectFileUpload ref={uploadRef} max={5} accept="image/*" />
|
|
42
|
+
</Form.Item>
|
|
43
|
+
<Button type="primary" onClick={handleSubmit}>
|
|
44
|
+
提交
|
|
45
|
+
</Button>
|
|
46
|
+
</Form>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 进阶用法
|
|
52
|
+
|
|
53
|
+
### 独立受控模式
|
|
54
|
+
|
|
55
|
+
不依赖 Form,通过 `useState` 手动管理文件列表:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { useRef } from 'react'
|
|
59
|
+
import { EffectFileUpload } from '@xfe-repo/web-components'
|
|
60
|
+
import type { EffectFileUploadRef, SimpleFileData } from '@xfe-repo/web-components'
|
|
61
|
+
|
|
62
|
+
function MyUploader() {
|
|
63
|
+
const uploadRef = useRef<EffectFileUploadRef>(null)
|
|
64
|
+
const [files, setFiles] = useState<SimpleFileData[]>([])
|
|
65
|
+
|
|
66
|
+
const handleSave = async () => {
|
|
67
|
+
const result = await uploadRef.current!.handleUpload()
|
|
68
|
+
if (result.result) {
|
|
69
|
+
console.log('上传成功:', result.data)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div>
|
|
75
|
+
<EffectFileUpload ref={uploadRef} max={5} accept="image/*" value={files} onChange={setFiles} />
|
|
76
|
+
<button onClick={handleSave}>上传</button>
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 选择后自动上传
|
|
83
|
+
|
|
84
|
+
设置 `uploadOnAdd` 后,文件添加时自动触发上传,无需手动调用 `handleUpload`。
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<EffectFileUpload
|
|
88
|
+
uploadOnAdd
|
|
89
|
+
onChange={(files) => {
|
|
90
|
+
console.log('已上传文件:', files)
|
|
91
|
+
}}
|
|
92
|
+
onUploadDone={(result) => {
|
|
93
|
+
if (result.result) {
|
|
94
|
+
message.success('上传成功')
|
|
95
|
+
}
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 附加表单数据
|
|
101
|
+
|
|
102
|
+
上传时携带额外字段:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<EffectFileUpload ref={uploadRef} extFormData={{ type: 'avatar', userId: '123' }} />
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 批量上传管理(Group)
|
|
109
|
+
|
|
110
|
+
使用 `EffectFileUpload.Group` 管理多个上传实例,统一触发上传:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { useState } from 'react'
|
|
114
|
+
import { EffectFileUpload } from '@xfe-repo/web-components'
|
|
115
|
+
import type { UploadResult } from '@xfe-repo/web-components'
|
|
116
|
+
|
|
117
|
+
function MultiUploader() {
|
|
118
|
+
const [needSave, setNeedSave] = useState(false)
|
|
119
|
+
|
|
120
|
+
const handleUploadDone = (results: Record<string, UploadResult>) => {
|
|
121
|
+
// results 的 key 是每个 EffectFileUpload 的 name
|
|
122
|
+
console.log('正面照:', results['front'])
|
|
123
|
+
console.log('背面照:', results['back'])
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<EffectFileUpload.Group needSave={needSave} onUploadDone={handleUploadDone}>
|
|
128
|
+
<EffectFileUpload name="front" accept="image/*" />
|
|
129
|
+
<EffectFileUpload name="back" accept="image/*" />
|
|
130
|
+
<button onClick={() => setNeedSave(true)}>全部上传</button>
|
|
131
|
+
</EffectFileUpload.Group>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 多 Ref 并行上传
|
|
137
|
+
|
|
138
|
+
多个上传字段时,使用 Promise.all 并行提交:
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
const imgRef = useRef<EffectFileUploadRef>(null)
|
|
142
|
+
const docRef = useRef<EffectFileUploadRef>(null)
|
|
143
|
+
|
|
144
|
+
const handleSubmit = async () => {
|
|
145
|
+
const values = await form.validateFields()
|
|
146
|
+
const [imgResult, docResult] = await Promise.all([
|
|
147
|
+
imgRef.current!.handleUpload(),
|
|
148
|
+
docRef.current!.handleUpload(),
|
|
149
|
+
])
|
|
150
|
+
await submitOrder({ ...values, images: imgResult.data, documents: docResult.data })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
<Form.Item label="商品图片">
|
|
154
|
+
<EffectFileUpload ref={imgRef} accept="image/*" />
|
|
155
|
+
</Form.Item>
|
|
156
|
+
<Form.Item label="相关文档">
|
|
157
|
+
<EffectFileUpload ref={docRef} accept=".pdf,.doc,.docx" />
|
|
158
|
+
</Form.Item>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 文件校验拦截
|
|
162
|
+
|
|
163
|
+
通过 onFileChange 返回 false 拦截不符合条件的文件:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<EffectFileUpload
|
|
167
|
+
onFileChange={(file) => {
|
|
168
|
+
if (file.size > 10 * 1024 * 1024) {
|
|
169
|
+
message.error('文件大小不能超过 10MB')
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
return true
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 使用提示
|
|
178
|
+
|
|
179
|
+
> 💡 `Group`、`extFormData`、`needSave` 在实际业务中使用率极低,大部分场景无需关注。
|
|
180
|
+
|
|
181
|
+
## Props
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
type EffectFileUploadProps = {
|
|
185
|
+
max?: number
|
|
186
|
+
accept?: string
|
|
187
|
+
remote?: boolean
|
|
188
|
+
deleteConfirm?: boolean
|
|
189
|
+
disabled?: boolean
|
|
190
|
+
needSave?: boolean
|
|
191
|
+
uploadOnAdd?: boolean
|
|
192
|
+
value?: SimpleFileData[]
|
|
193
|
+
defaultValue?: SimpleFileData[]
|
|
194
|
+
onChange?: (files: SimpleFileData[]) => void
|
|
195
|
+
files?: UploadFile[]
|
|
196
|
+
onFileChange?: (files: UploadFile[]) => void
|
|
197
|
+
onUpload?: () => void
|
|
198
|
+
onUploadDone?: (uploadResult: UploadResult) => void
|
|
199
|
+
extFormData?: Object
|
|
200
|
+
className?: string
|
|
201
|
+
name?: string
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
206
|
+
| ------------- | ----------------------------------- | ---- | ------------- | ------------------------------------------------------ |
|
|
207
|
+
| value | `SimpleFileData[]` | 否 | - | 受控值,已上传文件列表 |
|
|
208
|
+
| defaultValue | `SimpleFileData[]` | 否 | - | 默认已上传文件列表 |
|
|
209
|
+
| onChange | `(files: SimpleFileData[]) => void` | 否 | - | 上传完成后回调,返回所有文件的简化数据 |
|
|
210
|
+
| files | `UploadFile[]` | 否 | - | 直接控制 antd UploadFile 列表(底层控制) |
|
|
211
|
+
| onFileChange | `(files: UploadFile[]) => void` | 否 | - | 文件列表变化回调(添加/删除),返回 `false` 可阻止变更 |
|
|
212
|
+
| max | `number` | 否 | - | 最大上传文件数 |
|
|
213
|
+
| accept | `string` | 否 | - | 接受的文件类型,如 `image/*`、`.pdf,.doc` |
|
|
214
|
+
| uploadOnAdd | `boolean` | 否 | `false` | 是否在添加文件时自动上传 |
|
|
215
|
+
| needSave | `boolean` | 否 | - | 设为 `true` 时触发上传(适用于外部控制) |
|
|
216
|
+
| disabled | `boolean` | 否 | - | 禁用状态(上传中也会自动禁用) |
|
|
217
|
+
| remote | `boolean` | 否 | - | 是否为远程文件模式 |
|
|
218
|
+
| deleteConfirm | `boolean` | 否 | - | 删除文件前是否弹窗确认 |
|
|
219
|
+
| extFormData | `Object` | 否 | - | 上传时附加的表单字段 |
|
|
220
|
+
| onUpload | `() => void` | 否 | - | 上传开始时回调 |
|
|
221
|
+
| onUploadDone | `(result: UploadResult) => void` | 否 | - | 上传完成后回调 |
|
|
222
|
+
| name | `string` | 否 | 自动生成 UUID | 组件实例标识,在 Group 中用于区分不同上传实例 |
|
|
223
|
+
| className | `string` | 否 | - | 自定义样式类名 |
|
|
224
|
+
|
|
225
|
+
## 导出类型
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// 简化文件数据
|
|
229
|
+
export type SimpleFileData = {
|
|
230
|
+
id?: number
|
|
231
|
+
name?: string
|
|
232
|
+
fileName?: string
|
|
233
|
+
url: string
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 上传结果
|
|
237
|
+
export type UploadResult = {
|
|
238
|
+
result: boolean // 是否全部成功
|
|
239
|
+
msg: string // 结果信息
|
|
240
|
+
data: SimpleFileData[] // 上传后的文件列表
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Ref 暴露的方法
|
|
244
|
+
export type EffectFileUploadRef = {
|
|
245
|
+
handleUpload: () => Promise<UploadResult> // 手动触发上传
|
|
246
|
+
uploadTasks: Promise<UploadResult>[] // 当前进行中的上传任务
|
|
247
|
+
value: SimpleFileData[] // 当前文件列表
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 工具函数
|
|
252
|
+
|
|
253
|
+
### transformUploadFile2SimpleFile
|
|
254
|
+
|
|
255
|
+
将 antd `UploadFile[]` 转换为 `SimpleFileData[]`,用于自定义绑定场景。
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
export const transformUploadFile2SimpleFile: (uploadFiles?: UploadFile[]) => SimpleFileData[]
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### transformSimpleFile2UploadFile
|
|
262
|
+
|
|
263
|
+
将 `SimpleFileData[]` 转换为 antd `UploadFile[]`,用于数据回显。
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
export const transformSimpleFile2UploadFile: (files?: SimpleFileData[]) => UploadFile[]
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 使用示例
|
|
270
|
+
|
|
271
|
+
配合 `trigger` 和 `valuePropName` 自定义 Form.Item 绑定:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
import { EffectFileUpload, transformUploadFile2SimpleFile } from '@xfe-repo/web-components'
|
|
275
|
+
;<Form.Item
|
|
276
|
+
name="attachments"
|
|
277
|
+
trigger="onFileChange"
|
|
278
|
+
valuePropName="files"
|
|
279
|
+
getValueFromEvent={(files) => transformUploadFile2SimpleFile(files)}
|
|
280
|
+
>
|
|
281
|
+
<EffectFileUpload max={3} />
|
|
282
|
+
</Form.Item>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## API 数据源
|
|
286
|
+
|
|
287
|
+
- **内部接口**:`apiService.uploadUploadFile`
|
|
288
|
+
- **请求格式**:`FormData`(包含 `type: 'product'`、`file`、以及 `extFormData` 中的字段)
|
|
289
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ uploadUploadFile: customFn }}>` 替换默认实现
|
|
290
|
+
|
|
291
|
+
## 子组件
|
|
292
|
+
|
|
293
|
+
### EffectFileUpload.Group
|
|
294
|
+
|
|
295
|
+
> 批量上传管理器,包裹多个 EffectFileUpload 实例,统一控制上传时机。
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
<EffectFileUpload.Group needSave={needSave} onUploadDone={handleDone}>
|
|
299
|
+
<EffectFileUpload name="photo1" />
|
|
300
|
+
<EffectFileUpload name="photo2" />
|
|
301
|
+
</EffectFileUpload.Group>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
305
|
+
| ------------ | ------------------------------------------------- | ---- | ------ | --------------------------------------- |
|
|
306
|
+
| needSave | `boolean` | 是 | - | 设为 `true` 时触发所有子实例上传 |
|
|
307
|
+
| onUploadDone | `(results: Record<string, UploadResult>) => void` | 否 | - | 全部上传完成后回调,key 为各实例的 name |
|
|
308
|
+
| children | `ReactNode` | 是 | - | 子组件(EffectFileUpload 实例) |
|
|
309
|
+
|
|
310
|
+
## 常见陷阱
|
|
311
|
+
|
|
312
|
+
- ❌ 选择文件后直接读取 `onChange` 结果,以为文件已上传:
|
|
313
|
+
```tsx
|
|
314
|
+
// onChange 在上传完成后才触发,选择文件阶段不会触发
|
|
315
|
+
<EffectFileUpload onChange={(files) => saveToDb(files)} />
|
|
316
|
+
// 如果没有调用 handleUpload 或设置 uploadOnAdd,文件永远不会上传
|
|
317
|
+
```
|
|
318
|
+
- ✅ 确保触发上传后再处理结果:
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
;<EffectFileUpload ref={uploadRef} uploadOnAdd onChange={(files) => saveToDb(files)} />
|
|
322
|
+
// 或者手动调用
|
|
323
|
+
const result = await uploadRef.current!.handleUpload()
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
- ❌ 在 Group 中不设置 `name`,导致多个实例无法区分:
|
|
327
|
+
```tsx
|
|
328
|
+
<EffectFileUpload.Group needSave={save} onUploadDone={(results) => {
|
|
329
|
+
// results 的 key 是自动生成的 UUID,无法识别
|
|
330
|
+
}}>
|
|
331
|
+
```
|
|
332
|
+
- ✅ 为每个实例指定有意义的 `name`:
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
<EffectFileUpload name="front" />
|
|
336
|
+
<EffectFileUpload name="back" />
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
- ❌ 上传过程中重复调用 `handleUpload` —— 组件内部会等待之前的上传任务完成后再执行新任务
|
|
340
|
+
- ✅ 等待上传完成或使用 `uploadTasks` 检查是否有进行中的任务
|
|
341
|
+
|
|
342
|
+
## 相关组件
|
|
343
|
+
|
|
344
|
+
| 场景 | 组件 | 说明 |
|
|
345
|
+
| -------- | ---------------- | ----------------------------------------- |
|
|
346
|
+
| 底层渲染 | `FileUpload` | EffectFileUpload 内部使用的纯 UI 上传组件 |
|
|
347
|
+
| API 配置 | `ConfigProvider` | 提供 apiService.uploadUploadFile 接口实现 |
|
|
348
|
+
| 弹窗上传 | `WithModal` | 常在弹窗内使用文件上传 |
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# EffectLabelSelect
|
|
2
|
+
|
|
3
|
+
> 标签维度双选择器,左侧选择搜索维度(如商户号/商户名),右侧根据选中维度远程搜索并选择具体值。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectLabelSelect } from '@xfe-repo/web-components'
|
|
9
|
+
import type { SelectOptionItem, LabelDic } from '@xfe-repo/web-components'
|
|
10
|
+
// 工具函数
|
|
11
|
+
import { getStrValue, getSelectValue, getInitialValue, getFirstValue, getSecondValue } from '@xfe-repo/web-components'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 基本用法
|
|
15
|
+
|
|
16
|
+
### 标准三件套模式
|
|
17
|
+
|
|
18
|
+
业务中最常见的使用方式 — 同时使用 `customLabelInValue` + `optionsDisplayType` + `labelDic`:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<Form.Item name="merchantStr" noStyle>
|
|
22
|
+
<EffectLabelSelect customLabelInValue optionsDisplayType="labelAndValue" labelDic={{ phone: '手机号', name: '商户名称' }} />
|
|
23
|
+
</Form.Item>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
> 💡 三件套的含义:
|
|
27
|
+
>
|
|
28
|
+
> - `customLabelInValue`:value 格式为分号分隔字符串(`displayType;value;label;labelDicKey`)
|
|
29
|
+
> - `optionsDisplayType`:下拉选项展示格式(`labelAndValue` 显示为 `value - label`)
|
|
30
|
+
> - `labelDic`:自定义 label 字典,根据 `labelDicKey` 动态切换显示文案
|
|
31
|
+
|
|
32
|
+
### 独立受控模式
|
|
33
|
+
|
|
34
|
+
使用默认的商户号/商户名维度搜索商户列表:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { EffectLabelSelect } from '@xfe-repo/web-components'
|
|
38
|
+
|
|
39
|
+
function MyForm() {
|
|
40
|
+
const [value, setValue] = useState<string>()
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<EffectLabelSelect
|
|
44
|
+
value={value}
|
|
45
|
+
onChange={(val, option) => {
|
|
46
|
+
setValue(val)
|
|
47
|
+
console.log('选中项:', option) // { label, value, customFields }
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 进阶用法
|
|
55
|
+
|
|
56
|
+
### 自定义搜索维度
|
|
57
|
+
|
|
58
|
+
通过 `labelDic` 自定义左侧维度选项:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const customLabelDic: LabelDic = {
|
|
62
|
+
phone: '手机号',
|
|
63
|
+
email: '邮箱',
|
|
64
|
+
name: '姓名',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
<EffectLabelSelect
|
|
68
|
+
labelDic={customLabelDic}
|
|
69
|
+
defaultLabel="phone"
|
|
70
|
+
onFetchData={mySearchApi}
|
|
71
|
+
onTransformData={({ phone, email, name, ...rest }) => ({
|
|
72
|
+
label: name,
|
|
73
|
+
value: phone,
|
|
74
|
+
customFields: rest,
|
|
75
|
+
})}
|
|
76
|
+
onChange={(value) => console.log(value)}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### customLabelInValue 模式
|
|
81
|
+
|
|
82
|
+
当 `customLabelInValue` 为 `true` 时,value 会编码为 `展示类型;ID;名称;labelDicKey` 格式的字符串:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<EffectLabelSelect
|
|
86
|
+
customLabelInValue
|
|
87
|
+
onChange={(value) => {
|
|
88
|
+
// value 格式如 "label;123;某某商户;businessNo"
|
|
89
|
+
console.log(value)
|
|
90
|
+
}}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 显示 ID + 名称
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<EffectLabelSelect optionsDisplayType="labelAndValue" />
|
|
98
|
+
// 选项显示为 "123456 - 某某商户"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 自定义 labelDic 文案
|
|
102
|
+
|
|
103
|
+
通过前缀区分不同场景的同类搜索:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// 买家商户搜索
|
|
107
|
+
<EffectLabelSelect
|
|
108
|
+
customLabelInValue
|
|
109
|
+
optionsDisplayType="labelAndValue"
|
|
110
|
+
labelDic={{ phone: '买家手机号', name: '买家商户名' }}
|
|
111
|
+
/>
|
|
112
|
+
|
|
113
|
+
// 卖家商户搜索
|
|
114
|
+
<EffectLabelSelect
|
|
115
|
+
customLabelInValue
|
|
116
|
+
optionsDisplayType="labelAndValue"
|
|
117
|
+
labelDic={{ phone: '卖家手机号', name: '卖家商户名' }}
|
|
118
|
+
/>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Props
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
interface EffectLabelSelectProps extends Omit<SelectProps, 'onChange'> {
|
|
125
|
+
onChange?: (value?: string, selectedOptionItem?: SelectOptionItem) => void
|
|
126
|
+
defaultLabel?: string
|
|
127
|
+
labelDic?: LabelDic
|
|
128
|
+
value?: string
|
|
129
|
+
customLabelInValue?: boolean
|
|
130
|
+
onFetchData?: (params: any, config: any) => Promise<any>
|
|
131
|
+
onTransformData?: (params: Record<string, string>) => SelectOptionItem
|
|
132
|
+
fetchParams?: Object
|
|
133
|
+
optionsDisplayType?: 'labelAndValue'
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
138
|
+
| ------------------ | --------------------------------------- | ---- | ------------------------------------------ | --------------------------------------------------------- |
|
|
139
|
+
| value | `string` | 否 | - | 受控值 |
|
|
140
|
+
| onChange | `(value?, selectedOptionItem?) => void` | 否 | - | 值变化回调 |
|
|
141
|
+
| defaultLabel | `string` | 否 | `labelDic` 第一个 key | 默认选中的搜索维度 key |
|
|
142
|
+
| labelDic | `LabelDic` | 否 | `{ businessNo: '商户号', name: '商户名' }` | 搜索维度配置,key 为请求参数名,value 为显示文案 |
|
|
143
|
+
| customLabelInValue | `boolean` | 否 | `false` | 是否将 value 编码为 `展示类型;ID;名称;labelDicKey` 格式 |
|
|
144
|
+
| onFetchData | `(params, config) => Promise` | 否 | `apiService.merchantList` | 自定义数据请求函数 |
|
|
145
|
+
| onTransformData | `(params) => SelectOptionItem` | 否 | 默认转换函数 | 将接口返回的列表项转换为 `{ label, value, customFields }` |
|
|
146
|
+
| fetchParams | `Object` | 否 | `{}` | 额外请求参数,会合并到请求中 |
|
|
147
|
+
| optionsDisplayType | `'labelAndValue'` | 否 | - | 选项显示格式,设置后显示 `value - label` |
|
|
148
|
+
| disabled | `boolean` | 否 | - | 禁用状态 |
|
|
149
|
+
| className | `string` | 否 | - | 右侧 Select 的自定义样式类名 |
|
|
150
|
+
| style | `React.CSSProperties` | 否 | - | 自定义行内样式 |
|
|
151
|
+
|
|
152
|
+
## 导出类型
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// 选项数据结构
|
|
156
|
+
export type SelectOptionItem = {
|
|
157
|
+
value: string
|
|
158
|
+
label: string
|
|
159
|
+
customFields?: Object | null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 搜索维度配置
|
|
163
|
+
export type LabelDic = Record<string, string>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 导出工具函数
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// 编码 customLabelInValue 的值
|
|
170
|
+
export const getStrValue = (displayType: 'label' | 'value', value?: string, label?: string, labelDicKey?: string) => string | undefined
|
|
171
|
+
|
|
172
|
+
// 解码 customLabelInValue 的值
|
|
173
|
+
export const getSelectValue = (str?: string | null | number, returnType?: 'label' | 'value' | 'labelDicKey' | 'labelAndValue') =>
|
|
174
|
+
string | undefined
|
|
175
|
+
|
|
176
|
+
// 拼接 "value - label" 显示文本
|
|
177
|
+
export const getInitialValue = (label?: string, value?: string) => string
|
|
178
|
+
|
|
179
|
+
// 从 "value - label" 格式中取 value
|
|
180
|
+
export const getFirstValue = (str?: string) => string
|
|
181
|
+
|
|
182
|
+
// 从 "value - label" 格式中取 label
|
|
183
|
+
export const getSecondValue = (str?: string) => string
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## API 数据源
|
|
187
|
+
|
|
188
|
+
- **内部 Hook**:`useReqData`(基于 `useAsyncFn` + `useDebounce`,600ms 防抖)
|
|
189
|
+
- **默认接口**:`apiService.merchantList`
|
|
190
|
+
- **请求参数**:`{ page: 1, pageSize: 20, [curSelectLabel]: curSearchValue, ...fetchParams }`
|
|
191
|
+
- **可覆盖**:通过 `onFetchData` prop 传入自定义请求函数,或通过 `<ConfigProvider apiService={{ merchantList: customFn }}>` 替换默认实现
|
|
192
|
+
|
|
193
|
+
## 常见陷阱
|
|
194
|
+
|
|
195
|
+
- ❌ 切换左侧维度后期望保留右侧已选值:
|
|
196
|
+
```tsx
|
|
197
|
+
// 切换维度时组件会自动清空右侧选择值和搜索结果
|
|
198
|
+
```
|
|
199
|
+
- ✅ 在 `onChange` 中处理值可能为 `undefined` 的情况
|
|
200
|
+
|
|
201
|
+
- ❌ 使用 `customLabelInValue` 模式时直接展示 value:
|
|
202
|
+
```tsx
|
|
203
|
+
// value 格式为 "label;123;某某商户;businessNo",不可直接展示
|
|
204
|
+
```
|
|
205
|
+
- ✅ 使用 `getSelectValue` 解析所需字段:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
const displayText = getSelectValue(value, 'label') // "某某商户"
|
|
209
|
+
const id = getSelectValue(value, 'value') // "123"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- ❌ 忘记提供 `onTransformData` 而使用非商户接口
|
|
213
|
+
- ✅ 当自定义 `onFetchData` 时,务必同时提供 `onTransformData` 确保数据格式正确
|
|
214
|
+
|
|
215
|
+
- ⚠️ 使用 `customLabelInValue` 时,初始值必须是分号分隔格式字符串(`displayType;value;label;labelDicKey`),不能直接传普通字符串或数字。
|
|
216
|
+
|
|
217
|
+
## 相关组件
|
|
218
|
+
|
|
219
|
+
| 场景 | 组件 | 说明 |
|
|
220
|
+
| ------------ | ------------------- | --------------------------------------- |
|
|
221
|
+
| 简单下拉搜索 | `EffectStaffSelect` | 单维度员工选择器 |
|
|
222
|
+
| API 配置 | `ConfigProvider` | 提供 `apiService.merchantList` 接口实现 |
|