---
title: "FileUploader 文件上传"
description: "选择或拖入文件并以受控列表管理的上传控件"
category: Form
status: beta
since: 0.3.4
tags: [upload, file, form]
syncStatus: reviewed
verified: true
---

## 用法

### 基础
`accept` 限定类型，`max` 限定总数。
::::TuffDemoWrapper{demo="FileUploaderFileUploaderDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import { ref } from 'vue'

  const files = ref([])
  </script>

  <template>
    <TxFileUploader v-model="files" accept=".pdf,.png,.jpg" :max="5" />
  </template>
---
::::

### 选择后上传
`add` 只含本次新增的文件；`change` 与 `update:modelValue` 带完整列表。

```vue
<script setup lang="ts">
import type { FileUploaderFile } from '@talex-touch/tuffex/file-uploader'
import { ref } from 'vue'

const files = ref<FileUploaderFile[]>([])

async function uploadAdded(added: FileUploaderFile[]) {
  await Promise.all(added.map(item => uploadFile(item.file)))
}
</script>

<template>
  <TxFileUploader v-model="files" accept="image/*" :max="3" @add="uploadAdded" />
</template>
```

### 命令式打开
`pick()` 打开原生选择器，禁用时不执行。

```vue
<script setup lang="ts">
const uploader = ref<{ pick: () => void }>()
</script>

<template>
  <TxFileUploader ref="uploader" v-model="files" />
  <TxButton @click="uploader?.pick()">浏览文件</TxButton>
</template>
```

### 最佳实践

- 上传时用 `FileUploaderFile.file` 作 payload，其余字段只驱动界面。
- 服务端仍要校验数量、大小、MIME 与内容；`accept` 与 `max` 只是界面约束。
- 上传副作用监听 `add`；需要完整列表时监听 `change`。
- 生成的 `id` 只在当前界面会话内有效，不要持久化。
- 误拖入代价高的紧凑表单设置 `allowDrop=false`。

## API 参考

### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `modelValue` | `FileUploaderFile[]` | `[]` | 已选文件列表（受控）。 |
| `multiple` | `boolean` | `true` | 透传给原生 input。 |
| `accept` | `string` | `'*/*'` | 可接受的文件类型；透传给原生 input，也按同一规则过滤拖入的文件。 |
| `disabled` | `boolean` | `false` | 禁止浏览、删除与拖入。 |
| `max` | `number` | `10` | 已选文件总数上限。 |
| `showSize` | `boolean` | `true` | 在列表中显示文件大小。 |
| `allowDrop` | `boolean` | `true` | 允许拖入添加文件。 |
| `buttonText` | `string` | `'Choose files'` | 浏览按钮文案。 |
| `dropText` | `string` | `'Drop files here'` | 拖拽区主文案。 |
| `hintText` | `string` | `'or click to browse'` | 拖拽区提示文案。 |

### 事件

| 事件名 | 参数 | 说明 |
|------|------|------|
| `update:modelValue` | `FileUploaderFile[]` | 新增或删除后的完整列表。 |
| `change` | `FileUploaderFile[]` | 与 `update:modelValue` 同时触发。 |
| `add` | `FileUploaderFile[]` | 本次新增且被接受的文件。 |
| `remove` | `{ id: string, value: FileUploaderFile[] }` | 被删除的 id 与删除后的列表。 |

### 暴露方法

| 名称 | 类型 | 说明 |
|------|------|------|
| `pick` | `() => void` | 打开原生文件选择器；禁用时不执行。 |

### 类型

`FileUploaderFile`，列表中的一项：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | `string` | 生成的唯一 id，用作列表 key 与删除参数。 |
| `name` | `string` | 原始文件名。 |
| `size` | `number` | 文件大小（字节）。 |
| `type` | `string` | MIME 类型。 |
| `file` | `File` | 浏览器 `File` 对象。 |

## 概述

- 拖拽区是原生 `<button type="button">`，点击调用 `pick()`；隐藏的 `<input type="file">` 接收 `multiple`、`accept`、`disabled`。
- 每次选择后清空原生 input，同一文件可以再次选择。
- `multiple` 时新增数量受剩余容量 `max - modelValue.length` 限制，容量为零时不派发事件；单选时新文件替换原文件。
- 新增依次派发 `add`、`update:modelValue`、`change`；删除依次派发 `remove`、`update:modelValue`、`change`。
- `allowDrop=false` 或 `disabled` 时忽略拖入；拖入期间根节点带 `is-dragging`。
- `showSize` 时大小显示为 `B`、`KB` 或 `MB`。

## 技术实现

- 源码：`packages/tuffex/packages/components/src/file-uploader/`。

<TuffDocSourceLink />
