news 2026/10/14 3:15:34

MateChat 的 McAttachment 附件上传组件实战指南:从基础上传到拖拽与自定义校验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MateChat 的 McAttachment 附件上传组件实战指南:从基础上传到拖拽与自定义校验
  • 前端
  • UI组件
  • AI 应用

【免费下载链接】MateChat

前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com

项目地址:https://gitcode.com/DevCloudFE/MateChat
点击查看免费下载

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方法中,它对每个待上传文件依次执行三类检查,任一环节不通过即终止该文件的上传:

  1. 数量校验:fileList.value.length + files.length > props.maxCount时发出validChange(exceedCount)并直接返回;
  2. 类型校验:将accept按逗号拆分后逐个匹配,支持image/*通配、.pdf扩展名、精确 MIME 三种写法;
  3. 大小校验: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超出文件大小限制
beforeUploadRejectedbefore-upload钩子校验失败

只有通过全部校验的文件才会生成FileItem加入fileList,随后触发change事件并真正发起上传。

参数、事件与插槽速查

以下为McAttachment的完整参数、事件与插槽定义(来源:api.md,默认值可在 attachment-types.ts 中验证)。

参数

参数名类型默认值说明
v-modelFileItem[]-双向绑定的文件列表
upload-optionsUploadOptions{}核心上传配置,包含上传地址uri、请求方法、请求头等
disabledbooleanfalse是否禁用上传功能
acceptstring''接受上传的文件类型,与原生input的accept相同,如'image/*, .pdf'
max-countnumberInfinity允许上传的最大文件数量
max-sizenumberInfinity允许上传的单个文件最大尺寸,单位MB
multiplebooleantrue是否支持多选文件
draggablebooleantrue是否支持拖拽上传
before-upload(file: File) => boolean \| Promise<boolean>null上传前钩子,返回false或 reject 的 Promise 则停止上传
drop-placeholderstring--拖拽上传时,放置位置的展示信息
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'; }
属性类型是否必需说明
uristring \| URL是上传接口的服务器地址
method'POST' \| 'PUT' \| 'PATCH'否HTTP 请求方法,默认为'POST'
headersobject否自定义 HTTP 请求头
authTokenstring否用于身份验证的 Token
authTokenHeaderstring否承载authToken的请求头字段名,默认为'Authorization'
additionalParameterobject否随文件一同上传的额外参数,会被加入FormData
fileFieldNamestring否上传文件在FormData中的字段名,默认为'file'
withCredentialsboolean否是否在跨域请求中发送凭证(如 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),即可复用组件对文件列表的驱动模型,实现失败重传。

小结:一条完整的对话附件链路

将本文的三类用法归纳为一条可落地的实践链路:

  1. 在McInput的head插槽放McFileList(context="input")展示附件;
  2. 在extra插槽放McAttachment,配置upload-options.uri指向真实上传接口;
  3. 根据需求组合内置校验(accept/max-size/max-count)与before-upload自定义校验,通过valid-change统一处理校验结果;
  4. 需要拖拽体验时开启draggable,并用get-drop-container把放置区限定到输入框或指定容器;
  5. 用success/error/progress事件感知上传结果,用McFileList的remove/retry-upload完成删除与失败重传。

组件的校验、列表生成、进度上报与状态流转均由 Attachment.vue + use-upload.ts 封装,你只需要关注业务层的事件处理与 UI 组织,即可快速为对话界面接入可靠的附件上传能力。

  • 前端
  • UI组件
  • AI 应用

【免费下载链接】MateChat

前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com

项目地址:https://gitcode.com/DevCloudFE/MateChat
点击查看免费下载

相关推荐

上一篇:Pelican 草稿(Draft)文章机制详解:用 :status: draft 元数据管理未发布内容
下一篇:Skill Seekers 深度评分实战:以 HTTPX Skill 的 A 级质量评审为例

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 3:15:09

思考: C++ 万能引用与完美转发

万能引用、引用折叠与完美转发 总览&#xff1a;一条主线串起所有点 目标&#xff1a;让"传进来的原始参数"尽可能少经过临时对象&#xff0c;直接就地构造到目标里。所有规则都是为这件事服务的。一、两块基石 1. 类型 ≠ 值类别 一个表达式同时有两个属性&#xff…

作者头像 李华
网站建设 2026/10/14 3:14:46

Linux 系统调试学习2

1、ps、top、htop 分别干什么&#xff1f;先记这个区别&#xff1a;ps看“当前这一刻”的进程快照top实时刷新系统进程状态htop更友好的交互式 top所以&#xff1a;想快速查一个进程有没有启动 → ps想持续观察 CPU / 内存 → top想更方便地看线程、排序、搜索 → htopps aux 常…

作者头像 李华
网站建设 2026/10/14 3:14:45

AscendNPU-IR 入门指南:基于 MLIR 的昇腾亲和算子编译中间表示

编译器人工智能AscendCANN 【免费下载链接】AscendNPU-IR AscendNPU-IR是基于MLIR&#xff08;Multi-Level Intermediate Representation&#xff09;构建的&#xff0c;面向昇腾亲和算子编译时使用的中间表示&#xff0c;提供昇腾完备表达能力&#xff0c;通过编译优化提升昇腾…

作者头像 李华
网站建设 2026/10/14 3:14:24

Unity Windows原生窗口控制:无边框与状态栏适配实战

1. 项目概述&#xff1a;为什么在Unity里“动”Windows原生窗口是个刚需做Unity桌面应用的开发者&#xff0c;尤其是开发工具类、工业控制面板、数字孪生看板或者需要深度集成Windows系统行为的项目时&#xff0c;迟早会撞上这个墙&#xff1a;Unity默认的窗口管理太“温柔”了…

作者头像 李华