输入类

上传

文件选择上传

代码演示

如何引入

<script setup lang="ts">
    import { Upload } from '@fibrous/semi-ui-vue';
</script>

基本

最基本的用法,在触发区内放置一个 Button,点击它激活文件选择框,选择完成后自动开始上传

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

文件名超长省略

通过 showTooltip 属性,自定义设置文件名弹出提示

当类型为 boolean 时,控制是否弹出提示

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" :showTooltip="false">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

通过 #ellipsisTooltip 插槽可以自定义弹出层,插槽参数为 content(完整文件名)与 trigger(文件名元素,用 <component :is="trigger" /> 放进弹出层);showTooltip 传对象时可配置提示的类型与透传属性

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Tooltip, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" showTooltip>
        <template #ellipsisTooltip="{ content, trigger }">
            <Tooltip :content="content" position="bottom">
                <component :is="trigger" />
            </Tooltip>
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

添加提示文本

通过 prompt 插槽,设置自定义提示文本
通过 promptPosition 设置插槽位置,可选 left、right、bottom,默认为 right

请上传资格认证材料
请上传资格认证材料
请上传资格认证材料
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type PromptPositionType } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';
    const marginStyle: Partial<Record<PromptPositionType, string>> = {
        left: 'margin-right: 10px',
        right: 'margin-left: 10px',
    };
    const promptStyle = (pos: PromptPositionType) => `display: flex; align-items: center; color: var(--semi-color-text-2); height: 32px; ${marginStyle[pos] ?? ''}`;
    const positions: PromptPositionType[] = ['right', 'left', 'bottom'];
</script>

<template>
    <template v-for="(pos, index) in positions" :key="pos">
        <div v-if="index" style="margin-bottom: 12px; margin-top: 12px; border-bottom: 1px solid var(--semi-color-border)" />
        <Upload :action="action" :promptPosition="pos">
            <template #prompt>
                <div :style="promptStyle(pos)">请上传资格认证材料</div>
            </template>
            <Button theme="light">
                <template #icon>
                    <IconUpload />
                </template>
                点击上传
            </Button>
        </Upload>
    </template>
</template>

当 listType 为 picture 时,promptPosition 位置的参照对象为图片墙列表整体

请上传认证材料
dy.jpeg
resso.jpeg
请上传认证材料
dy.jpeg
resso.jpeg
代码
<script setup lang="ts">
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Upload, type FileItem, type PromptPositionType } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';
    const marginStyle: Partial<Record<PromptPositionType, string>> = {
        right: 'margin-left: 10px',
    };
    const promptStyle = (pos: PromptPositionType) => `display: flex; align-items: center; color: var(--semi-color-text-2); height: 100%; ${marginStyle[pos] ?? ''}`;
    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'dy.jpeg',
            status: 'success',
            size: '130kb',
            url: '/assets/placeholder/avatar-a.svg',
        },
        {
            uid: '5',
            name: 'resso.jpeg',
            status: 'wait',
            percent: 50,
            size: '222kb',
            url: '/assets/placeholder/avatar-b.svg',
        },
    ];
    const positions: PromptPositionType[] = ['right', 'bottom'];
</script>

<template>
    <template v-for="(pos, index) in positions" :key="pos">
        <div v-if="index" style="margin-bottom: 12px; margin-top: 12px; border-bottom: 1px solid var(--semi-color-border)" />
        <Upload :action="action" :defaultFileList="defaultFileList" listType="picture" :promptPosition="pos">
            <template #prompt>
                <div :style="promptStyle(pos)">请上传认证材料</div>
            </template>
            <IconPlus aria-label="上传图片" size="extra-large" />
        </Upload>
    </template>
</template>

点击头像触发上传

上传头像
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconCamera } from '@fibrous/semi-icons-vue';
    import { Avatar, Toast, Upload } from '@fibrous/semi-ui-vue';

    const url = ref('/assets/placeholder/avatar-a.svg');

    const onSuccess = () => {
        Toast.success('头像更新成功');
        url.value = '/assets/ttmoment.jpeg';
    };

    const maskStyle = {
        backgroundColor: 'var(--semi-color-overlay-bg)',
        height: '100%',
        width: '100%',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        color: 'var(--semi-color-white)',
    };
</script>

<template>
    <Upload accept="image/*" action="https://api.semi.design/upload" class="avatar-upload" :showUploadList="false" @error="Toast.error('上传失败')" @success="onSuccess">
        <!-- the avatar is the whole content of the upload button, so its alt names the button -->
        <Avatar alt="上传头像" :src="url" :style="{ margin: '4px' }">
            <template #hoverMask>
                <div :style="maskStyle">
                    <IconCamera />
                </div>
            </template>
        </Avatar>
    </Upload>
</template>
.avatar-upload .semi-upload-add {
    border-radius: 50%; // 确保只有圆是点击热区
}

自定义上传属性

通过设置 data、headers 可添加自定义上传属性

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';

    // A data function is evaluated per upload, so time is the upload moment
    const data = () => ({
        role: 'ies',
        time: new Date().getTime(),
    });
    const headers = {
        'x-tt-semi': 'semi-upload',
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :data="data" :headers="headers">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

上传文件类型

通过 accept 属性(input 的原生 html 属性)可以限制上传的文件类型。

accept 支持传入以下两种类型字符串:

  • 文件后缀名集合(推荐),如 .jpg、.png 等;
  • 文件类型的 MIME types 集合,可参考MDN 文档

例如只允许用户上传 PNG 和 PDF 文件,accept 可以这样写: accept = '.pdf,.png' 或 accept = 'application/pdf,image/png'(将 PNG 与 PDF 的 MIME type 通过,连接起来即可)。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';
    const imageOnly = 'image/*';
    const videoOnly = 'video/*';
    const fileLimit = '.pdf,.png,.jpeg';
</script>

<template>
    <Upload :accept="imageOnly" :action="action" :style="{ marginBottom: '12px' }">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            上传图片
        </Button>
    </Upload>
    <Upload :accept="videoOnly" :action="action" :style="{ marginBottom: '12px' }">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            上传视频
        </Button>
    </Upload>
    <Upload :accept="fileLimit" :action="action">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            上传 PDF, PNG, JPEG
        </Button>
    </Upload>
</template>

上传文件夹

通过传入 directory 为 true,可以支持上传文件夹下的所有文件

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" directory>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            上传文件夹
        </Button>
    </Upload>
</template>

一次选中多个文件

通过设置 multiple 属性可以支持同时选中多个文件上传。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" multiple>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

限制文件总数量

通过设置 limit 属性可以限制最大可上传的文件数
当 limit 为1时,始终用最新上传的代替当前,并不会触发 exceed 事件

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type UploadOnChangeProps } from '@fibrous/semi-ui-vue';

    const limit = 1;
    const onChange = (props: UploadOnChangeProps) => {
        console.log(props.fileList);
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :limit="limit" @change="onChange">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(最多{{ limit }}项)
        </Button>
    </Upload>
</template>

达到上限时禁用触发控件而不是 Upload,这样仍能移除已上传的文件。

代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Toast, Upload, type UploadOnChangeProps } from '@fibrous/semi-ui-vue';

    const limit = 2;
    // Only the trigger button is disabled; Upload itself stays enabled so files can still be removed.
    const disabled = ref(false);
    const onChange = (props: UploadOnChangeProps) => {
        disabled.value = props.fileList.length === limit;
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :limit="limit" @change="onChange" @exceed="Toast.warning(`最多只允许上传${limit}个文件`)">
        <Button :disabled="disabled" theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(最多{{ limit }}项)
        </Button>
    </Upload>
</template>

照片墙模式下,当已上传文件数量等于 limit 时,会自动隐藏上传入口

dyBag.png
dyBag2.jpeg
代码
<script setup lang="ts">
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Toast, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'dyBag.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
        {
            uid: '2',
            name: 'dyBag2.jpeg',
            status: 'success',
            size: '222KB',
            preview: true,
            fileInstance: new File([new ArrayBuffer(2048)], 'dyBag2.jpeg', { type: 'image/png' }),
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
    ];
</script>

<template>
    <Upload accept="image/*" action="https://api.semi.design/upload" :defaultFileList="defaultFileList" :limit="2" listType="picture" @exceed="Toast.warning('最多只允许上传2个文件')">
        <IconPlus aria-label="上传图片" size="extra-large" />
    </Upload>
</template>

限制上传文件大小

通过 maxSize 和 minSize 属性可以自定义上传文件大小的限制,通过监听 @sizeError 可以在超出限制时得到通知。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Toast, Upload, type CustomFile } from '@fibrous/semi-ui-vue';

    const onSizeError = (file: CustomFile) => Toast.error(`${file.name} size invalid`);
