安安~我是ChiYu~
昨天,活動日期和預約時段終於各自歸位。接下來只剩把海報、參與名單和場地照片傳上去,我原本想得很簡單:畫一個框,讓管理員把檔案拖進去,收工。
幫我做一個漂亮的檔案上傳區,要能拖進去。
昨天結尾留下的,就是這個版本。

圖 1:和昨天結尾是同一個 AI 第一版。入口看起來完整,檔案進來後的驗證、逐筆狀態與復原操作全部缺席。
AI 很配合。虛線框、上傳圖示和「把檔案拖到這裡」全部到齊,第一眼確實很像那麼一回事。
接著我放進三份檔案:活動海報.zip 格式不合、佈置照片正在上傳、參與名單傳到一半斷線。畫面只告訴我「已選擇 3 個檔案」,完全沒交代哪一份能用、哪一份還在跑,以及失敗的那份要怎麼救。
如果上傳區只負責把檔案吃進去,它跟黑洞最大的差別,大概只剩外面多了一圈虛線。
在 Vibe Coding 裡只說「要能拖進去」,AI 自然會把力氣花在入口。可是上傳真正麻煩的地方都發生在檔案進來以後:驗證、等待、傳送、失敗、重試,以及送出前確認。
我先到網站的 檔案上傳流程完整比較 頁面,把三份檔案放回各自的流程階段。

圖 2:加入、驗證、排隊、傳送與送出前確認是不同階段。Dropzone 只負責檔案怎麼進來。
這張比較頁讓問題很明顯。Dropzone 做完了「怎麼進門」,後面卻還缺誰能進來、每一份走到哪裡,以及出錯後能不能單獨處理。
這篇原本的開發紀錄保留了 Prompt 和最終 Demo,偏偏少了第一版畫面。我不想拿完成版倒推一段「AI 當時肯定做得很糟」的劇情,所以在 2026 年 7 月 27 日,使用 Codex Desktop 與當日的 GPT-5 系列模型重新執行原始需求。
這次重跑沒有偷塞格式、進度或重試規格,仍然只有原本那一句:
幫我做一個漂亮的檔案上傳區,要能拖進去。
開頭圖 1 就是這句 Prompt 的輸出。我把它移到文章最前面,因為後面的上傳生命週期,都是在補這張第一版沒有地方回答的問題。
AI 沒有做錯,它確實完成了我寫下的需求。問題在我只交代「怎麼進門」,完全沒說進門後要去哪裡。
當三份檔案同時出現,第一版沒有地方回答:「哪一份不合格?哪一份正在傳?哪一份失敗?」拖放入口完成了,上傳流程還沒開始。
我第一個想到的補法,是替三份檔案各加一條進度條。再想一下就發現不對:進度條只能說明傳到哪裡,格式錯誤、等待、取消、重試與送出前確認還是沒人負責。
所以我沒有叫 AI 再畫三條 Upload Progress,而是先拆上傳生命週期。
我原本替上傳流程排過精確到秒的時間線,後來越看越不對。真實速度會跟檔案大小、網路與服務狀況一起變動,秒數寫得再漂亮也只是一本正經地亂猜。
真正需要留下的是狀態轉換:每一份檔案現在在哪裡,畫面要交代什麼,使用者此刻又能做什麼。
| 流程階段 | 三份檔案各自的狀態 | 使用者最想知道什麼 | 畫面需要的責任 |
|---|---|---|---|
| 加入前 | 尚未選取任何檔案 | 我能點選、拖放,還是兩者都可以? | File Input 是基本入口;Dropzone 讓拖曳使用者少一步。 |
| 驗證後 | 活動海報.zip 不符合格式;照片與名單可繼續 |
哪一份被拒絕,原因是什麼? | 格式、大小與數量驗證要直接點名檔案,不能只顯示一行「上傳失敗」。 |
| 傳送中 | 佈置照片有真實傳送進度;名單在等待或傳送 | 哪一份正在傳?可以取消嗎? | Upload Queue 保留每筆狀態;Upload Progress 交代單一檔案的傳送情況。 |
| 暫時失敗 | 參與名單遇到網路中斷;其他檔案狀態不變 | 這份要重試、移除,還是改檔? | 依失敗原因提供下一步,不能只亮紅色。 |
| 重試完成後 | 照片與名單完成;不合格的 ZIP 仍留著可移除 | 我現在要送出的到底是哪幾份? | File Preview 顯示檔名、類型與大小,讓人確認或移除誤選。 |
這張表也決定了元件分工。File Input 與 Dropzone 負責加入檔案;Upload Queue 保存多筆檔案各自的狀態;Upload Progress 交代正在傳送的那一筆;File Preview 則負責送出前的最後確認。
「已選擇 3 個檔案」只算出數量,沒有說出任何一份檔案的處境。接下來逐一看這五個元件如何把資訊補回來。
File Input|檔案選擇 是最樸素,也最不能少的入口。使用者點一下,從裝置挑檔案;欄位附近再說明可接受的格式、單檔大小與數量。

