输入控件
Upload
Upload component for file selection and upload with comprehensive features
单文件上传
多文件上传
"use client";
import { Upload } from "@/components/composed/upload/upload";
export function UploadDemo() {
const handleSelect = (files: File[]) => {
console.log("Selected files:", files);
};
const handleChange = (fileList: any[]) => {
console.log("File list changed:", fileList);
};
return (
<div className="w-full max-w-md space-y-6">
<div>
<h3 className="mb-2 text-sm font-medium">单文件上传</h3>
<Upload
buttonText="选择文件"
onSelect={handleSelect}
onChange={handleChange}
/>
</div>
<div>
<h3 className="mb-2 text-sm font-medium">多文件上传</h3>
<Upload
multiple
buttonText="选择多个文件"
onSelect={handleSelect}
onChange={handleChange}
/>
</div>
</div>
);
}
Upload 组件用于文件选择和上传,支持单文件/多文件上传、文件类型限制、文件大小限制等功能,适用于头像上传、文档上传、图片上传等场景。
概述
- 单文件/多文件上传:支持单文件和多文件上传模式
- 文件限制:支持文件类型限制(accept)和文件大小限制(maxSize)
- 自定义上传:支持自定义上传请求逻辑
- 状态管理:完整的上传状态管理(idle、uploading、success、error)
- 受控/非受控:支持受控和非受控两种使用模式
- 类型安全:完整的 TypeScript 类型定义
快速开始
import { Upload } from "@/registry/wuhan/composed/upload/upload";
export function Example() {
return <Upload buttonText="上传文件" />;
}特性
- 灵活的上传模式:支持单文件和多文件上传
- 文件验证:内置文件类型和大小验证
- 实时状态反馈:显示上传进度和状态(上传中、成功、失败)
- 自定义请求:完全自定义的上传请求逻辑
- 文件列表管理:支持文件列表的增删改查
- 无缝集成:使用 block-button 组件,保持界面一致性
安装
代码演示
基础用法
最基础的上传组件使用。
"use client";
import { Upload } from "@/components/composed/upload/upload";
export function UploadDefault() {
return (
<div className="w-full max-w-md">
<Upload />
</div>
);
}
单文件/多文件上传
演示单文件和多文件上传的区别。
单文件上传
多文件上传
"use client";
import { Upload } from "@/components/composed/upload/upload";
export function UploadDemo() {
const handleSelect = (files: File[]) => {
console.log("Selected files:", files);
};
const handleChange = (fileList: any[]) => {
console.log("File list changed:", fileList);
};
return (
<div className="w-full max-w-md space-y-6">
<div>
<h3 className="mb-2 text-sm font-medium">单文件上传</h3>
<Upload
buttonText="选择文件"
onSelect={handleSelect}
onChange={handleChange}
/>
</div>
<div>
<h3 className="mb-2 text-sm font-medium">多文件上传</h3>
<Upload
multiple
buttonText="选择多个文件"
onSelect={handleSelect}
onChange={handleChange}
/>
</div>
</div>
);
}
自定义上传请求
使用 customRequest 实现自定义上传逻辑。
"use client";
import {
Upload,
type UploadFile,
} from "@/components/composed/upload/upload";
export function UploadCustomRequest() {
// 模拟上传请求
const handleUpload = async (file: File): Promise<any> => {
return new Promise((resolve, reject) => {
// 模拟网络延迟
setTimeout(() => {
// 模拟随机成功/失败
if (Math.random() > 0.3) {
resolve({
url: `https://example.com/files/${file.name}`,
id: Math.random().toString(36).substr(2, 9),
});
} else {
reject(new Error("网络错误,上传失败"));
}
}, 2000);
});
};
const handleChange = (fileList: UploadFile[]) => {
console.log("Current file list:", fileList);
};
return (
<div className="w-full max-w-md">
<Upload
multiple
buttonText="上传到服务器"
customRequest={handleUpload}
onChange={handleChange}
/>
</div>
);
}
文件限制
演示文件类型和大小限制。
只允许上传图片
accept="image/*"
文件大小限制(2MB)
maxSize=2MB
最多上传 3 个文件
maxCount=3
只允许上传 PDF 和 Word 文档
accept=".pdf,.doc,.docx"
"use client";
import { Upload } from "@/components/composed/upload/upload";
export function UploadRestrictions() {
return (
<div className="w-full max-w-md space-y-6">
<div>
<h3 className="mb-2 text-sm font-medium">只允许上传图片</h3>
<p className="mb-3 text-xs text-[var(--Text-text-tertiary)]">
{`accept="image/*"`}
</p>
<Upload accept="image/*" buttonText="选择图片" />
</div>
<div>
<h3 className="mb-2 text-sm font-medium">文件大小限制(2MB)</h3>
<p className="mb-3 text-xs text-[var(--Text-text-tertiary)]">
maxSize=2MB
</p>
<Upload
multiple
maxSize={2 * 1024 * 1024}
buttonText="选择文件(≤2MB)"
/>
</div>
<div>
<h3 className="mb-2 text-sm font-medium">最多上传 3 个文件</h3>
<p className="mb-3 text-xs text-[var(--Text-text-tertiary)]">
maxCount=3
</p>
<Upload multiple maxCount={3} buttonText="选择文件(最多3个)" />
</div>
<div>
<h3 className="mb-2 text-sm font-medium">
只允许上传 PDF 和 Word 文档
</h3>
<p className="mb-3 text-xs text-[var(--Text-text-tertiary)]">
{`accept=".pdf,.doc,.docx"`}
</p>
<Upload accept=".pdf,.doc,.docx" buttonText="选择文档" />
</div>
</div>
);
}
受控模式
使用受控模式管理文件列表。
已选择 0 个文件
"use client";
import * as React from "react";
import {
Upload,
type UploadFile,
} from "@/components/composed/upload/upload";
export function UploadControlled() {
const [fileList, setFileList] = React.useState<UploadFile[]>([]);
const handleChange = (newFileList: UploadFile[]) => {
setFileList(newFileList);
};
const handleRemove = (file: UploadFile) => {
console.log("Removing file:", file.name);
};
const handleClear = () => {
setFileList([]);
};
return (
<div className="w-full max-w-md space-y-4">
<Upload
fileList={fileList}
multiple
onChange={handleChange}
onRemove={handleRemove}
/>
<div className="flex items-center gap-4 text-sm">
<span className="text-[var(--Text-text-tertiary)]">
已选择 {fileList.length} 个文件
</span>
{fileList.length > 0 && (
<button
onClick={handleClear}
className="text-[var(--Text-text-brand)] hover:underline"
>
清空列表
</button>
)}
</div>
</div>
);
}
禁用状态
禁用上传功能。
"use client";
import { Upload } from "@/components/composed/upload/upload";
export function UploadDisabled() {
return (
<div className="w-full max-w-md">
<Upload disabled buttonText="上传已禁用" />
</div>
);
}
API
Upload
上传组件的主要 API。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
fileList | UploadFile[] | - | 文件列表(受控模式) |
defaultFileList | UploadFile[] | [] | 默认文件列表(非受控模式) |
multiple | boolean | false | 是否支持多文件上传 |
accept | string | - | 接受的文件类型,例如:"image/*" 或 ".jpg,.png" |
maxSize | number | - | 文件大小限制(字节),例如:5 * 1024 * 1024 (5MB) |
maxCount | number | - | 最大文件数量 |
disabled | boolean | false | 是否禁用 |
buttonText | string | "上传文件" | 上传按钮文本 |
onSelect | (files: File[]) => void | - | 文件选择时的回调 |
onChange | (fileList: UploadFile[]) => void | - | 文件列表变化时的回调 |
onRemove | (file: UploadFile) => void | - | 删除文件时的回调 |
customRequest | (file: File) => Promise<any> | - | 自定义上传请求 |
className | string | - | 自定义类名 |
itemRender | (file: UploadFile, defaultRender: ReactNode) => ReactNode | - | 自定义文件列表项渲染 |
UploadFile
文件对象类型定义。
interface UploadFile {
uid: string; // 文件唯一标识
file: File; // 原生文件对象
name: string; // 文件名
size: number; // 文件大小(字节)
type: string; // 文件类型
status: UploadStatus; // 上传状态
progress?: number; // 上传进度(0-100)
error?: string; // 错误信息
response?: any; // 响应数据
}UploadStatus
上传状态类型。
type UploadStatus = "idle" | "uploading" | "success" | "error";使用场景
1. 图片上传
<Upload
accept="image/*"
maxSize={5 * 1024 * 1024}
buttonText="上传图片"
/>2. 文档上传
<Upload
accept=".pdf,.doc,.docx"
multiple
maxCount={5}
buttonText="上传文档"
/>3. 头像上传
<Upload
accept="image/jpeg,image/png"
maxSize={2 * 1024 * 1024}
buttonText="上传头像"
customRequest={async (file) => {
const formData = new FormData();
formData.append('avatar', file);
const response = await fetch('/api/upload-avatar', {
method: 'POST',
body: formData,
});
return response.json();
}}
/>4. 批量文件上传
<Upload
multiple
maxCount={10}
buttonText="批量上传"
customRequest={uploadToServer}
onChange={(fileList) => {
console.log('当前文件列表:', fileList);
}}
/>设计指南
文件状态指示
- 空闲(idle):文件已选择但未开始上传,显示普通文件图标
- 上传中(uploading):文件正在上传,显示加载动画图标
- 成功(success):文件上传成功,显示普通文件图标
- 失败(error):文件上传失败,显示红色文件图标和错误信息
文件信息显示
每个文件项显示以下信息:
- 文件状态图标(左侧)
- 文件名(中间,支持截断)
- 文件大小(文件名右侧)
- 删除按钮(最右侧)
错误处理
- 文件大小超限:立即显示错误状态,不触发上传
- 上传失败:显示错误状态和错误信息
- 类型不匹配:浏览器原生限制,无法选择
可访问性
- 使用
aria-label为删除按钮提供无障碍标签 - 按钮支持键盘操作
- 文件选择支持原生文件选择器
- 状态变化有明确的视觉反馈
常见问题
如何实现自定义上传请求?
使用 customRequest 属性传入自定义上传函数:
const handleUpload = async (file: File) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('上传失败');
}
return response.json();
};
<Upload customRequest={handleUpload} />如何限制文件类型?
使用 accept 属性指定允许的文件类型:
// 只允许图片
<Upload accept="image/*" />
// 只允许特定格式
<Upload accept=".pdf,.doc,.docx" />
// 允许特定 MIME 类型
<Upload accept="image/jpeg,image/png" />如何监听文件列表变化?
使用 onChange 回调监听文件列表变化:
<Upload
onChange={(fileList) => {
console.log('当前文件列表:', fileList);
// 可以在这里处理文件列表变化
}}
/>如何使用受控模式?
传入 fileList 和 onChange 实现受控模式:
const [fileList, setFileList] = useState<UploadFile[]>([]);
<Upload
fileList={fileList}
onChange={setFileList}
/>原语组件
Upload 组件基于以下原语组件构建:
UploadContainerPrimitive: 上传容器UploadTriggerPrimitive: 上传触发器UploadInputPrimitive: 文件输入框UploadFileListPrimitive: 文件列表容器UploadFileItemPrimitive: 文件列表项UploadFileIconPrimitive: 文件图标容器UploadFileNamePrimitive: 文件名UploadFileSizePrimitive: 文件大小UploadFileRemovePrimitive: 删除按钮UploadFileErrorPrimitive: 错误信息
这些原语组件可以单独使用来构建自定义的上传界面。