</script>

<template>
    <Upload action="https://api.semi.design/upload" :maxSize="1024" :minSize="200" @sizeError="onSizeError">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(最小 200KB,最大 1MB)
        </Button>
    </Upload>
</template>

自定义列表操作区

listType 为 list 时,可以通过 #fileOperation 插槽自定义列表操作区,参数为 UploadFileItemSlotProps

已选择文件
dyBag.png130KB
代码
<script setup lang="ts">
    import { IconDelete, IconDownload, IconEyeOpened, IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';
    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'dyBag.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
    ];
</script>

<template>
    <Upload :action="action" :defaultFileList="defaultFileList" :itemStyle="{ width: '300px' }">
        <template #fileOperation="{ onRemove }">
            <div style="display: flex; column-gap: 8px; padding: 0 8px">
                <Button aria-label="预览" size="small" theme="borderless" type="tertiary">
                    <template #icon>
                        <IconEyeOpened />
                    </template>
                </Button>
                <Button aria-label="下载" size="small" theme="borderless" type="tertiary">
                    <template #icon>
                        <IconDownload />
                    </template>
                </Button>
                <Button aria-label="删除" size="small" theme="borderless" type="tertiary" @click="onRemove">
                    <template #icon>
                        <IconDelete />
                    </template>
                </Button>
            </div>
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

自定义文件列表标题

listType 为 list 时,可以自定义文件列表顶部的标题区域,分为两个插槽:

只替换标题文字

#fileListTitle 仅替换标题文字,清空按钮保持默认样式:

已上传文件
document.pdf130KB
report.xlsx222KB
📁 重要文件
document.pdf130KB
report.xlsx222KB
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'document.pdf',
            status: 'success',
            size: '130KB',
        },
        {
            uid: '2',
            name: 'report.xlsx',
            status: 'success',
            size: '222KB',
        },
    ];
</script>

<template>
    <Upload :action="action" :defaultFileList="defaultFileList" style="margin-bottom: 20px">
        <template #fileListTitle>已上传文件</template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            自定义标题文字
        </Button>
    </Upload>
    <Upload :action="action" :defaultFileList="defaultFileList">
        <template #fileListTitle>
            <span style="color: var(--semi-color-primary); font-weight: 600">📁 重要文件</span>
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            带样式的标题
        </Button>
    </Upload>
</template>

替换整个标题区

#fileListHeader 替换整个标题区域,包括清空按钮。提供该插槽后,即使它渲染为空也不回落到默认标题。插槽参数如下:

  • fileList: 当前文件列表
  • onClear: 清空文件的函数
  • clearText: 清空按钮的默认文案(根据当前语言环境)
📂 共 2 个文件
document.pdf130KB
report.xlsx222KB
已选择 2 个文件
document.pdf130KB
report.xlsx222KB
代码
<script setup lang="ts">
    import { IconClose, IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'document.pdf',
            status: 'success',
            size: '130KB',
        },
        {
            uid: '2',
            name: 'report.xlsx',
            status: 'success',
            size: '222KB',
        },
    ];
</script>

<template>
    <Upload :action="action" :defaultFileList="defaultFileList" style="margin-bottom: 20px">
        <template #fileListHeader="{ fileList, onClear, clearText }">
            <div style="display: flex; align-items: center; justify-content: space-between; width: 100%">
                <span style="color: var(--semi-color-primary)">📂 共 {{ fileList.length }} 个文件</span>
                <Button size="small" type="danger" @click="onClear">
                    <template #icon>
                        <IconClose />
                    </template>
                    {{ clearText }}
                </Button>
            </div>
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            自定义清空按钮样式
        </Button>
    </Upload>
    <Upload :action="action" :defaultFileList="defaultFileList">
        <template #fileListHeader="{ fileList, onClear }">
            <div style="display: flex; align-items: center; justify-content: space-between; width: 100%">
                <span>
                    已选择
                    <strong style="color: var(--semi-color-danger)">{{ fileList.length }}</strong>
                    个文件
                </span>
                <button style="padding: 0; border: none; background: none; font: inherit; cursor: pointer; color: var(--semi-color-link); font-size: 12px" type="button" @click="onClear">全部移除</button>
            </div>
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            完全自定义标题区域
        </Button>
    </Upload>
</template>

自定义预览逻辑

listType 为 list 时,可以通过 #previewFile 插槽自定义预览逻辑,插槽内容会替换原缩略图,渲染为空时也不回落到默认缩略图
例如你不需要对图片类型进行缩略图预览时,可以在 #previewFile 中恒定渲染一个 <IconFile />
假如你希望点击图片时放大预览,则可以在 #previewFile 中使用 Image 组件
或者你希望使用额外的操作区来实现点击放大预览,你也可以结合 #fileOperation 放置一些自定义元素例如 Icon 图标实现点击放大

已选择文件
dyBag.png130KB
dyBag2.png130KB
代码
<script setup lang="ts">
    import { IconFile, IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Image, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const url = '/assets/root-web-sites/edit-bag.jpeg';
    const defaultFileList: FileItem[] = [
        { uid: '1', name: 'dyBag.png', status: 'success', size: '130KB', url },
        { uid: '2', name: 'dyBag2.png', status: 'success', size: '130KB', url },
    ];
</script>

<template>
    <Upload action="https://api.semi.design/upload" :defaultFileList="defaultFileList">
        <template #previewFile="{ file }">
            <IconFile v-if="file.uid === '1'" size="large" />
            <Image v-else alt="" :src="file.url" />
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

结合 #fileOperation 与 ImagePreview 的示例,以下示例点击右侧第一个 Icon 可放大图片预览

已选择文件
dyBag.png130KB
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconDelete, IconDownload, IconExpand, IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, ImagePreview, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const visible = ref(false);
    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'dyBag.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
    ];
</script>

<template>
    <Upload action="https://api.semi.design/upload" :defaultFileList="defaultFileList" :itemStyle="{ width: '300px' }">
        <template #fileOperation="{ file, onRemove }">
            <div style="display: flex; column-gap: 8px; padding: 0 8px">
                <Button aria-label="预览" size="small" theme="borderless" type="tertiary" @click="visible = true">
                    <template #icon>
                        <IconExpand />
                    </template>
                </Button>
                <Button aria-label="下载" size="small" theme="borderless" type="tertiary">
                    <template #icon>
                        <IconDownload />
                    </template>
                </Button>
                <Button aria-label="删除" size="small" theme="borderless" type="tertiary" @click="onRemove()">
                    <template #icon>
                        <IconDelete />
                    </template>
                </Button>
                <ImagePreview v-model:visible="visible" :src="file.url" />
            </div>
        </template>
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

默认文件列表

通过 defaultFileList 可以展示已上传的文件。当需要预览默认文件的缩略图时,你可以将 defaultFileList 内对应 item 的 preview 属性设为 true

已选择文件
dyBag.jpeg130KB
dyBag2.jpeg222KB
上传失败
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'dyBag.jpeg',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
        {
            uid: '2',
            name: 'dyBag2.jpeg',
            status: 'uploadFail',
            size: '222KB',
            preview: true,
            fileInstance: new File([new ArrayBuffer(2048)], 'dyBag2.jpeg', { type: 'image/png' }),
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
    ];
</script>

<template>
    <Upload action="https://api.semi.design/upload" :defaultFileList="defaultFileList">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

受控组件

