Vue3 + TypeScript 實現文件拖拽上傳

應用效果:

實例代碼:CommonApplyBasicInfoForm.vue

<script setup lang="ts" name="CommonApplyBasicInfoForm">
......
// 選擇文件列表
const selectedFiles = ref<FileList | null>(null);
// 通過 FormData 對象實現文件上傳
let formData = new FormData();
// 受理文件列表
const acceptFiles = ref<ApplyBasicFileVO[]>([]);// 拖放事件處理函數,拖放文件,輕開鼠標左鍵觸發
const onDrop = async (e: DragEvent) => {// 獲取文件對象列表(拖放的文件列表)if (e.dataTransfer) selectedFiles.value = e.dataTransfer.files;// 判斷文件列表是否為空if (!selectedFiles.value?.length) {ElMessage.warning("請選擇至少一個文件");return;}try {// 清空 FormData 表單數據的內容:重新賦值,創建新實例,舊數據被丟棄(完全清空),需要使用 let 聲明對象,不能使用 const 聲明對象formData = new FormData();// 添加多個文件到 FormDatafor (const file of selectedFiles.value) {formData.append("uploadFiles", file);}// 添加受理編號到 FormDataformData.append("outerApplyId", applyBasicInfo.value.outerApplyId);// 發送請求,上傳多個文件到后端服務器await applyBasicInfoUploadFilesService(formData);ElMessage.success("文件上傳成功!");// 獲取已上傳的文件列表await fetchUploadedFiles(applyBasicInfo.value.outerApplyId);} catch (error) {ElMessage.error("文件上傳失敗!");}
};// 獲取已上傳的文件列表
const fetchUploadedFiles = async (outerApplyId: string) => {const result = await applyBasicInfoQueryFilesService(outerApplyId);acceptFiles.value = result.data;
};
......
</script><template>
......<!-- 拖拽上傳區域 --><!-- @drop.prevent="onDrop":監聽拖拽放置事件,阻止默認行為并調用onDrop方法 --><!-- @dragover.prevent:監聽拖拽懸停事件并阻止默認行為(某些瀏覽器默認打開拖放的文件),也可以寫為 @dragover.prevent="" 或 @dragover.prevent = "dragover = true" 或 @dragover.prevent = "method" --><!-- @dragleave.prevent:監聽拖拽離開事件并阻止默認行為,也可以寫為 @dragleave.prevent="" 或 @dragleave.prevent = "dragover = false" 或 @dragleave.prevent = "method" --><!-- @dragleave="":監聽拖拽離開事件,也可以寫成 @dragleave="false" 或 @dragleave="dragover = false" 或 @dragleave="method"--><div class="drag-drop-area" @drop.prevent="onDrop" @dragover.prevent><div class="drag-icon"><!-- <i class="fas fa-cloud-upload-alt"></i> --><el-icon color="#4a6bdf" size="60"><MostlyCloudy /></el-icon></div><h3>拖放文件到此處</h3><p>或者點擊選擇文件</p></div>
......
</template><style scoped lang="scss">
......
.drag-drop-area {flex: 1;border: 2px dashed #4a6bdf;border-radius: 12px;padding: 10px;text-align: center;cursor: pointer;transition: all 0.3s ease;
}.drag-drop-area:hover{// transform 對元素進行2D或3D變換,常用于微調元素位置或解決某些瀏覽器渲染問題,屬于CSS3變換屬性,不會影響文檔流和其他元素布局// translateX(1px) 表示在X軸方向上平移1像素,正值表示向下移動,負值表示向上移動// translateY(1px) 表示在Y軸方向上平移1像素,正值表示向右移動,負值表示向左移動transform: translateY(-1px);background: #f0f4ff;
}.drag-icon {font-size: 48px;color: #4a6bdf;margin-bottom: 15px;
}
......
</style>

TypeScript :拖放事件處理函數

typescript

