## 46 · 上传 Upload ### 46.1 用途 把手机里的文件交给服务端(实名认证的身份证照、报销的发票、工单的附件)。移动端与桌面端的关键差别是**没有拖拽**:桌面上可以「把文件拖进虚线框」,手机上既没有 hover 也没有拖放,所以**触发按钮是唯一入口**,虚线框只能表达「这里可以放东西」而不能作为交互方式;另一个差别是**相机**——很多上传场景其实期望的是「拍一张」,宿主可以用同一个触发器同时给出「拍照」与「从相册选择」,但那是宿主的编排,不是本组件的职责。 ### 46.2 结构(anatomy) - `upload`:根元素,纵向排列「标题行 + 触发器 + 文件列表」 - `upload__header`:标题与计数(`已选 N / 上限 M`) - `upload__trigger`:选择触发器,点击调起系统文件选择器;虚线框是 `variant=dashed`,实心按钮是 `variant=button` - `upload__list` / `upload__item`:文件列表与单行,行高不小于 44px - `upload__thumb`:缩略图位(图片用背景图,其他类型放扩展名文字块) - `upload__body` / `upload__name` / `upload__meta`:文件名与「进度条 + 状态文案」 - `upload__track` / `upload__bar`:进度条轨道与进度段,宽度取 `--kole-m-upload-percent` - `upload__actions`:行内动作(重试 / 删除),各自 44px 热区 ### 46.3 变体维度 - `variant`:`dashed`(虚线框,页面级上传区)/ `button`(实心按钮,列表内嵌的「+ 添加」) - `maxCount`:文件数上限(到达上限后触发器变成提示行,不再可点) ### 46.4 状态 - pending:待上传(进度条不占宽,文案「待上传」) - uploading:上传中(品牌色进度条,文案给百分比;`role="progressbar"` + `aria-valuenow`) - success:成功(满格 + 成功色 +「已上传」) - error:失败(满格 + 错误色 + 失败原因,行内出现「重试」) - empty:还没有任何文件时的说明文案 - disabled:整块置灰(状态类 `is-disabled`,触发器与删除按钮都不可用);到 `maxCount` 后触发器变提示行 ### 46.5 交互与触控 - 点触发器调起系统文件选择器(本组件不发请求,选完由宿主拿文件并自行上传) - 删除按钮自己 44px 热区,点一次移除该项;删除进行中(`uploading`)时文案是「取消」 - 失败行提供「重试」,重试由宿主重新发起,组件只回传下标 - 进度不自己走:**没有内置定时器或假进度**,百分比全部由宿主回传,避免出现「看起来在上传其实没动」 - 列表为空时显示空态文案;到 `maxCount` 后触发器改成不可点的提示行 ### 46.6 无障碍 - 触发器是原生 `button`,带 `aria-disabled`;到上限时用 `disabled` 让键盘跳过 - 进度条写 `role="progressbar"` + `aria-valuemin` / `aria-valuemax` / `aria-valuenow`,`aria-label` 说明是哪个文件的进度 - 删除 / 重试按钮各自带 `aria-label`(含文件名),读屏播报「删除 合同扫描件.pdf」而不是孤零零的「删除」 - 状态文案是文字而不只是颜色(「已上传」「上传失败」),颜色不作为唯一信息通道 ### 46.7 doNotInvent - 真实的传输:请求、分片、断点续传、并发数(本组件只回传事件,不发任何请求) - 图片压缩、裁剪、水印与方向纠正 - 服务端的校验规则(大小上限、类型白名单以文案与 `accept` 表达,不代为判断) - 拍照与相册的原生调起(由宿主在 `select` 事件里自行调用平台 API) ### 46.8 unknowns - 上传中能否同时继续添加文件(当前允许,列表各自独立) - 失败自动重试的次数与退避策略 - 是否需要在成功行上展示服务端返回的文件 id / URL ---