- 前端
- UI组件
- AI 应用
【免费下载链接】MateChat
前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com
McAttachment是 MateChat 中专注于文件上传的组件,它的典型应用场景是与McInput、McFileList组合,在对话输入框内构建"选择文件 → 校验 → 上传 → 展示文件列表 → 失败重试"的完整附件链路。读完本文,你将掌握upload-options上传配置、内置的类型/大小校验、拖拽上传(draggable、get-drop-container)以及before-upload自定义校验钩子的完整用法,并理解组件底层的校验与XMLHttpRequest上传实现。
组件定位与引入方式
McAttachment组件只做一件事:文件上传。它通过v-model维护一个FileItem[]文件列表,并与 McInput(提供输入框容器)和 McFileList(提供附件列表展示、移除、重试能力)配合使用,三者在对话输入框内各司其职:
McInput的head插槽放置McFileList,用于展示已选附件;McInput的extra插槽放置McAttachment,作为附件上传入口。
按需引入方式:
import { McAttachment } from '@matechat/core';从源码看,该组件通过 Attachment.vue 定义(组件名McAttachment),并在 index.ts 中挂载install方法支持app.component('McAttachment', ...)的注册方式,因此既可以按需单独引入,也可以作为库的一部分被全局注册。
基本用法:配置上传地址与内置校验
组件最基础的用法,是通过upload-options配置上传地址(uri),并通过accept与max-size两个属性启用内置的文件类型与大小校验。
<template> <McInput v-model="inputValue" placeholder="点击左侧图标上传附件..."> <!-- 1. 文件列表放置在 head 插槽 --> <template #head> <McFileList v-if="fileList.length > 0" :file-items="fileList" context="input" @remove="handleRemove" @retry-upload="handleRetryUpload" /> </template> <!-- 2. 附件上传器放置在 extra 插槽 --> <template #extra> <div class="input-foot-left"> <McAttachment v-model="fileList" :draggable="false" :upload-options="uploadOptions" accept="image/*" :max-size="0.5" @success="handleSuccess" @error="handleError" > <i class="icon-appendix"></i> </McAttachment> </div> </template> </McInput> </template> <script setup lang="ts"> import { ref } from 'vue'; import type { FileItem, UploadOptions } from '@matechat/core/Attachment'; const inputValue = ref(''); const fileList = ref<FileItem[]>([]); // 核心:配置上传参数 const uploadOptions = ref<UploadOptions>({ uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0', }); // 监听上传成功和失败事件 const handleSuccess = (file: File, response: any) => { console.log(`文件 ${file.name} 上传成功,响应:`, response); }; const handleError = (file: File, error: any) => { console.error(`文件 ${file.name} 上传失败,错误:`, error); }; // 处理文件列表的移除事件 const handleRemove = (file: FileItem) => { fileList.value = fileList.value.filter((item) => item.uid !== file.uid); }; // 处理文件列表的重试上传事件 const handleRetryUpload = (file: FileItem) => { const targetFile = fileList.value.find((item) => item.uid === file.uid); if (targetFile) { targetFile.status = 'uploading'; targetFile.percentage = 0; } }; </script>对这个示例逐点拆解:
v-model="fileList":文件列表由v-model双向绑定。组件的fileList通过 Vue 3 的defineModel定义(默认[]),选择文件后组件会把FileItem直接push进你传入的数组(见 use-upload.ts),因此在@remove中按uid过滤即可同步移除。accept="image/*":限定只允许图片类型。它同时作用于两处:原生<input type="file">的文件选择器过滤,以及组件自身的useUpload校验逻辑(见下文源码分析)。max-size="0.5":单个文件最大 0.5MB(注意单位是 MB)。超过会被拦截,并触发valid-change事件。@success/@error:上传成功/失败回调,回调参数分别为(file: File, response, fileList)与(file: File, error, fileList)。draggable="false":在只需要点击选择文件的场景关闭拖拽。- 默认插槽内容:
<i class="icon-appendix">即上传触发按钮的图标,你可以在默认插槽中任意自定义按钮样式。
底层的三段式校验
组件内置校验的具体逻辑位于 use-upload.ts 的uploadFiles方法中,它对每个待上传文件依次执行三类检查,任一环节不通过即终止该文件的上传:
- 数量校验:
fileList.value.length + files.length > props.maxCount时发出validChange(exceedCount)并直接返回; - 类型校验:将
accept按逗号拆分后逐个匹配,支持image/*通配、.pdf扩展名、精确 MIME 三种写法; - 大小校验:
file.size / 1024 / 1024 > props.maxSize,以 MB 为单位比较。
类型校验的匹配规则(对应 use-upload.ts):
- 以
*/结尾(如image/*):判断file.type是否以对应前缀开头; - 以
.开头(如.pdf):判断文件名是否以该扩展名结尾; - 否则按完整 MIME 类型精确匹配。
拖拽上传:整页拖放与局部放置区域
draggable属性默认为true,启用后组件会渲染一个透明的拖拽放置层。下面的示例在基础用法的位置上配置了multiple多选、drop-placeholder放置提示,并通过get-drop-container把可释放文件的区域限定到输入框容器。
<template> <McInput v-model="inputValue" ref="inputEl" placeholder="拖拽文件到页面,或点击左侧图标上传附件..."> <!-- 1. 文件列表放置在 head 插槽 --> <template #head> <McFileList v-if="dragFileList.length > 0" :file-items="dragFileList" context="input" @remove="(file) => (dragFileList = dragFileList.filter((f) => f.uid !== file.uid))" /> </template> <!-- 2. 附件上传器放置在 extra 插槽 --> <template #extra> <div class="input-foot-left"> <McAttachment v-model="dragFileList" :upload-options="uploadOptions" accept="image/*" :max-size="0.5" multiple drop-placeholder="请放置到此处" :get-drop-container="dropContainer" > <i class="icon-appendix"></i> </McAttachment> </div> </template> </McInput> </template> <script setup lang="ts"> import { ref } from 'vue'; import type { FileItem, UploadOptions, McInput } from '@matechat/core/Attachment'; const inputValue = ref(''); const inputEl = ref(); const dragFileList = ref<FileItem[]>([]); const uploadOptions = ref<UploadOptions>({ uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0', }); const dropContainer = () => inputEl.value.$el; </script>拖拽机制的源码实现位于 drop-area.vue:
- 放置层通过
<Teleport to="body">挂载到body上,与组件本身脱离,从而能在整个页面范围内响应拖拽; - 使用
dragenter/dragleave计数器(dragCounter)区分"真正进入"与"在子元素间穿梭",避免进入子元素导致放置层状态抖动; - 若提供了
get-drop-container,会通过getBoundingClientRect()计算容器位置与尺寸,把放置层精确对齐到容器(如示例中的输入框inputEl.$el);若未提供,则铺满整个body(inset: 0),实现整页拖拽; drop时从dataTransfer.files取文件,依次触发drop事件并调用uploadFiles进入上传流程,同时会先判断isDisabled状态。
drop-placeholder对应放置层的提示文案(默认插槽dropPlaceholder可以自定义),当未传该属性时,组件会回退到国际化文案Attachment.dragToUpload(见 Attachment.vue 中useMcI18n的使用),说明该组件已接入 MateChat 的 Locale 国际化体系。
自定义上传前校验:before-upload 钩子
当内置的类型与大小校验不够用时,可以通过before-upload钩子注入自定义校验。它接收File,返回boolean或Promise<boolean>;返回false(或返回一个被 reject 的 Promise)即终止该文件上传。下面的示例同时使用max-count限制附件总数,并监听valid-change获取所有校验结果。
<template> <McInput v-model="inputValue" placeholder="点击左侧图标上传附件..."> <!-- 1. 文件列表放置在 head 插槽 --> <template #head> <McFileList v-if="validatedList.length > 0" :file-items="validatedList" context="input" @remove="(file) => (validatedList = validatedList.filter((f) => f.uid !== file.uid))" /> </template> <!-- 2. 附件上传器放置在 extra 插槽 --> <template #extra> <div class="input-foot-left"> <McAttachment v-model="validatedList" :draggable="false" :upload-options="uploadOptions" :before-upload="handleBeforeUpload" accept="image/*" :max-size="0.5" :max-count="3" multiple @valid-change="onValidChange" > <i class="icon-appendix"></i> </McAttachment> </div> </template> </McInput> </template> <script setup lang="ts"> import { ref } from 'vue'; import type { FileItem, UploadOptions, McInput } from '@matechat/core/Attachment'; const inputValue = ref(''); const validatedList = ref<FileItem[]>([]); const uploadOptions = ref<UploadOptions>({ uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0', }); const handleBeforeUpload = (file: File) => { // 除了组件内置的 accept 和 size 校验,你还可以添加自定义的校验逻辑 if (file.name.includes('test')) { return false; } return true; }; const onValidChange = (e) => { console.log('valid result', e); }; </script>结合源码,校验的完整优先级是:数量限制 → 类型校验(accept)→ 大小校验(max-size)→ before-upload 自定义钩子。其中before-upload支持异步:源码使用Promise.resolve(props.beforeUpload(file))等待钩子完成,若结果为false或 Promise 被 reject,都会以beforeUploadRejected记录校验结果(见 use-upload.ts)。
所有未通过校验的文件会汇总为IValidResult[],通过valid-change事件统一抛出。校验结果类型(ValidResultType)共有四种(见 docs/components/attachment/api.md):
| 校验结果类型 | 含义 |
|---|---|
exceedCount | 超出文件数量限制 |
unsupportedFileType | 不支持的文件类型 |
exceedSizeLimit | 超出文件大小限制 |
beforeUploadRejected | before-upload钩子校验失败 |
只有通过全部校验的文件才会生成FileItem加入fileList,随后触发change事件并真正发起上传。
参数、事件与插槽速查
以下为McAttachment的完整参数、事件与插槽定义(来源:api.md,默认值可在 attachment-types.ts 中验证)。
参数
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| v-model | FileItem[] | - | 双向绑定的文件列表 |
| upload-options | UploadOptions | {} | 核心上传配置,包含上传地址uri、请求方法、请求头等 |
| disabled | boolean | false | 是否禁用上传功能 |
| accept | string | '' | 接受上传的文件类型,与原生input的accept相同,如'image/*, .pdf' |
| max-count | number | Infinity | 允许上传的最大文件数量 |
| max-size | number | Infinity | 允许上传的单个文件最大尺寸,单位MB |
| multiple | boolean | true | 是否支持多选文件 |
| draggable | boolean | true | 是否支持拖拽上传 |
| before-upload | (file: File) => boolean \| Promise<boolean> | null | 上传前钩子,返回false或 reject 的 Promise 则停止上传 |
| drop-placeholder | string | -- | 拖拽上传时,放置位置的展示信息 |
| get-drop-container | () => HTMLElement | -- | 拖拽上传时,可以释放文件的区域 |
事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| change | 文件状态改变时触发,主要在文件被选择后 | (file: File, fileList: FileItem[]) => void |
| success | 文件上传成功时触发 | (file: File, response: any, fileList: FileItem[]) => void |
| error | 文件上传失败时触发 | (file: File, error: any, fileList: FileItem[]) => void |
| progress | 文件上传时触发 | (file: File, fileList: FileItem[]) => void |
| drop | 文件在可放置区域释放时触发 | (files: File[]) => void |
| valid-change | 有校验结论时触发 | (e: IValidResult[]) => void |
事件声明在 attachment-types.ts 中做了运行时校验(如success要求file instanceof File && Array.isArray(fileList)),不满足条件的触发会在开发期被警告,这也是组件对回调参数类型严谨性的体现。
插槽
| 插槽名 | 说明 |
|---|---|
| default | 自定义上传附件按钮样式 |
| dropPlaceholder | 拖拽上传时,自定义放置位置的展示信息 |
深入:UploadOptions 与底层上传实现
upload-options是整个组件的"引擎配置",它接收UploadOptions对象,驱动组件内置的XMLHttpRequest上传逻辑。类型定义(api.md):
export interface UploadOptions { uri: string | URL; method?: 'POST' | 'PUT' | 'PATCH'; headers?: { [key: string]: string }; authToken?: string; authTokenHeader?: string; additionalParameter?: { [key: string]: string | Blob }; fileFieldName?: string; withCredentials?: boolean; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; }| 属性 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
uri | string \| URL | 是 | 上传接口的服务器地址 |
method | 'POST' \| 'PUT' \| 'PATCH' | 否 | HTTP 请求方法,默认为'POST' |
headers | object | 否 | 自定义 HTTP 请求头 |
authToken | string | 否 | 用于身份验证的 Token |
authTokenHeader | string | 否 | 承载authToken的请求头字段名,默认为'Authorization' |
additionalParameter | object | 否 | 随文件一同上传的额外参数,会被加入FormData |
fileFieldName | string | 否 | 上传文件在FormData中的字段名,默认为'file' |
withCredentials | boolean | 否 | 是否在跨域请求中发送凭证(如 Cookies),默认为false |
responseType | 'arraybuffer' \| 'blob' \| 'json' \| 'text' | 否 | 手动设置期望的服务器响应类型 |
仓库中公共上传器的实现(uploader.ts,Vue 侧经@matechat/common/Attachment/common/uploader.ts引用)完整呈现了这些配置的落地方式:
- 请求体:构造
FormData,用fileFieldName || 'file'作为文件字段名formData.append(fileFieldName, file, file.name),随后遍历additionalParameter追加额外参数; - 请求头:遍历
headers逐项setRequestHeader;若配置了authToken,则以authTokenHeader || 'Authorization'作为请求头字段承载 Token; - 进度:通过
xhr.upload.onprogress计算Math.round((e.loaded * 100) / e.total)得到百分比; - 成功判定:
xhr.status落在[200, 300)区间视为成功,响应值依据responseType(text或缺省时取responseText)返回;否则与网络错误一并转入onError,错误对象携带status、statusText与响应内容; - 凭证与响应:
withCredentials与responseType均直接透传给xhr。
文件列表状态流转与本地预览
v-model中的每个元素遵循FileItem接口(api.md):
export interface FileItem<T = unknown, E = unknown> { uid: number; name: string; size: number; type?: string; status?: FileStatus; percentage?: number; id?: string | number; response?: T; error?: E; thumbUrl?: string; url?: string; }从 use-upload.ts 的getFileItem可以看到组件生成FileItem时的几个关键细节:
uid由Date.now()起始的自增计数分配,因此示例中handleRemove可以依赖uid精确定位文件;- 组件会调用
URL.createObjectURL(file)生成本地预览地址,同时赋给url与thumbUrl——这意味着文件在未上传或上传过程中即可在McFileList中渲染缩略图,提升交互反馈; - 状态机沿
uploading → success / uploadError流转(FileStatus还包含downloading / downloadError,供下载场景使用),上传过程中percentage会随progress事件持续更新。
因此,配合McFileList的retry-upload事件,你可以在重试逻辑中把对应FileItem的状态重置回uploading、percentage置 0(如基础示例中的handleRetryUpload),即可复用组件对文件列表的驱动模型,实现失败重传。
小结:一条完整的对话附件链路
将本文的三类用法归纳为一条可落地的实践链路:
- 在
McInput的head插槽放McFileList(context="input")展示附件; - 在
extra插槽放McAttachment,配置upload-options.uri指向真实上传接口; - 根据需求组合内置校验(
accept/max-size/max-count)与before-upload自定义校验,通过valid-change统一处理校验结果; - 需要拖拽体验时开启
draggable,并用get-drop-container把放置区限定到输入框或指定容器; - 用
success/error/progress事件感知上传结果,用McFileList的remove/retry-upload完成删除与失败重传。
组件的校验、列表生成、进度上报与状态流转均由 Attachment.vue + use-upload.ts 封装,你只需要关注业务层的事件处理与 UI 组织,即可快速为对话界面接入可靠的附件上传能力。
- 前端
- UI组件
- AI 应用
【免费下载链接】MateChat
前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com
相关推荐
MateChat Angular 版 McAttachment 附件上传组件实战:从拖拽上传、内置校验到 XMLHttpRequest 底层实现
MateChat Angular 版 McAttachment 附件上传组件实战:从拖拽上传、内置校验到 XMLHttpRequest 底层实现 本文基于 Ma
前端UI组件AI 应用element-react Upload 上传组件完全指南:从点击上传、拖拽上传到自定义上传请求
element react Upload 上传组件完全指南:从点击上传、拖拽上传到自定义上传请求 本文以 element react(Element UI 的
前端UI组件设计系统ng-zorro-antd Upload 组件实战指南:文件选择、拖拽上传与自定义上传实现
ng zorro antd Upload 组件实战指南:文件选择、拖拽上传与自定义上传实现 本指南以 ng zorro antd 的 Upload(上传)组件为
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考