iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
Vibe Coding

別再只叫 AI 做漂亮一點:30 天 Vibe Coding UI 元件驗收實驗系列 第 10

Day 10|檔案不是選完就好:上傳流程的完整狀態

  • 分享至 

  • xImage
  •  

Day 10|檔案不是選完就好:上傳流程的完整狀態

安安~我是ChiYu~

昨天,活動日期和預約時段終於各自歸位。接下來只剩把海報、參與名單和場地照片傳上去,我原本想得很簡單:畫一個框,讓管理員把檔案拖進去,收工。

幫我做一個漂亮的檔案上傳區,要能拖進去。

昨天結尾留下的,就是這個版本。

模糊 Prompt 產生的第一版檔案上傳區

圖 1:和昨天結尾是同一個 AI 第一版。入口看起來完整,檔案進來後的驗證、逐筆狀態與復原操作全部缺席。

AI 很配合。虛線框、上傳圖示和「把檔案拖到這裡」全部到齊,第一眼確實很像那麼一回事。

接著我放進三份檔案:活動海報.zip 格式不合、佈置照片正在上傳、參與名單傳到一半斷線。畫面只告訴我「已選擇 3 個檔案」,完全沒交代哪一份能用、哪一份還在跑,以及失敗的那份要怎麼救。

如果上傳區只負責把檔案吃進去,它跟黑洞最大的差別,大概只剩外面多了一圈虛線。

在 Vibe Coding 裡只說「要能拖進去」,AI 自然會把力氣花在入口。可是上傳真正麻煩的地方都發生在檔案進來以後:驗證、等待、傳送、失敗、重試,以及送出前確認。

我先到網站的 檔案上傳流程完整比較 頁面,把三份檔案放回各自的流程階段。

檔案上傳比較頁:先找到使用者位於流程的哪一步

圖 2:加入、驗證、排隊、傳送與送出前確認是不同階段。Dropzone 只負責檔案怎麼進來。

這張比較頁讓問題很明顯。Dropzone 做完了「怎麼進門」,後面卻還缺誰能進來、每一份走到哪裡,以及出錯後能不能單獨處理。

原始 Prompt 重跑: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:點一下就能選檔,這個基本入口不能消失

File Input|檔案選擇 是最樸素,也最不能少的入口。使用者點一下,從裝置挑檔案;欄位附近再說明可接受的格式、單檔大小與數量。

Vibe UI Atlas 的 File Input 實際 Demo:以可點擊的檔案選擇欄提供上傳入口

圖 3:File Input 提供可點擊的選檔入口,旁邊直接說明可接受格式、大小與數量。

不是每個人都習慣拖曳,手機上大概也沒有人能很帥氣地把桌面檔案甩進瀏覽器。即使畫面主角是 Dropzone,File Input 仍要留下來,讓點擊、鍵盤與觸控使用者能完成同一件事。

Dropzone:拖放可以省一步,不能變成唯一入口

Dropzone|拖放上傳區 讓習慣拖曳的人少走一步,但它是快捷入口,不是唯一入口。MDN 的拖放範例仍保留可觸發選檔的 <input type="file">;W3C 對 Dragging Movements 的要求也指出,拖曳功能要有不依賴拖曳的操作方式。MDN:File drag and drop W3C:Understanding Dragging Movements

網站 Demo 明列可接受格式與單檔上限,也保留點擊和 Enter 選檔的路徑。我故意加入 ZIP 檔後,畫面沒有只亮紅框,而是直接指出檔名與不符合的規則。

UI 元件百科的 Dropzone 驗證失敗畫面

圖 4:活動海報.zip 不符合格式時,錯誤訊息點名檔案並留下修正方向。

HTML 的 accept 屬性只會在挑選檔案時提供格式提示,不是安全驗證。檔案格式、大小、權限與內容仍要在伺服器端重新檢查。MDN:<input type="file">

Upload Queue:三份檔案要保留三組獨立狀態

Upload Queue|上傳佇列 適合同時處理多份附件。Queue 不是把檔名排成一列就交差,每一筆都要有自己的等待、上傳中、完成、失敗或取消狀態。

網站 Demo 同時放了一筆上傳中、一筆完成與一筆因網路中斷而失敗的檔案。三筆資料都留在畫面上,操作卻不相同。

UI 元件百科的 Upload Queue 實際操作畫面

圖 5:失敗列提供重試與移除,已完成及仍在上傳的檔案繼續保留原本狀態。

「取消」與「重試」也不能當成兩顆長得不一樣的裝飾按鈕。

取消用在正在傳送的檔案,按下後進度不能繼續增加。重試則適合網路中斷或暫時性伺服器錯誤。檔案超過 10 MB、格式不支援時,對同一份檔案按一百次重試也不會突然合格,這時應請使用者換檔再加入。

如果 AI 替每一列都塞一顆「重試」,卻沒分失敗原因,那不叫功能完整,只能算按鈕全勤。

