@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,205 @@
|
|
|
1
|
+
# EffectSeriesSelect
|
|
2
|
+
|
|
3
|
+
> 系列下拉选择器,支持按分类和品牌过滤、远程搜索、图片预览和无限滚动加载,数据通过 `apiService.seriesComboBox` 自动获取。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectSeriesSelect } from '@xfe-repo/web-components'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 基本用法
|
|
12
|
+
|
|
13
|
+
最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Form.Item name="seriesId" label="系列">
|
|
17
|
+
<EffectSeriesSelect categoryId={categoryId} brandId={brandId} />
|
|
18
|
+
</Form.Item>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 级联使用(标准模式)
|
|
22
|
+
|
|
23
|
+
EffectSeriesSelect 通常作为 分类→品牌→系列 级联链的最后一环:
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
const categoryId = Form.useWatch('categoryIds', form)?.at(-1)
|
|
27
|
+
const brandId = Form.useWatch('brandId', form)
|
|
28
|
+
|
|
29
|
+
<Form.Item name="seriesId" label="系列">
|
|
30
|
+
<EffectSeriesSelect
|
|
31
|
+
categoryId={categoryId}
|
|
32
|
+
brandId={brandId}
|
|
33
|
+
key={`${categoryId}-${brandId}`}
|
|
34
|
+
/>
|
|
35
|
+
</Form.Item>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> ⚠️ 建议同时传入 `categoryId` 和 `brandId`,仅传一个会降低过滤精度。
|
|
39
|
+
|
|
40
|
+
### 独立受控模式
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { EffectSeriesSelect } from '@xfe-repo/web-components'
|
|
44
|
+
|
|
45
|
+
function MyForm() {
|
|
46
|
+
const [seriesId, setSeriesId] = useState<number>()
|
|
47
|
+
|
|
48
|
+
return <EffectSeriesSelect value={seriesId} onChange={(value) => setSeriesId(value)} />
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 进阶用法
|
|
53
|
+
|
|
54
|
+
### 按分类 + 品牌过滤系列
|
|
55
|
+
|
|
56
|
+
`categoryId` 或 `brandId` 变更时,组件会自动重新请求系列列表并清空已选值。
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
60
|
+
const [brandId, setBrandId] = useState<number>()
|
|
61
|
+
const [seriesId, setSeriesId] = useState<number>()
|
|
62
|
+
|
|
63
|
+
<EffectCategoryCascade
|
|
64
|
+
onChange={(values, selectedList) => {
|
|
65
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
66
|
+
const lastItem = selectedList[selectedList.length - 1]
|
|
67
|
+
setCategoryId(Number(lastItem.value))
|
|
68
|
+
}
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
<EffectBrandSelect
|
|
72
|
+
categoryId={categoryId}
|
|
73
|
+
value={brandId}
|
|
74
|
+
onChange={(value) => setBrandId(value as number)}
|
|
75
|
+
/>
|
|
76
|
+
<EffectSeriesSelect
|
|
77
|
+
categoryId={categoryId}
|
|
78
|
+
brandId={brandId}
|
|
79
|
+
value={seriesId}
|
|
80
|
+
onChange={(value) => setSeriesId(value)}
|
|
81
|
+
/>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Props
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
type ConditionsType = { categoryId?: number; brandId?: number }
|
|
88
|
+
|
|
89
|
+
type Props = ConditionsType & {
|
|
90
|
+
className?: string
|
|
91
|
+
onChange?: (value?: number, name?: string) => void
|
|
92
|
+
value?: number
|
|
93
|
+
defaultValue?: string
|
|
94
|
+
disabled?: boolean
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
99
|
+
| ------------ | ----------------------------------------- | ---- | ------ | --------------------------------------- |
|
|
100
|
+
| value | `number` | 否 | - | 受控值(系列 ID) |
|
|
101
|
+
| defaultValue | `string` | 否 | - | 默认值 |
|
|
102
|
+
| onChange | `(value?: number, name?: string) => void` | 否 | - | 值变化回调,第二个参数为系列名称 |
|
|
103
|
+
| categoryId | `number` | 否 | - | 分类 ID,变更时自动重新加载并清空选中值 |
|
|
104
|
+
| brandId | `number` | 否 | - | 品牌 ID,变更时自动重新加载并清空选中值 |
|
|
105
|
+
| disabled | `boolean` | 否 | - | 禁用状态 |
|
|
106
|
+
| className | `string` | 否 | - | 自定义样式类名 |
|
|
107
|
+
|
|
108
|
+
## 导出类型
|
|
109
|
+
|
|
110
|
+
组件本身未导出独立类型。内部使用了从 `EffectSkuSelect` 导入的 `ItemOption`、`ValueType`、`SelectDropdown` 等。
|
|
111
|
+
|
|
112
|
+
## API 数据源
|
|
113
|
+
|
|
114
|
+
- **内部 Hook**:`useSeriesOptions`(使用 SWR 缓存)
|
|
115
|
+
- **内部接口**:`apiService.seriesComboBox`
|
|
116
|
+
- **请求参数**:`{ categoryId, brandId, id, seriesName, page, pageSize }`
|
|
117
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ seriesComboBox: customFn }}>` 替换默认实现
|
|
118
|
+
|
|
119
|
+
## 依赖 Props
|
|
120
|
+
|
|
121
|
+
| 依赖属性 | 类型 | 来源 | 说明 |
|
|
122
|
+
| ---------- | -------- | ---------------------------- | ------------------ |
|
|
123
|
+
| categoryId | `number` | EffectCategoryCascade 的输出 | 按分类过滤系列列表 |
|
|
124
|
+
| brandId | `number` | EffectBrandSelect 的输出 | 按品牌过滤系列列表 |
|
|
125
|
+
|
|
126
|
+
### 联动示例
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { useState } from 'react'
|
|
130
|
+
import { EffectCategoryCascade, EffectBrandSelect, EffectSeriesSelect, isSingleTreeSelected } from '@xfe-repo/web-components'
|
|
131
|
+
import type { TreeValueType, TreeSelectList, BrandValueType } from '@xfe-repo/web-components'
|
|
132
|
+
|
|
133
|
+
function CategoryBrandSeriesFilter() {
|
|
134
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
135
|
+
const [brandId, setBrandId] = useState<number>()
|
|
136
|
+
const [seriesId, setSeriesId] = useState<number>()
|
|
137
|
+
|
|
138
|
+
const handleCategoryChange = (values?: TreeValueType, selectedList?: TreeSelectList) => {
|
|
139
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
140
|
+
const lastItem = selectedList[selectedList.length - 1]
|
|
141
|
+
setCategoryId(Number(lastItem.value))
|
|
142
|
+
} else {
|
|
143
|
+
setCategoryId(undefined)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div>
|
|
149
|
+
<EffectCategoryCascade onChange={handleCategoryChange} />
|
|
150
|
+
<EffectBrandSelect categoryId={categoryId} value={brandId} onChange={(value) => setBrandId(value as number)} />
|
|
151
|
+
<EffectSeriesSelect categoryId={categoryId} brandId={brandId} value={seriesId} onChange={(value) => setSeriesId(value)} />
|
|
152
|
+
</div>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 常见陷阱
|
|
158
|
+
|
|
159
|
+
- ❌ `categoryId` 或 `brandId` 变更后仍然期望保留之前选中的系列值:
|
|
160
|
+
```tsx
|
|
161
|
+
// conditions 变更时组件内部会自动清空 value 并触发 onChange()
|
|
162
|
+
```
|
|
163
|
+
- ✅ 在 `onChange` 回调中处理清空逻辑:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<EffectSeriesSelect
|
|
167
|
+
categoryId={categoryId}
|
|
168
|
+
brandId={brandId}
|
|
169
|
+
onChange={(value) => {
|
|
170
|
+
// value 可能为 undefined(上游条件变更导致的自动清空)
|
|
171
|
+
setSeriesId(value)
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- ❌ 只传 `brandId` 不传 `categoryId` 期望获取全量系列列表——两个条件独立生效,不传即不过滤
|
|
177
|
+
- ✅ 根据业务需要传递对应的过滤条件
|
|
178
|
+
|
|
179
|
+
- ⚠️ 仅传 `brandId` 不传 `categoryId` 会降低过滤精度,返回该品牌在所有分类下的系列。建议始终同时传入两个参数。
|
|
180
|
+
|
|
181
|
+
- ❌ value 为 `0` 时被误判为空值——组件内部 `value || undefined` 会将 `0` 视为 falsy
|
|
182
|
+
- ✅ 使用有效的正整数 ID 作为 value
|
|
183
|
+
|
|
184
|
+
### 多选版本(EffectSeriesSelectV2)
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { EffectSeriesSelectV2 } from '@xfe-repo/web-components'
|
|
188
|
+
|
|
189
|
+
;<Form.Item name="seriesIds" label="系列(多选)">
|
|
190
|
+
<EffectSeriesSelectV2 categoryId={categoryId} brandId={brandId} mode="multiple" />
|
|
191
|
+
</Form.Item>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
> 💡 V2 固定为多选模式,需同时传入 `categoryId` 和 `brandId`。详见 [EffectSeriesSelectV2](./EffectSeriesSelectV2.md)。
|
|
195
|
+
|
|
196
|
+
## 相关组件
|
|
197
|
+
|
|
198
|
+
| 场景 | 组件 | 说明 |
|
|
199
|
+
| -------- | ----------------------- | --------------------------------------- |
|
|
200
|
+
| 级联上游 | `EffectCategoryCascade` | 提供 categoryId |
|
|
201
|
+
| 级联上游 | `EffectBrandSelect` | 提供 brandId |
|
|
202
|
+
| 级联下游 | `EffectSpuSelect` | 使用 seriesId 过滤 SPU 列表 |
|
|
203
|
+
| 多选版本 | `EffectSeriesSelectV2` | 固定多选模式的系列选择器 |
|
|
204
|
+
| 聚合使用 | `EffectScopeSelect` | 内部已集成本组件,无需单独使用 |
|
|
205
|
+
| API 配置 | `ConfigProvider` | 提供 apiService.seriesComboBox 接口实现 |
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# EffectSeriesSelectV2
|
|
2
|
+
|
|
3
|
+
> 系列多选器 V2,基于品牌和分类筛选系列列表,支持远程搜索和滚动加载更多,固定 `mode="multiple"` 多选模式。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectSeriesSelectV2 } from '@xfe-repo/web-components'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 基本用法
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { EffectSeriesSelectV2 } from '@xfe-repo/web-components'
|
|
15
|
+
|
|
16
|
+
function MyForm() {
|
|
17
|
+
const [seriesIds, setSeriesIds] = useState<number[]>([])
|
|
18
|
+
|
|
19
|
+
return <EffectSeriesSelectV2 brandId={1} categoryId={100} value={seriesIds} onChange={(values) => setSeriesIds(values)} />
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 进阶用法
|
|
24
|
+
|
|
25
|
+
### 分类 → 品牌 → 系列三级联动
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { EffectCategoryCascade, EffectBrandSelect, EffectSeriesSelectV2 } from '@xfe-repo/web-components'
|
|
29
|
+
|
|
30
|
+
function CategoryBrandSeriesFilter() {
|
|
31
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
32
|
+
const [brandId, setBrandId] = useState<number>()
|
|
33
|
+
const [seriesIds, setSeriesIds] = useState<number[]>([])
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div>
|
|
37
|
+
<EffectCategoryCascade
|
|
38
|
+
onChange={(values, selectedList) => {
|
|
39
|
+
if (selectedList && isSingleTreeSelected(selectedList)) {
|
|
40
|
+
setCategoryId(Number(selectedList[selectedList.length - 1].value))
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<EffectBrandSelect categoryId={categoryId} value={brandId} onChange={(value) => setBrandId(value as number)} />
|
|
45
|
+
<EffectSeriesSelectV2 brandId={brandId} categoryId={categoryId} value={seriesIds} onChange={setSeriesIds} />
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 自定义分页大小
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<EffectSeriesSelectV2 brandId={1} categoryId={100} pageSize={20} value={seriesIds} onChange={setSeriesIds} />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Props
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
type EffectSeriesSelectV2Props = Omit<SelectProps<number[]>, 'mode' | 'onChange'> & {
|
|
61
|
+
allowClear?: boolean
|
|
62
|
+
brandId?: number
|
|
63
|
+
categoryId?: number
|
|
64
|
+
onChange?: (value: number[]) => void
|
|
65
|
+
pageSize?: number
|
|
66
|
+
placeholder?: string
|
|
67
|
+
value?: number[]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
72
|
+
| ------------ | --------------------------- | ---- | -------------- | ------------------------------------------------------ |
|
|
73
|
+
| value | `number[]` | 否 | - | 受控值,选中的系列 ID 数组 |
|
|
74
|
+
| onChange | `(value: number[]) => void` | 否 | - | 值变化回调 |
|
|
75
|
+
| brandId | `number` | 否 | - | **核心依赖**:品牌 ID,变更时重新加载系列列表 |
|
|
76
|
+
| categoryId | `number` | 否 | - | **核心依赖**:分类 ID,变更时重新加载系列列表 |
|
|
77
|
+
| pageSize | `number` | 否 | `10` | 每页加载数量(滚动加载更多) |
|
|
78
|
+
| allowClear | `boolean` | 否 | `true` | 是否显示清除按钮 |
|
|
79
|
+
| placeholder | `string` | 否 | `'请选择系列'` | 占位符文本 |
|
|
80
|
+
| disabled | `boolean` | 否 | - | 禁用状态(继承自 SelectProps) |
|
|
81
|
+
| className | `string` | 否 | - | 自定义样式类名(继承自 SelectProps) |
|
|
82
|
+
| ...restProps | `SelectProps` | 否 | - | 其他 Ant Design Select 属性(除 `mode` 和 `onChange`) |
|
|
83
|
+
|
|
84
|
+
> **注意**:`mode` 固定为 `"multiple"`,无法通过 props 修改。
|
|
85
|
+
|
|
86
|
+
## API 数据源
|
|
87
|
+
|
|
88
|
+
- **内部接口**:`apiService.seriesList`
|
|
89
|
+
- **请求参数**:`{ brandId, categoryId, page, pageSize, seriesName }`
|
|
90
|
+
- **前置条件**:`brandId` 和 `categoryId` 都必须存在才会发起请求
|
|
91
|
+
- **搜索策略**:300ms 防抖远程搜索
|
|
92
|
+
- **分页策略**:下拉滚动到底部自动加载下一页,追加到已有选项列表
|
|
93
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ seriesList: customFn }}>` 替换默认实现
|
|
94
|
+
|
|
95
|
+
## 依赖 Props
|
|
96
|
+
|
|
97
|
+
| 依赖属性 | 类型 | 来源 | 说明 |
|
|
98
|
+
| ---------- | -------- | ---------------------------- | ------------------ |
|
|
99
|
+
| brandId | `number` | EffectBrandSelect 的输出 | 按品牌过滤系列列表 |
|
|
100
|
+
| categoryId | `number` | EffectCategoryCascade 的输出 | 按分类过滤系列列表 |
|
|
101
|
+
|
|
102
|
+
### 联动示例
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
const [categoryId, setCategoryId] = useState<number>()
|
|
106
|
+
const [brandId, setBrandId] = useState<number>()
|
|
107
|
+
const [seriesIds, setSeriesIds] = useState<number[]>([])
|
|
108
|
+
|
|
109
|
+
// brandId 或 categoryId 变更时,EffectSeriesSelectV2 会自动重新加载列表
|
|
110
|
+
<EffectSeriesSelectV2
|
|
111
|
+
brandId={brandId}
|
|
112
|
+
categoryId={categoryId}
|
|
113
|
+
value={seriesIds}
|
|
114
|
+
onChange={setSeriesIds}
|
|
115
|
+
/>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 常见陷阱
|
|
119
|
+
|
|
120
|
+
- ❌ 只传 `brandId` 不传 `categoryId`(或反过来):
|
|
121
|
+
```tsx
|
|
122
|
+
// 两者都为空时选项列表为空,不会发起请求
|
|
123
|
+
```
|
|
124
|
+
- ✅ 确保 `brandId` 和 `categoryId` 同时存在
|
|
125
|
+
|
|
126
|
+
- ❌ 尝试修改 `mode` 为单选:
|
|
127
|
+
```tsx
|
|
128
|
+
// mode 被从 Props 中 Omit 掉,固定为 "multiple"
|
|
129
|
+
```
|
|
130
|
+
- ✅ 如需单选系列,请使用 `EffectSeriesSelect` 组件
|
|
131
|
+
|
|
132
|
+
- ❌ 期望 `brandId`/`categoryId` 变更后保留已选值:
|
|
133
|
+
```tsx
|
|
134
|
+
// 依赖变更时组件会重新加载选项列表,但不会自动清空 value
|
|
135
|
+
// 需要自行在上游变更时重置 seriesIds
|
|
136
|
+
```
|
|
137
|
+
- ✅ 在上游变更回调中清空系列值:
|
|
138
|
+
```tsx
|
|
139
|
+
<EffectBrandSelect
|
|
140
|
+
onChange={(value) => {
|
|
141
|
+
setBrandId(value as number)
|
|
142
|
+
setSeriesIds([]) // 手动清空系列选择
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 相关组件
|
|
148
|
+
|
|
149
|
+
| 场景 | 组件 | 说明 |
|
|
150
|
+
| -------- | ----------------------- | ------------------------------------- |
|
|
151
|
+
| 级联上游 | `EffectCategoryCascade` | 提供 categoryId |
|
|
152
|
+
| 级联上游 | `EffectBrandSelect` | 提供 brandId |
|
|
153
|
+
| 单选替代 | `EffectSeriesSelect` | 系列单选器(V1) |
|
|
154
|
+
| API 配置 | `ConfigProvider` | 提供 `apiService.seriesList` 接口实现 |
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# EffectSkuRecognize
|
|
2
|
+
|
|
3
|
+
> SKU 图片智能识别组件,通过传入图片 URL 进行智能识图,展示相似 SKU 卡片列表供用户选择。
|
|
4
|
+
|
|
5
|
+
## 导入
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { EffectSkuRecognize } from '@xfe-repo/web-components'
|
|
9
|
+
import type { EffectSkuRecognizeProps } from '@xfe-repo/web-components'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 基本用法
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { EffectSkuRecognize } from '@xfe-repo/web-components'
|
|
16
|
+
|
|
17
|
+
function SkuMatcher() {
|
|
18
|
+
const [skuId, setSkuId] = useState<number>()
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<EffectSkuRecognize
|
|
22
|
+
image="https://example.com/product.jpg"
|
|
23
|
+
onChange={(skuId, skuItem) => {
|
|
24
|
+
setSkuId(skuId)
|
|
25
|
+
console.log('选中 SKU:', skuItem)
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 进阶用法
|
|
33
|
+
|
|
34
|
+
### 限定品牌和分类范围
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<EffectSkuRecognize image={imageUrl} brandId={123} categoryId={456} onChange={(skuId, skuItem) => setSkuId(skuId)} />
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 限制结果数量
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<EffectSkuRecognize image={imageUrl} limitSize={5} onChange={(skuId, skuItem) => setSkuId(skuId)} />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 紧凑布局
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<EffectSkuRecognize image={imageUrl} size="small" onChange={(skuId, skuItem) => setSkuId(skuId)} />
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 识别临时 SKU
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<EffectSkuRecognize image={imageUrl} skuType="tmp" onChange={(skuId, skuItem) => setSkuId(skuId)} />
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Props
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
export interface EffectSkuRecognizeProps {
|
|
62
|
+
categoryId?: number
|
|
63
|
+
brandId?: number
|
|
64
|
+
image: string
|
|
65
|
+
limitSize?: number
|
|
66
|
+
onChange?: (skuId: number, skuItem: ListSkuV2) => void
|
|
67
|
+
value?: number
|
|
68
|
+
defaultValue?: number
|
|
69
|
+
disabled?: boolean
|
|
70
|
+
size?: 'small' | 'default'
|
|
71
|
+
skuType?: 'std' | 'tmp'
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
76
|
+
| ------------ | -------------------------- | ------ | ----------- | --------------------------------------------- |
|
|
77
|
+
| image | `string` | **是** | - | 用于识别的图片 URL |
|
|
78
|
+
| value | `number` | 否 | - | 受控值(SKU ID) |
|
|
79
|
+
| defaultValue | `number` | 否 | - | 默认值 |
|
|
80
|
+
| onChange | `(skuId, skuItem) => void` | 否 | - | 选中 SKU 回调,返回 skuId 和完整 SKU 数据 |
|
|
81
|
+
| categoryId | `number` | 否 | - | 分类 ID,缩小识别范围 |
|
|
82
|
+
| brandId | `number` | 否 | - | 品牌 ID,缩小识别范围 |
|
|
83
|
+
| limitSize | `number` | 否 | - | 最多显示的结果数量 |
|
|
84
|
+
| size | `'small' \| 'default'` | 否 | `'default'` | 卡片网格布局尺寸 |
|
|
85
|
+
| skuType | `'std' \| 'tmp'` | 否 | `'std'` | SKU 类型,`'std'` 标准 SKU / `'tmp'` 临时 SKU |
|
|
86
|
+
| disabled | `boolean` | 否 | - | 禁用时隐藏"就是它"选择按钮 |
|
|
87
|
+
|
|
88
|
+
## 导出类型
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
export interface EffectSkuRecognizeProps { ... }
|
|
92
|
+
|
|
93
|
+
// ListSkuV2 来自 @api/product,包含以下关键字段:
|
|
94
|
+
// skuId, skuName, brandName, seriesName, image, skuImage, score, name 等
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API 数据源
|
|
98
|
+
|
|
99
|
+
- **内部接口**:`apiService.productImageSpuSkuList`
|
|
100
|
+
- **请求参数**:`{ type: 'sku', skuType, brandId, categoryId, image }`
|
|
101
|
+
- **触发时机**:组件挂载时、`image`/`brandId`/`categoryId`/`skuType` 变更时自动触发识别
|
|
102
|
+
- **可覆盖**:通过 `<ConfigProvider apiService={{ productImageSpuSkuList: customFn }}>` 替换
|
|
103
|
+
|
|
104
|
+
## 依赖 Props
|
|
105
|
+
|
|
106
|
+
| 依赖属性 | 类型 | 来源 | 说明 |
|
|
107
|
+
| ---------- | -------- | ---------------------------- | ---------------- |
|
|
108
|
+
| brandId | `number` | EffectBrandSelect 的输出 | 限定识别品牌范围 |
|
|
109
|
+
| categoryId | `number` | EffectCategoryCascade 的输出 | 限定识别分类范围 |
|
|
110
|
+
|
|
111
|
+
## 内部结构
|
|
112
|
+
|
|
113
|
+
组件渲染为一个 `Spin` 加载容器,内部使用 `OSSImage.PreviewGroup` 包裹 `List` 列表。每个结果项为一个 `Card`,包含:
|
|
114
|
+
|
|
115
|
+
- `Badge`:显示相似度分数
|
|
116
|
+
- `OSSImage`:SKU 图片(支持预览)
|
|
117
|
+
- `Link`:跳转到 SKU 详情页 `/dashboard/product/standardSku/detail?skuId=xxx`
|
|
118
|
+
- `Button`:"就是它"选择按钮(disabled 时隐藏)
|
|
119
|
+
|
|
120
|
+
预览模式下底部工具栏也有"就是它"按钮。
|
|
121
|
+
|
|
122
|
+
## 常见陷阱
|
|
123
|
+
|
|
124
|
+
- ❌ 不传 `image` prop:
|
|
125
|
+
```tsx
|
|
126
|
+
// image 为空字符串时不会发起请求,组件不会显示任何结果
|
|
127
|
+
```
|
|
128
|
+
- ✅ 确保传入有效的图片 URL
|
|
129
|
+
|
|
130
|
+
- ❌ 期望 `disabled` 时隐藏整个组件:
|
|
131
|
+
```tsx
|
|
132
|
+
// disabled 仅隐藏 "就是它" 按钮,结果列表仍然展示
|
|
133
|
+
```
|
|
134
|
+
- ✅ 如需完全隐藏,请在外层自行控制渲染
|
|
135
|
+
|
|
136
|
+
- ❌ `image` 变更时未注意到会自动重新识别:
|
|
137
|
+
```tsx
|
|
138
|
+
// 每次 image 变化都会触发新的识别请求
|
|
139
|
+
```
|
|
140
|
+
- ✅ 确保 image 稳定引用,避免不必要的重复请求
|
|
141
|
+
|
|
142
|
+
## 相关组件
|
|
143
|
+
|
|
144
|
+
| 场景 | 组件 | 说明 |
|
|
145
|
+
| ------------ | ----------------- | ---------------------------------------- |
|
|
146
|
+
| SKU 选择替代 | `EffectSkuSelect` | 基于搜索的 SKU 下拉选择器 |
|
|
147
|
+
| SKU 表格选择 | `EffectSkuTable` | 分类/品牌/系列/SPU/SKU 联动表格 |
|
|
148
|
+
| 图片展示 | `OSSImage` | 内部使用的 OSS 图片组件 |
|
|
149
|
+
| API 配置 | `ConfigProvider` | 提供 `apiService.productImageSpuSkuList` |
|