使用 v-model:fileList 绑定文件列表,Upload 在列表变化时通过 update:fileList 事件回写。只用单向绑定 :fileList 而不回写时,列表保持传入的值。@change 事件仍会收到 { fileList, currentFile },其中 currentFile 在通过 insert 方法插入文件后为 null。

已选择文件
dyBag.jpeg130KB
dy.jpeg222KB
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type FileItem, type UploadOnChangeProps } from '@fibrous/semi-ui-vue';

    const list = ref<FileItem[]>([
        {
            uid: '1',
            name: 'dyBag.jpeg',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/root-web-sites/edit-bag.jpeg',
        },
        {
            uid: '2',
            name: 'dy.jpeg',
            status: 'uploading',
            size: '222KB',
            percent: 50,
            preview: true,
            fileInstance: new File([new ArrayBuffer(2048)], 'dy.jpeg', { type: 'image/jpeg' }),
            url: '/assets/placeholder/avatar-a.svg',
        },
    ]);

    const onChange = ({ fileList, currentFile }: UploadOnChangeProps) => {
        console.log('change', fileList, currentFile);
    };
</script>

<template>
    <Upload v-model:fileList="list" action="https://api.semi.design/upload" :showRetry="false" @change="onChange">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

图片墙

设置 listType = 'picture',用户可以上传图片并在列表中显示缩略图
如果通过 defaultFileList 或 fileList 设置已上传的文件列表时,会自动读取对象数组中的 url 属性用于展示图片

dy.png
代码
<script setup lang="ts">
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'dy.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/placeholder/avatar-a.svg',
        },
    ];
</script>

<template>
    <Upload accept="image/*" action="https://api.semi.design/upload" :defaultFileList="defaultFileList" listType="picture" multiple>
        <IconPlus aria-label="上传图片" size="extra-large" />
    </Upload>
</template>

设置 showPicInfo,可以查看图片基础信息

music.png
1
brand.png
2
代码
<script setup lang="ts">
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const url = '/assets/placeholder/avatar-b.svg';
    const defaultFileList: FileItem[] = [
        { uid: '1', name: 'music.png', status: 'success', size: '130KB', preview: true, url },
        { uid: '2', name: 'brand.png', status: 'success', size: '130KB', preview: true, url },
    ];
</script>

<template>
    <Upload accept="image/*" action="https://api.semi.design/upload" :defaultFileList="defaultFileList" listType="picture" multiple showPicInfo>
        <IconPlus aria-label="上传图片" size="extra-large" />
    </Upload>
</template>

图片墙放大预览

配合 Image 组件,通过 #thumbnail 插槽,可以实现点击图片放大预览

music.png
代码
<script setup lang="ts">
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Image, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';
    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'music.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/placeholder/avatar-b.svg',
        },
    ];
</script>

<template>
    <Upload accept="image/*" :action="action" :defaultFileList="defaultFileList" listType="picture" multiple>
        <template #thumbnail="{ file }">
            <Image :alt="file.name" :src="file.url" />
        </template>
        <IconPlus aria-label="Upload picture" size="extra-large" />
    </Upload>
</template>

可以通过 #picPreviewIcon 插槽,@previewClick 事件来自定义预览图标,当显示替换图标 showReplace 时,不会再显示预览图标
当需要自定义预览/替换功能时,需要关闭替换功能,使用 #picPreviewIcon 监听图标点击事件即可。
@previewClick 监听的是单张图片容器的点击事件。监听它会同时让文件卡片可点击,并显示预览按钮

resso.png
1
代码
<script setup lang="ts">
    import { IconEyeOpened, IconPlus } from '@fibrous/semi-icons-vue';
    import { Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'resso.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/placeholder/avatar-b.svg',
        },
    ];

    const handlePreview = (file: FileItem) => {
        window.open(file.url, 'imagePreview', 'width=300,height=300');
    };
</script>

<template>
    <Upload accept="image/*" action="https://api.semi.design/upload" :defaultFileList="defaultFileList" listType="picture" multiple showPicInfo @previewClick="handlePreview">
        <template #picPreviewIcon>
            <IconEyeOpened style="color: var(--semi-color-white); font-size: 24px" />
        </template>
        <IconPlus aria-label="上传图片" size="extra-large" />
    </Upload>
</template>

图片墙设置宽高

通过设置 picHeight, picWidth,可以统一设置图片墙元素的宽高。如果同时使用 #thumbnail 插槽渲染 Image 组件来实现点击放大预览,你需要同时指定 Image 组件的 width 和 height

image-1.jpg
点击添加图片
代码
<script setup lang="ts">
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Image, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'image-1.jpg',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/root-web-sites/abstract.jpg',
        },
    ];
</script>

<template>
    <Upload accept="image/*" action="https://api.semi.design/upload" :defaultFileList="defaultFileList" listType="picture" multiple :picHeight="110" :picWidth="200">
        <template #thumbnail="{ file }">
            <Image :alt="file.name" :height="110" :src="file.url" :width="200" />
        </template>
        <IconPlus aria-label="上传图片" size="extra-large" style="margin: 4px" />
        点击添加图片
    </Upload>
</template>

设置 hotSpotLocation 自定义点击热区的顺序,默认在照片墙列表结尾


resso.png
1
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconPlus } from '@fibrous/semi-icons-vue';
    import { Radio, RadioGroup, Upload, type FileItem, type UploadProps } from '@fibrous/semi-ui-vue';

    const action = 'https://api.semi.design/upload';
    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'resso.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/placeholder/avatar-b.svg',
        },
    ];
    const handlePreview = (file: FileItem) => {
        window.open(file.url, 'imagePreview', 'width=300,height=300');
    };
    const hotSpotLocation = ref<UploadProps['hotSpotLocation']>('end');
</script>

<template>
    <RadioGroup :modelValue="hotSpotLocation" type="button" @update:modelValue="hotSpotLocation = $event as UploadProps['hotSpotLocation']">
        <Radio value="start">start</Radio>
        <Radio value="end">end</Radio>
    </RadioGroup>
    <hr />
    <Upload accept="image/*" :action="action" :defaultFileList="defaultFileList" :hotSpotLocation="hotSpotLocation" listType="picture" multiple showPicInfo @previewClick="handlePreview">
        <IconPlus aria-label="上传图片" size="extra-large" />
    </Upload>
</template>

禁用

已选择文件
first.png130KB
second.png222KB
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type FileItem } from '@fibrous/semi-ui-vue';

    const defaultFileList: FileItem[] = [
        {
            uid: '1',
            name: 'first.png',
            status: 'success',
            size: '130KB',
            preview: true,
            url: '/assets/placeholder/avatar-a.svg',
        },
        {
            uid: '2',
            name: 'second.png',
            status: 'validateFail',
            size: '222KB',
            preview: true,
            url: '/assets/placeholder/avatar-a.svg',
        },
    ];
</script>

<template>
    <Upload action="https://api.semi.design/upload" :defaultFileList="defaultFileList" disabled>
        <Button disabled theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

手动触发上传

uploadTrigger="custom",选中文件后将不会自动触发上传。需要通过模板引用调用暴露的 upload 方法触发

