@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,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` |