@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.
Files changed (47) hide show
  1. package/dist/index.css +0 -24
  2. package/dist/index.js +4 -6
  3. package/dist/index.mjs +4 -6
  4. package/package.json +6 -5
  5. package/skills/GUIDE.md +78 -0
  6. package/skills/SKILL.md +78 -0
  7. package/skills/TEMPLATE.md +170 -0
  8. package/skills/references/ApiService.md +136 -0
  9. package/skills/references/CInput.md +196 -0
  10. package/skills/references/CTable.md +274 -0
  11. package/skills/references/CVideo.md +94 -0
  12. package/skills/references/Clock.md +53 -0
  13. package/skills/references/ConfigProvider.md +198 -0
  14. package/skills/references/Countdown.md +109 -0
  15. package/skills/references/Counter.md +75 -0
  16. package/skills/references/Currency.md +112 -0
  17. package/skills/references/EffectAddressCascade.md +110 -0
  18. package/skills/references/EffectBrandSelect.md +231 -0
  19. package/skills/references/EffectBrandTransfer.md +143 -0
  20. package/skills/references/EffectCategoryCascade.md +228 -0
  21. package/skills/references/EffectFileUpload.md +349 -0
  22. package/skills/references/EffectLabelSelect.md +222 -0
  23. package/skills/references/EffectMerchantSelect.md +183 -0
  24. package/skills/references/EffectReservoirSelect.md +178 -0
  25. package/skills/references/EffectScopeSelect.md +220 -0
  26. package/skills/references/EffectSeriesSelect.md +205 -0
  27. package/skills/references/EffectSeriesSelectV2.md +154 -0
  28. package/skills/references/EffectSkuRecognize.md +149 -0
  29. package/skills/references/EffectSkuSelect.md +251 -0
  30. package/skills/references/EffectSkuTable.md +184 -0
  31. package/skills/references/EffectSpuSelect.md +189 -0
  32. package/skills/references/EffectStaffSelect.md +150 -0
  33. package/skills/references/EffectWarehouseSelect.md +183 -0
  34. package/skills/references/EffectWithFilePanel.md +205 -0
  35. package/skills/references/FileUpload.md +126 -0
  36. package/skills/references/Iconfont.md +31 -0
  37. package/skills/references/Loading.md +68 -0
  38. package/skills/references/MultiWindow.md +145 -0
  39. package/skills/references/OSSImage.md +230 -0
  40. package/skills/references/PrivacyField.md +90 -0
  41. package/skills/references/QRCode.md +148 -0
  42. package/skills/references/RichTextEditor.md +119 -0
  43. package/skills/references/SearchForm.md +270 -0
  44. package/skills/references/SearchList.md +128 -0
  45. package/skills/references/WithModal.md +328 -0
  46. package/skills/references/WithPanel.md +307 -0
  47. package/skills/references/commonFn.md +254 -0