圖 3:File Input 提供可點擊的選檔入口,旁邊直接說明可接受格式、大小與數量。
不是每個人都習慣拖曳,手機上大概也沒有人能很帥氣地把桌面檔案甩進瀏覽器。即使畫面主角是 Dropzone,File Input 仍要留下來,讓點擊、鍵盤與觸控使用者能完成同一件事。
Dropzone|拖放上傳區 讓習慣拖曳的人少走一步,但它是快捷入口,不是唯一入口。MDN 的拖放範例仍保留可觸發選檔的 <input type="file">;W3C 對 Dragging Movements 的要求也指出,拖曳功能要有不依賴拖曳的操作方式。MDN:File drag and drop W3C:Understanding Dragging Movements
網站 Demo 明列可接受格式與單檔上限,也保留點擊和 Enter 選檔的路徑。我故意加入 ZIP 檔後,畫面沒有只亮紅框,而是直接指出檔名與不符合的規則。

圖 4:活動海報.zip 不符合格式時,錯誤訊息點名檔案並留下修正方向。
HTML 的 accept 屬性只會在挑選檔案時提供格式提示,不是安全驗證。檔案格式、大小、權限與內容仍要在伺服器端重新檢查。MDN:<input type="file">
Upload Queue|上傳佇列 適合同時處理多份附件。Queue 不是把檔名排成一列就交差,每一筆都要有自己的等待、上傳中、完成、失敗或取消狀態。
網站 Demo 同時放了一筆上傳中、一筆完成與一筆因網路中斷而失敗的檔案。三筆資料都留在畫面上,操作卻不相同。

圖 5:失敗列提供重試與移除,已完成及仍在上傳的檔案繼續保留原本狀態。
「取消」與「重試」也不能當成兩顆長得不一樣的裝飾按鈕。
取消用在正在傳送的檔案,按下後進度不能繼續增加。重試則適合網路中斷或暫時性伺服器錯誤。檔案超過 10 MB、格式不支援時,對同一份檔案按一百次重試也不會突然合格,這時應請使用者換檔再加入。
如果 AI 替每一列都塞一顆「重試」,卻沒分失敗原因,那不叫功能完整,只能算按鈕全勤。
Upload Progress|上傳進度 專心回答「這一份檔案傳到哪裡」。瀏覽器知道總大小與已傳量時,畫面可以顯示百分比和已傳大小;如果目前拿不到總量,就應改用不定進度,不能每秒自己加 7%,演得比真正的上傳還投入。

圖 6:這個 Demo 已知檔案總量,因此同步顯示百分比、已傳大小與取消控制,狀態不只靠動畫表示。
如果另一個流程允許上傳 100 MB 的活動開場影片,單一 Upload Progress 會比硬塞進多檔 Queue 更容易讀。這裡說的是另一種產品規則;LumenDesk 目前的活動素材仍限制單檔 10 MB。
File Preview|檔案預覽 負責最後確認。使用者要看得到檔名、類型、大小,必要時還要有縮圖或文件摘要,選錯也能在送出前移除。