代码
<script setup lang="ts">
    import { useTemplateRef } from 'vue';
    import { IconPlus, IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';

    const uploadRef = useTemplateRef('uploadRef');
    const action = 'https://api.semi.design/upload';
</script>

<template>
    <div>
        <Upload ref="uploadRef" accept="image/gif, image/png, image/jpeg, image/bmp, image/webp" :action="action" uploadTrigger="custom" @error="(...v) => console.log(...v)" @success="(...v) => console.log(...v)">
            <Button style="margin-inline-end: 8px" theme="light">
                <template #icon>
                    <IconPlus />
                </template>
                选择文件
            </Button>
        </Upload>
        <Button theme="light" @click="uploadRef?.upload()">
            <template #icon>
                <IconUpload />
            </template>
            开始上传
        </Button>
    </div>
</template>

拖拽上传

draggable='true',可以使用拖拽功能

点击上传文件或拖拽文件到这里
支持任意类型文件
代码
<script setup lang="ts">
    import { Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" draggable>
        <template #dragMainText>点击上传文件或拖拽文件到这里</template>
        <template #dragSubText>支持任意类型文件</template>
    </Upload>
</template>

可以通过 #dragIcon、#dragMainText、#dragSubText 插槽快捷设置拖拽区内容。#dragIcon、#dragMainText 渲染为空时显示默认图标和文案

点击上传文件或拖拽文件到这里
仅支持jpeg、pdf
代码
<script setup lang="ts">
    import { IconBolt } from '@fibrous/semi-icons-vue';
    import { Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload accept="application/pdf,.jpeg" action="https://api.semi.design/upload" draggable style="margin-top: 10px">
        <template #dragIcon>
            <IconBolt />
        </template>
        <template #dragMainText>点击上传文件或拖拽文件到这里</template>
        <template #dragSubText>仅支持jpeg、pdf</template>
    </Upload>
</template>

还可以通过默认插槽传入内容,完全自定义拖拽区的显示

demo img
Wow, you can really dance.
代码
<script setup lang="ts">
    import { IconBolt } from '@fibrous/semi-icons-vue';
    import { Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload accept="application/pdf,.jpeg" action="https://api.semi.design/upload" draggable style="margin-top: 10px">
        <template #dragIcon>
            <IconBolt />
        </template>
        <div class="components-upload-demo-drag-area">
            <img alt="demo img" height="96" src="/assets/placeholder/avatar-a.svg" style="border-radius: 4px" />
            <div style="font-size: 14px; margin-top: 8px; flex-basis: 100%; text-align: center; color: var(--semi-color-tertiary)">Wow, you can really dance.</div>
        </div>
    </Upload>
</template>

Scss 样式如下

.components-upload-demo-drag-area {
    border-radius: var(--semi-border-radius-small);
    border: 2px dashed var(--semi-color-border);
    width: 100%;
    padding: 12px;
    background-color: var(--semi-color-tertiary-light-default);
    display: flex;
    cursor: pointer;
    flex-wrap: wrap;
    justify-content: center;
    &:hover {
        background-color: var(--semi-color-primary-light-default);
        border-color: var(--semi-color-primary);
    }
}

粘贴上传

设置 addOnPasting 后,在页面任意位置粘贴(paste 事件)的文件会加入文件列表,不需要剪贴板授权。读取粘贴的文件失败时触发 pastingError 事件。被 KeepAlive 失活的 Upload 不响应粘贴。

复制截图或文件后,在页面上按 Ctrl/Cmd + V
也可以点击或拖拽上传
代码
<script setup lang="ts">
    import { Toast, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" addOnPasting draggable @pastingError="Toast.error('读取剪贴板中的文件失败')">
        <template #dragMainText>复制截图或文件后,在页面上按 Ctrl/Cmd + V</template>
        <template #dragSubText>也可以点击或拖拽上传</template>
    </Upload>
</template>

上传前自定义校验

可通过 beforeUpload 钩子,对文件状态进行更新,这是在网络上传前,选择文件后进行校验,({ file: FileItem, fileList: Array<FileItem> }) => beforeUploadResult | Promise | boolean 同步校验时需返回 boolean(true 为校验通过,false 为校验失败,校验失败会阻止文件网络上传)或者一个 Object 对象,具体结构如下

// beforeUploadResult:
{
    fileInstance?: File,
    status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait',
    validateMessage?: string, // 文件的校验信息,需要节点时使用 #fileValidateMessage 插槽
    shouldUpload: boolean, // 是否需要上传。默认为true,如果为false,该fileItem只会被展示在列表中,不会触发上传操作
    autoRemove: boolean, // 是否从fileList中移除该文件,默认为false
}
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type BeforeUploadObjectResult, type BeforeUploadProps } from '@fibrous/semi-ui-vue';

    let count = 0;

    const transformFile = (fileInstance: File) => {
        if (count === 0) {
            return new File([fileInstance], 'newFileName', { type: 'image/png' });
        }
        return fileInstance;
    };

    const beforeUpload = ({ file }: BeforeUploadProps) => {
        let result: BeforeUploadObjectResult;
        if (count > 0) {
            result = {
                autoRemove: false,
                fileInstance: file.fileInstance,
                shouldUpload: true,
            };
        } else {
            result = {
                autoRemove: false,
                fileInstance: file.fileInstance,
                status: 'validateFail',
                shouldUpload: false,
            };
        }
        count += 1;
        return result;
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :beforeUpload="beforeUpload" :transformFile="transformFile">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(上传前同步校验)
        </Button>
    </Upload>
</template>

异步校验时,需返回 Promise,Promise resolve 代表检验通过,reject 代表校验失败,不会触发上传。
resolve/reject 时可以传入 object(结构同上 beforeUploadResult)

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type BeforeUploadObjectResult, type BeforeUploadProps } from '@fibrous/semi-ui-vue';

    let count = 0;

    const beforeUpload = ({ file }: BeforeUploadProps) => {
        return new Promise<BeforeUploadObjectResult>((resolve, reject) => {
            if (count > 1) {
                count += 1;
                resolve({ autoRemove: false, shouldUpload: true });
            } else {
                const result: BeforeUploadObjectResult = {
                    autoRemove: false,
                    fileInstance: file.fileInstance,
                    status: 'validateFail',
                    shouldUpload: false,
                    validateMessage: `第${count + 1}个注定失败`,
                };
                count += 1;
                reject(result);
            }
        });
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :beforeUpload="beforeUpload">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(上传前异步校验)
        </Button>
    </Upload>
</template>

上传后更新文件信息

可以通过 afterUpload 钩子,对文件状态,校验信息,文件名进行更新。
({ response: any, file: FileItem, fileList: Array<FileItem> }) => afterUploadResult
afterUpload 在上传完成后(xhr.onload)且没有发生错误的情况下触发,需返回一个 Object 对象(不支持异步返回),具体结构如下

// afterUploadResult:
{
    status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait';
    // 文件的校验信息,需要节点时使用 #fileValidateMessage 插槽
    validateMessage?: string;
    // 是否从fileList中移除该文件,默认为false
    autoRemove?: boolean;
    // 文件的名称
    name?: string;
    // 预览文件的url,一般为当次上传请求中 Server 接收到文件后返回的存储地址
    url?: string
}
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type AfterUploadProps, type AfterUploadResult } from '@fibrous/semi-ui-vue';

    const afterUpload = ({ response }: AfterUploadProps): AfterUploadResult => {
        if (response.status_code === 200) {
            return {
                autoRemove: false,
                status: 'uploadFail',
                validateMessage: '内容不合法',
                name: 'RenameByServer.jpg',
                url: '/assets/root-web-sites/edit-bag.jpeg',
            };
        }
        return {};
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :afterUpload="afterUpload">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

自定义请求

当传入 customRequest 时, 相当于使用的自定义的请求方法替换了 upload 内置的 xhr 请求,用户需要自行接管上传行为。
可在入参中获取到当前操作的 file 对象,用户自行实现上传过程,并且在适当的时候调用 customRequest 入参中的 onProgress、onError、onSuccess 以更新 Upload 组件内部状态进而驱动 UI 更新
customRequest 包含以下入参

{
    // 当前文件名称
    fileName: string,
    // 用户设置的props.data
    data: object,
    // FileItem,具体结构参考下面的文档
    file: FileItem,
    // original File Object which extends Blob, 浏览器实际获取到的文件对象(https://developer.mozilla.org/zh-CN/docs/Web/API/File)
    fileInstance: File,
    // 上传过程中应调用的函数,event需要包含 total、loaded属性
    onProgress: (event: { total: number, loaded: number }) => any,
    // 上传出错时应调用的函数
    onError: (userXhr: { status?: number }, e?: Event) => any,
    // 上传成功后应调用的函数, response为上传成功后的请求结果
    onSuccess: (response: any, e?: Event) => any,
    // 用户设置的props.withCredentials
    withCredentials: boolean,
    // 用户设置的props.action
    action: string,
}
代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type UploadCustomRequestArgs } from '@fibrous/semi-ui-vue';

    const mockRequest = ({ onProgress, onSuccess }: UploadCustomRequestArgs) => {
        let count = 0;
        const interval = setInterval(() => {
            if (count === 100) {
                clearInterval(interval);
                onSuccess({});
                return;
            }
            onProgress({ total: 100, loaded: count });
            count += 20;
        }, 500);
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :customRequest="mockRequest">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传
        </Button>
    </Upload>
</template>

图片裁切

通过 crop 属性启用图片裁切功能。支持点击选择、拖拽、粘贴、替换文件时进行裁切。

基本用法

设置 crop 启用默认裁切配置(:crop="true" 或布尔简写 crop)。

裁切前会先按 accept 与 limit 过滤,被拒绝或超出数量的图片不会进入裁切。浏览器只能编码 PNG、JPEG、WebP,其他格式(GIF、BMP、SVG)的裁切结果为 PNG,文件的 type 与扩展名随之变为 image/png 与 .png。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Upload action="https://api.semi.design/upload" crop @success="(response, file) => console.log('上传成功:', response, file)">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(启用裁切)
        </Button>
    </Upload>
</template>

自定义裁切配置

通过对象形式配置裁切参数,包括宽高比、形状、质量等。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type UploadCropProps } from '@fibrous/semi-ui-vue';

    const crop: UploadCropProps = {
        aspectRatio: 16 / 9,
        shape: 'rect',
        quality: 0.8,
        modalTitle: '裁切图片',
        modalOkText: '确认裁切',
        modalCancelText: '取消',
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :crop="crop" @success="(response, file) => console.log('上传成功:', response, file)">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(16:9 裁切)
        </Button>
    </Upload>
</template>

圆形裁切

适用于头像上传场景,设置 shape: 'round' 启用圆形裁切。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';

    const crop = {
        aspectRatio: 1,
        shape: 'round',
        quality: 0.9,
        modalTitle: '裁切头像',
    } as const;
</script>

<template>
    <Upload action="https://api.semi.design/upload" :crop="crop" @success="(response, file) => console.log('上传成功:', response, file)">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(圆形裁切)
        </Button>
    </Upload>
</template>

裁切前确认

通过 beforeCrop 回调,在裁切前进行确认或其他处理。返回 false 可跳过裁切直接上传。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload, type UploadProps } from '@fibrous/semi-ui-vue';

    const crop = { aspectRatio: 1, shape: 'round' } as const;

    const beforeCrop: UploadProps['beforeCrop'] = (file, fileList) => {
        console.log('beforeCrop:', file, fileList);
        // true continues cropping; false skips it and uploads directly
        return window.confirm('是否裁切图片?');
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :beforeCrop="beforeCrop" :crop="crop" @cropError="(error) => console.error('裁切失败:', error)" @success="(response, file) => console.log('上传成功:', response, file)">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            点击上传(裁切前确认)
        </Button>
    </Upload>
</template>

拖拽上传裁切

拖拽上传时也会触发裁切功能。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';

    const crop = {
        aspectRatio: 1,
        shape: 'rect',
    } as const;
</script>

<template>
    <Upload action="https://api.semi.design/upload" :crop="crop" draggable @drop="(_e, files) => console.log('拖拽文件:', files)" @success="(response, file) => console.log('上传成功:', response, file)">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            拖拽上传(启用裁切)
        </Button>
    </Upload>
</template>

自定义裁切弹窗样式

通过 cropModalProps 自定义裁切弹窗的样式和属性。弹窗的 width 缺省为 600,传入的值优先。

cropModalProps 的类型为 UploadCropModalProps,不含 visible、title、okText、cancelText、onOk、onCancel:标题和按钮文字用 crop 的 modalTitle、modalOkText、modalCancelText 配置,其余由组件内置。

代码
<script setup lang="ts">
    import { IconUpload } from '@fibrous/semi-icons-vue';
    import { Button, Upload } from '@fibrous/semi-ui-vue';

    const crop = {
        aspectRatio: 1,
        shape: 'rect',
    } as const;

    const cropModalProps = {
        width: 800,
        style: { height: '600px' },
        bodyStyle: { height: '500px', backgroundColor: '#f5f5f5' },
    };
</script>

<template>
    <Upload action="https://api.semi.design/upload" :crop="crop" :cropModalProps="cropModalProps" @success="(response, file) => console.log('上传成功:', response, file)">
        <Button theme="light">
            <template #icon>
                <IconUpload />
            </template>
            自定义弹窗样式
        </Button>
    </Upload>
</template>

API 参考


Upload

Props

属性说明类型默认值
accept接受上传的文件类型,写法同 input 的 accept:文件后缀(如 .jpg)或 MIME type,用逗号分隔。stringsemiGlobalDefault("Upload", "accept")
action文件上传地址,必填。stringundefined
addOnPasting粘贴(paste 事件)时是否把剪贴板中的文件加入文件列表,无需额外授权。booleansemiDefault("Upload", "addOnPasting", false)
afterUpload上传完成且没有出错后的钩子,根据返回的对象更新文件状态、校验信息、名称和预览地址;不支持异步返回。Type((object: AfterUploadProps) => AfterUploadResult)undefined
beforeClear清空文件前的回调,返回 false、resolve(false) 或 reject 会阻止清空。Type((fileList: FileItem[]) => boolean | Promise<boolean>)undefined
beforeCrop图片裁切前的回调,只用第一张选中的图片调用一次,结果决定整批是否裁切。返回 false 跳过裁切直接上传,支持异步返回。Type((file: File, fileList: File[]) => boolean | Promise<boolean>)undefined
beforeRemove移除文件前的回调,返回 false、resolve(false) 或 reject 会阻止移除。Type((file: FileItem, fileList: FileItem[]) => boolean | Promise<boolean>)undefined
beforeUpload上传文件前的钩子,根据返回的对象更新文件状态并控制是否上传;返回 boolean 表示通过或失败,返回 Promise 时 resolve 为通过、reject 为失败。Type((object: BeforeUploadProps) => boolean | BeforeUploadObjectResult | Promise<BeforeUploadObjectResult>)undefined
capture媒体拍摄的方式,可选 user、environment;传 true 输出空值属性,传 false 不输出该属性。boolean | "user" | "environment"semiGlobalDefault("Upload", "capture")
crop启用图片裁切。传 true 使用默认配置,传对象自定义裁切参数;点击选择、拖拽、粘贴、替换都会裁切。boolean | UploadCropPropssemiDefault("Upload", "crop", false)
cropModalProps裁切弹窗 Modal 的属性;visible、title、okText、cancelText、onOk、onCancel 由组件内置,width 缺省为 600。UploadCropModalPropssemiGlobalDefault("Upload", "cropModalProps")
customRequest替换内置 XMLHttpRequest 的自定义请求方法,需自行调用入参的 onProgress、onError、onSuccess 更新状态。Type((object: UploadCustomRequestArgs) => void)undefined
data上传时附带的额外参数,或根据文件返回额外参数的函数。TypeRecord<string, any> | ((file: File) => Record<string, unknown>)undefined
defaultFileList初始的已上传文件列表,非受控。每项的 uid 必填且不能重复。FileItem[]semiDefault("Upload", "defaultFileList", [])
directory是否上传文件夹。booleansemiDefault("Upload", "directory", false)
disabled是否禁用。禁用后不再显示移除、重试、替换和清空操作。booleansemiDefault("Upload", "disabled", false)
draggable是否支持拖拽上传。booleansemiDefault("Upload", "draggable", false)
fileList受控的文件列表,配合 v-model:fileList 使用。每项的 uid 必填且不能重复。FileItem[]semiGlobalDefault("Upload", "fileList")
fileName作用与 name 相同,主要在 Form.Upload 中使用,避免与 Field 的 name 冲突。stringsemiGlobalDefault("Upload", "fileName")
headers上传时附带的 headers,或根据文件返回 headers 的函数。TypeRecord<string, any> | ((file: File) => Record<string, string>)undefined
hotSpotLocation照片墙添加按钮的位置,可选 start 或 end。"start" | "end"semiDefault("Upload", "hotSpotLocation", "end")
itemStyle文件卡片的内联样式。StyleValuesemiGlobalDefault("Upload", "itemStyle")
limit最多允许上传的文件个数。为 1 时始终用最新选择的文件替换当前文件,不触发 exceed。numbersemiGlobalDefault("Upload", "limit")
listType文件列表的展示类型,可选 list、picture、none;none 不渲染文件列表,只保留触发区。"list" | "none" | "picture"semiDefault("Upload", "listType", "list")
maxSize文件体积上限,单位 KB。numbersemiGlobalDefault("Upload", "maxSize")
minSize文件体积下限,单位 KB。numbersemiGlobalDefault("Upload", "minSize")
multiple是否允许一次选择多个文件。booleansemiDefault("Upload", "multiple", false)
name上传时使用的文件字段名。stringsemiGlobalDefault("Upload", "name")
onPreviewClick监听方式为 @previewClick。传入后文件卡片可点击,并显示预览按钮;是否传入会改变渲染,因此保留为函数 prop。Type((fileItem: FileItem) => void)undefined
picHeight照片墙模式下图片的展示高度。string | numbersemiGlobalDefault("Upload", "picHeight")
picWidth照片墙模式下图片的展示宽度。string | numbersemiGlobalDefault("Upload", "picWidth")
promptPosition提示文本的位置,可选 left、right、bottom。list 模式相对触发区,picture 模式相对图片列表。"bottom" | "left" | "right"semiDefault("Upload", "promptPosition", "right")
showClearlist 模式下,limit 不为 1 且至少有一个文件时,是否展示清空按钮。booleansemiDefault("Upload", "showClear", true)
showPicInfo是否显示图片信息,只在照片墙模式下有效。booleansemiDefault("Upload", "showPicInfo", false)
showReplace上传成功后,文件卡片内是否展示替换按钮。booleansemiDefault("Upload", "showReplace", false)
showRetry上传失败时,文件卡片内是否展示重试按钮。booleansemiDefault("Upload", "showRetry", true)
showTooltip文件名过长时是否展示提示,或配置提示的类型(tooltip 或 popover)和透传属性;自定义弹出内容使用 ellipsisTooltip 插槽。boolean | ShowTooltipsemiDefault("Upload", "showTooltip", true)
showUploadList是否显示文件列表。booleansemiDefault("Upload", "showUploadList", true)
transformFile选中文件后、上传前的转换函数,返回要上传的 File。Type((file: File) => File)undefined
uploadTrigger触发上传的时机,auto 选中后自动上传,custom 需调用 upload 方法。"auto" | "custom"semiDefault("Upload", "uploadTrigger", "auto")
validateStatus校验状态,影响根元素的样式,可选 default、error、warning、success。"error" | "success" | "default" | "warning"semiGlobalDefault("Upload", "validateStatus")
withCredentials上传请求是否携带 Cookie。booleansemiDefault("Upload", "withCredentials", false)

Events

事件说明回调参数
update:fileList文件列表变化时触发,用于 v-model:fileList。[fileList: FileItem[]]
acceptInvalid接收到的文件不符合 accept 时触发,参数为不符合的文件。[files: File[]]
change文件状态变化时触发,包括上传中、成功、失败。currentFile 在 insert 后为 null,清空时不带。会遮住隐藏 input 冒泡出来的原生 change。[object: UploadOnChangeProps]
clear点击清空后触发。[]
cropError图片裁切失败时触发。[error: Error]
drop拖拽的元素在拖拽区上释放时触发,参数为原生 drop 事件、拖入的文件和当前文件列表。会遮住根元素上的原生 drop。[e: DragEvent, files: File[], fileList: FileItem[]]
error上传出错时触发。使用 customRequest 时,xhr 是使用方传给 onError 的对象。Type[e: UploadCustomError, file: CustomFile, fileList: FileItem[], xhr: XMLHttpRequest | { status?: number | undefined; }]
exceed文件总数超出 limit 时触发,参数为截断前本次选中的原始文件。[fileList: File[]]
fileChange选中文件后触发。[files: File[]]
openFileDialog打开系统文件选择窗口时触发。[]
pastingErroraddOnPasting 为 true 且读取剪贴板中的文件失败时触发。[error: Error]
progress上传过程中触发,参数为百分比、文件和当前文件列表。[percent: number, file: CustomFile, fileList: FileItem[]]
remove移除文件后触发。来自 defaultFileList 且没有 File 对象的项,currentFile 为 undefined。Type[currentFile: CustomFile | undefined, fileList: FileItem[], currentFileItem: FileItem]
retry点击重试时触发。[fileItem: FileItem]
sizeError文件体积不符合 maxSize 或 minSize 时触发。[file: CustomFile, fileList: FileItem[]]
success上传成功后触发,参数为服务端响应、文件和当前文件列表。[responseBody: any, file: CustomFile, fileList: FileItem[]]

Slots

插槽说明作用域参数
default触发区内容。拖拽模式下替换整个默认拖拽区内容。any
dragIcon拖拽区的图标,渲染为空时显示默认图标。any
dragMainText拖拽区的主文本,渲染为空时显示语言包文案。any
dragSubText拖拽区的帮助文本。any
prompt提示文本。与写在默认插槽中不同,点击它不会触发上传。any
validateMessageUpload 整体的校验信息。any
fileListTitle只替换文件列表的标题文字,保留清空按钮。any
fileListHeader替换整个标题区(含清空按钮),参数为 fileList、onClear、clearText。UploadFileListHeaderSlotProps
fileItem替换每张文件卡片,参数为 UploadFileItemSlotProps;对某个文件渲染为空时不显示该文件。UploadFileItemSlotProps
previewFile自定义预览逻辑,替换原缩略图,只在 list 模式下有效;渲染为空时不回落默认缩略图。UploadFileItemSlotProps
thumbnail自定义照片墙缩略图,只在照片墙模式下有效。UploadFileItemSlotProps
picInfo自定义照片墙信息,只在照片墙模式且 showPicInfo 为 true 时有效。UploadFileItemSlotProps
picPreviewIcon自定义照片墙悬停时的预览图标,只在照片墙模式下有效。UploadFileItemSlotProps
picClose自定义照片墙的关闭按钮,参数为 class 和 onRemove,只在照片墙模式下有效。UploadPicCloseSlotProps
fileOperation自定义列表项的操作区。UploadFileItemSlotProps
fileValidateMessage自定义文件卡片的校验信息,渲染为空时回落到文件自身的 validateMessage 字符串。UploadFileItemSlotProps
ellipsisTooltip自定义文件名过长时的弹出内容,参数为 content 和 trigger。TypographyEllipsisTooltipProps

Exposes

名称说明类型
insert在 index 位置插入文件,不传 index 则插入到末尾。Type(files: CustomFile[], index?: number | undefined) => void
upload开始上传等待中的文件,配合 uploadTrigger 为 custom 使用。Type() => void
openFileDialog打开文件选择窗口。Type() => void

class、style、aria-* 等未声明的 attrs 落在根元素上,根元素带 aria-label 或 aria-labelledby 时自动加 role="group"。

Upload.FileCard

Upload.FileCard 是默认的文件卡片,用于在 #fileItem 等插槽中保留默认外观:<Upload.FileCard v-bind="props" />。class、style、data-*、aria-* 落在根元素上。

Props

属性说明类型默认值
file要展示的文件,必填。FileItemundefined
index文件在列表中的位置。numberundefined
listType卡片的展示类型,可选 list、picture、none;none 不渲染卡片。"list" | "none" | "picture""list"
disabled是否禁用,禁用后不显示移除、重试和替换按钮。booleanundefined
showRetry上传失败时是否展示重试按钮,文件自身的 showRetry 优先。booleanundefined
showReplace上传成功后是否展示替换按钮,文件自身的 showReplace 优先。booleanundefined
showPicInfo是否显示图片信息,只在照片墙模式下有效。booleanundefined
showTooltip文件名过长时是否展示提示,或配置提示的类型和透传属性。boolean | ShowTooltiptrue
picWidth照片墙模式下图片的宽度。string | numberundefined
picHeight照片墙模式下图片的高度。string | numberundefined
validateMessage校验信息,优先于文件自身的 validateMessage。stringundefined
onPreviewClick监听方式为 @previewClick。传入后卡片可点击,并显示预览按钮;是否传入会改变渲染,因此保留为函数 prop。Type(() => void)undefined

Events

事件说明回调参数
remove点击移除时触发。[]
retry点击重试时触发。[]
replace点击替换时触发。[]

Slots

插槽说明作用域参数
previewFile自定义预览逻辑,替换原缩略图,只在 list 模式下有效。UploadFileItemSlotProps
thumbnail自定义照片墙缩略图,只在照片墙模式下有效。UploadFileItemSlotProps
picInfo自定义照片墙信息,只在照片墙模式且 showPicInfo 为 true 时有效。UploadFileItemSlotProps
picPreviewIcon自定义照片墙悬停时的预览图标,只在照片墙模式下有效。UploadFileItemSlotProps
picClose自定义照片墙的关闭按钮,参数为 class 和 onRemove,只在照片墙模式下有效。UploadPicCloseSlotProps
fileOperation自定义列表项的操作区。UploadFileItemSlotProps
validateMessage自定义校验信息,渲染为空时回落到字符串。插槽为该文件渲染出内容时,图片墙同样显示校验图标。UploadFileItemSlotProps
ellipsisTooltip自定义文件名过长时的弹出内容,参数为 content 和 trigger。TypographyEllipsisTooltipProps

Exposes

名称说明类型
无

Interfaces

FileItem Interface

interface FileItem {
    event?: Event; // xhr 事件
    fileInstance?: File; // 浏览器实际获取到的文件对象,继承自 Blob(https://developer.mozilla.org/zh-CN/docs/Web/API/File)
    name: string;
    percent?: number; // 上传进度百分比
    preview?: boolean; // 是否根据url进行预览
    response?: any; // xhr的response, 请求成功时为respoonse body,请求失败时为对应 error
    shouldUpload?: boolean; // 是否应该继续上传
    showReplace?: boolean; // 单独控制该file是否展示替换按钮
    showRetry?: boolean; // 单独控制该file是否展示重试按钮
    size: string; // 文件大小,单位kb
    status: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait';
    uid: string; // 文件唯一标识符,如果当前文件是通过upload选中添加的,会自动生成uid。如果是defaultFileList, 需要自行保证不会重复
    url?: string;
    validateMessage?: string; // 需要节点时使用 #fileValidateMessage 插槽
}

FileItem 只包含上述字段,写入未声明的字段会报类型错误。

UploadFileItemSlotProps Interface

#fileItem、#thumbnail、#picInfo、#picPreviewIcon、#fileOperation、#previewFile、#fileValidateMessage 收到的参数。文件数据放在 file 字段中,另有文件卡片的属性。参数不含 key,可以直接写 <Upload.FileCard v-bind="props" /> 渲染默认卡片。

interface UploadFileItemSlotProps {
    file: FileItem;
    index?: number; // 文件在列表中的位置
    listType: 'picture' | 'list' | 'none';
    disabled?: boolean;
    showRetry?: boolean;
    showReplace?: boolean;
    showPicInfo?: boolean;
    showTooltip?: boolean | ShowTooltip;
    picWidth?: string | number;
    picHeight?: string | number;
    validateMessage?: string;
    style?: StyleValue; // 来自 itemStyle
    onRemove: () => void; // 移除该文件
    onRetry: () => void; // 重新上传该文件
    onReplace: () => void; // 替换该文件
    onPreviewClick?: () => void; // 仅在监听了 Upload 的 previewClick 时存在
}

UploadPicCloseSlotProps Interface

#picClose 收到的参数:

interface UploadPicCloseSlotProps {
    class: string;
    onRemove: (e: MouseEvent) => void;
}

UploadFileListHeaderSlotProps Interface

#fileListHeader 收到的参数:

interface UploadFileListHeaderSlotProps {
    fileList: FileItem[]; // 当前文件列表
    onClear: () => void; // 清空文件的函数
    clearText: string; // 清空按钮的默认文案(根据当前语言环境)
}

CropProps Interface

crop 属性传入对象时的配置项:

Vue 中该类型的导出名为 UploadCropProps。

interface CropProps {
    aspectRatio?: number; // 裁切框宽高比,例如 16/9, 1, 4/3
    shape?: 'rect' | 'round' | 'roundRect'; // 裁切框形状,默认 'rect'
    minZoom?: number; // 最小缩放比例
    maxZoom?: number; // 最大缩放比例
    zoomStep?: number; // 缩放步长
    quality?: number; // 输出图片质量 0-1,默认 0.92
    fill?: string; // 填充颜色,默认透明 'rgba(0, 0, 0, 0)'
    modalTitle?: string; // 裁切弹窗标题
    modalOkText?: string; // 确认按钮文字
    modalCancelText?: string; // 取消按钮文字
}

Methods

Upload 通过 defineExpose 暴露 insert、upload、openFileDialog,可以用模板引用调用,类型为 UploadHandle,说明见上方 Upload 的 Exposes 表。

Accessibility

Upload组件是一个可交互的控件,在点击或拖拽时触发文件选择,文件选中后会在文件列表内展示状态。

ARIA

  • 触发区是 role="button",Enter 与 Space 可激活。触发区内容自带控件(如 Button)时,触发区不加角色,由该控件充当按钮,点击它打开文件选择。该控件被禁用(原生 disabled 或 aria-disabled="true")时仍算使用方自己的控件,触发区不会变成按钮,Enter 与 Space 也不会打开文件选择
  • 禁用 Upload 时请同时给该控件传 disabled。达到 limit 时只禁用该控件、不禁用 Upload,用户仍可移除已上传的文件
  • 根元素带 aria-label 或 aria-labelledby 时自动加 role="group",使用方显式传了 role 时以使用方为准
  • 文件列表添加 role="list",并用 aria-label 描述;照片墙的添加按钮在列表之外
  • 列表模式下的替换按钮直接作为 Tooltip 的触发器,Tab 键只停靠一次

文案规范

  • 上传按钮
  • 帮助文本
    • 帮助文本使用语句书写规范,首字母大写,可以不需要句号
  • 用户出错提示
    • 清晰地告诉用户为什么文件无法被上传,并且告知用户如何操作能够成功上传
    • 帮助文本使用语句书写规范,首字母大写
    • 简洁的用语让用户能够一眼读懂,比如 File size must be less than 20MB, File type must be .gif, .jpg, .png or .svg

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$color-upload-textvar(--semi-color-text-0)上传文件卡片等默认文本颜色
$color-upload_assist-textvar(--semi-color-text-2)上传文件列表标题及文件大小文本颜色
$color-upload-bordervar(--semi-color-border)图片墙添加按钮及拖拽区域描边颜色
$color-upload_card-bg-hovervar(--semi-color-fill-1)上传文件卡片背景色 - 悬浮
$color-upload_card-bgvar(--semi-color-fill-0)上传文件卡片背景色 - 默认
$color-upload_card_fail-bg-hovervar(--semi-color-danger-light-hover)上传失败文件卡片背景色 - 悬浮
$color-upload_card_fail-bgvar(--semi-color-danger-light-default)上传失败文件卡片背景色 - 默认
$color-upload_clear-textvar(--semi-color-primary)上传清空按钮文本颜色
$color-upload_drag_area-bg-hovervar(--semi-color-primary-light-default)上传可拖拽区域背景颜色 - 悬浮
$color-upload_drag_area-border-hovervar(--semi-color-primary)上传可拖拽区域描边颜色 - 悬浮
$color-upload_drag_area_disabled-textvar(--semi-color-disabled-text)上传可拖拽区域禁用文本颜色
$color-upload_drag_area-bgvar(--semi-color-tertiary-light-default)上传可拖拽区域背景颜色 - 默认
$color-upload_drag_area_iconvar(--semi-color-primary)上传可拖拽区域图标颜色
$color-upload_drag_area_tips-textvar(--semi-color-primary)上传可拖拽区域提示文本颜色
$color-upload_drag_area_main-text-defaultvar(--semi-color-text-0)上传可拖拽区主要提示文本颜色 - 默认
$color-upload_drag_area_main-text-hovervar(--semi-color-text-0)上传可拖拽区主要提示文本颜色 - 悬浮
$color-upload_drag_area_main-text-activevar(--semi-color-text-0)上传可拖拽区主要提示文本颜色 - 按下
$color-upload_drag_area_sub-text-defaultvar(--semi-color-text-0)上传可拖拽区次要提示文本颜色 - 默认
$color-upload_drag_area_sub-text-hovervar(--semi-color-text-0)上传可拖拽区次要提示文本颜色 - 悬浮
$color-upload_drag_area_sub-text-activevar(--semi-color-text-0)上传可拖拽区次要提示文本颜色 - 按下
$color-upload_file_card_fail_info-textvar(--semi-color-danger)上传文件卡片失败提示信息文本颜色
$color-upload_file_card_preview_placeholder-bgvar(--semi-grey-3)文件卡片默认预览背景颜色
$color-upload_file_card_preview_placeholder-textvar(--semi-white)文件卡片默认预览图颜色
$color-upload_file_card_retry-bgvar(--semi-color-white)图片墙上传卡片重新上传按钮背景颜色
$color-upload_file_card_retry-textvar(--semi-color-primary)图片墙上传卡片重新上传按钮文本颜色
$color-upload-iconvar(--semi-color-tertiary)图片墙上传图标加号颜色
$color-upload_pic_add-bg-activevar(--semi-color-fill-2)图片墙上传背景色 - 按下
$color-upload_pic_add-bg-hovervar(--semi-color-fill-1)图片墙上传背景色 - 悬浮
$color-upload_pic_add-bgvar(--semi-color-fill-0)图片墙上传背景色 - 默认
$color-upload_picture_file_card_loading_error-iconvar(--semi-color-danger)图片墙上传卡片加载及失败状态图标颜色
$color-upload_picture_file_card_error-bordervar(--semi-color-danger)图片墙上传卡片失败状态描边颜色
$color-upload_picture_file_card_pic_info-textvar(--semi-color-white)图片墙图片信息(序号)文字颜色
$color-upload_picture_file_card_close-iconvar(--semi-color-white)图片墙关闭图标颜色
$color-upload_picture_file_card_hover-bgvar(--semi-color-overlay-bg)图片墙预览悬浮背景色
$color-upload_preview-iconvar(--semi-color-text-2)上传文件卡片文本颜色
$color-upload_retry-textvar(--semi-color-primary)上传文件卡片重新上传按钮文本颜色
$color-upload_replace-textvar(--semi-color-white)图片墙上传卡片替换按钮文本颜色
$height-upload_file_card52px上传文件卡片高度
$width-upload_file_card250px上传文件卡片宽度
$height-upload_file_pic_card96px图片墙上传卡片高度
$width-upload_file_pic_card96px图片墙上传卡片宽度
$width-upload_file_card_preview36px文件卡片预览图标宽度
$height-upload_file_card_preview36px文件卡片预览图标高度
$width-upload_file_card_preview_img36px文件卡片预览图片宽度
$width-upload_file_card-icon11px文件卡片图标宽度
$width-upload_picture_add-border2px图片墙添加按钮描边宽度
$width-upload_picture_file_card_img96px图片墙上传卡片宽度
$width-upload_picture_file_card_close16px图片墙上传卡片删除按钮图标宽度
$width-upload_file_card_retry24px重新上传按钮宽度
$width-upload_file_card_retry-icon13px重新上传按钮图标宽度
$width-upload_picture_file_card_loading-icon14px上传加载 spin 宽度
$width-upload_drag_area-border2px可拖拽上传描边宽度
$spacing-upload_title_choosen-marginRight$spacing-tight已选择文件标题右侧外边距
$spacing-upload_title-marginBottom$spacing-extra-tight文件列表标题底部外边距
$spacing-upload_file_card_preview-margin$spacing-tight上传文件卡片预览外边距
$spacing-upload_file_card_info_size-marginLeft$spacing-tight上传文件卡片文件大小文本左侧外边距
$spacing-upload_file_card_info_retry-marginLeft$spacing-tight上传文件卡片重新上传按钮左侧外边距
$spacing-upload_file_card_info_progress-marginTop4px上传文件卡片进度顶部外边距
$spacing-upload_file_card_close-marginLeft$spacing-tight上传文件卡片删除按钮左侧外边距
$spacing-upload_file_card_close-marginRight$spacing-tight上传文件卡片删除按钮右侧外边距
$spacing-upload_file_card_icon-marginRight$spacing-super-tight上传文件卡片图标右侧外边距
$spacing-upload_picture_file_card-gap$spacing-tight图片墙卡片之间边距
$spacing-upload_picture_file_card-marginBottom$spacing-tight图片墙上传卡片底部外边距
$spacing-upload_picture_file_card_close-top8px图片墙上传卡片删除按钮顶部位置
$spacing-upload_picture_file_card_close-right8px图片墙上传卡片删除右侧位置
$spacing-upload_picture_file_card_loading_error-bottom6px图片墙上传卡片加载中图标底部位置
$spacing-upload_picture_file_card_loading_error-right6px图片墙上传卡片报错图标底部位置
$spacing-upload_picture_file_card_icon_error-top-1px图片墙上传卡片加载中图标顶部位置
$spacing-upload_drag_area-padding$spacing-base-tight可拖拽上传拖拽区域内边距
$spacing-upload_drag_area_main_text-marginBottom$spacing-extra-tight可拖拽上传拖拽标题底部外边距
$radius-upload_file_cardvar(--semi-border-radius-medium)上传文件卡片圆角
$radius-upload_file_card_previewvar(--semi-border-radius-small)上传文件卡片预览图圆角
$radius-upload_picture_addvar(--semi-border-radius-small)图片墙上传文件添加按钮圆角
$radius-upload_picture_file_card_imgvar(--semi-border-radius-small)图片墙上传卡片圆角
$radius-upload_picture_file_card_closevar(--semi-border-radius-circle)图片墙上传卡片删除按钮圆角
$radius-upload_file_card_retryvar(--semi-border-radius-circle)上传文件卡片重新上传圆角
$radius-upload_drag_areavar(--semi-border-radius-small)可拖拽上传拖拽区域圆角
$font-upload_file_card_info_name-fontWeight$font-weight-bold上传文件卡片文件名字重
$font-upload_file_card_info_size-fontWeight$font-weight-regular上传文件卡片文件尺寸字重
$font-upload_drag_area_tips-fontWeight600可拖拽上传提示文本字重
$font-upload_picture_file_card_pic_info-fontSize12px图片墙图片信息字体大小
$font-upload_picture_file_card_pic_info-fontWeight600图片墙图片信息文本字重

FAQ

  • 什么时候会展示重试按钮?
    • 当 showRetry 为 true,且当前文件是由于网络原因错误导致的上传失败时,会展示重试按钮。其他如校验失败,上传成功等状态是不会展示重试按钮的。
  • 什么时候会展示替换按钮?
    • 当 showReplace 为true,且当前文件状态为已上传时,会展示替换按钮。
  • Semi Upload把图片存到哪里了?
    • Semi Upload不负责图片的保存,当你使用 Upload 组件时需要自定义 action。你可以选择把 action 设置为自己的服务器地址或者图片服务地址。
  • Form.Upload props.name无效?
    • Form.Field 中有 props.name,Upload也有 props.name,同名 props 会冲突。使用 Form.Upload 时,可以转为使用 props.fileName,避免冲突
  • 上传图片后没有调用 XXX 方法?
    • 如果你设置了 accept,可以尝试把 accept 属性去掉,然后再看是否调用了改方法。去掉后调用了该方法说明,accept 在当前环境下获取的 file type 与设置的 accept 不符,上传行为提前终止。可以打个断点到 upload/foundation.js checkFileFormat 函数,看下获取的 file.type 真实值是否符合预期。