Vue.jsでDrag and DropでMultipleなFileUploadモジュールを作りました。
gifのような動作でファイルのアップロードができるUIです。
上記のイメージのように、このFileUploadUIでは下記の機能をサポートしました。
☑️ファイル選択(複数)
☑️Drag and Drop(複数)
☑️ファイル追加
☑️ファイル削除
このサンプルでは送信ボタンを押下したときにファイルのサーバへのアップロードが開始されます。
今後Drag and Drop時にサーバーへアップロードする機能を追加する予定です。
デモ
Drop files or Browse
-
{{ file.name }}
ソースコード
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" rel="stylesheet" /> <div id="drag_drop_app" class="file_uploader_module"> <input type="file" ref="file_upload" style="display: none" @change="onFileChange" multiple> <div class="drop_area" @dragenter="dragEnter" @dragleave="dragLeave" @dragover.prevent @drop.prevent="dropFile" :class="{enter: isEnter}" type="button" @click="$refs.file_upload.click()"> <i class="fas fa-arrow-circle-up text-6xl mb-3 fa-5x upload-icon"></i> <b>Drop files or Browse</b> </div> <ul class="flex"> <li class="flex-col" v-for="(file,index) in files" :key="index" @click="deleteFile(index)"> <div style="position: relative;"> </div> <div class="file-box" style="display: block;"> <span> {{ file.name }} <i class="fa fa-times delete-mark"></i> </span> </div> </li> </ul> <div v-show="files.length"> <button class="button" @clikc="sendFile">Send</button> </div> </div>
const drag_drop_app = new Vue({ el: "#drag_drop_app", data: { isEnter: false, files: [] }, methods: { dragEnter() { this.isEnter = true; }, dragLeave() { this.isEnter = false; }, dragOver() { }, dropFile() { this.files.push(...event.dataTransfer.files) this.isEnter = false; }, deleteFile(index) { this.files.splice(index, 1) }, upload: function () { // FormData を利用して File を POST する let formData = new FormData(); formData.append('files', this.uploadFile); let config = { headers: { 'content-type': 'multipart/form-data' } }; axios .post('yourUploadUrl', formData, config) .then(function (response) { }) .catch(function (error) { }) }, sendFile() { this.files.forEach(file => { let form = new FormData() form.append('file', file) axios.post('url', form).then(response => { console.log(response.data) }).catch(error => { console.log(error) }) }) }, btnclick() { this.$refs.input.click(); }, onFileChange(e) { let files = e.target.files || e.dataTransfer.files; this.files.push(...files) console.log(files) } } })
/* NEW FILE DRAG AND DROP MODULE */ :root { --background-color:#f3f3f3; --background-color-hover:#b9b9b9; --border-color:#6d6d6d; --border-color-hover:#757575; --line-border: 2px; --text-color-dark: #505050; } .file_uploader_module { margin: 15px; } .drop_area { display: flex; justify-content: center; align-items: center; flex-direction: column; max-width: 700px; min-height: 150px; border: 5px dashed var(--border-color); background-color:var(--background-color); color: var(--text-color-dark); border-radius: 20px; box-sizing: border-box; transition: background-color 160ms ease; font-weight:bold; } .enter { border: 5px dashed var(--border-color-hover); background-color: var(--background-color-hover); } ul { margin: 0; padding: 0; list-style-type: none; } .flex { display: flex; align-items: center; } .flex-col { display: flex; flex-direction: column; align-items: center; margin: 0.5em; font-size: 10px; } .button { padding: 0.5em 1.5em; background-color:var(--background-color); color: var(--text-color-dark); font-size: 14px; font-weight: bold; border-radius: 5px; border: 3px solid var(--border-color); } .upload { color :var(--text-color-dark); } .file-box{ font-size: 15px; padding: 2px 8px; border: 3px solid var(--border-color); background-color:var(--background-color); color: var(--text-color-dark); font-weight:bold; } .delete-mark { font-size: 20px; color: var(--text-color-dark); padding-left:20px; vertical-align: middle; }