圖 7:File Preview 顯示檔名、類型與移除操作,讓使用者在送出前確認內容。
「已選擇 3 個檔案」只證明數量對了,沒有證明內容正確。對海報、名單與場地照片這種差異很大的附件,File Preview 才能讓管理員在按下發布前發現自己抓錯檔。
前面的 ZIP 檔用來測格式驗證,它從一開始就不該進入上傳。接下來我換成第二組三份格式與大小都合格的檔案,專門測網路中斷:檔案本身沒有問題,使用者應該能重試,而且只重試失敗的那一筆。
我把第二版的壓力測試定成三個同時存在的狀態:活動海報停在 62%、參與名單已完成、活動佈置圖因網路問題失敗。
按下活動佈置圖那一列的「重試」時,我要求海報維持 62%、名單繼續保留完成狀態,只有佈置圖重新傳送。完成後摘要再從 1/3 改成 2/3。
這就是我對 Upload Queue 的選型判斷:狀態屬於每一個檔案,不是整個上傳框共用一個成功或失敗。壞掉一筆,另外兩筆不用陪它重來。
如果 Prompt 只寫「上傳失敗可以重試」,AI 仍可能把整批重新送一次。因此我把四個條件寫清楚:重試只作用在失敗列、其他列維持原狀、摘要重新計算,重試期間也要阻止同一列被連按。
網站上的五個元件頁都可以實際操作。你可以故意加入錯誤格式、取消傳送或重試失敗檔案,看看畫面是否把現在的狀態與下一步說清楚。
第一次只叫 AI 畫入口,第二次我把檔案限制、狀態轉換與局部復原一起寫進 Prompt:
為 LumenDesk 的「活動素材」建立檔案上傳狀態機,不要只做一個拖放區。
可接受 PDF、PNG、JPG、XLSX;單檔最大 10 MB,最多 3 個檔案。提供 File Input 的點擊選檔入口,也提供 Dropzone 讓人拖放;兩條路徑都要能加入同一份佇列。Dropzone 必須有鍵盤可達的選檔替代方式。
檔案加入時先驗證格式、大小與數量。若不符合,保留其他正確檔案,並清楚顯示檔名與原因。不要只寫「上傳失敗」。
多檔上傳使用 Upload Queue,每一列顯示檔名、類型、大小與自己的狀態:等待、上傳中、完成、失敗、取消。上傳中可取消,取消後進度不得再增加;網路中斷的失敗項目可重試或移除;格式或大小不符的項目不要提供無意義的重試。
單一大型檔案使用 Upload Progress。已知總量時顯示百分比與已傳大小;未知總量時使用不定進度。上傳中可取消;只有後端真的支援分段與續傳時,才提供暫停與繼續,不要先畫兩顆尚未接上服務的按鈕。提交前提供 File Preview,顯示檔名、類型與大小,讓使用者移除選錯的檔案。任一檔案失敗時,其他檔案必須保留自己的狀態與可用操作。
不要只用顏色表達成功、失敗或上傳中;每一種狀態都要有文字說明與下一步操作。
第二版不再只有一個虛線框。它多了逐筆狀態、局部操作與摘要,畫面確實比第一版複雜;上傳流程本來就有這些事情,只是第一版全部沒說。

圖 8:第二版把三份檔案拆成獨立狀態;重試活動佈置圖時,另外兩列維持原狀。
我讓「活動佈置圖.png」失敗,再只重試這一筆。實際結果如下:
| 檔案或摘要 | 重試前 | 重試後 |
|---|---|---|
| 活動海報 | 上傳到 62% | 維持 62%,沒有重設。 |
| 參與名單 | 已完成 | 仍是完成,沒有消失。 |
| 活動佈置圖 | 失敗 | 改成完成。 |
| 佇列摘要 | 1/3 已完成 | 2/3 已完成,1 筆待處理。 |
逐筆重試、保留其他檔案狀態,以及用文字區分等待、完成與失敗都通過。鍵盤替代路徑則通過目前的 File Input Demo。
管理員現在看得到哪一份有問題、失敗原因是什麼,也知道只修這一份不會害其他檔案重來。這才是我願意驗收的第二版。
目前能證明的是逐筆狀態、局部重試與鍵盤選檔入口。真實大型檔案、斷線續傳、後端掃毒與權限驗證都不在這個靜態 Demo 裡。
進度條跑到 100%,只能代表畫面完成了模擬流程,不能反推正式上傳服務也已經完成。這幾項仍要等真正串接 API、檔案儲存與安全檢查後再驗證。
在 UI 階段交付前,我仍會試五個動作:選 ZIP 檔、加入超過大小的檔案、讓其中一筆模擬網路中斷、取消另一筆正在上傳的檔案,再移除一筆已完成檔案。
只要其中一次讓我不知道「現在怎麼了、接下來能做什麼」,上傳流程就還沒完成。
現在三份檔案可以各自等待、上傳、失敗和重試。一筆出錯,其他兩筆不會被清空。
我把活動名稱留空、Email 填錯再按發布,畫面卻只亮起幾個紅框。上傳流程已經知道如何復原,整份表單還沒有修正路徑。
為了確認不是活動頁自己搞事,我又把前幾天完成的「新增成員」表單抓回來。姓名留空、Email 少了 @,登入帳號只填兩個字元,送出後得到同一種回覆:紅框,紅框,還是紅框。
我把這個「只剩紅框」的第一版問題還原到網站,先把明天的事故現場留下來。

圖 9:姓名留空、Email 格式錯誤、登入帳號太短;畫面只用紅框表示失敗,沒有欄位旁錯誤或驗證摘要。
明天,我會從這三個紅框繼續檢查。Label、欄位旁錯誤與頁首摘要要怎麼接力,才不會讓使用者在整份表單裡玩找碴?
<input type="file">
資料查閱:2026-08-07。