Attachment 输入附件
用于展示一组附件信息集合。
何时使用
Attachment 组件用于需要展示一组附件信息集合的场景。
代码演示
基本
基础用法,可以通过 getDropContainer
支持拖拽上传。
ts
<script setup lang="tsx">
import { CloudUploadOutlined, LinkOutlined } from '@ant-design/icons-vue';
import { App, Button, Flex, Switch } from 'ant-design-vue';
import { Attachments } from 'ant-design-x-vue';
import { ref, 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
}
return (
<div ref="basic-container">
<Flex vertical gap="middle" align="flex-start">
<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 />} />}
/>
<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.Custom Placeholder Node
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: 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}>
<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}>
<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>
隐藏源代码
超出样式
控制附件超出区域长度时的展示方式。
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>
隐藏源代码
文件卡片
单独的文件卡片,用于一些展示场景。
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>
隐藏源代码
API
Attachment 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> | - | - |
tsx
interface PlaceholderType {
icon?: VNode;
title?: VNode | string;
description?: VNode | string;
}
Attachments Expose
属性 | 说明 | 类型 | 版本 |
---|---|---|---|
nativeElement | 获取原生节点 | HTMLElement | - |
upload | 手工调用上传文件 | (file: File) => void | - |