您好,登錄后才能下訂單哦!
這篇“自定義input組件怎么實現拖拽文件上傳”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“自定義input組件怎么實現拖拽文件上傳”文章吧。
<tag-input id="uploadTag" ref="uploadTag" v-model="fileNameList" size="small" @input="removeFile" ></tag-input>
頁面加載時監聽拖拽事件,監聽后將文件放置下發fileList參數列表中
mounted() { setTimeout(() => { this.$nextTick(() => { if (this.$refs.uploadTag) { let dropEle = this.$refs.uploadTag.$el // 禁止拖拽文件后打開文件 dropEle.addEventListener('drop', e => { e.preventDefault(); e.stopPropagation(); }, false) dropEle.addEventListener('dragover', e => { e.preventDefault(); e.stopPropagation(); }, false) dropEle.addEventListener('dragleave', e => { e.preventDefault(); e.stopPropagation(); }, false) // 處理拖拽文件的邏輯 dropEle.addEventListener('drop', e => this.watchFileUpload(e)) } }) }, 1000) }
// 拖拽上傳 private watchFileUpload(e) { e.preventDefault(); e.stopPropagation(); var df = e.dataTransfer; var dropFiles = []; // 拖拽的文件,會放到這里 var dealFileCnt = 0; // 讀取文件是個異步的過程,需要記錄處理了多少個文件了 var allFileLen = df.files.length; // 所有的文件的數量,給非Chrome瀏覽器使用的變量 // 檢測是否已經把所有的文件都遍歷過了 function checkDropFinish() { dealFileCnt++; } if (df.items !== undefined) { // Chrome拖拽文件邏輯 for (var i = 0; i < df.items.length; i++) { var item = df.items[i]; if (item.kind === "file" && item.webkitGetAsEntry().isFile) { var file = item.getAsFile(); dropFiles.push(file); } } } else { // 非Chrome拖拽文件邏輯 for (var i = 0; i < allFileLen; i++) { var dropFile = df.files[i]; if (dropFile.type) { dropFiles.push(dropFile); checkDropFinish(); } else { try { var fileReader = new FileReader(); fileReader.readAsDataURL(dropFile.slice(0, 3)); fileReader.addEventListener('load', function (e) { console.log(e, 'load'); dropFiles.push(dropFile); checkDropFinish(); }, false); fileReader.addEventListener('error', function (e) { console.log(e, 'error,不可以上傳文件夾'); checkDropFinish(); }, false); } catch (e) { console.log(e, 'catch error,不可以上傳文件夾'); checkDropFinish(); } } } } dropFiles.forEach(item => { this.fileList.push(item) }) this.fileNameList = this.fileList.map(item => { if (item.name) { return item.name } if (item.fileName) { return item.fileName } }); }
// 附件刪除 下拉框 private removeFile(nameList, name) { // 記錄刪除的附件信息 this.fileList.splice(this.fileList.findIndex(item => item.fileName === name || item.name === name), 1) this.fileNameList = this.fileList.map(item => item.name || item.fileName); }
<template> <div class="yh-input-tag input-tag-wrapper" ref="InputTag" @click="foucusTagInput" > <el-tag v-for="(tag, idx) in innerTags" :key="tag" :size="size" :closable="!readonly" :disable-transitions="false" @close="remove(tag, idx)" >{{ tag }}</el-tag > <input :readonly="readonly || readonlyIpt" class="tag-input" :class="[size ? 'yh-input-tag--' + size : '']" : :placeholder="isplaceholder" v-model="newTag" @keydown.delete.stop="removeLastTag" @keydown="addNew" @blur="blurTagInput" /> </div> </template> <script> export default { name: 'InputTag', props: { value: { type: Array, default: () => [] }, addTagOnKeys: { type: Array, default: () => [13, 188, 9] }, readonly: { type: Boolean, default: false }, // 輸入框只讀 readonlyIpt: { type: Boolean, default: false }, size: String, placeholder: { type: String, default: '請輸入' } }, inject: { elForm: { default: '' }, elFormItem: { default: '' } }, data () { return { newTag: '', innerTags: [...this.value], currentTag: null, widthStyle: { minWidth: '10px' } } }, computed: { isplaceholder () { let str = '' if(this.value?.length > 0) { this.$nextTick(() => { if (this.$refs.yhInputTag) { this.$refs.InputTag.style.padding = '0' } }) str = '' } else { this.$nextTick(() => { if (this.$refs.yhInputTag) { this.$refs.InputTag.style.padding = '0 15px' } }) str = this.placeholder } return str }, // 表單禁用關聯 inputDisabled() { return this.disabled || (this.elForm || {}).disabled; } }, watch: { value: { handler(newVal, oldVal) { if (this.elForm && oldVal !== undefined && newVal !== oldVal) { this.elForm.validateField(this.elFormItem.prop) } if (newVal) { this.innerTags = [...newVal] } }, deep: true, immediate: true } }, methods: { foucusTagInput () { if (this.readonly || this.readonlyIpt || !this.$el.querySelector('.tag-input')) { return } else { this.$el.querySelector('.tag-input').focus() this.widthStyle = { minWidth: '10px' } } }, blurTagInput (e) { this.addNew(e) this.widthStyle = { width: '0px' } }, addNew (e) { if (e && (!this.addTagOnKeys.includes(e.keyCode)) && (e.type !== 'blur')) { return } if (e) { e.stopPropagation() e.preventDefault() } let addSuucess = false if (this.newTag.includes(',')) { this.newTag.split(',').forEach(item => { if (this.addTag(item.trim())) { addSuucess = true } }) } else { if (this.addTag(this.newTag.trim())) { addSuucess = true } } if (addSuucess) { this.tagChange() this.newTag = '' } }, addTag (tag) { tag = tag.trim() if (tag && !this.innerTags.includes(tag)) { this.innerTags.push(tag) return true } return false }, remove (tag, index) { this.innerTags.splice(index, 1) this.currentTag = tag this.tagChange() }, removeLastTag () { if (this.newTag) { return } this.innerTags.pop() this.tagChange() }, tagChange () { this.$forceUpdate() this.$emit('input', JSON.parse(JSON.stringify(this.innerTags)), this.currentTag) } } } </script> <style scoped> .input-tag-wrapper { position: relative; font-size: 14px; background-color: #fff; background-image: none; border-radius: 4px; border: 1px solid #DCDFE6; box-sizing: border-box; color: #575757; display: inline-block; cursor: text; outline: none; padding: 0 15px; transition: border-color .2s cubic-bezier(.645,.045,.355,1); width: 100%; line-height: normal; &:hover{ border-color: #C5C6C7; } &:focus{ border-color: #d32f2f; } .el-tag{ box-sizing: border-box; border-color: transparent; margin: 2px 0 2px 6px; background-color: #f0f2f5; display: inline-flex; max-width: 100%; align-items: center; } } .tag-input { background: transparent; border: 0; font-size: 14px; outline: none; padding-left: 0; height: 26px; &::placeholder { color: #C8C9CA; } } .yh-input-tag--mini{ height: 26px; line-height: 26px; .tag { height: 16px; } } .yh-input-tag--small{ height: 30px; line-height: 30px; .tag { height: 20px; } } .yh-input-tag--medium{ height: 34px; line-height: 34px; .tag { height: 24px; } } // 表單標簽選擇器必填樣式 .el-form-item.is-error .input-tag-wrapper, .el-form-item.is-error .input-tag-wrapper:focus { border-color: #bc1126 !important; } </style>
可支持手動拖拽上傳
多圖上傳按鈕+多圖上傳彈窗+圖片上的預覽刪除圖標
<template> <div> <div class="many"> <el-form-item> <div class="upload-item"> <el-button type="primary" @click="uploadFile">多圖上傳</el-button> </div> </el-form-item> </div> <el-dialog title="圖片預覽" :visible.sync="dialogImgVisible" width="50%"> <img :src="dialogImageUrl" alt="" class="previewImg" /> </el-dialog> <!--多圖上傳彈窗界面--> <el-dialog :title="'上傳'" :visible.sync="dialogFormVisible" custom-class="pub_dialog" > <el-form > <!--內容部分 --> <el-form-item><!----> <div > <label>選擇文件:</label> <div> <div class="desc">支持 jpg, png 圖片格式,且不超過500kb</div> <el-upload :action="UPLOAD_URL" :headers="authorToken" :auto-upload="true" accept="image/jpg,image/png,image/jpeg" :on-success="handleSuccess" :before-upload="handleBeforeUpload" :show-file-list="false" multiple :limit="10" :on-exceed="handleExceed" :file-list="fileList"> <el-button size="small" type="primary">上傳圖片</el-button> </el-upload> </div> </div> <div class="fileList" > <div class="item" v-for="(item,index) in images" :key="index"> <img :src="item.url" alt="" :key="index" class = "imgList"> <div class="scissor-icon"> <i class="el-icon-scissors" @click="changeFile(item)"></i> </div> <div class="delete-icon"> <i class="el-icon-delete" @click="handleRemove(item)"></i> </div> <div class="search-icon"> <i class="el-icon-search" @click="handlePreview(item)"></i> </div> <el-input type="textarea" :autosize="{ minRows: 7, maxRows: 7}" placeholder="請輸入圖片描述" v-model="item.manyDescription" :key="index" > </el-input> </div> </div> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="closeDialog">確 定</el-button> </div> </el-dialog> </div> </template>
1.變量數據區域
代碼如下(示例):
<script> export default { name: "UploadMany", data() { return { textarea:'', dialogImageUrl: '', dialogImgVisible: false, dialogVisible: false, fileList: [], imgs: [], images: [], UPLOAD_URL: "/v1/admin/common/upload",//這里填寫你的后端上傳接口地址 authorToken: { Authorization: 'Bearer ' + sessionStorage.getItem("token"), siteId:sessionStorage.getItem("siteId") }, param: { token: '' }, fileNum: 0, num: 0, dialogFormVisible: false,//添加表單彈出框是否顯示 dialogChangeVisible: false, picsList: [], //頁面顯示的數組 // 防止重復提交 loading: true, } },
2.方法區域
代碼如下(示例):
methods: { //刪除方法 handleRemove(file) { console.log(file) // 1.獲取將要刪除的圖片的臨時路徑 const filePath = file.url // 2.從數組中,找到這個圖片對應的索引值 const i = this.imgs.findIndex((x) => x.url === filePath) // 3.調用數組的 splice 方法,把圖片信息對象,從 pics 數組中移除 this.imgs.splice(i, 1) console.log(this.imgs) }, //預覽方法 handlePreview(file) { console.log(file); this.dialogImageUrl = file.url; this.dialogImgVisible = true; }, //限制上傳文件個數 handleExceed(files, fileList) { this.$message.warning(`當前限制選擇 10 個文件,本次選擇了 ${files.length} 個文件,共選擇了 ${files.length + fileList.length} 個文件`); }, //上傳成功后 handleSuccess(response, fileList) { console.log(response); console.log(fileList) this.loading = false if(response.code === 200){ this.imgs.push({name: response.data.resourceName, url: response.data.resourceUrl, manyDescription: '', manyResourceId: response.data.id}) this.num++; if(this.num == this.fileNum){ for(let i = 0; i < this.num ; i++){ this.$emit('getManyImg', this.imgs[i]) } this.num = 0; this.fileNum = 0; this.images = this.imgs; this.imgs = []; } }else{ this.$message.error('上傳失敗'); } }, handleBeforeUpload(file) { // 這里做可以做文件校驗操作 const isImg = /^image\/\w+$/i.test(file.type) if (!isImg && this.fileType == 'image/*') { this.$message.error('只能上傳 JPG、PNG、GIF 格式!') return false } this.fileNum++; }, uploadFile(){ this.dialogFormVisible = true; this.loading = false; }, closeDialog(){ this.dialogFormVisible = false; this.imgs = []; this.images = []; } } }
1.在你需要用到的界面vue里導入組件
import UploadMany from '@/components/upload/UploadMany'; import {getToken} from '@/utils/auth'; export default { name: "TestEditor", components: { UploadMany, },
2.template部分使用組件
<el-col :span="24"> <el-form-item prop="manyImg" label="多圖上傳:" :label-width="imgWidth" class="form"> <upload-many v-model="dialogForm.manyImg" @getManyImg="getManyImg" ></upload-many> <div class="fileList" > <div class="item" v-for="(itemPhoto,indexPhoto) in dialogForm.images" :key="indexPhoto"> <div class="item-left" > <img :src="itemPhoto.url" alt="" :key="indexPhoto" class = "imgList"> <div class="item-bottom"> <div class="search-icon"> <i class="el-icon-search" @click="handlePreview(itemPhoto)"></i> </div> <div class="delete-icon"> <i class="el-icon-delete" @click="handleRemove(itemPhoto)"></i> </div> </div> </div> <el-input type="textarea" :autosize="{ minRows: 7, maxRows: 7}" placeholder="請輸入圖片描述" v-model="itemPhoto.manyDescription" > </el-input> </div> </div> </el-form-item> </el-col>
3.方法部分
getManyImg(imgs) { this.dialogForm.images.push(imgs); console.log(this.dialogForm.images) }, handleRemove(file) { console.log(file) // 1.獲取將要刪除的圖片的臨時路徑 const filePath = file.url // 2.從數組中,找到這個圖片對應的索引值 const i = this.dialogForm.images.findIndex((x) => x.url === filePath) // 3.調用數組的 splice 方法,把圖片信息對象,從 pics 數組中移除 this.dialogForm.images.splice(i, 1) }, //預覽圖片 handlePreview(file) { console.log(file); this.dialogImageUrl = file.url; this.dialogImgVisible = true; },
<template> <div> <div class="many"> <el-form-item> <div class="upload-item"> <el-button type="primary" @click="uploadFile">多圖上傳</el-button> </div> </el-form-item> </div> <!--裁剪彈窗--> <!-- vueCropper 剪裁圖片實現--> <el-dialog title="圖片剪裁" :visible.sync="dialogChangeVisible" append-to-body> <div class="cropper-content"> <div class="cropper" > <vueCropper ref="cropper" :img="option.img" :outputSize="option.size" :outputType="option.outputType" :info="true" :full="option.full" :canMove="option.canMove" :canMoveBox="option.canMoveBox" :original="option.original" :autoCrop="option.autoCrop" :fixed="option.fixed" :fixedNumber="option.fixedNumber" :centerBox="option.centerBox" :infoTrue="option.infoTrue" :fixedBox="option.fixedBox" ></vueCropper> </div> </div> <div slot="footer" class="dialog-footer"> <el-button @click="dialogChangeVisible = false">取 消</el-button> <el-button type="primary" @click="finish" :loading="loading">確認</el-button> </div> </el-dialog> <el-dialog title="圖片預覽" :visible.sync="dialogImgVisible" width="50%"> <img :src="dialogImageUrl" alt="" class="previewImg" /> </el-dialog> <!--多圖上傳彈窗界面--> <el-dialog :title="'上傳'" :visible.sync="dialogFormVisible" custom-class="pub_dialog" > <el-form > <!--內容部分 --> <el-form-item><!----> <div > <label>選擇文件:</label> <div> <div class="desc">支持 jpg, png 圖片格式,且不超過500kb</div> <el-upload :action="UPLOAD_URL" :headers="authorToken" :auto-upload="true" accept="image/jpg,image/png,image/jpeg" :on-success="handleSuccess" :before-upload="handleBeforeUpload" :show-file-list="false" multiple :limit="10" :on-exceed="handleExceed" :file-list="fileList"> <el-button size="small" type="primary">上傳圖片</el-button> </el-upload> </div> </div> <div class="fileList" > <div class="item" v-for="(item,index) in images" :key="index"> <img :src="item.url" alt="" :key="index" class = "imgList"> <div class="scissor-icon"> <i class="el-icon-scissors" @click="changeFile(item)"></i> </div> <!-- <div class="refresh-icon">--> <!-- <i class="el-icon-refresh" @click="handleRemove()"></i>--> <!-- </div>--> <div class="delete-icon"> <i class="el-icon-delete" @click="handleRemove(item)"></i> </div> <div class="search-icon"> <i class="el-icon-search" @click="handlePreview(item)"></i> </div> <el-input type="textarea" :autosize="{ minRows: 7, maxRows: 7}" placeholder="請輸入圖片描述" v-model="item.manyDescription" :key="index" > </el-input> </div> </div> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="closeDialog">確 定</el-button> </div> </el-dialog> </div> </template> <script> import VueCropper from 'vue-cropper' import Vue from "vue"; Vue.use(VueCropper) export default { name: "UploadMany", data() { return { textarea:'', dialogImageUrl: '', dialogImgVisible: false, dialogVisible: false, fileList: [], imgs: [], images: [], UPLOAD_URL: "/v1/admin/common/upload", authorToken: { Authorization: 'Bearer ' + sessionStorage.getItem("token"), siteId:sessionStorage.getItem("siteId") }, param: { token: '' }, fileNum: 0, num: 0, dialogFormVisible: false,//添加表單彈出框是否顯示 dialogChangeVisible: false, // 裁剪組件的基礎配置option option: { img: '', // 裁剪圖片的地址 info: true, // 裁剪框的大小信息 outputSize: 0.8, // 裁剪生成圖片的質量 outputType: 'jpeg', // 裁剪生成圖片的格式 canScale: false, // 圖片是否允許滾輪縮放 autoCrop: true, // 是否默認生成截圖框 // autoCropWidth: 300, // 默認生成截圖框寬度 // autoCropHeight: 200, // 默認生成截圖框高度 fixedBox: true, // 固定截圖框大小 不允許改變 fixed: true, // 是否開啟截圖框寬高固定比例 fixedNumber: [7, 5], // 截圖框的寬高比例 full: true, // 是否輸出原圖比例的截圖 canMoveBox: false, // 截圖框能否拖動 original: false, // 上傳圖片按照原始比例渲染 centerBox: false, // 截圖框是否被限制在圖片里面 infoTrue: true // true 為展示真實輸出圖片寬高 false 展示看到的截圖框寬高 }, picsList: [], //頁面顯示的數組 // 防止重復提交 loading: true, } }, methods: { handleRemove(file) { console.log(file) // 1.獲取將要刪除的圖片的臨時路徑 const filePath = file.url // 2.從數組中,找到這個圖片對應的索引值 const i = this.imgs.findIndex((x) => x.url === filePath) // 3.調用數組的 splice 方法,把圖片信息對象,從 pics 數組中移除 this.imgs.splice(i, 1) console.log(this.imgs) }, handlePreview(file) { console.log(file); this.dialogImageUrl = file.url; this.dialogImgVisible = true; }, handleExceed(files, fileList) { this.$message.warning(`當前限制選擇 10 個文件,本次選擇了 ${files.length} 個文件,共選擇了 ${files.length + fileList.length} 個文件`); }, handleSuccess(response, fileList) { console.log(response); console.log(fileList) this.loading = false if(response.code === 200){ this.imgs.push({name: response.data.resourceName, url: response.data.resourceUrl, manyDescription: '', manyResourceId: response.data.id}) this.num++; if(this.num == this.fileNum){ for(let i = 0; i < this.num ; i++){ this.$emit('getManyImg', this.imgs[i]) } this.num = 0; this.fileNum = 0; this.images = this.imgs; this.imgs = []; } }else{ this.$message.error('上傳失敗'); } }, // 裁剪按鈕 限制圖片大小 changeFile(file) { console.log(file) this.option.img = file.url console.log(this.option.img) this.dialogChangeVisible = true }, // 點擊裁剪 finish() { this.$refs.cropper.getCropBlob((data) => { console.log(data) console.log(data.size) this.$data.dialogChangeVisible = false this.axios.post("/v1/admin/common/upload",data).then((res) => { let code = res.data.code; if (code == 200) { this.$data.dialogFormVisible = false this.$message.success("上傳成功"); } }).catch((error) => { console.log(error); }); }) }, handleBeforeUpload(file) { // 這里做可以做文件校驗操作 const isImg = /^image\/\w+$/i.test(file.type) if (!isImg && this.fileType == 'image/*') { this.$message.error('只能上傳 JPG、PNG、GIF 格式!') return false } this.fileNum++; }, uploadFile(){ this.dialogFormVisible = true; this.loading = false; }, closeDialog(){ this.dialogFormVisible = false; this.imgs = []; this.images = []; } } } </script> <style lang="scss" scoped> .el-dialog{ width: 50%; } .item { width: 300px; height: 140px; position: relative; display: flex; margin: 10px; .delete-icon { display: none; } .refresh-icon { display: none; } .search-icon { display: none; } .scissor-icon { display: none; } &:hover { .scissor-icon { display: block; position: absolute; width: 35px; height: 40px; line-height: 40px; left: 100px; top: 100px; background: rgba(59, 60, 61, 0.5); // box-sizing: content-box; z-index: 999; cursor: pointer; text-align: center; i { margin: 8px 10px 0 0; display: block; font-size: 24px; color: white; } } .delete-icon { display: block; position: absolute; width: 35px; height: 40px; left: 0px; top: 100px; background: rgba(59, 60, 61, 0.5); // box-sizing: content-box; z-index: 999; cursor: pointer; text-align: center; i { margin: 8px 10px 0 10px; display: block; font-size: 24px; color: white; } } .refresh-icon { display: block; position: absolute; width: 35px; height: 40px; left: 35px; top: 100px; background: rgba(59, 60, 61, 0.5); // box-sizing: content-box; z-index: 999; cursor: pointer; text-align: center; i { margin: 8px 10px 0 0; display: block; font-size: 24px; color: white; } } .search-icon { display: block; position: absolute; width: 65px; height: 40px; left: 35px; top: 100px; background: rgba(59, 60, 61, 0.5); // box-sizing: content-box; z-index: 999; cursor: pointer; text-align: center; i { margin: 8px 10px 0 10px; display: block; font-size: 24px; color: white; } } } } .imgList { border: 1px dashed #d9d9d9; border-radius: 5px; box-sizing: border-box; width: 180px; height: 180px; margin-top: 0px; &:hover { border: 1px dashed #409eff; } } // 截圖 .cropper-content { .cropper { width: auto; height: 300px; } } .previewImg { width: 50%; height: 100% } </style>
以上就是關于“自定義input組件怎么實現拖拽文件上傳”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。