@xfe-repo/web-components 1.6.1 → 1.7.0
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/dist/index.css +0 -24
- package/dist/index.js +4 -6
- package/dist/index.mjs +4 -6
- package/package.json +6 -5
- package/skills/GUIDE.md +78 -0
- package/skills/SKILL.md +78 -0
- package/skills/TEMPLATE.md +170 -0
- package/skills/references/ApiService.md +136 -0
- package/skills/references/CInput.md +196 -0
- package/skills/references/CTable.md +274 -0
- package/skills/references/CVideo.md +94 -0
- package/skills/references/Clock.md +53 -0
- package/skills/references/ConfigProvider.md +198 -0
- package/skills/references/Countdown.md +109 -0
- package/skills/references/Counter.md +75 -0
- package/skills/references/Currency.md +112 -0
- package/skills/references/EffectAddressCascade.md +110 -0
- package/skills/references/EffectBrandSelect.md +231 -0
- package/skills/references/EffectBrandTransfer.md +143 -0
- package/skills/references/EffectCategoryCascade.md +228 -0
- package/skills/references/EffectFileUpload.md +349 -0
- package/skills/references/EffectLabelSelect.md +222 -0
- package/skills/references/EffectMerchantSelect.md +183 -0
- package/skills/references/EffectReservoirSelect.md +178 -0
- package/skills/references/EffectScopeSelect.md +220 -0
- package/skills/references/EffectSeriesSelect.md +205 -0
- package/skills/references/EffectSeriesSelectV2.md +154 -0
- package/skills/references/EffectSkuRecognize.md +149 -0
- package/skills/references/EffectSkuSelect.md +251 -0
- package/skills/references/EffectSkuTable.md +184 -0
- package/skills/references/EffectSpuSelect.md +189 -0
- package/skills/references/EffectStaffSelect.md +150 -0
- package/skills/references/EffectWarehouseSelect.md +183 -0
- package/skills/references/EffectWithFilePanel.md +205 -0
- package/skills/references/FileUpload.md +126 -0
- package/skills/references/Iconfont.md +31 -0
- package/skills/references/Loading.md +68 -0
- package/skills/references/MultiWindow.md +145 -0
- package/skills/references/OSSImage.md +230 -0
- package/skills/references/PrivacyField.md +90 -0
- package/skills/references/QRCode.md +148 -0
- package/skills/references/RichTextEditor.md +119 -0
- package/skills/references/SearchForm.md +270 -0
- package/skills/references/SearchList.md +128 -0
- package/skills/references/WithModal.md +328 -0
- package/skills/references/WithPanel.md +307 -0
- package/skills/references/commonFn.md +254 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# EffectBrandSelect
|
|
2
|
+
|
|
3
|
+
> 品牌下拉选择器,支持分类过滤、远程搜索、多选,数据通过 `apiService.brandComboBox` 自动获取。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectBrandSelect } from '@xfe-repo/web-components'
|
|
9
|
+
import type { BrandValueType, BrandOptions } from '@xfe-repo/web-components'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 基本用法
|
|
13
|
+
|
|
14
|
+
最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
<Form.Item name="brandId" label="品牌">
|
|
18
|
+
<EffectBrandSelect categoryId={categoryId} />
|
|
19
|
+
</Form.Item>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 独立受控模式
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { EffectBrandSelect } from '@xfe-repo/web-components'
|
|
26
|
+
|
|
27
|
+
function MyForm() {
|
|
28
|
+
const [brandId, setBrandId] = useState<number>()
|
|
29
|
+
|
|
30
|
+
return <EffectBrandSelect value={brandId} onChange={(value) => setBrandId(value as number)} />
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 进阶用法
|
|
35
|
+
|
|
36
|
+
### 按分类过滤品牌
|
|
37
|
+
|
|
38
|
+
`categoryId` 变更时,组件会自动重新请求品牌列表并清空已选值。
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
42
|
+
const [brandId, setBrandId] = useState<number>()
|
|
43
|
+
|
|
44
|
+
<EffectCategoryCascade
|
|
45
|
+
onChange={(values, selectedList) => {
|
|
46
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
47
|
+
const lastItem = selectedList[selectedList.length - 1]
|
|
48
|
+
setCategoryId(Number(lastItem.value))
|
|
49
|
+
}
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
<EffectBrandSelect
|
|
53
|
+
categoryId={categoryId}
|
|
54
|
+
value={brandId}
|
|
55
|
+
onChange={(value) => setBrandId(value as number)}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 多选模式
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const [brandIds, setBrandIds] = useState<number[]>()
|
|
63
|
+
|
|
64
|
+
<EffectBrandSelect
|
|
65
|
+
mode="multiple"
|
|
66
|
+
value={brandIds}
|
|
67
|
+
onChange={(value, labels) => {
|
|
68
|
+
setBrandIds(value as number[])
|
|
69
|
+
// labels 多选时以 '>>>' 连接,如 "Nike>>>Adidas"
|
|
70
|
+
console.log('选中品牌名称:', labels)
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 显示"其它"品牌
|
|
76
|
+
|
|
77
|
+
默认隐藏"其它"品牌选项,需要时显式开启:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<EffectBrandSelect showOtherBrand />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### onChange 同步品牌名称
|
|
84
|
+
|
|
85
|
+
onChange 第二参数携带品牌名称,需手动同步到隐藏字段:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<Form.Item name="brandId" label="品牌">
|
|
89
|
+
<EffectBrandSelect
|
|
90
|
+
categoryId={categoryId}
|
|
91
|
+
key={categoryId}
|
|
92
|
+
onChange={(brandId, brandName) => {
|
|
93
|
+
form.setFieldValue('brandName', brandName)
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
</Form.Item>
|
|
97
|
+
<Form.Item name="brandName" hidden><Input /></Form.Item>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Props
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
type Props = {
|
|
104
|
+
className?: string
|
|
105
|
+
onChange?: (value?: BrandValueType, label?: string) => void
|
|
106
|
+
value?: BrandValueType
|
|
107
|
+
defaultValue?: BrandValueType
|
|
108
|
+
disabled?: boolean
|
|
109
|
+
categoryId?: number
|
|
110
|
+
mode?: 'multiple' | 'tags'
|
|
111
|
+
sysBizTypeId?: InQueryBrandComboBox['sysBizTypeId']
|
|
112
|
+
/** 是否显示其他品牌,默认不露出 */
|
|
113
|
+
showOtherBrand?: boolean
|
|
114
|
+
style?: React.CSSProperties
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
119
|
+
| -------------- | -------------------------- | ---- | ------- | ----------------------------------------------- |
|
|
120
|
+
| value | `BrandValueType` | 否 | - | 受控值 |
|
|
121
|
+
| defaultValue | `BrandValueType` | 否 | - | 默认值 |
|
|
122
|
+
| onChange | `(value?, label?) => void` | 否 | - | 值变化回调,多选时 label 以 `>>>` 连接 |
|
|
123
|
+
| categoryId | `number` | 否 | - | 分类 ID,变更时自动重新加载品牌列表并清空选中值 |
|
|
124
|
+
| mode | `'multiple' \| 'tags'` | 否 | - | 多选/标签模式 |
|
|
125
|
+
| sysBizTypeId | `string` | 否 | - | 业务品牌类型 ID |
|
|
126
|
+
| showOtherBrand | `boolean` | 否 | `false` | 是否显示"其它"品牌选项(value=1310) |
|
|
127
|
+
| disabled | `boolean` | 否 | - | 禁用状态 |
|
|
128
|
+
| className | `string` | 否 | - | 自定义样式类名 |
|
|
129
|
+
| style | `React.CSSProperties` | 否 | - | 自定义行内样式 |
|
|
130
|
+
|
|
131
|
+
## 导出类型
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// 品牌值类型:单选为 number,多选为 number[]
|
|
135
|
+
export type BrandValueType = number | number[] | string | undefined
|
|
136
|
+
|
|
137
|
+
// 品牌选项列表
|
|
138
|
+
export type BrandOptions = { label: string; value: number }[]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## API 数据源
|
|
142
|
+
|
|
143
|
+
- **内部 Hook**:`useBrandOptions`(使用 SWR 缓存)
|
|
144
|
+
- **内部接口**:`apiService.brandComboBox`
|
|
145
|
+
- **请求参数**:`{ categoryId, sysBizTypeId, brandName, brandId, page: 1, pageSize: 500 }`
|
|
146
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ brandComboBox: customFn }}>` 替换默认实现
|
|
147
|
+
|
|
148
|
+
## 依赖 Props
|
|
149
|
+
|
|
150
|
+
| 依赖属性 | 类型 | 来源 | 说明 |
|
|
151
|
+
| ------------ | -------- | ---------------------------- | -------------------------------------- |
|
|
152
|
+
| categoryId | `number` | EffectCategoryCascade 的输出 | 按分类过滤品牌列表,变更时清空已选品牌 |
|
|
153
|
+
| sysBizTypeId | `string` | 业务参数 | 业务品牌类型过滤 |
|
|
154
|
+
|
|
155
|
+
### 联动示例
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { useState } from 'react'
|
|
159
|
+
import { EffectCategoryCascade, EffectBrandSelect, isSingleTreeSelected } from '@xfe-repo/web-components'
|
|
160
|
+
import type { TreeValueType, TreeSelectList, BrandValueType } from '@xfe-repo/web-components'
|
|
161
|
+
|
|
162
|
+
function CategoryBrandFilter() {
|
|
163
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
164
|
+
const [brandId, setBrandId] = useState<number>()
|
|
165
|
+
|
|
166
|
+
const handleCategoryChange = (values?: TreeValueType, selectedList?: TreeSelectList) => {
|
|
167
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
168
|
+
const lastItem = selectedList[selectedList.length - 1]
|
|
169
|
+
setCategoryId(Number(lastItem.value))
|
|
170
|
+
} else {
|
|
171
|
+
setCategoryId(undefined)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div>
|
|
177
|
+
<EffectCategoryCascade onChange={handleCategoryChange} />
|
|
178
|
+
<EffectBrandSelect categoryId={categoryId} value={brandId} onChange={(value) => setBrandId(value as number)} />
|
|
179
|
+
</div>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 使用提示
|
|
185
|
+
|
|
186
|
+
> 💡 不传 `categoryId` 时,组件加载全部品牌(不按分类过滤)。适用于跨分类的品牌搜索场景。
|
|
187
|
+
|
|
188
|
+
## 常见陷阱
|
|
189
|
+
|
|
190
|
+
### 分类切换后强制刷新
|
|
191
|
+
|
|
192
|
+
当 `categoryId` 变化时,需要通过 `key` 强制重新挂载组件以刷新品牌列表:
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<Form.Item name="brandId" label="品牌">
|
|
196
|
+
<EffectBrandSelect categoryId={categoryId} key={categoryId} />
|
|
197
|
+
</Form.Item>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
> ⚠️ 不设置 `key` 时,categoryId 变化后品牌列表可能残留旧分类的数据。
|
|
201
|
+
|
|
202
|
+
- ❌ `categoryId` 变更后仍然期望保留之前选中的品牌值:
|
|
203
|
+
```tsx
|
|
204
|
+
// categoryId 变更时组件内部会自动清空 value 并触发 onChange()
|
|
205
|
+
```
|
|
206
|
+
- ✅ 在 `onChange` 回调中处理清空逻辑:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<EffectBrandSelect
|
|
210
|
+
categoryId={categoryId}
|
|
211
|
+
onChange={(value) => {
|
|
212
|
+
// value 可能为 undefined(categoryId 变更导致的自动清空)
|
|
213
|
+
setBrandId(value as number | undefined)
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- ❌ 用字符串作为 value 传入(虽然类型允许 string,但 options 的 value 是 number)
|
|
219
|
+
- ✅ 使用 number 类型的 value
|
|
220
|
+
|
|
221
|
+
- ❌ 在多选模式下解析 label 时忽略分隔符
|
|
222
|
+
- ✅ 多选模式的 label 以 `>>>` 连接多个品牌名,使用 `label.split('>>>')` 解析
|
|
223
|
+
|
|
224
|
+
## 相关组件
|
|
225
|
+
|
|
226
|
+
| 场景 | 组件 | 说明 |
|
|
227
|
+
| -------- | ----------------------- | -------------------------------------- |
|
|
228
|
+
| 级联上游 | `EffectCategoryCascade` | 提供 categoryId 过滤品牌列表 |
|
|
229
|
+
| 级联下游 | `EffectSeriesSelect` | 使用品牌 ID 过滤系列列表 |
|
|
230
|
+
| 聚合使用 | `EffectScopeSelect` | 内部已集成本组件,无需单独使用 |
|
|
231
|
+
| API 配置 | `ConfigProvider` | 提供 apiService.brandComboBox 接口实现 |
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# EffectBrandTransfer
|
|
2
|
+
|
|
3
|
+
> 品牌穿梭框,基于 Ant Design Transfer 实现品牌多选,支持分类过滤和搜索,数据复用 `useBrandOptions` Hook 获取。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectBrandTransfer } from '@xfe-repo/web-components'
|
|
9
|
+
import type { EffectBrandTransferProps, TransferValueType, TransferPropValueType } from '@xfe-repo/web-components'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 基本用法
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { EffectBrandTransfer } from '@xfe-repo/web-components'
|
|
16
|
+
|
|
17
|
+
function MyForm() {
|
|
18
|
+
const [brandIds, setBrandIds] = useState<number[]>()
|
|
19
|
+
|
|
20
|
+
return <EffectBrandTransfer value={brandIds} onChange={(value) => setBrandIds(value as number[])} />
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 进阶用法
|
|
25
|
+
|
|
26
|
+
### 按分类过滤品牌
|
|
27
|
+
|
|
28
|
+
`categoryId` 变更时,组件会自动重新请求品牌列表并清空已选值。
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
32
|
+
const [brandIds, setBrandIds] = useState<number[]>()
|
|
33
|
+
|
|
34
|
+
<EffectCategoryCascade
|
|
35
|
+
onChange={(values, selectedList) => {
|
|
36
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
37
|
+
const lastItem = selectedList[selectedList.length - 1]
|
|
38
|
+
setCategoryId(Number(lastItem.value))
|
|
39
|
+
}
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
<EffectBrandTransfer
|
|
43
|
+
categoryId={categoryId}
|
|
44
|
+
value={brandIds}
|
|
45
|
+
onChange={(value, name) => {
|
|
46
|
+
setBrandIds(value as number[])
|
|
47
|
+
// name 多选时以 '>>>' 连接,如 "Nike>>>Adidas"
|
|
48
|
+
console.log('选中品牌名称:', name)
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 显示"其它"品牌
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<EffectBrandTransfer showOtherBrand />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Props
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
export type EffectBrandTransferProps = {
|
|
63
|
+
className?: string
|
|
64
|
+
onChange?: (value?: BrandValueType, name?: string) => void
|
|
65
|
+
value?: TransferPropValueType
|
|
66
|
+
defaultValue?: TransferPropValueType
|
|
67
|
+
disabled?: boolean
|
|
68
|
+
categoryId?: number
|
|
69
|
+
sysBizTypeId?: InQueryBrandComboBox['sysBizTypeId']
|
|
70
|
+
/** 是否显示其他品牌,默认不露出 */
|
|
71
|
+
showOtherBrand?: boolean
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
76
|
+
| -------------- | ------------------------- | ---- | ------- | ---------------------------------------- |
|
|
77
|
+
| value | `number[] \| undefined` | 否 | - | 受控值,已选中的品牌 ID 数组 |
|
|
78
|
+
| defaultValue | `number[] \| undefined` | 否 | - | 默认值 |
|
|
79
|
+
| onChange | `(value?, name?) => void` | 否 | - | 值变化回调,name 以 `>>>` 连接多个品牌名 |
|
|
80
|
+
| categoryId | `number` | 否 | - | 分类 ID,变更时自动重新加载并清空选中值 |
|
|
81
|
+
| sysBizTypeId | `string` | 否 | - | 业务品牌类型 ID |
|
|
82
|
+
| showOtherBrand | `boolean` | 否 | `false` | 是否显示"其它"品牌选项(key=1310) |
|
|
83
|
+
| disabled | `boolean` | 否 | - | 禁用状态 |
|
|
84
|
+
| className | `string` | 否 | - | 自定义样式类名 |
|
|
85
|
+
|
|
86
|
+
## 导出类型
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// 穿梭框内部使用的值类型(string 数组)
|
|
90
|
+
export type TransferValueType = string[]
|
|
91
|
+
|
|
92
|
+
// Props 中 value/defaultValue 的类型
|
|
93
|
+
export type TransferPropValueType = number[] | undefined
|
|
94
|
+
|
|
95
|
+
// Props 类型
|
|
96
|
+
export type EffectBrandTransferProps = { ... }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API 数据源
|
|
100
|
+
|
|
101
|
+
- **内部 Hook**:`useBrandOptions`(复用 EffectBrandSelect 的 Hook,使用 SWR 缓存)
|
|
102
|
+
- **内部接口**:`apiService.brandComboBox`
|
|
103
|
+
- **请求参数**:`{ categoryId, sysBizTypeId, brandName, brandId, pageSize: 500 }`
|
|
104
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ brandComboBox: customFn }}>` 替换默认实现
|
|
105
|
+
|
|
106
|
+
## 依赖 Props
|
|
107
|
+
|
|
108
|
+
| 依赖属性 | 类型 | 来源 | 说明 |
|
|
109
|
+
| ------------ | -------- | ---------------------------- | -------------------------------------- |
|
|
110
|
+
| categoryId | `number` | EffectCategoryCascade 的输出 | 按分类过滤品牌列表,变更时清空已选品牌 |
|
|
111
|
+
| sysBizTypeId | `string` | 业务参数 | 业务品牌类型过滤 |
|
|
112
|
+
|
|
113
|
+
## 常见陷阱
|
|
114
|
+
|
|
115
|
+
- ❌ `categoryId` 变更后仍然期望保留已穿梭的品牌:
|
|
116
|
+
```tsx
|
|
117
|
+
// categoryId 或 sysBizTypeId 变更时,组件内部会自动清空 targetKeys 并触发 onChange()
|
|
118
|
+
```
|
|
119
|
+
- ✅ 在 `onChange` 回调中处理清空逻辑:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<EffectBrandTransfer
|
|
123
|
+
categoryId={categoryId}
|
|
124
|
+
onChange={(value) => {
|
|
125
|
+
// value 可能为 undefined(依赖属性变更导致的自动清空)
|
|
126
|
+
setBrandIds(value as number[] | undefined)
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- ❌ 将 `value` 传入字符串数组
|
|
132
|
+
- ✅ 使用 `number[]` 类型的 value,组件内部会自动转换为 string 用于 Transfer
|
|
133
|
+
|
|
134
|
+
- ❌ 在 `onChange` 中直接使用 name 而不拆分
|
|
135
|
+
- ✅ 多选时 name 以 `>>>` 连接,使用 `name.split('>>>')` 解析
|
|
136
|
+
|
|
137
|
+
## 相关组件
|
|
138
|
+
|
|
139
|
+
| 场景 | 组件 | 说明 |
|
|
140
|
+
| ------------ | ----------------------- | --------------------------------------------------- |
|
|
141
|
+
| 级联上游 | `EffectCategoryCascade` | 提供 categoryId 过滤品牌列表 |
|
|
142
|
+
| 下拉选择替代 | `EffectBrandSelect` | 品牌下拉选择器(单选/多选),共享 `useBrandOptions` |
|
|
143
|
+
| API 配置 | `ConfigProvider` | 提供 `apiService.brandComboBox` 接口实现 |
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# EffectCategoryCascade
|
|
2
|
+
|
|
3
|
+
> 分类级联选择器,基于 antd Cascader 封装,是级联链(分类→品牌→系列→SPU→SKU)的起点,数据通过 `apiService.categoryListToTree` 自动获取。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectCategoryCascade, isSingleTreeSelected } from '@xfe-repo/web-components'
|
|
9
|
+
import type {
|
|
10
|
+
TreeSelectItem,
|
|
11
|
+
TreeSelectList,
|
|
12
|
+
TreeSingleValueType,
|
|
13
|
+
TreeValueType,
|
|
14
|
+
CustomTreeItem,
|
|
15
|
+
CustomTreeOptions,
|
|
16
|
+
} from '@xfe-repo/web-components'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 基本用法
|
|
20
|
+
|
|
21
|
+
最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<Form.Item name="categoryIds" label="分类">
|
|
25
|
+
<EffectCategoryCascade changeOnSelect />
|
|
26
|
+
</Form.Item>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 独立受控模式
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { EffectCategoryCascade } from '@xfe-repo/web-components'
|
|
33
|
+
import type { TreeValueType, TreeSelectList } from '@xfe-repo/web-components'
|
|
34
|
+
|
|
35
|
+
function MyForm() {
|
|
36
|
+
const handleChange = (value?: TreeValueType, selectedList?: TreeSelectList) => {
|
|
37
|
+
console.log('选中的分类路径值:', value) // 如 [1, 11, 111]
|
|
38
|
+
console.log('选中的分类详情:', selectedList) // 如 [{ value: 1, label: '手表', level: 0 }, ...]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return <EffectCategoryCascade onChange={handleChange} />
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 进阶用法
|
|
46
|
+
|
|
47
|
+
### 受控模式
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
const [categoryValue, setCategoryValue] = useState<TreeValueType>()
|
|
51
|
+
|
|
52
|
+
<EffectCategoryCascade
|
|
53
|
+
value={categoryValue}
|
|
54
|
+
onChange={(value, selectedList) => {
|
|
55
|
+
setCategoryValue(value)
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 多选模式
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<EffectCategoryCascade
|
|
64
|
+
multiple
|
|
65
|
+
onChange={(values, selectedList) => {
|
|
66
|
+
// values: [[1, 11, 111], [2, 22, 222]]
|
|
67
|
+
// selectedList: [[{ value: 1, ... }, ...], [{ value: 2, ... }, ...]]
|
|
68
|
+
console.log('多选值:', values)
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 选择即触发(不需要选到叶子节点)
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<EffectCategoryCascade
|
|
77
|
+
changeOnSelect
|
|
78
|
+
onChange={(value, selectedList) => {
|
|
79
|
+
// 选择任意层级都会触发
|
|
80
|
+
console.log('当前选择:', value)
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 获取分类原始数据
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { TreeCategory } from '@api/product'
|
|
89
|
+
|
|
90
|
+
;<EffectCategoryCascade
|
|
91
|
+
onGetData={(dataList?: TreeCategory[]) => {
|
|
92
|
+
// 分类树数据加载完成后回调
|
|
93
|
+
console.log('分类树数据:', dataList)
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 编辑态禁用
|
|
99
|
+
|
|
100
|
+
编辑页面中分类不可修改时:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<Form.Item name="categoryIds" label="分类">
|
|
104
|
+
<EffectCategoryCascade changeOnSelect disabled={isEdit} />
|
|
105
|
+
</Form.Item>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Props
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
type Props = Omit<CascaderProps<any>, 'onChange' | 'defaultValue' | 'value'> & {
|
|
112
|
+
className?: string
|
|
113
|
+
onChange?: (value?: TreeValueType, selected?: TreeSelectList) => void
|
|
114
|
+
defaultValue?: TreeValueType
|
|
115
|
+
value?: TreeValueType
|
|
116
|
+
sysBizTypeId?: string
|
|
117
|
+
onGetData?: (dataList?: TreeCategory[]) => void
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
122
|
+
| -------------- | ----------------------------- | ---- | ------ | --------------------------------------------------------------------------------- |
|
|
123
|
+
| value | `TreeValueType` | 否 | - | 受控值,单选为 `Array<string \| number>`,多选为 `Array<Array<string \| number>>` |
|
|
124
|
+
| defaultValue | `TreeValueType` | 否 | - | 默认值 |
|
|
125
|
+
| onChange | `(value?, selected?) => void` | 否 | - | 值变化回调,第二个参数包含每级节点的详细信息 |
|
|
126
|
+
| sysBizTypeId | `string` | 否 | - | 业务分类类型 ID,变更时重新加载分类树并清空选中值 |
|
|
127
|
+
| onGetData | `(dataList?) => void` | 否 | - | 分类树数据加载完成回调 |
|
|
128
|
+
| multiple | `boolean` | 否 | - | 继承自 antd Cascader,开启多选模式 |
|
|
129
|
+
| changeOnSelect | `boolean` | 否 | - | 继承自 antd Cascader,选择任意层级即触发 onChange |
|
|
130
|
+
| className | `string` | 否 | - | 自定义样式类名 |
|
|
131
|
+
|
|
132
|
+
> **注意**:继承了 antd `CascaderProps` 的其他属性(如 `placeholder`、`disabled`、`showSearch` 等),但 `onChange`、`defaultValue`、`value` 被自定义实现覆盖。
|
|
133
|
+
|
|
134
|
+
## 导出类型
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// 单个选中项
|
|
138
|
+
export type TreeSelectItem = {
|
|
139
|
+
value: string | number
|
|
140
|
+
label: string
|
|
141
|
+
level: string | number
|
|
142
|
+
isLastLevel?: boolean
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 选中项列表:单选为 TreeSelectItem[],多选为 TreeSelectItem[][]
|
|
146
|
+
export type TreeSelectList = TreeSelectItem[] | TreeSelectItem[][]
|
|
147
|
+
|
|
148
|
+
// 单选值类型:表示一条分类路径
|
|
149
|
+
export type TreeSingleValueType = Array<string | number>
|
|
150
|
+
|
|
151
|
+
// 值类型:单选为 TreeSingleValueType,多选为 TreeSingleValueType[]
|
|
152
|
+
export type TreeValueType = TreeSingleValueType | TreeSingleValueType[]
|
|
153
|
+
|
|
154
|
+
// 分类树节点(扩展自 TreeCategory)
|
|
155
|
+
export type CustomTreeItem = TreeCategory & { index?: number }
|
|
156
|
+
export type CustomTreeOptions = CustomTreeItem[] | CustomTreeItem[][]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 辅助函数
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// 判断 selectedList 是否为单选模式的结果
|
|
163
|
+
export const isSingleTreeSelected = (selectList?: TreeSelectList): selectList is TreeSelectItem[]
|
|
164
|
+
|
|
165
|
+
// 将选项转换为 selectedList
|
|
166
|
+
export const transformOptionToSelectedList = (selectedOptions?: CustomTreeOptions, multiple?: boolean) => TreeSelectList
|
|
167
|
+
|
|
168
|
+
// 将 selectedList 转换为 values
|
|
169
|
+
export const transformSelectListToValues = (selectList?: TreeSelectList, multiple?: boolean) => TreeValueType
|
|
170
|
+
|
|
171
|
+
// 将 values 转换为 selectedList(注意:此时 label 为 value 的字符串形式)
|
|
172
|
+
export const transformValuesToSelectList = (values?: TreeValueType, multiple?: boolean) => TreeSelectList
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## API 数据源
|
|
176
|
+
|
|
177
|
+
- **内部 Hook**:`useCategoryTree`(使用 SWR 缓存)
|
|
178
|
+
- **内部接口**:`apiService.categoryListToTree`
|
|
179
|
+
- **请求参数**:`{ sysBizTypeId }`
|
|
180
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ categoryListToTree: customFn }}>` 替换默认实现
|
|
181
|
+
|
|
182
|
+
## 使用提示
|
|
183
|
+
|
|
184
|
+
> 💡 约 70% 业务场景传 `changeOnSelect`,允许用户选择任意级别分类。建议默认使用。
|
|
185
|
+
|
|
186
|
+
> 💡 常见宽度:`style={{ width: 160 }}` 或 `style={{ width: 200 }}`。
|
|
187
|
+
|
|
188
|
+
## 常见陷阱
|
|
189
|
+
|
|
190
|
+
- ❌ 混淆单选和多选时 `value`、`onChange` 的类型:
|
|
191
|
+
```tsx
|
|
192
|
+
// 单选:value = [1, 11, 111],selectedList = [{ value: 1, ... }, ...]
|
|
193
|
+
// 多选:value = [[1, 11], [2, 22]],selectedList = [[...], [...]]
|
|
194
|
+
```
|
|
195
|
+
- ✅ 使用 `isSingleTreeSelected` 安全地判断 selectedList 模式:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
onChange={(values, selectedList) => {
|
|
199
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
200
|
+
// 单选模式
|
|
201
|
+
const lastCategoryId = selectedList[selectedList.length - 1]?.value
|
|
202
|
+
}
|
|
203
|
+
}}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- ❌ 从 `onChange` 的 `value` 中直接取最后一级分类 ID 来传给 EffectBrandSelect:
|
|
207
|
+
```tsx
|
|
208
|
+
// value 的元素类型是 string | number,可能不是纯 number
|
|
209
|
+
```
|
|
210
|
+
- ✅ 从 `selectedList` 中取值并转换:
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
const lastItem = selectedList[selectedList.length - 1]
|
|
214
|
+
const categoryId = Number(lastItem.value)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
- ❌ `sysBizTypeId` 变更后期望保留已选分类
|
|
218
|
+
- ✅ `sysBizTypeId` 变更时组件会自动清空选中值并触发 `onChange()`
|
|
219
|
+
|
|
220
|
+
- ⚠️ 编辑回填时使用 `setTimeout(() => form.setFieldsValue(...))` 可能产生时序问题,建议使用 `useEffect` + `form.setFieldsValue` 在数据加载完成后统一设置。
|
|
221
|
+
|
|
222
|
+
## 相关组件
|
|
223
|
+
|
|
224
|
+
| 场景 | 组件 | 说明 |
|
|
225
|
+
| -------- | ------------------- | ------------------------------------------- |
|
|
226
|
+
| 级联下游 | `EffectBrandSelect` | 使用分类末级 ID 作为 categoryId 过滤品牌 |
|
|
227
|
+
| 聚合使用 | `EffectScopeSelect` | 内部已集成本组件,无需单独使用 |
|
|
228
|
+
| API 配置 | `ConfigProvider` | 提供 apiService.categoryListToTree 接口实现 |
|