@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,150 @@
1
+ # EffectStaffSelect
2
+
3
+ > 员工选择器,支持两种模式:传入部门 ID 时预加载该部门员工列表(前端过滤),不传时按关键字远程搜索员工。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { EffectStaffSelect } from '@xfe-repo/web-components'
9
+ ```
10
+
11
+ ## 基本用法
12
+
13
+ 最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
14
+
15
+ ```tsx
16
+ <Form.Item name="staffId" label="员工">
17
+ <EffectStaffSelect />
18
+ </Form.Item>
19
+ ```
20
+
21
+ ### 远程搜索模式(不传 deptId)
22
+
23
+ ```tsx
24
+ import { EffectStaffSelect } from '@xfe-repo/web-components'
25
+
26
+ function MyForm() {
27
+ const [staffId, setStaffId] = useState<number>()
28
+
29
+ return (
30
+ <EffectStaffSelect
31
+ value={staffId}
32
+ onChange={(value, name) => {
33
+ setStaffId(value)
34
+ console.log('员工姓名:', name)
35
+ }}
36
+ />
37
+ )
38
+ }
39
+ ```
40
+
41
+ ### 部门员工模式(传入 deptId)
42
+
43
+ ```tsx
44
+ <EffectStaffSelect deptId={10} value={staffId} onChange={(value, name) => setStaffId(value)} />
45
+ ```
46
+
47
+ ## 进阶用法
48
+
49
+ ### 部门联动
50
+
51
+ ```tsx
52
+ function DeptStaffPicker() {
53
+ const [deptId, setDeptId] = useState<number>()
54
+ const [staffId, setStaffId] = useState<number>()
55
+
56
+ return (
57
+ <div>
58
+ <DeptSelect onChange={(id) => setDeptId(id)} />
59
+ <EffectStaffSelect deptId={deptId} value={staffId} onChange={(value) => setStaffId(value)} />
60
+ </div>
61
+ )
62
+ }
63
+ ```
64
+
65
+ ## Props
66
+
67
+ ```typescript
68
+ type EffectStaffSelectProps = {
69
+ className?: string
70
+ onChange?: (value?: number, name?: string) => void
71
+ deptId?: number
72
+ value?: number
73
+ defaultValue?: number
74
+ disabled?: boolean
75
+ placeholder?: string
76
+ style?: React.CSSProperties
77
+ }
78
+ ```
79
+
80
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
81
+ | ------------ | ------------------------- | ---- | ------------------ | ----------------------------------------------------------- |
82
+ | value | `number` | 否 | - | 受控值(员工 user_id) |
83
+ | defaultValue | `number` | 否 | - | 默认值 |
84
+ | onChange | `(value?, name?) => void` | 否 | - | 值变化回调,返回员工 ID 和姓名 |
85
+ | deptId | `number` | 否 | - | 部门 ID。传入时:预加载部门员工、前端过滤;不传时:远程搜索 |
86
+ | disabled | `boolean` | 否 | - | 禁用状态 |
87
+ | placeholder | `string` | 否 | 动态生成(见说明) | 占位符文本 |
88
+ | className | `string` | 否 | - | 自定义样式类名 |
89
+ | style | `React.CSSProperties` | 否 | - | 自定义行内样式 |
90
+
91
+ ### placeholder 默认值逻辑
92
+
93
+ - 有 `deptId`:`"请选择员工姓名"`
94
+ - 无 `deptId`:`"请先输入员工姓名"`
95
+
96
+ ## API 数据源
97
+
98
+ - **内部接口**:`apiService.businessList`
99
+ - **请求参数**:`{ nameFilter: string, deptId?: number, page: 1, pageSize: 20 }`
100
+ - **搜索策略**:
101
+ - 有 `deptId`:挂载时预加载部门员工,搜索时使用前端 `optionFilterProp="label"` 过滤
102
+ - 无 `deptId`:输入时 600ms 防抖后远程搜索
103
+ - **可覆盖**:通过 `<ConfigProvider apiService={{ businessList: customFn }}>` 替换默认实现
104
+
105
+ ## 依赖 Props
106
+
107
+ | 依赖属性 | 类型 | 来源 | 说明 |
108
+ | -------- | -------- | ---------- | ------------------------------------ |
109
+ | deptId | `number` | 部门选择器 | 切换搜索模式,变更时重新加载员工列表 |
110
+
111
+ ### 联动说明
112
+
113
+ `deptId` 变更时:
114
+
115
+ 1. 清空已有选项列表
116
+ 2. 如果有新的 `deptId`,自动请求该部门员工列表
117
+ 3. 保持 `value` 为 `controlValue`(受控模式)
118
+
119
+ ## 常见陷阱
120
+
121
+ - ❌ 不传 `deptId` 时期望挂载后自动加载数据:
122
+ ```tsx
123
+ // 无 deptId 模式下,只有用户输入搜索后才会请求
124
+ // notFoundContent 显示 "请先输入员工姓名"
125
+ ```
126
+ - ✅ 如需预加载数据,传入 `deptId`
127
+
128
+ - ❌ `deptId` 变更后期望已选值自动更新:
129
+ ```tsx
130
+ // deptId 变更时 value 会保持为 controlValue
131
+ // 但选项列表已刷新,之前选中的员工可能不在新列表中
132
+ ```
133
+ - ✅ 在 `deptId` 变更时手动清空 value:
134
+
135
+ ```tsx
136
+ const handleDeptChange = (nextDeptId: number) => {
137
+ setDeptId(nextDeptId)
138
+ setStaffId(undefined) // 清空已选员工
139
+ }
140
+ ```
141
+
142
+ - ❌ 使用 `onSearch` 覆盖搜索行为(组件不暴露此 prop)
143
+ - ✅ 组件内部已处理搜索逻辑,无需额外配置
144
+
145
+ ## 相关组件
146
+
147
+ | 场景 | 组件 | 说明 |
148
+ | ---------- | ------------------- | --------------------------------------- |
149
+ | 多维度搜索 | `EffectLabelSelect` | 支持切换搜索维度的通用选择器 |
150
+ | API 配置 | `ConfigProvider` | 提供 `apiService.businessList` 接口实现 |
@@ -0,0 +1,183 @@
1
+ # EffectWarehouseSelect
2
+
3
+ > 仓库选择器,支持按仓库类型筛选(自有仓/门店仓),数据通过 `apiService.storeComboBox` 获取并使用 SWR 缓存。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { EffectWarehouseSelect } from '@xfe-repo/web-components'
9
+ import type { WarehouseOption } from '@xfe-repo/web-components'
10
+ ```
11
+
12
+ ## 基本用法
13
+
14
+ 最常见用法 — 在 Form.Item 中使用,value/onChange 由 Form 自动注入:
15
+
16
+ ```tsx
17
+ <Form.Item name="warehouseId" label="仓库">
18
+ <EffectWarehouseSelect />
19
+ </Form.Item>
20
+ ```
21
+
22
+ ### 独立受控模式
23
+
24
+ ```tsx
25
+ import { EffectWarehouseSelect } from '@xfe-repo/web-components'
26
+
27
+ function MyForm() {
28
+ const [warehouseId, setWarehouseId] = useState<string>()
29
+
30
+ return (
31
+ <EffectWarehouseSelect
32
+ value={warehouseId}
33
+ onChange={(value, name) => {
34
+ setWarehouseId(value)
35
+ console.log('仓库名称:', name)
36
+ }}
37
+ />
38
+ )
39
+ }
40
+ ```
41
+
42
+ ## 进阶用法
43
+
44
+ ### 按仓库类型筛选
45
+
46
+ ```tsx
47
+ // 仅显示自有仓
48
+ <EffectWarehouseSelect warehouseType="SELF_OWNED" />
49
+
50
+ // 仅显示门店仓
51
+ <EffectWarehouseSelect warehouseType="STORE" />
52
+ ```
53
+
54
+ ### 显示所有商户仓库
55
+
56
+ 默认仅显示当前用户商户的仓库,设置 `currentMerchantOnly={false}` 显示所有:
57
+
58
+ ```tsx
59
+ <EffectWarehouseSelect currentMerchantOnly={false} />
60
+ ```
61
+
62
+ ### 仓库 → 库区 → 库位级联
63
+
64
+ ```tsx
65
+ import { EffectWarehouseSelect, EffectReservoirSelect, EffectShelveSelect } from '@xfe-repo/web-components'
66
+
67
+ function WarehouseLocationPicker() {
68
+ const [warehouseId, setWarehouseId] = useState<string>()
69
+ const [reservoirId, setReservoirId] = useState<number>()
70
+ const [shelveCode, setShelveCode] = useState<string>()
71
+
72
+ return (
73
+ <div>
74
+ <EffectWarehouseSelect
75
+ value={warehouseId}
76
+ onChange={(value) => {
77
+ setWarehouseId(value)
78
+ setReservoirId(undefined)
79
+ setShelveCode(undefined)
80
+ }}
81
+ />
82
+ <EffectReservoirSelect
83
+ warehouseId={warehouseId ? Number(warehouseId) : undefined}
84
+ value={reservoirId}
85
+ onChange={(value) => {
86
+ setReservoirId(value)
87
+ setShelveCode(undefined)
88
+ }}
89
+ />
90
+ <EffectShelveSelect
91
+ warehouseId={warehouseId ? Number(warehouseId) : undefined}
92
+ reservoirId={reservoirId}
93
+ value={shelveCode}
94
+ onChange={(value) => setShelveCode(value)}
95
+ />
96
+ </div>
97
+ )
98
+ }
99
+ ```
100
+
101
+ ## Props
102
+
103
+ ```typescript
104
+ interface EffectWarehouseSelectProps {
105
+ className?: string
106
+ disabled?: boolean
107
+ value?: string
108
+ onChange?: (value?: string, name?: string) => void
109
+ warehouseType?: string
110
+ currentMerchantOnly?: boolean
111
+ }
112
+ ```
113
+
114
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
115
+ | ------------------- | ------------------------- | ---- | ------ | ------------------------------------------------------ |
116
+ | value | `string` | 否 | - | 受控值(仓库 ID,注意是 string 类型) |
117
+ | onChange | `(value?, name?) => void` | 否 | - | 值变化回调,返回仓库 ID(string)和名称 |
118
+ | warehouseType | `string` | 否 | - | 仓库类型筛选:`'SELF_OWNED'` 自有仓 / `'STORE'` 门店仓 |
119
+ | currentMerchantOnly | `boolean` | 否 | `true` | 是否仅显示当前用户商户的仓库 |
120
+ | disabled | `boolean` | 否 | - | 禁用状态 |
121
+ | className | `string` | 否 | - | 自定义样式类名 |
122
+
123
+ ## 导出类型
124
+
125
+ ```typescript
126
+ // 选项数据结构
127
+ export type WarehouseOption = {
128
+ label: string // 仓库名称
129
+ value: number // 仓库 ID
130
+ }
131
+ ```
132
+
133
+ ## API 数据源
134
+
135
+ - **内部 Hook**:`useStoreOptions`(基于 `useSWRImmutable` 缓存)
136
+ - **内部接口**:`apiService.storeComboBox`
137
+ - **请求参数**:`{ type: '', currentMerchantOnly, warehouseType }`
138
+ - **缓存策略**:SWR Immutable 缓存,key 为 `['warehouse/store/combo-box', currentMerchantOnly, warehouseType]`
139
+ - **重试策略**:失败自动重试 3 次,5 秒内去重
140
+ - **可覆盖**:通过 `<ConfigProvider apiService={{ storeComboBox: customFn }}>` 替换默认实现
141
+
142
+ ## 常见陷阱
143
+
144
+ - ❌ 将 `value` 类型视为 `number`:
145
+ ```tsx
146
+ // value 类型是 string,不是 number!
147
+ const [warehouseId, setWarehouseId] = useState<number>() // ❌
148
+ ```
149
+ - ✅ 使用 `string` 类型:
150
+
151
+ ```tsx
152
+ const [warehouseId, setWarehouseId] = useState<string>() // ✅
153
+ ```
154
+
155
+ - ❌ 传给下游 EffectReservoirSelect 时忘记类型转换:
156
+ ```tsx
157
+ // EffectReservoirSelect 的 warehouseId 是 number 类型
158
+ <EffectReservoirSelect warehouseId={warehouseId} /> // ❌ string 类型不兼容
159
+ ```
160
+ - ✅ 做类型转换:
161
+
162
+ ```tsx
163
+ <EffectReservoirSelect warehouseId={warehouseId ? Number(warehouseId) : undefined} />
164
+ ```
165
+
166
+ - ⚠️ `initialValue` 类型陷阱:后端返回的仓库 ID 可能是 `number` 类型,但组件期望 `string`。需确保类型一致:
167
+
168
+ ```tsx
169
+ <Form.Item name="warehouseId" initialValue={String(data.warehouseId)}>
170
+ <EffectWarehouseSelect />
171
+ </Form.Item>
172
+ ```
173
+
174
+ - ❌ 期望 `onChange` 清空时返回 `''`
175
+ - ✅ 清空时 `onChange` 返回 `(undefined, undefined)`
176
+
177
+ ## 相关组件
178
+
179
+ | 场景 | 组件 | 说明 |
180
+ | -------- | ----------------------- | ---------------------------------------- |
181
+ | 级联下游 | `EffectReservoirSelect` | 使用仓库 ID 获取库区列表 |
182
+ | 级联下游 | `EffectShelveSelect` | 使用仓库 ID + 库区 ID 获取库位列表 |
183
+ | API 配置 | `ConfigProvider` | 提供 `apiService.storeComboBox` 接口实现 |
@@ -0,0 +1,205 @@
1
+ # EffectWithFilePanel
2
+
3
+ > 文件上传面板,将 `children` 作为触发元素,点击后弹出抽屉面板进行文件上传。内部集成 `WithPanel` 抽屉和 `EffectFileUpload` 上传组件。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { EffectWithFilePanel } from '@xfe-repo/web-components'
9
+ import type { SimpleFileData } from '@xfe-repo/web-components'
10
+ ```
11
+
12
+ ## 基本用法
13
+
14
+ ```tsx
15
+ import { Button } from 'antd'
16
+ import { EffectWithFilePanel } from '@xfe-repo/web-components'
17
+
18
+ function MyForm() {
19
+ return (
20
+ <EffectWithFilePanel
21
+ onChange={(files) => console.log('文件变更:', files)}
22
+ onSave={(files) => {
23
+ console.log('保存文件:', files)
24
+ // 返回 true/false 或 Promise<boolean> 控制面板是否关闭
25
+ }}
26
+ >
27
+ <Button>上传附件</Button>
28
+ </EffectWithFilePanel>
29
+ )
30
+ }
31
+ ```
32
+
33
+ ## 进阶用法
34
+
35
+ ### 查看模式(禁用上传)
36
+
37
+ ```tsx
38
+ <EffectWithFilePanel disabled defaultValue={existingFiles}>
39
+ <Button>查看附件</Button>
40
+ </EffectWithFilePanel>
41
+ // disabled 时:标题显示 "查看附件",按钮文字显示 "确定"
42
+ ```
43
+
44
+ ### 限制文件类型和数量
45
+
46
+ ```tsx
47
+ <EffectWithFilePanel max={5} accept=".pdf,.doc,.docx" onChange={(files) => setFiles(files)} onSave={handleSave}>
48
+ <Button>上传合同</Button>
49
+ </EffectWithFilePanel>
50
+ ```
51
+
52
+ ### 自定义标题和头部内容
53
+
54
+ ```tsx
55
+ <EffectWithFilePanel title="上传报告" header={<Alert message="请上传 PDF 格式的报告文件" type="info" />} onSave={handleSave}>
56
+ <Button>上传报告</Button>
57
+ </EffectWithFilePanel>
58
+ ```
59
+
60
+ ### 远程上传模式
61
+
62
+ ```tsx
63
+ <EffectWithFilePanel
64
+ remote
65
+ onSave={async (files) => {
66
+ await submitFiles(files)
67
+ return true
68
+ }}
69
+ >
70
+ <Button>上传文件</Button>
71
+ </EffectWithFilePanel>
72
+ ```
73
+
74
+ ### 文件导入模式
75
+
76
+ ```tsx
77
+ <EffectWithFilePanel
78
+ accept=".csv,.xlsx"
79
+ onChange={(file) => {
80
+ // ⚠️ 建议在此处添加二次确认弹窗
81
+ Modal.confirm({
82
+ title: '确认导入?',
83
+ onOk: () => dispatch(effectImport(file)),
84
+ })
85
+ }}
86
+ />
87
+ ```
88
+
89
+ ## Props
90
+
91
+ ```typescript
92
+ type Props = {
93
+ disabled?: boolean
94
+ defaultValue?: SimpleFileData[]
95
+ onChange?: (files: SimpleFileData[]) => void
96
+ onSave?: (files?: SimpleFileData[]) => Promise<boolean | void> | boolean | void
97
+ max?: number
98
+ accept?: string
99
+ remote?: boolean
100
+ children: ReactElement
101
+ header?: ReactElement
102
+ title?: string
103
+ }
104
+ ```
105
+
106
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
107
+ | ------------ | --------------------------------------------------------- | ------ | ------------------ | ------------------------------------------------------ |
108
+ | children | `ReactElement` | **是** | - | 触发元素,点击后打开面板 |
109
+ | defaultValue | `SimpleFileData[]` | 否 | - | 已有文件列表 |
110
+ | onChange | `(files) => void` | 否 | - | 文件列表变更回调 |
111
+ | onSave | `(files?) => Promise<boolean \| void> \| boolean \| void` | 否 | - | 点击保存按钮时的回调,返回 false 阻止关闭 |
112
+ | max | `number` | 否 | - | 最大文件数量限制 |
113
+ | accept | `string` | 否 | - | 接受的文件类型(如 `'.pdf,.doc'`),设置后显示格式提示 |
114
+ | remote | `boolean` | 否 | - | 是否启用远程上传模式 |
115
+ | disabled | `boolean` | 否 | - | 禁用上传功能(查看模式) |
116
+ | header | `ReactElement` | 否 | - | 面板头部自定义内容,渲染在上传区域上方 |
117
+ | title | `string` | 否 | 动态生成(见说明) | 面板标题 |
118
+
119
+ ### title 和按钮文字默认值逻辑
120
+
121
+ | 状态 | title 默认值 | 按钮文字 |
122
+ | ---------------- | ------------ | ---------------------------- |
123
+ | `disabled=false` | `"上传附件"` | `"保存"` → 上传中 `"上传中"` |
124
+ | `disabled=true` | `"查看附件"` | `"确定"` |
125
+
126
+ ## 关联类型
127
+
128
+ ```typescript
129
+ // 文件数据结构(来自 EffectFileUpload)
130
+ type SimpleFileData = {
131
+ id?: number
132
+ name?: string
133
+ fileName?: string
134
+ url: string
135
+ }
136
+ ```
137
+
138
+ ## 内部结构
139
+
140
+ ```
141
+ EffectWithFilePanel
142
+ ├── WithPanel (抽屉容器)
143
+ │ ├── header (自定义头部)
144
+ │ ├── accept 格式提示
145
+ │ └── EffectFileUpload (文件上传组件)
146
+ ```
147
+
148
+ ### 保存流程
149
+
150
+ 1. 用户点击"保存"按钮
151
+ 2. 调用 `uploadRef.current.handleUpload()` 触发所有待上传文件的上传
152
+ 3. 上传完成后调用 `onSave(uploadRef.current.value)` 传出最新文件列表
153
+ 4. `onSave` 返回值控制面板是否关闭
154
+
155
+ ### 关闭面板保护
156
+
157
+ 上传进行中时尝试关闭面板,会弹出确认对话框:
158
+
159
+ - 标题:"文件上传中, 确定关闭么?"
160
+ - 内容:"如果关闭,此次上传保存将会失败"
161
+
162
+ ## 常见陷阱
163
+
164
+ - ❌ 忘记传 `children`:
165
+ ```tsx
166
+ // children 是必需的触发元素
167
+ <EffectWithFilePanel onSave={handleSave} /> // ❌ 报错
168
+ ```
169
+ - ✅ 始终传入一个 ReactElement 作为触发元素:
170
+
171
+ ```tsx
172
+ <EffectWithFilePanel onSave={handleSave}>
173
+ <Button>上传</Button>
174
+ </EffectWithFilePanel>
175
+ ```
176
+
177
+ - ❌ 直接使用 `onChange` 获取最终文件列表:
178
+ ```tsx
179
+ // onChange 在文件列表变更时就会触发,包括文件尚未上传完成时
180
+ ```
181
+ - ✅ 使用 `onSave` 获取最终上传完成后的文件列表:
182
+
183
+ ```tsx
184
+ <EffectWithFilePanel
185
+ onSave={(files) => {
186
+ // files 是上传完成后的最终文件列表
187
+ console.log('最终文件:', files)
188
+ }}
189
+ >
190
+ <Button>上传</Button>
191
+ </EffectWithFilePanel>
192
+ ```
193
+
194
+ - ❌ `accept` 仅做 UI 提示,不设防:
195
+ ```tsx
196
+ // accept 会传给 EffectFileUpload,但需确认底层是否做了严格校验
197
+ ```
198
+ - ✅ 在 `onSave` 中做二次校验确保文件类型正确
199
+
200
+ ## 相关组件
201
+
202
+ | 场景 | 组件 | 说明 |
203
+ | -------- | ------------------ | -------------------- |
204
+ | 底层上传 | `EffectFileUpload` | 文件上传核心组件 |
205
+ | 抽屉容器 | `WithPanel` | 可复用的抽屉面板组件 |
@@ -0,0 +1,126 @@
1
+ # FileUpload
2
+
3
+ > 基础文件上传组件,基于 antd Upload 封装,支持图片预览、文件数量限制、远程扫码上传,**不包含 API 上传逻辑**。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { FileUpload } from '@xfe-repo/web-components'
9
+ import type { ModifyFileData } from '@xfe-repo/web-components'
10
+ ```
11
+
12
+ ## 基本用法
13
+
14
+ ```tsx
15
+ import { FileUpload } from '@xfe-repo/web-components'
16
+ import { UploadFile } from 'antd/es/upload/interface'
17
+
18
+ function MyUpload() {
19
+ const [files, setFiles] = useState<UploadFile[]>([])
20
+
21
+ return <FileUpload value={files} onChange={(newFiles) => setFiles(newFiles)} max={5} />
22
+ }
23
+ ```
24
+
25
+ ## 进阶用法
26
+
27
+ ### 删除确认
28
+
29
+ ```tsx
30
+ <FileUpload
31
+ value={files}
32
+ onChange={setFiles}
33
+ deleteConfirm // 删除前弹出确认对话框
34
+ />
35
+ ```
36
+
37
+ ### 限制文件类型
38
+
39
+ ```tsx
40
+ <FileUpload accept="image/png,image/jpeg" value={files} onChange={setFiles} />
41
+ ```
42
+
43
+ ### 单文件上传
44
+
45
+ ```tsx
46
+ <FileUpload multiple={false} max={1} value={files} onChange={setFiles} />
47
+ ```
48
+
49
+ ### 远程扫码上传
50
+
51
+ ```tsx
52
+ <FileUpload
53
+ remote // 启用远程上传,显示扫码按钮
54
+ value={files}
55
+ onChange={setFiles}
56
+ />
57
+ ```
58
+
59
+ ### 自定义上传按钮
60
+
61
+ ```tsx
62
+ <FileUpload value={files} onChange={setFiles}>
63
+ <Button icon={<UploadOutlined />}>点击上传</Button>
64
+ </FileUpload>
65
+ ```
66
+
67
+ ## Props
68
+
69
+ ```typescript
70
+ type FileUploadProps = {
71
+ multiple?: boolean
72
+ classNames?: string
73
+ disabled?: boolean
74
+ value?: UploadFile[]
75
+ defaultValue?: UploadFile[]
76
+ type?: UploadListType
77
+ onChange?: (value: UploadFile[], modifyFileData: ModifyFileData) => void
78
+ children?: ReactNode
79
+ accept?: string
80
+ max?: number
81
+ remote?: boolean
82
+ deleteConfirm?: boolean
83
+ }
84
+ ```
85
+
86
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
87
+ | ------------- | --------------------------------------------------------------- | ---- | ---------------- | ------------------------------------------ |
88
+ | value | `UploadFile[]` | 否 | `[]` | 受控文件列表 |
89
+ | defaultValue | `UploadFile[]` | 否 | - | 默认文件列表(非受控) |
90
+ | onChange | `(value: UploadFile[], modifyFileData: ModifyFileData) => void` | 否 | - | 文件变更回调,第二参数包含 add/remove 信息 |
91
+ | max | `number` | 否 | `9` | 最大文件数量 |
92
+ | multiple | `boolean` | 否 | `true` | 是否支持多选 |
93
+ | accept | `string` | 否 | - | 接受的文件类型(MIME 类型) |
94
+ | disabled | `boolean` | 否 | - | 是否禁用 |
95
+ | type | `UploadListType` | 否 | `'picture-card'` | 上传列表展示类型 |
96
+ | remote | `boolean` | 否 | - | 是否启用远程扫码上传 |
97
+ | deleteConfirm | `boolean` | 否 | - | 删除前是否弹出确认框 |
98
+ | classNames | `string` | 否 | - | 自定义 className |
99
+ | children | `ReactNode` | 否 | - | 自定义上传按钮 |
100
+
101
+ ## 导出类型
102
+
103
+ ### ModifyFileData
104
+
105
+ ```typescript
106
+ export type ModifyFileData = {
107
+ add?: UploadFile[]
108
+ remove?: UploadFile[]
109
+ }
110
+ ```
111
+
112
+ ## 常见陷阱
113
+
114
+ - ❌ `FileUpload` 不会自动上传文件到服务器,它只管理本地文件列表
115
+ - ✅ 需要自动上传到 OSS 请使用 `EffectFileUpload`
116
+
117
+ - ❌ 达到 `max` 上限后上传按钮会自动隐藏,而非禁用
118
+ - ✅ 需要删除已有文件后才能继续上传
119
+
120
+ ## 相关组件
121
+
122
+ | 场景 | 组件 | 说明 |
123
+ | -------------- | ------------------ | ------------------------------- |
124
+ | 自动上传到 OSS | `EffectFileUpload` | 集成 API 上传能力的文件上传 |
125
+ | 图片展示 | `OSSImage` | 展示 OSS 图片 |
126
+ | 二维码 | `QRCode` | remote 模式内部使用 QRCode 组件 |
@@ -0,0 +1,31 @@
1
+ # Iconfont
2
+
3
+ > 自定义图标组件,基于 `@ant-design/icons` 的 `createFromIconfontCN` 创建,加载阿里巴巴图标库。
4
+
5
+ ## 导入
6
+
7
+ ```tsx
8
+ import { Iconfont } from '@xfe-repo/web-components'
9
+ ```
10
+
11
+ ## 基本用法
12
+
13
+ ```tsx
14
+ import { Iconfont } from '@xfe-repo/web-components'
15
+
16
+ function MyIcon() {
17
+ return <Iconfont type="icon-example" style={{ fontSize: 24, color: '#1890ff' }} />
18
+ }
19
+ ```
20
+
21
+ ## Props
22
+
23
+ `Iconfont` 继承 `@ant-design/icons` 的 `IconFontProps`:
24
+
25
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
26
+ | --------- | --------------- | ---- | ------- | ------------------------------------------ |
27
+ | type | `string` | 是 | - | 图标名称,对应 iconfont 项目中的 icon name |
28
+ | style | `CSSProperties` | 否 | - | 行内样式(常用于设置 fontSize 和 color) |
29
+ | className | `string` | 否 | - | 自定义 className |
30
+ | rotate | `number` | 否 | - | 旋转角度 |
31
+ | spin | `boolean` | 否 | `false` | 是否有旋转动画 |