Upload Progress:知道總量才顯示百分比,否則別假裝精確

Upload Progress|上傳進度 專心回答「這一份檔案傳到哪裡」。瀏覽器知道總大小與已傳量時,畫面可以顯示百分比和已傳大小;如果目前拿不到總量,就應改用不定進度,不能每秒自己加 7%,演得比真正的上傳還投入。

Vibe UI Atlas 的 Upload Progress 實際 Demo:單一檔案的進度、已傳大小與取消控制

圖 6:這個 Demo 已知檔案總量,因此同步顯示百分比、已傳大小與取消控制,狀態不只靠動畫表示。

如果另一個流程允許上傳 100 MB 的活動開場影片,單一 Upload Progress 會比硬塞進多檔 Queue 更容易讀。這裡說的是另一種產品規則;LumenDesk 目前的活動素材仍限制單檔 10 MB。

File Preview:送出前看一眼,確認選到的就是這份

File Preview|檔案預覽 負責最後確認。使用者要看得到檔名、類型、大小,必要時還要有縮圖或文件摘要,選錯也能在送出前移除。

Vibe UI Atlas 的 File Preview 實際 Demo:確認檔名、類型與移除誤選檔案

圖 7:File Preview 顯示檔名、類型與移除操作,讓使用者在送出前確認內容。

「已選擇 3 個檔案」只證明數量對了,沒有證明內容正確。對海報、名單與場地照片這種差異很大的附件,File Preview 才能讓管理員在按下發布前發現自己抓錯檔。

作者判斷:重試只處理失敗檔案,另外兩筆必須留在原位

前面的 ZIP 檔用來測格式驗證,它從一開始就不該進入上傳。接下來我換成第二組三份格式與大小都合格的檔案,專門測網路中斷:檔案本身沒有問題,使用者應該能重試,而且只重試失敗的那一筆。

我把第二版的壓力測試定成三個同時存在的狀態:活動海報停在 62%、參與名單已完成、活動佈置圖因網路問題失敗。

按下活動佈置圖那一列的「重試」時,我要求海報維持 62%、名單繼續保留完成狀態,只有佈置圖重新傳送。完成後摘要再從 1/3 改成 2/3。

這就是我對 Upload Queue 的選型判斷:狀態屬於每一個檔案,不是整個上傳框共用一個成功或失敗。壞掉一筆,另外兩筆不用陪它重來。

如果 Prompt 只寫「上傳失敗可以重試」,AI 仍可能把整批重新送一次。因此我把四個條件寫清楚:重試只作用在失敗列、其他列維持原狀、摘要重新計算,重試期間也要阻止同一列被連按。

網站上的五個元件頁都可以實際操作。你可以故意加入錯誤格式、取消傳送或重試失敗檔案,看看畫面是否把現在的狀態與下一步說清楚。

改寫 Prompt:把上傳規則寫成逐筆狀態

第一次只叫 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 不等於正式上傳服務

目前能證明的是逐筆狀態、局部重試與鍵盤選檔入口。真實大型檔案、斷線續傳、後端掃毒與權限驗證都不在這個靜態 Demo 裡。

進度條跑到 100%,只能代表畫面完成了模擬流程,不能反推正式上傳服務也已經完成。這幾項仍要等真正串接 API、檔案儲存與安全檢查後再驗證。

在 UI 階段交付前,我仍會試五個動作:選 ZIP 檔、加入超過大小的檔案、讓其中一筆模擬網路中斷、取消另一筆正在上傳的檔案,再移除一筆已完成檔案。

只要其中一次讓我不知道「現在怎麼了、接下來能做什麼」,上傳流程就還沒完成。

三份附件有復原路徑,整份表單還只會亮紅框

現在三份檔案可以各自等待、上傳、失敗和重試。一筆出錯,其他兩筆不會被清空。

我把活動名稱留空、Email 填錯再按發布,畫面卻只亮起幾個紅框。上傳流程已經知道如何復原,整份表單還沒有修正路徑。

為了確認不是活動頁自己搞事,我又把前幾天完成的「新增成員」表單抓回來。姓名留空、Email 少了 @,登入帳號只填兩個字元,送出後得到同一種回覆:紅框,紅框,還是紅框。

我把這個「只剩紅框」的第一版問題還原到網站,先把明天的事故現場留下來。

第一版新增成員表單只有三個紅框,沒有錯誤原因與修正路徑

圖 9:姓名留空、Email 格式錯誤、登入帳號太短;畫面只用紅框表示失敗,沒有欄位旁錯誤或驗證摘要。

明天,我會從這三個紅框繼續檢查。Label、欄位旁錯誤與頁首摘要要怎麼接力,才不會讓使用者在整份表單裡玩找碴?

延伸閱讀

資料查閱:2026-08-07。


上一篇
Day 9|輸入日期、選時間還是看整月?五種日期時間元件怎麼選
系列文
別再只叫 AI 做漂亮一點:30 天 Vibe Coding UI 元件驗收實驗10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言