@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,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` | 常在弹窗中使用商品范围选择 |
|