# 文件上传

拖拽或点击选择文件，带文件列表、进度条、类型与大小校验、数量限制，失败可重试，上传中可取消。可以用 data-action 直接 POST 到接口，也可以用自己的函数上传；两者都不给时只收集文件，由你在提交表单时取。只有列表模式；头像上传和图片卡片墙没有做。

**依赖**：`zhaoui.css` 与 `zhaoui.js`（原生 HTML、CSS、JS，不需要框架或 jQuery）。只用核心包时，还要加 `modular/upload.min.js` 和 `modular/upload.min.css`；全量版已包含。

## 用法

```html
<div class="zh-upload" data-zh-upload data-accept=".pdf,.png,.jpg" data-max="3" data-max-mb="5" data-hint="PDF、PNG、JPG · 单个不超过 5 MB · 最多 3 个" style="max-width:32rem">
  <div class="zh-upload-foot"><span class="zh-upload-count zh-hint">已添加 0 / 3 个</span></div>
</div>
```

## 样式类

`zh-upload`、`zh-upload-drop`、`zh-upload-list`、`zh-upload-item`、`zh-upload-title`、`zh-upload-hint`、`zh-upload-link`、`zh-upload-errors`

## 属性

| 属性 | 用在 | 取值 | 说明 |
|---|---|---|---|
| `data-zh-upload` | <div class="zh-upload"> |  | 声明为上传；没有写 zh-upload-drop 时库会生成默认的拖拽区 |
| `data-accept` | 容器 | 如 .pdf,.png,image/* | 允许的类型：扩展名或 MIME；也会传给系统的文件选择窗口 |
| `data-max-mb` | 容器 | 数字 | 单个文件大小上限（MB） |
| `data-max` | 容器 | 数字 | 最多文件数；设为 1 时选新文件会替换旧的 |
| `data-action` | 容器 | 接口地址 | 用 XHR 以 multipart/form-data 上传到这里，文件字段名由 data-field 指定（默认 file）；返回 2xx 算成功 |
| `data-credentials` | 容器 |  | 跨域上传时带上 Cookie |
| `data-hint` | 容器 | 提示文字 | 生成默认拖拽区时显示的限制说明 |
| `is-disabled` | 容器（class） |  | 禁用，不能选择也不能拖入 |

## 方法

- `ZhaoUI.upload.files(el)`：返回列表里的 File 数组，可自己放进 FormData 提交
- `ZhaoUI.upload.uploader(el, fn)`：设置自己的上传函数 fn(file, { onProgress(百分比), onAbort(回调) })，返回 Promise；会替代 data-action
- `ZhaoUI.upload.add(el, files)`：以代码方式加入文件（同样会校验）
- `ZhaoUI.upload.clear(el)`：清空列表

## 事件

| 事件 | 触发于 | detail | 说明 |
|---|---|---|---|
| `zh:upload-add` | 容器 | undefined | 文件通过校验并加入列表 |
| `zh:upload-reject` | 容器 | undefined | 文件没通过校验（类型、大小、数量） |
| `zh:upload-done` | 容器 | undefined | 上传成功；result 是服务器返回的文本或你的函数的返回值 |
| `zh:upload-error` | 容器 | undefined | 上传失败 |
| `zh:upload-remove` | 容器 | undefined | 从列表移除（含上传中取消） |

## 无障碍

- 拖拽区是 role=button，可聚焦，回车或空格打开文件选择；不会只支持拖拽
- 校验错误写在 role=alert 区域，会被读屏立即朗读
- 进度条是 role=progressbar，带文件名和 aria-valuenow
- 列表里的按钮都带文件名，如“移除 报告.pdf”
- 只靠前端校验不可靠，服务端必须再校验类型与大小

## 常见问题

### 文件上传是什么？

拖拽或点击选择文件，带文件列表、进度条、类型与大小校验、数量限制，失败可重试，上传中可取消。可以用 data-action 直接 POST 到接口，也可以用自己的函数上传；两者都不给时只收集文件，由你在提交表单时取。只有列表模式；头像上传和图片卡片墙没有做。

### 怎么使用文件上传？

先在页面里引入 zhaoui.css 和 zhaoui.js（放在 </body> 前，不加 defer），再照本页「用法」里的标记书写；需要用代码控制时，用「方法」里列出的接口。

### 文件上传有哪些事件？

zh:upload-add（文件通过校验并加入列表）；zh:upload-reject（文件没通过校验（类型、大小、数量））；zh:upload-done（上传成功；result 是服务器返回的文本或你的函数的返回值）；zh:upload-error（上传失败）；zh:upload-remove（从列表移除（含上传中取消））。

### 文件上传有哪些方法？

ZhaoUI.upload.files(el)：返回列表里的 File 数组，可自己放进 FormData 提交；ZhaoUI.upload.uploader(el, fn)：设置自己的上传函数 fn(file, { onProgress(百分比), onAbort(回调) })，返回 Promise；会替代 data-action；ZhaoUI.upload.add(el, files)：以代码方式加入文件（同样会校验）；ZhaoUI.upload.clear(el)：清空列表。

### 文件上传支持键盘和读屏吗？

拖拽区是 role=button，可聚焦，回车或空格打开文件选择；不会只支持拖拽；校验错误写在 role=alert 区域，会被读屏立即朗读；进度条是 role=progressbar，带文件名和 aria-valuenow。

### 文件上传需要依赖框架吗？

不需要。ZhaoUI 是原生 HTML、CSS 和 JavaScript，不依赖 jQuery、Vue 或 React。