@@ -0,0 +1,183 @@
1
+ # EffectMerchantSelect
2
+
3
+ > 商户下拉选择器,支持按商户号、商户平台名、主账号手机号三种搜索源切换查询,数据通过 `apiService.merchantList` 自动获取。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { EffectMerchantSelect } from '@xfe-repo/web-components'
9
+ import type { MerchantOptions } from '@xfe-repo/web-components'
10
+ ```
11
+
12
+ ## 基本用法
13
+
14
+ 最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
15
+
16
+ ```tsx
17
+ <Form.Item name="enterpriseNo" label="商户">
18
+ <EffectMerchantSelect />
19
+ </Form.Item>
20
+ ```
21
+
22
+ ### 独立受控模式
23
+
24
+ ```tsx
25
+ import { EffectMerchantSelect } from '@xfe-repo/web-components'
26
+
27
+ function MyForm() {
28
+ const [merchantNo, setMerchantNo] = useState<string>()
29
+
30
+ return <EffectMerchantSelect value={merchantNo} onChange={(value) => setMerchantNo(value)} />
31
+ }
32
+ ```
33
+
34
+ ## 进阶用法
35
+
36
+ ### 切换搜索源
37
+
38
+ 组件左侧有一个搜索源下拉框,可在运行时切换搜索字段。也可通过 `source` 指定初始搜索源:
39
+
40
+ ```tsx
41
+ <EffectMerchantSelect
42
+ source="platformName" // 初始按商户平台名搜索
43
+ value={merchantNo}
44
+ onChange={(value, name) => {
45
+ setMerchantNo(value)
46
+ console.log('商户名:', name) // 返回 platformName
47
+ }}
48
+ />
49
+ ```
50
+
51
+ ### 自定义标签展示模式
52
+
53
+ ```tsx
54
+ // 选项标签展示为 "商户平台名 - 企业编号" 格式
55
+ <EffectMerchantSelect labelDisplayMode="platformName-enterpriseNo" value={merchantNo} onChange={(value) => setMerchantNo(value)} />
56
+ ```
57
+
58
+ ### onChange 同步商户名称
59
+
60
+ Form 管理 enterpriseNo,但 enterpriseName 需要 useState 单独保存:
61
+
62
+ ```tsx
63
+ const [merchantName, setMerchantName] = useState('')
64
+
65
+ <Form.Item name="enterpriseNo" label="商户">
66
+ <EffectMerchantSelect
67
+ onChange={(no, name) => {
68
+ setMerchantName(name)
69
+ }}
70
+ />
71
+ </Form.Item>
72
+ ```
73
+
74
+ ### 按手机号搜索
75
+
76
+ ```tsx
77
+ <Form.Item name="enterpriseNo" label="商户">
78
+ <EffectMerchantSelect source="phone" placeholder="请输入手机号搜索" />
79
+ </Form.Item>
80
+ ```
81
+
82
+ > 💡 `source` 可选值:`"businessNo"`(默认,按商户号)、`"platformName"`(按平台名称)、`"phone"`(按手机号)。
83
+
84
+ ## Props
85
+
86
+ ```typescript
87
+ type Source = 'businessNo' | 'platformName' | 'phone'
88
+
89
+ interface EffectMerchantSelectProps {
90
+ className?: string
91
+ onChange?: (value?: string, name?: string) => void
92
+ value?: string
93
+ /** 搜索字段来源 */
94
+ source?: Source
95
+ disabled?: boolean
96
+ /** 选项标签展示模式 */
97
+ labelDisplayMode?: LabelDisplayMode
98
+ }
99
+ ```
100
+
101
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
102
+ | ---------------- | ------------------------------------------- | ---- | ---------------- | ------------------------------------------- |
103
+ | value | `string` | 否 | - | 受控值(企业编号 enterpriseNo) |
104
+ | onChange | `(value?: string, name?: string) => void` | 否 | - | 值变化回调,第二个参数为商户的 platformName |
105
+ | source | `'businessNo' \| 'platformName' \| 'phone'` | 否 | `'businessNo'` | 初始搜索字段来源 |
106
+ | labelDisplayMode | `LabelDisplayMode` | 否 | `'platformName'` | 选项标签展示模式 |
107
+ | disabled | `boolean` | 否 | - | 禁用状态 |
108
+ | className | `string` | 否 | - | 自定义样式类名 |
109
+
110
+ ## 导出类型
111
+
112
+ ```typescript
113
+ // 商户选项列表类型
114
+ export type MerchantOptions = {
115
+ label: string
116
+ value: string
117
+ originalName: string
118
+ data?: MerchantListBus
119
+ }[]
120
+
121
+ // 标签展示模式
122
+ export type LabelDisplayMode = 'platformName' | 'platformName-enterpriseNo'
123
+ ```
124
+
125
+ > **注意**:`LabelDisplayMode` 定义在内部 `useMerchantOptions` 模块中,未从包入口直接导出。使用时直接传字面量 `'platformName'` 或 `'platformName-enterpriseNo'` 即可。
126
+
127
+ ## API 数据源
128
+
129
+ - **内部 Hook**:`useMerchantOptions`(使用 SWR 缓存,`revalidateOnMount: false`)
130
+ - **内部接口**:`apiService.merchantList`
131
+ - **请求参数**:`{ businessNo?, name?, phone?, page: 1, pageSize: 20, labelDisplayMode }`
132
+ - **可覆盖**:通过 `<ConfigProvider apiService={{ merchantList: customFn }}>` 替换默认实现
133
+
134
+ ### 搜索源切换逻辑
135
+
136
+ 组件搜索行为较特殊:
137
+
138
+ 1. 左侧有一个独立的 Select 下拉框用于切换搜索源(`sourceControl`)
139
+ 2. 切换搜索源时,已选值和选项列表会被清空
140
+ 3. 搜索使用 800ms 防抖延迟(比其他 Effect 选择器的 300ms 更长)
141
+ 4. 请求参数根据当前搜索源动态构建:`{ [sourceControl]: searchValue, labelDisplayMode }`
142
+
143
+ ```
144
+ 搜索源选项:
145
+ - businessNo → 按商户号搜索
146
+ - platformName → 按商户平台名搜索
147
+ - phone → 按主账号手机号搜索
148
+ ```
149
+
150
+ ## 常见陷阱
151
+
152
+ - ❌ 期望 `value` 是商户名——实际 value 是企业编号 `enterpriseNo`(string 类型):
153
+ ```tsx
154
+ // value 不是商户名,而是企业编号
155
+ <EffectMerchantSelect value="某某商户" />
156
+ ```
157
+ - ✅ 使用企业编号作为 value,通过 onChange 的第二个参数获取商户名:
158
+
159
+ ```tsx
160
+ <EffectMerchantSelect
161
+ value={enterpriseNo}
162
+ onChange={(value, name) => {
163
+ setEnterpriseNo(value) // 企业编号
164
+ setMerchantName(name) // 商户平台名
165
+ }}
166
+ />
167
+ ```
168
+
169
+ - ❌ 搜索无结果时以为组件坏了——首次渲染不发起搜索请求(`revalidateOnMount: false`),需要用户输入触发
170
+ - ✅ 如果需要默认展示选项,通过 `value` 传入企业编号,组件会自动发起反查请求
171
+
172
+ - ❌ 切换搜索源后期望保留之前的选中值——切换时会清空值和选项
173
+ - ✅ 在切换搜索源后重新搜索并选择
174
+
175
+ - ❌ 接口报错时组件白屏——组件内部有 error 处理,会展示红色错误文本
176
+ - ✅ 接口异常时检查 ConfigProvider 中 merchantList 配置是否正确
177
+
178
+ ## 相关组件
179
+
180
+ | 场景 | 组件 | 说明 |
181
+ | ---------- | ------------------- | ------------------------------------- |
182
+ | API 配置 | `ConfigProvider` | 提供 apiService.merchantList 接口实现 |
183
+ | 类似选择器 | `EffectBrandSelect` | 相似的远程搜索选择器模式 |
@@ -0,0 +1,178 @@
1
+ # EffectReservoirSelect
2
+
3
+ > 库区选择器,依赖 `warehouseId` 获取库区列表,支持搜索和级联联动。同模块还导出 `EffectShelveSelect` 库位选择器。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { EffectReservoirSelect, EffectShelveSelect } from '@xfe-repo/web-components'
9
+ ```
10
+
11
+ ## 基本用法
12
+
13
+ ```tsx
14
+ import { EffectReservoirSelect } from '@xfe-repo/web-components'
15
+
16
+ function MyForm() {
17
+ const [reservoirId, setReservoirId] = useState<number>()
18
+
19
+ return (
20
+ <EffectReservoirSelect
21
+ warehouseId={100}
22
+ value={reservoirId}
23
+ onChange={(value, name) => {
24
+ setReservoirId(value)
25
+ console.log('库区名称:', name)
26
+ }}
27
+ />
28
+ )
29
+ }
30
+ ```
31
+
32
+ ## 进阶用法
33
+
34
+ ### 仓库 → 库区 → 库位三级联动
35
+
36
+ ```tsx
37
+ import { EffectWarehouseSelect, EffectReservoirSelect, EffectShelveSelect } from '@xfe-repo/web-components'
38
+
39
+ function WarehouseLocationPicker() {
40
+ const [warehouseId, setWarehouseId] = useState<number>()
41
+ const [reservoirId, setReservoirId] = useState<number>()
42
+ const [shelveCode, setShelveCode] = useState<string>()
43
+
44
+ return (
45
+ <div>
46
+ <EffectWarehouseSelect
47
+ value={warehouseId ? String(warehouseId) : undefined}
48
+ onChange={(value) => setWarehouseId(value ? Number(value) : undefined)}
49
+ />
50
+ <EffectReservoirSelect warehouseId={warehouseId} value={reservoirId} onChange={(value) => setReservoirId(value)} />
51
+ <EffectShelveSelect
52
+ warehouseId={warehouseId}
53
+ reservoirId={reservoirId}
54
+ value={shelveCode}
55
+ onChange={(value) => setShelveCode(value)}
56
+ />
57
+ </div>
58
+ )
59
+ }
60
+ ```
61
+
62
+ ### 筛选异常库区
63
+
64
+ ```tsx
65
+ <EffectReservoirSelect warehouseId={100} reservoirType="abnormal" />
66
+ ```
67
+
68
+ ## Props
69
+
70
+ ### EffectReservoirSelect Props
71
+
72
+ ```typescript
73
+ type Props = {
74
+ className?: string
75
+ onChange?: (value?: number, name?: string) => void
76
+ value?: number
77
+ defaultValue?: string
78
+ disabled?: boolean
79
+ warehouseId?: number
80
+ reservoirType?: string
81
+ }
82
+ ```
83
+
84
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
85
+ | ------------- | ------------------------- | ---- | ---------- | --------------------------------------------------------- |
86
+ | value | `number` | 否 | - | 受控值(库区 ID) |
87
+ | defaultValue | `string` | 否 | - | 默认值 |
88
+ | onChange | `(value?, name?) => void` | 否 | - | 值变化回调,返回库区 ID 和名称 |
89
+ | warehouseId | `number` | 否 | - | **核心依赖**:仓库 ID,变更时重新加载库区列表并清空选中值 |
90
+ | reservoirType | `string` | 否 | `'normal'` | 库区类型,`'normal'` 普通库区 / `'abnormal'` 异常库区 |
91
+ | disabled | `boolean` | 否 | - | 禁用状态 |
92
+ | className | `string` | 否 | - | 自定义样式类名 |
93
+
94
+ ### EffectShelveSelect Props
95
+
96
+ ```typescript
97
+ type Props = {
98
+ className?: string
99
+ onChange?: (value?: string, name?: string) => void
100
+ value?: string
101
+ defaultValue?: string
102
+ disabled?: boolean
103
+ warehouseId?: number
104
+ reservoirId?: number
105
+ valueType?: 'code' | 'id'
106
+ }
107
+ ```
108
+
109
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
110
+ | ------------ | ------------------------- | ---- | -------- | --------------------------------------------- |
111
+ | value | `string` | 否 | - | 受控值(库位编码或 ID) |
112
+ | defaultValue | `string` | 否 | - | 默认值 |
113
+ | onChange | `(value?, name?) => void` | 否 | - | 值变化回调 |
114
+ | warehouseId | `number` | 否 | - | 仓库 ID |
115
+ | reservoirId | `number` | 否 | - | **核心依赖**:库区 ID,需同时传入 warehouseId |
116
+ | valueType | `'code' \| 'id'` | 否 | `'code'` | 值类型:`code` 返回库位编码,`id` 返回库位 ID |
117
+ | disabled | `boolean` | 否 | - | 禁用状态 |
118
+ | className | `string` | 否 | - | 自定义样式类名 |
119
+
120
+ ## API 数据源
121
+
122
+ ### EffectReservoirSelect
123
+
124
+ - **内部接口**:`apiService.reservoirList`
125
+ - **请求参数**:`{ page: 1, pageSize: 500, warehouseIdFilter: String(warehouseId), reservoirStateFilter: '1', reservoirType }`
126
+ - **前置条件**:`warehouseId` 必须存在才会发起请求
127
+
128
+ ### EffectShelveSelect
129
+
130
+ - **内部接口**:`apiService.shelveList`
131
+ - **请求参数**:`{ page: 1, pageSize: 2000, warehouseId, reservoirId, status: 1 }`
132
+ - **前置条件**:`warehouseId` 和 `reservoirId` 都必须存在才会发起请求
133
+
134
+ ## 依赖 Props
135
+
136
+ | 依赖属性 | 类型 | 来源 | 说明 |
137
+ | ----------- | -------- | ---------------------------- | ------------------------- |
138
+ | warehouseId | `number` | EffectWarehouseSelect 的输出 | 库区选择器必须依赖仓库 ID |
139
+ | reservoirId | `number` | EffectReservoirSelect 的输出 | 库位选择器必须依赖库区 ID |
140
+
141
+ ## 子组件
142
+
143
+ | 组件 | 说明 |
144
+ | ----------------------- | ---------- |
145
+ | `EffectReservoirSelect` | 库区选择器 |
146
+ | `EffectShelveSelect` | 库位选择器 |
147
+
148
+ ## 常见陷阱
149
+
150
+ - ❌ 不传 `warehouseId` 就使用 EffectReservoirSelect:
151
+ ```tsx
152
+ // 没有 warehouseId 时不会发起请求,选择器显示 "请先选择仓库"
153
+ ```
154
+ - ✅ 确保 `warehouseId` 存在后再渲染或传入:
155
+
156
+ ```tsx
157
+ <EffectReservoirSelect warehouseId={warehouseId} />
158
+ ```
159
+
160
+ - ❌ `warehouseId` 变更后期望保留之前选中的库区值:
161
+ ```tsx
162
+ // warehouseId 变更时组件内部会自动清空 value 并触发 onChange()
163
+ ```
164
+ - ✅ 在 `onChange` 中正确处理 `undefined` 值
165
+
166
+ - ❌ EffectShelveSelect 只传 reservoirId 不传 warehouseId:
167
+ ```tsx
168
+ // 两个 ID 都存在时才会发起请求
169
+ ```
170
+ - ✅ 同时传入 warehouseId 和 reservoirId
171
+
172
+ ## 相关组件
173
+
174
+ | 场景 | 组件 | 说明 |
175
+ | -------- | ----------------------- | --------------------------------------------------------- |
176
+ | 级联上游 | `EffectWarehouseSelect` | 提供 warehouseId |
177
+ | 级联下游 | `EffectShelveSelect` | 使用 reservoirId 获取库位列表 |
178
+ | API 配置 | `ConfigProvider` | 提供 `apiService.reservoirList` / `apiService.shelveList` |
@@ -0,0 +1,220 @@
1
+ # EffectScopeSelect
2
+
3
+ > 商品范围选择复合组件,级联聚合 分类→品牌→系列→SPU→SKU 五级选择器,通过 `scopeLevel` 控制显示深度。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { EffectScopeSelect, ScopeLevelEnum } from '@xfe-repo/web-components'
9
+ import type { ScopeSelectValueType, ScopeSelectProps } from '@xfe-repo/web-components'
10
+ ```
11
+
12
+ ## 基本用法
13
+
14
+ 最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
15
+
16
+ ```tsx
17
+ <Form.Item name="scope" label="适用范围">
18
+ <EffectScopeSelect categoryChangeOnSelect />
19
+ </Form.Item>
20
+ ```
21
+
22
+ ### 独立受控模式
23
+
24
+ ```tsx
25
+ import { EffectScopeSelect, ScopeLevelEnum } from '@xfe-repo/web-components'
26
+ import type { ScopeSelectValueType } from '@xfe-repo/web-components'
27
+
28
+ function MyForm() {
29
+ const [scope, setScope] = useState<ScopeSelectValueType>()
30
+
31
+ return (
32
+ <EffectScopeSelect
33
+ value={scope}
34
+ onChange={(value) => {
35
+ console.log(value)
36
+ // { categoryIds: [1, 11], brandId: 100, seriesId: 200, spuId: 300, skuId: 400 }
37
+ setScope(value)
38
+ }}
39
+ />
40
+ )
41
+ }
42
+ ```
43
+
44
+ ## 进阶用法
45
+
46
+ ### 限制显示层级
47
+
48
+ 使用 `scopeLevel` 控制选择器显示到哪一级。例如只需要选到品牌:
49
+
50
+ ```tsx
51
+ <EffectScopeSelect
52
+ scopeLevel={ScopeLevelEnum.BRAND} // 只显示:分类 + 品牌
53
+ onChange={(value) => {
54
+ // value: { categoryIds: [1, 11], brandId: 100 }
55
+ }}
56
+ />
57
+ ```
58
+
59
+ ### 设置必选层级
60
+
61
+ 使用 `requireLevel` 标记哪些层级为必填(显示红色星号),并控制 onChange 的触发时机。当选择达到 `requireLevel` 指定的层级时才触发 `onChange`。
62
+
63
+ ```tsx
64
+ <EffectScopeSelect
65
+ scopeLevel={ScopeLevelEnum.SKU}
66
+ requireLevel={ScopeLevelEnum.BRAND} // 分类和品牌为必选
67
+ onChange={(value) => {
68
+ // 选择品牌后即触发 onChange
69
+ }}
70
+ />
71
+ ```
72
+
73
+ ### 分类选择即触发
74
+
75
+ 默认分类需选到叶子节点才触发下一级。设置 `categoryChangeOnSelect` 后,选择任意层级即可触发。
76
+
77
+ ```tsx
78
+ <EffectScopeSelect categoryChangeOnSelect onChange={(value) => setScope(value)} />
79
+ ```
80
+
81
+ ### 受控模式
82
+
83
+ ```tsx
84
+ const [scope, setScope] = useState<ScopeSelectValueType>({
85
+ categoryIds: [1, 11, 111],
86
+ brandId: 100,
87
+ })
88
+
89
+ <EffectScopeSelect
90
+ value={scope}
91
+ onChange={setScope}
92
+ />
93
+ ```
94
+
95
+ ### ScopeSelectValueType ↔ 后端 DTO 转换
96
+
97
+ ```tsx
98
+ // 前端 → 后端
99
+ const toDTO = (scope: ScopeSelectValueType) => ({
100
+ categoryFst: scope.categoryIds?.[0],
101
+ categoryScd: scope.categoryIds?.[1],
102
+ categoryTrd: scope.categoryIds?.[2],
103
+ brandId: scope.brandId,
104
+ seriesId: scope.seriesId,
105
+ })
106
+
107
+ // 后端 → 前端
108
+ const fromDTO = (dto) => ({
109
+ categoryIds: [dto.categoryFst, dto.categoryScd, dto.categoryTrd].filter(Boolean),
110
+ brandId: dto.brandId,
111
+ seriesId: dto.seriesId,
112
+ })
113
+ ```
114
+
115
+ ## 使用提示
116
+
117
+ > 💡 `categoryChangeOnSelect` 在所有业务场景中均有使用,建议默认传入。
118
+
119
+ ## Props
120
+
121
+ ```typescript
122
+ export type ScopeSelectProps = {
123
+ value?: ScopeSelectValueType
124
+ defaultValue?: ScopeSelectValueType
125
+ scopeLevel?: ScopeLevelEnum
126
+ requireLevel?: ScopeLevelEnum
127
+ categoryChangeOnSelect?: boolean
128
+ onChange?: (value?: ScopeSelectValueType) => void
129
+ }
130
+ ```
131
+
132
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
133
+ | ---------------------- | ---------------------- | ---- | ----------------------------- | ---------------------------------------------------- |
134
+ | value | `ScopeSelectValueType` | 否 | - | 受控值 |
135
+ | defaultValue | `ScopeSelectValueType` | 否 | - | 默认值 |
136
+ | scopeLevel | `ScopeLevelEnum` | 否 | `ScopeLevelEnum.SKU` (5) | 控制显示到哪一级选择器 |
137
+ | requireLevel | `ScopeLevelEnum` | 否 | `ScopeLevelEnum.CATEGORY` (1) | 必填层级标记(显示星号),同时控制 onChange 触发时机 |
138
+ | categoryChangeOnSelect | `boolean` | 否 | - | 分类选择是否无需选到叶子节点即可触发 |
139
+ | onChange | `(value?) => void` | 否 | - | 值变化回调,选择达到 requireLevel 时触发 |
140
+
141
+ ## 导出类型
142
+
143
+ ```typescript
144
+ // 选择结果值类型
145
+ export type ScopeSelectValueType = {
146
+ categoryIds?: number[] // 分类路径 ID 数组
147
+ brandId?: number // 品牌 ID
148
+ seriesId?: number // 系列 ID
149
+ spuId?: number // SPU ID
150
+ skuId?: number // SKU ID
151
+ }
152
+
153
+ // 层级枚举
154
+ export enum ScopeLevelEnum {
155
+ CATEGORY = 1, // 分类
156
+ BRAND = 2, // 品牌
157
+ SERIES = 3, // 系列
158
+ SPU = 4, // SPU
159
+ SKU = 5, // SKU
160
+ }
161
+ ```
162
+
163
+ ## 子组件
164
+
165
+ EffectScopeSelect 内部组合了以下组件,按级联顺序渐进显示:
166
+
167
+ | 层级 | 内部组件 | 显示条件 | 数据依赖 |
168
+ | ---- | ----------------------- | ---------------------------- | --------------------------- |
169
+ | 1 | `EffectCategoryCascade` | 始终显示 | 无 |
170
+ | 2 | `EffectBrandSelect` | `scopeLevel >= 2` 且已选分类 | `categoryId`(分类末级 ID) |
171
+ | 3 | `EffectSeriesSelect` | `scopeLevel >= 3` 且已选品牌 | `categoryId` + `brandId` |
172
+ | 4 | `EffectSpuSelect` | `scopeLevel >= 4` 且已选系列 | `categoryId` + `seriesId` |
173
+ | 5 | `EffectSkuSelect` | `scopeLevel >= 5` 且已选 SPU | `categoryId` + `spuId` |
174
+
175
+ > 每一级的显示取决于上一级是否已选择有效值(非 undefined/falsy),上一级变更时下级自动清空。
176
+
177
+ ## 常见陷阱
178
+
179
+ - ❌ 混淆 `scopeLevel` 和 `requireLevel`:
180
+ ```tsx
181
+ // scopeLevel 控制「显示哪些选择器」
182
+ // requireLevel 控制「哪些是必填的」并影响 onChange 触发时机
183
+ <EffectScopeSelect scopeLevel={ScopeLevelEnum.SPU} requireLevel={ScopeLevelEnum.SKU} />
184
+ // 这会导致 onChange 永远不触发:因为 SKU 选择器不会显示(scopeLevel < SKU)
185
+ ```
186
+ - ✅ 确保 `requireLevel <= scopeLevel`:
187
+
188
+ ```tsx
189
+ <EffectScopeSelect scopeLevel={ScopeLevelEnum.SPU} requireLevel={ScopeLevelEnum.BRAND} />
190
+ ```
191
+
192
+ - ❌ 期望 `onChange` 在每次选择时都触发:
193
+ ```tsx
194
+ // 实际上只有在选择达到 requireLevel 指定的层级时才会触发
195
+ ```
196
+ - ✅ 如需在每次选择时都触发,将 `requireLevel` 设为 `ScopeLevelEnum.CATEGORY`(默认值)
197
+
198
+ - ❌ 当已经使用 EffectScopeSelect 时又单独使用内部的 EffectCategoryCascade、EffectBrandSelect 等
199
+ - ✅ EffectScopeSelect 已内部集成所有级联组件的联动逻辑,直接使用即可
200
+
201
+ - ⚠️ Form.List 动态行中使用时,需添加自定义 validator 防止重复:
202
+ ```tsx
203
+ validator: (_, value) => {
204
+ const key = JSON.stringify(value)
205
+ const isDup = allRows.filter((r) => JSON.stringify(r) === key).length > 1
206
+ return isDup ? Promise.reject('范围重复') : Promise.resolve()
207
+ }
208
+ ```
209
+
210
+ ## 相关组件
211
+
212
+ | 场景 | 组件 | 说明 |
213
+ | -------- | ----------------------- | ----------------------------------------------------- |
214
+ | 分类选择 | `EffectCategoryCascade` | 内部使用,单独使用时参见其文档 |
215
+ | 品牌选择 | `EffectBrandSelect` | 内部使用,单独使用时参见其文档 |
216
+ | 系列选择 | `EffectSeriesSelect` | 内部使用,提供系列级选择 |
217
+ | SPU 选择 | `EffectSpuSelect` | 内部使用,提供 SPU 级选择 |
218
+ | SKU 选择 | `EffectSkuSelect` | 内部使用,提供 SKU 级选择 |
219
+ | API 配置 | `ConfigProvider` | 所有内部 Effect 组件依赖 ConfigProvider 提供 API 服务 |
220
+ | 弹窗使用 | `WithModal` | 常在弹窗中使用商品范围选择 |