文件上传 · upload

上传是「导入 CSV / 附一份财报 / 传凭证」的必经入口。这个组件的核心不在拖放区好看, 而在于:JS 没加载时它仍然是能用的,以及文件有没有真的进到表单里。

1 · 基础形态:零 JS 也能用

支持 CSV / XLSX,单个不超过 20 MB

这一块没有任何 JS:浏览器原生的"选择文件"按钮经 ::file-selector-button 美化,选完立刻在按钮右侧回显文件名 —— 不需要脚本读 files 再写进 DOM。表单提交、FormData、 accept 过滤全走原生路径。

2 · 增强形态:拖放区 + 文件列表

    可以一次选多个;再次拖入会替换整个列表

    拖放区是一个 <label for>:点它 = 原生打开选择器, 这一步不依赖 JS。脚本只负责三件事 —— 把拖入的 File 塞进 input.files、重画列表、派发 fl-upload。 所以 <form> 拿到的永远是真实文件,不是"组件自己记的一份清单"。

    3 · 上传中:进度既是图形也是文字

    三个状态的差别不只是颜色:左侧图形(空心圆 / 勾 / 叹号)、 中间文件名的灰度、以及右侧那行文字("上传中 42%" / "2.4 MB" / "超过 20 MB")。 把这一块截图转成灰度打印,信息一条都不丢。

    4 · 校验拒绝:一个都没收进来

      下面这一组是脚本真实拖入 3 个不合规文件的结果

      被拒的文件不会进入 input.files —— 它们根本没被接收,所以提交时不会悄悄带上一个 30 MB 的扫描件。 拒绝原因写在文件名的位置,不是只把边框变红。

      5 · 紧凑档(侧栏 / 抽屉)

        6 · 槽宽适配(看槽宽,不看视口)

          左侧这块是 240px 宽的槽(外层加了 data-cq)。 它比右边窄,但没有"响应式跳跃":内边距与图标各降一档,限制说明没有被藏起来 —— 藏掉它,用户就不知道该选什么文件了。

          7 · 禁用

            先在"基础信息"里选好主体类型,这里才能上传

            禁用不用 opacity:半透明会让文字对比度随底色漂移, 暗色下直接不达标。这里换的是明确色值,边框同时从虚线变实线 —— 不用颜色也能看出"这一档不一样"。

            对比:两种"拖放区"

            ❌ 常见写法:拖放区是唯一入口

            把原生 <input> 设成 display:none 图省事 —— 两栏看起来一模一样,差别在键盘: 后果是键盘 Tab 永远聚焦不到它(不可聚焦元素进不了 Tab 序列), 读屏也拿不到可访问名字 —— 鼠标用户能用,键盘用户完全无法上传。

            ✅ 本库:隐藏的是像素,不是控件

            同样看不见原生控件,但它只是 1px + opacity:0: 仍在 Tab 序列里、仍在表单里、仍能被 label 触发。 聚焦时焦点环画在拖放区上,用户看到的是"这块被选中了"。