// 定義事件處理函數類型
type DropEventHandler = (event: DragEvent) => void;// 假設這是在Vue 3 Composition API環境中的代碼
// 定義響應式變量類型
interface ReactiveState {dragover: { value: boolean };
}// 定義文件處理函數類型
type AddFilesFunction = (files: File[]) => void;// 完整的onDrop函數實現
const onDrop: DropEventHandler = (event: DragEvent): void => {// 確保事件對象存在if (!event) {console.error('Drop event is undefined or null');return;}// 確保dataTransfer對象存在if (!event.dataTransfer) {console.error('Data transfer is not available in this event');return;}// 防止默認行為(某些瀏覽器可能會嘗試打開拖放的文件)event.preventDefault();// 更新響應式狀態(假設這是在Vue組件中)// 這里假設dragover是一個ref對象,包含value屬性dragover.value = false;try {// 獲取拖放的文件列表并轉換為數組const droppedFiles: File[] = Array.from(event.dataTransfer.files);// 驗證是否確實有文件if (droppedFiles.length === 0) {console.warn('No files were dropped');return;}// 調用處理函數addFiles(droppedFiles);} catch (error) {console.error('Error processing dropped files:', error);}
};// 輔助函數:驗證文件類型
const isValidFileType = (file: File, allowedTypes?: string[]): boolean => {if (!allowedTypes || allowedTypes.length === 0) {return true; // 如果沒有限制,所有文件類型都有效}return allowedTypes.some(type => {// 支持通配符,如 "image/*"if (type.endsWith('/*')) {const category = type.split('/')[0];return file.type.startsWith(`${category}/`);}return file.type === type;});
};// 輔助函數:驗證文件大小
const isValidFileSize = (file: File, maxSize?: number): boolean => {if (!maxSize) {return true; // 如果沒有大小限制,所有文件都有效}return file.size <= maxSize;
};// 增強版的addFiles函數(帶驗證)
const addFiles: AddFilesFunction = (files: File[]): void => {// 定義允許的文件類型和最大文件大小const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf', 'text/plain'];const maxFileSize = 10 * 1024 * 1024; // 10MB// 過濾有效文件const validFiles = files.filter(file => isValidFileType(file, allowedTypes) && isValidFileSize(file, maxFileSize));// 處理無效文件const invalidFiles = files.filter(file => !isValidFileType(file, allowedTypes) || !isValidFileSize(file, maxFileSize));// 記錄或處理無效文件if (invalidFiles.length > 0) {console.warn(`${invalidFiles.length} files were rejected due to type or size restrictions`);// 可以在這里顯示用戶通知invalidFiles.forEach(file => {if (!isValidFileType(file, allowedTypes)) {console.warn(`File ${file.name} has an invalid type: ${file.type}`);} else if (!isValidFileSize(file, maxFileSize)) {console.warn(`File ${file.name} exceeds the size limit: ${formatFileSize(file.size)}`);}});}// 如果有有效文件,繼續處理if (validFiles.length > 0) {// 實際的文件處理邏輯console.log(`Processing ${validFiles.length} valid files`);// 這里可以調用上傳函數或其他處理邏輯// uploadFiles(validFiles);}
};// 格式化文件大小的輔助函數
const formatFileSize = (bytes: number): string => {if (bytes === 0) return '0 Bytes';const k = 1024;const sizes = ['Bytes', 'KB', 'MB', 'GB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};// 如果需要,可以添加事件監聽器的設置函數
const setupDropZone = (element: HTMLElement): void => {// 防止默認的拖放行為const preventDefault = (e: DragEvent) => {e.preventDefault();e.stopPropagation();};// 設置事件監聽器element.addEventListener('dragenter', preventDefault);element.addEventListener('dragover', preventDefault);element.addEventListener('dragleave', preventDefault);element.addEventListener('drop', onDrop);// 添加視覺反饋element.addEventListener('dragenter', () => {dragover.value = true;element.classList.add('drag-over');});element.addEventListener('dragleave', () => {dragover.value = false;element.classList.remove('drag-over');});element.addEventListener('drop', () => {dragover.value = false;element.classList.remove('drag-over');});
};// 假設的Vue 3組件中使用示例
/*
import { ref } from 'vue';export default {setup() {const dragover = ref(false);// 在模板渲染后設置拖放區域onMounted(() => {const dropZone = document.getElementById('drop-zone');if (dropZone) {setupDropZone(dropZone);}});return {dragover};}
};
*/// 導出函數和類型(如果使用模塊系統)
export type { DropEventHandler, AddFilesFunction };
export { onDrop, addFiles, setupDropZone, isValidFileType, isValidFileSize, formatFileSize };

關鍵改進點

  1. 類型注解

    • 為事件參數?event?添加了?DragEvent?類型

    • 為函數添加了明確的返回類型 (void)

    • 定義了自定義類型?DropEventHandler?和?AddFilesFunction

  2. 錯誤處理

    • 添加了空值檢查,確保事件對象和?dataTransfer?存在

    • 使用 try-catch 塊處理可能的異常

    • 添加了文件驗證邏輯

  3. 文件驗證

    • 添加了文件類型驗證函數?isValidFileType

    • 添加了文件大小驗證函數?isValidFileSize

    • 在?addFiles?函數中過濾無效文件

  4. 輔助功能

    • 添加了文件大小格式化函數?formatFileSize

    • 添加了拖放區域設置函數?setupDropZone

    • 提供了完整的視覺反饋處理

  5. 代碼健壯性

    • 添加了詳細的警告和錯誤日志

    • 提供了更完整的用戶體驗(如視覺反饋)

    • 考慮了邊界情況(如空文件列表)

使用說明

這個 TypeScript 實現不僅轉換了原始的 JavaScript 代碼,還增加了類型安全性和錯誤處理。在實際項目中,您可能需要根據具體框架(如 Vue、React 或 Angular)調整實現方式。

如果您是在 Vue 3 中使用這個函數,可能需要將?dragover.value?替換為適當的響應式引用,并根據組件結構調整代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95661.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95661.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95661.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025全國大學生數學建模C題保姆級思路模型(持續更新):NIPT 的時點選擇與胎兒的異常判定

2025全國大學生數學建模C題保姆級思路模型&#xff08;持續更新&#xff09;&#xff1a;NIPT 的時點選擇與胎兒的異常判定&#xff0c;完整持續更新內容見文末名片 胎兒遺傳信息檢測與臨床決策數學建模分析講義 問題一&#xff1a;Y染色體濃度的影響因素探索——線性回歸的“偵…

【筆記】Software Engineering at Google

聚焦核心原則&#xff0c;挑取最讓我眼前一亮的實踐點&#xff0c;特別是那些能直接啟發或解決我當前工作中痛點的部分。0. 序言 最近集中精力速讀了關于 ?Google 軟件工程實踐? 的諸多資料&#xff08;包括官方出版物、工程博客、技術演講以及社區討論&#xff09;。面對 G…

無人機防風技術難點解析

防風防御模塊的技術難點主要體現在以下幾個方面風場感知與精準建模1.復雜風場的實時感知&#xff1a;風&#xff0c;尤其是近地面的風&#xff0c;常常是無規則、瞬息萬變的陣風、湍流或風切變。無人機需要通過各種傳感器&#xff08;如空速計、慣性測量單元&#xff08;IMU&am…

HarmonyOS 應用開發深度解析:ArkTS 聲明式 UI 與精細化狀態管理

好的&#xff0c;請看這篇關于 HarmonyOS 應用開發中聲明式 UI 狀態管理的技術文章。 HarmonyOS 應用開發深度解析&#xff1a;ArkTS 聲明式 UI 與精細化狀態管理 引言 隨著 HarmonyOS 4、5 的廣泛應用和 HarmonyOS NEXT 的發布&#xff0c;基于 API 12 及以上的應用開發已成為…

機器學習入門,第一個MCP示例

前面我們已經搭建了屬于自己的AI大模型&#xff1a;詳情見 https://blog.csdn.net/hl_java/article/details/150591424?spm1001.2014.3001.5501 近期MCP概念這么火&#xff0c;那么它到底是什么呢&#xff0c;借一個例子為你講解 第一步&#xff1a;理解MCP的核心價值 MCP (Mo…

flutter 中間組件自適應寬度

使用Flexible IntrinsicWidth Row(children: [Text(第一個text),IntrinsicWidth(child: ConstrainedBox(constraints: BoxConstraints(maxWidth: 200), // 最大寬度限制child: Text(中間的text可能很長也可能很短,overflow: TextOverflow.ellipsis,maxLines: 1,),),),Text(第三…

TDengine 時間函數 DAYOFWEEK 用戶手冊

DAYOFWEEK 函數使用手冊 函數描述 DAYOFWEEK 函數用于返回指定日期是一周中的第幾天。該函數遵循標準的星期編號約定&#xff0c;返回值范圍為 1-7&#xff0c;其中&#xff1a; 1 星期日 (Sunday)2 星期一 (Monday)3 星期二 (Tuesday)4 星期三 (Wednesday)5 星期四 (T…

【STM32】貪吃蛇 [階段 3] 增強模塊結構(架構優化)

這篇博客是 承接&#xff1a;【項目思維】貪吃蛇&#xff08;嵌入式進階方向&#xff09;中 聚焦于 &#x1f9f1; 階段 3&#xff1a;增強模塊結構&#xff08;架構優化&#xff09; 中的 菜單系統&#xff08;Menu System&#xff09;&#xff0c;這部分的結構優化可以學到的…

江協科技STM32學習筆記補充之004

STM32 ISP 一鍵下載電路&#xff08;按功能、邏輯與時序拆解&#xff09;

數據庫小冊(1)

1. 關系型數據庫主要考點關系型數據庫&#xff1a;架構索引鎖語法理論規范2. 如何設計一個關系型數據庫設計即模塊劃分。數據庫最主要的功能是存儲我們的數據&#xff0c;所以需要一個存儲的文件系統。我們要把涉及到的物流數據提供邏輯的形式給組織和表示出來&#xff0c;這是…

記錄收入最高的一次私活 選號網,需要大量賣號的人可能需要,比如游戲腳本批量跑的號

選號網管理后臺(上傳游戲信息、賬號信息、 查看記錄) http://124.223.214.5:180/admin1 選號網客戶端(PC/H5頁面 給客戶篩選商品用) http://124.223.214.5:181/ 該在線地址僅供低頻率測試&#xff0c;正式使用需要另外部署。 功能不滿足可以聯系開發者定制 一、項目的由來 …

熱烈慶祝“中國抗戰勝利80周年”,織信低代碼助力國之重器砥礪前行!

“從保家衛國到科技強軍&#xff0c;織信低代碼平臺為軍工企業數字化轉型注入新動能。”80年后的今天&#xff0c;國人記憶從未褪色。2025年9月3日&#xff0c;正值中國抗戰勝利80周年閱兵之際&#xff0c;我國國防軍工力量在經歷長期的艱苦奮斗后&#xff0c;現今終于迎來了曙…

PostgreSQL與SQL Server:B樹索引差異及去重的優勢

PostgreSQL與SQL Server&#xff1a;B樹索引差異及去重的優勢 在優化查詢性能方面&#xff0c;索引是數據庫工程師可使用的最強大工具之一。PostgreSQL和Microsoft SQL Server&#xff08;或Azure SQL&#xff09;都將B樹索引用作其默認索引結構&#xff0c;但每個系統實現、維…

【微實驗】使用MATLAB制作一張賽博古琴?

當一個理工音樂人沒錢去買古琴&#xff0c;我直接用代碼畫一個古琴&#xff01;目錄 零、總腳本&#xff1a; 一、核心功能&#xff1a;交互模塊拆解 二、核心價值 一、初始化腳本&#xff1a;參數配置與啟動界面 ①廢話不說&#xff0c;直接上代碼 ②代碼模塊拆解與詳細解…

畢業項目推薦:67-基于yolov8/yolov5/yolo11的大棚黃瓜檢測識別系統(Python+卷積神經網絡)

文章目錄 項目介紹大全&#xff08;可點擊查看&#xff0c;不定時更新中&#xff09;概要一、整體資源介紹技術要點功能展示&#xff1a;功能1 支持單張圖片識別功能2 支持遍歷文件夾識別功能3 支持識別視頻文件功能4 支持攝像頭識別功能5 支持結果文件導出&#xff08;xls格式…

無人機小尺寸RFSOC ZU47DR板卡

整板尺寸&#xff1a;120*120mmFPGA: XCZU47DR-2FFVE1156I;DDR&#xff1a;PS側8GB 2400Mhz*64bit / PL側 4GB 2400Mhz*32bit&#xff1b;2路(QSP0QSPI1)/單片512Mb、共計1Gb&#xff1b;千兆以太網&#xff1a;1路&#xff08;PS側&#xff09;&#xff1b;主要接口資源如下&a…

LangGraph(一):入門從0到1(零基礎)

文章目錄LangGraph入門從0到10?? 安裝 & 確認環境1?? 把 LangGraph 想象成「自動化的做菜流水線」2?? 最小可運行例子&#xff1a;一句話復讀機3?? 加一個小節點&#xff1a;把用戶輸入變大寫4?? 條件邊&#xff1a;如果用戶說 quit 就結束&#xff0c;否則復讀5…

學習數據結構(16)快速排序

快速排序的基本思想&#xff1a;快速排序是Hoare于1962年提出的一種二叉樹結構的交換排序方法&#xff0c;其基本思想為&#xff1a;任取待排序元素序列中的某元素作為基準值&#xff0c;按照該基準值將待排序集合分割成兩子序列&#xff0c;左子序列中所有元素均小于基準值&am…

uni-app iOS 上架常見問題與解決方案,實戰經驗全解析

uni-app 讓開發者能夠“一套代碼&#xff0c;多端運行”&#xff0c;極大降低了開發成本。 但當應用進入 iOS 上架階段 時&#xff0c;不少團隊發現流程并沒有想象中那么順利&#xff1a;證書問題、打包失敗、上傳出錯、審核被拒……這些都可能讓項目卡殼。 本文結合實際案例&a…

洗衣機的智能升級集成方案WT2606B屏幕驅動+AI語音控制

2025&#xff0c;洗衣機市場正從功能滿足轉向體驗升級&#xff0c;企業正面臨哪些轉型難點?一文為您解讀洗衣機行業智能化升級之路。傳統洗衣機就像是一個"沉默的工人"&#xff0c;只能通過簡單的LED指示燈告訴你它在工作&#xff0c;卻無法讓你真正了解它在干嘛。用…