Attachments 输入附件
用于展示一组附件信息集合。
何时使用
Attachments 组件用于需要展示一组附件信息集合的场景。
代码演示
基本
基础用法,可以通过 getDropContainer
支持拖拽上传。
Drag & Drop files here
Support file type: image, video, audio, document, etc.ts
<script setup lang="tsx">
import { CloudUploadOutlined, LinkOutlined } from '@ant-design/icons-vue';
import { App, Button, Flex, Switch } from 'ant-design-vue';
import { Sender, Attachments } from 'ant-design-x-vue';
import { ref, computed, useTemplateRef } from 'vue';
defineOptions({ name: 'AXAttachmentBasic' });
const Demo = () => {
const { message } = App.useApp();
const fullScreenDrop = ref(false);
const customContent = ref(true);
const divRef = useTemplateRef<HTMLDivElement>('basic-container');
const triggerFullScreenDrop = (v: boolean) => {
fullScreenDrop.value = v
}
const triggerCustomContent = (v: boolean) => {
customContent.value = v
}
const attachmentsNode = computed(() => (
<Attachments
beforeUpload={() => false}
onChange={({ file }) => {
message.info(`Mock upload: ${file.name}`);
}}
getDropContainer={() => (fullScreenDrop.value ? document.body : divRef.value)}
placeholder={{
icon: <CloudUploadOutlined />,
title: 'Drag & Drop files here',
description: 'Support file type: image, video, audio, document, etc.',
}}
children={customContent.value && <Button type="text" icon={<LinkOutlined />} />}
/>));
return (
<div ref="basic-container">
<Flex vertical gap="middle" align="flex-start">
<Sender prefix={attachmentsNode.value} />
<Switch
checked={fullScreenDrop.value}
onChange={(checked) => {
triggerFullScreenDrop(checked as boolean)
}}
checkedChildren="Full screen drop"
unCheckedChildren="Full screen drop"
/>
<Switch
checked={customContent.value}
onChange={(checked) => {
triggerCustomContent(checked as boolean)
}}
checkedChildren="use default content"
unCheckedChildren="custom content"
/>
</Flex>
</div>
)
}
defineRender(() => {
return (
<App>
<Demo />
</App>
)
});
</script>
隐藏源代码
占位信息
修改占位信息。
Click or Drop files here
Support file type: image, video, audio, document, etc.Drop file here
Custom Placeholder Node
Drop file here
ts
<script setup lang="tsx">
import { CloudUploadOutlined } from '@ant-design/icons-vue';
import { Button, Flex, Result, theme } from 'ant-design-vue';
import { Attachments, type AttachmentsProps } from 'ant-design-x-vue';
import { computed, type CSSProperties, ref } from 'vue';
defineOptions({ name: 'AXAttachmentPlaceholder' });
const presetFiles: AttachmentsProps['items'] = [
{
uid: '1',
name: 'uploading file.xlsx',
status: 'uploading',
url: 'http://www.baidu.com/xxx.png',
percent: 93,
},
{
uid: '2',
name: 'uploaded file.docx',
status: 'done',
size: 123456,
description: 'Customize description',
url: 'http://www.baidu.com/yyy.png',
},
{
uid: '3',
name: 'upload error with long text file name.zip',
status: 'error',
response: 'Server Error 500', // custom error message to show
url: 'http://www.baidu.com/zzz.png',
},
{
uid: '4',
name: 'image uploading preview.png',
status: 'uploading',
percent: 33,
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '5',
name: 'image done preview.png',
status: 'done',
size: 123456,
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '6',
name: 'image error preview.png',
status: 'error',
response: 'Server Error 500', // custom error message to show
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
];
type ExtractFunc<T> = T extends (...args: any) => any ? T : never;
const getPlaceholderFn = (
inlinePlaceholder: ReturnType<ExtractFunc<AttachmentsProps['placeholder']>>,
) => {
return (type: 'inline' | 'drop') =>
type === 'drop'
? {
title: 'Drop file here',
}
: inlinePlaceholder;
};
const { token } = theme.useToken();
const items = ref<AttachmentsProps['items']>([]);
const sharedBorderStyle = computed<CSSProperties>(() => ({
borderRadius: token.value.borderRadius,
overflow: 'hidden',
background: token.value.colorBgContainer,
}));
const sharedAttachmentProps = computed<AttachmentsProps>(() => ({
beforeUpload: () => false,
items: items.value,
onChange: ({ fileList }) => {
console.log('onChange:', fileList);
items.value = fileList;
},
}));
defineRender(() => {
return (
<Flex
vertical
gap="middle"
style={{
padding: token.value.padding,
background: token.value.colorBgContainerDisabled,
}}
>
<div style={sharedBorderStyle.value}>
<Attachments
{...sharedAttachmentProps.value}
placeholder={getPlaceholderFn({
icon: <CloudUploadOutlined />,
title: 'Click or Drop files here',
description: 'Support file type: image, video, audio, document, etc.',
})}
/>
</div>
<div style={sharedBorderStyle.value}>
<Attachments
{...sharedAttachmentProps.value}
placeholder={getPlaceholderFn(
<Result
title="Custom Placeholder Node"
icon={<CloudUploadOutlined />}
extra={<Button type="primary">Do Upload</Button>}
style={{ padding: 0 }}
/>,
)}
/>
</div>
<Flex gap="middle">
<Button
style={{ flex: '1 1 50%' }}
disabled={!!items.value.length}
type="primary"
onClick={() => {
items.value = presetFiles
}}
>
Fill Files
</Button>
<Button
style={{ flex: '1 1 50%' }}
disabled={!items.value.length}
onClick={() => {
items.value = []
}}
>
Reset Files
</Button>
</Flex>
</Flex>
)
});
</script>
隐藏源代码
超出样式
控制附件超出区域长度时的展示方式。

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview

Preview
Click or Drop files here
Support file type: image, video, audio, document, etc.Click or Drop files here
Support file type: image, video, audio, document, etc.ts
<script setup lang="tsx">
import { CloudUploadOutlined } from '@ant-design/icons-vue';
import { Flex, Segmented, Switch } from 'ant-design-vue';
import { Attachments, type AttachmentsProps } from 'ant-design-x-vue';
import { ref } from 'vue';
defineOptions({ name: 'AXAttachmentOverflow' });
const presetFiles: AttachmentsProps['items'] = Array.from({ length: 30 }).map((_, index) => ({
uid: String(index),
name: `file-${index}.jpg`,
status: 'done',
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
}));
const overflow = ref<AttachmentsProps['overflow']>('wrap');
const items = ref<AttachmentsProps['items']>(presetFiles);
const disabled = ref(false);
defineRender(() => {
return (
<Flex vertical gap="middle">
<Flex gap="middle" align="center">
<Segmented
options={[
{ value: 'wrap', label: 'Wrap' },
{ value: 'scrollX', label: 'Scroll X' },
{ value: 'scrollY', label: 'Scroll Y' },
]}
value={overflow.value}
onChange={(v) => {
overflow.value = v as AttachmentsProps['overflow'];
}}
style={{ marginInlineEnd: 'auto' }}
/>
<Switch
checked={items.value.length !== 0}
onChange={() => {
items.value = items.value.length ? [] : presetFiles
}}
checkedChildren="Data"
unCheckedChildren="Data"
/>
<Switch
checked={disabled.value}
onChange={(v) => {
disabled.value = v as boolean;
}}
checkedChildren="Disabled"
unCheckedChildren="Disabled"
/>
</Flex>
<Attachments
overflow={overflow.value}
items={items.value}
onChange={(info) => {
items.value = info.fileList
}}
beforeUpload={() => false}
placeholder={{
icon: <CloudUploadOutlined />,
title: 'Click or Drop files here',
description: 'Support file type: image, video, audio, document, etc.',
}}
disabled={disabled.value}
/>
</Flex>
)
})
</script>
隐藏源代码
组合示例
配合 Sender.Header 使用,在对话中插入附件。
Attachments
Upload files
Click or drag files to this area to uploadts
<script setup lang="tsx">
import { CloudUploadOutlined, LinkOutlined } from '@ant-design/icons-vue';
import { App, Button, Flex, Badge, type UploadProps } from 'ant-design-vue';
import { Attachments, Sender } from 'ant-design-x-vue';
import { computed, onUnmounted, ref } from 'vue';
defineOptions({ name: 'AXAttachmentWithSender' });
type FileType = Parameters<UploadProps['beforeUpload']>[0];
const open = ref(true);
const items = ref([]);
const text = ref('');
const senderRef = ref<InstanceType<typeof Sender>>(null);
onUnmounted(() => {
// Clear all created object URLs when the component is unmounted
items.value.forEach(item => {
if (item.url?.startsWith('blob:')) {
URL.revokeObjectURL(item.url);
}
});
});
const Demo = () => {
const senderHeader = computed(() => (
<Sender.Header
title="Attachments"
styles={{
content: {
padding: 0,
},
}}
open={open.value}
onOpenChange={v => open.value = v}
forceRender
>
<Attachments
// Mock not real upload file
beforeUpload={() => false}
items={items.value}
onChange={({ file, fileList }) => {
if (file.status === 'removed') {
items.value = fileList;
return;
}
file.url = window.URL.createObjectURL(file as FileType)
// file.thumbUrl = URL of the thumbnail image
items.value = fileList.map((item) => {
if (item.uid === file.uid && file.status !== 'removed' && item.originFileObj) {
// clear URL
if (item.url?.startsWith('blob:')) {
URL.revokeObjectURL(item.url);
}
// create new preview URL
return {
...item,
url: URL.createObjectURL(item.originFileObj),
};
}
return item;
});
}}
placeholder={(type) =>
type === 'drop'
? {
title: 'Drop file here',
}
: {
icon: <CloudUploadOutlined />,
title: 'Upload files',
description: 'Click or drag files to this area to upload',
}
}
getDropContainer={() => senderRef.value?.nativeElement}
/>
</Sender.Header>
));
const badgeNode = computed(() => (
<Badge dot={items.value.length > 0 && !open.value}>
<Button onClick={() => open.value = !open.value} icon={<LinkOutlined />} />
</Badge>
))
return (
<Flex style={{ minHeight: '250px' }} align="flex-end">
<Sender
ref={senderRef}
header={senderHeader.value}
prefix={
badgeNode.value
}
value={text.value}
onChange={v => text.value = v}
onSubmit={() => {
items.value = []
text.value = ''
}}
/>
</Flex>
);
};
defineRender(() => {
return (
<App>
<Demo />
</App>
)
});
</script>
隐藏源代码
文件卡片
单独的文件卡片,用于一些展示场景。
excel-file
.xlsx
109 KB
word-file
.docx
217 KB
image-file
.png
326 KB
pdf-file
.pdf
434 KB
ppt-file
.pptx
543 KB
video-file
.mp4
651 KB
audio-file
.mp3
760 KB
zip-file
.zip
868 KB
markdown-file
.md
Custom description here

ts
<script setup lang="tsx">
import { App, Flex } from 'ant-design-vue';
import { Attachments } from 'ant-design-x-vue';
defineOptions({ name: 'AXAttachmentFiles' });
const Demo = () => {
const filesList = [
{
uid: '1',
name: 'excel-file.xlsx',
size: 111111,
},
{
uid: '2',
name: 'word-file.docx',
size: 222222,
},
{
uid: '3',
name: 'image-file.png',
size: 333333,
},
{
uid: '4',
name: 'pdf-file.pdf',
size: 444444,
},
{
uid: '5',
name: 'ppt-file.pptx',
size: 555555,
},
{
uid: '6',
name: 'video-file.mp4',
size: 666666,
},
{
uid: '7',
name: 'audio-file.mp3',
size: 777777,
},
{
uid: '8',
name: 'zip-file.zip',
size: 888888,
},
{
uid: '9',
name: 'markdown-file.md',
size: 999999,
description: 'Custom description here',
},
{
uid: '10',
name: 'image-file.png',
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
size: 123456,
},
];
return (
<Flex vertical gap="middle">
{filesList.map((file, index) => (
<Attachments.FileCard key={index} item={file} />
))}
</Flex>
);
};
defineRender(() => {
return (
<App>
<Demo />
</App>
)
});
</script>
隐藏源代码
自定义文件卡片
自定义文件卡片的图标、图标颜色、文件范围。
excel-file
.xlsx
109 KB
image-file
.png
326 KB
pdf-file
.pdf
434 KB
video-file
.mp4
651 KB
audio-file
.mp3
760 KB
ts
<script setup lang="tsx">
import { App, Flex } from 'ant-design-vue';
import { Attachments } from 'ant-design-x-vue';
import { VideoCameraFilled, AudioFilled, CopyFilled, FileImageFilled } from '@ant-design/icons-vue';
import type { VNode } from 'vue';
defineOptions({ name: 'AXAttachmentFiles' });
const Demo = () => {
const filesList: {
uid: string;
name: string;
size: number;
description?: string;
thumbUrl?: string;
url?: string;
icon?: VNode;
type?: 'file' | 'image';
}[] = [
{
uid: '1',
name: 'excel-file.xlsx',
size: 111111,
icon: <CopyFilled />,
type: 'file',
},
{
uid: '2',
name: 'image-file.png',
size: 333333,
icon: <FileImageFilled />,
type: 'file',
},
{
uid: '3',
name: 'pdf-file.pdf',
size: 444444,
icon: <CopyFilled />,
type: 'file',
},
{
uid: '4',
name: 'video-file.mp4',
size: 666666,
icon: <VideoCameraFilled />,
type: 'file',
},
{
uid: '5',
name: 'audio-file.mp3',
size: 777777,
icon: <AudioFilled />,
type: 'file',
},
];
return (
<Flex vertical gap="middle">
{filesList.map((file, index) => (
<Attachments.FileCard key={index} item={file} icon={file.icon} type={file.type} />
))}
</Flex>
);
};
defineRender(() => {
return (
<App>
<Demo />
</App>
)
});
</script>
隐藏源代码
API
Attachments Props
继承 antdv Upload 属性。
属性 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
classNames | 自定义样式类名,见下 | Record<string, string> | - | - |
disabled | 是否禁用 | boolean | false | - |
getDropContainer | 设置拖拽时,可以释放文件的区域 | () => HTMLElement | - | - |
items | 附件列表,同 Upload fileList | Attachment[] | - | - |
overflow | 文件列表超出时样式 | 'wrap' | 'scrollX' | 'scrollY' | - | - |
placeholder | 没有文件时的占位信息 | PlaceholderType | ((type: 'inline' | 'drop') => PlaceholderType) | - | - |
rootClassName | 根节点的样式类名 | string | - | - |
rootStyle | 根节点的样式对象 | CSSProperties | - | - |
styles | 自定义样式对象,见下 | Record<string, CSSProperties> | - | - |
imageProps | 图片属性,同 antdv Image 属性 | ImageProps | - | - |
tsx
interface PlaceholderType {
icon?: VNode;
title?: VNode | string;
description?: VNode | string;
}
Attachments Slots
插槽名 | 说明 | 类型 |
---|---|---|
placeholder | 没有文件时的占位信息 | { type: "inline" | "drop" } |
Attachments Expose
属性 | 说明 | 类型 | 版本 |
---|---|---|---|
nativeElement | 获取原生节点 | HTMLElement | - |
upload | 手工调用上传文件 | (file: File | File[] | FileList) => void | - |
Attachments.FileCard Props
属性 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
item | 附件,同 Upload UploadFile | Attachment | - | - |
onRemove | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象,Promise 对象 resolve(false) 或 reject 时不移除 | (item: Attachment) => boolean | Promise | - | - |
icon | 自定义图标 | VNode | PresetIcons | - | - |
type | 是否图片类型 | 'file' | 'image' | file | - |
ts
type PresetIcons = 'default' | 'excel' | 'image' | 'markdown' | 'pdf' | 'ppt' | 'word' | 'zip' | 'video' | 'audio';
Semantic DOM
Upload File
Drag or click to upload file.Upload File
Drag or click to upload file.placeholder
占位符

Preview

Preview

Preview
list
列表容器item
列表项
主题变量(Design Token)
贡献者
贡献者






