Vue的移動(dòng)端多圖上傳插件vue-easy-uploader的示例代碼
前言
這段時(shí)間趕項(xiàng)目,需要用到多文件上傳,用Vue進(jìn)行前端項(xiàng)目開發(fā)。在網(wǎng)上找了不少插件,都不是十分滿意,有的使用起來繁瑣,有的不能適應(yīng)本項(xiàng)目。就打算自己折騰一下,寫一個(gè)Vue的上傳插件,一勞永逸,以后可以直接使用。
目前vue-easy-uploader已上傳到GitHub和NPM,使用起來方便簡單,不需要繁瑣的配置即可投入生產(chǎn),不過需要后端配合,實(shí)現(xiàn)上傳接口。
本項(xiàng)目GitHub地址: https://github.com/quanzaiyu/vue-easy-uploader
本項(xiàng)目NPM地址: https://www.npmjs.com/package/vue-easy-uploader
詳細(xì)的使用方法都在倉庫Readme中,就不贅述,這里談下本插件的設(shè)計(jì)開發(fā)思路。
插件介紹
vue-easy-uploader
是一個(gè)多圖上傳插件。主要特性包括:
- 多文件上傳
- 上傳圖片預(yù)覽
- 上傳狀態(tài)監(jiān)測(cè)
- 刪除指定圖片
- 清空?qǐng)D片
- 重新上傳
后期版本迭代將不限于圖片,往通用文件上傳進(jìn)行改進(jìn)。
先看看上傳插件使用時(shí)候的效果圖:
目錄結(jié)構(gòu)
index.js # 主入口文件 store.js # 狀態(tài)管理 uploader.vue # 上傳組件
文件解析
index.js
import uploader from './uploader' import store from './store' let plugin = {} plugin.install = function (_Vue, _store) { _Vue.component('uploader', uploader) _store.registerModule('imgstore', store) } export default plugin
這是插件的主入口文件,注冊(cè)了全局的上傳組件和狀態(tài)管理,使用時(shí)只需要在項(xiàng)目入口文件(一般是main.js)中加入以下代碼即可引入此插件:
import Vue from 'vue' import Vuex from 'vuex' import uploader from 'vue-easy-uploader' let store = new Vuex.Store({}) Vue.use(uploader, store)
store.js
此文件為狀態(tài)管理配置文件,主要包含三個(gè)state:
img_upload_cache # 上傳文件緩存 img_paths # 上傳狀態(tài),包括 ready selected uploading finished img_status # 上傳后的路徑反饋數(shù)組(數(shù)據(jù)結(jié)構(gòu)為Set集合)
針對(duì)每個(gè)state都有自己的mutation,用于改變state,規(guī)范上mutation都需要使用大寫字母加下劃線的形式,本人習(xí)慣使用小寫字母,不過都不是原則上的問題。
最重要的一個(gè)state是img_status,用于監(jiān)視圖片上傳的狀態(tài)。包括以下幾個(gè)狀態(tài):
ready # 上傳開始前的準(zhǔn)備狀態(tài) selected # 已選擇上傳文件 uploading # 開始上傳 finished # 上傳完畢
在組件中可以通過改變上傳狀態(tài)實(shí)現(xiàn)文件的上傳,同時(shí)也可以監(jiān)聽上傳狀態(tài)的變化而執(zhí)行回調(diào)。如:
methods: { upload () { this.$store.commit('set_img_status', 'uploading') }, submit () { // some code } } computed: { ...mapState({ imgStatus: state => state.imgstore.img_status }) }, watch: { imgStatus () { if (this.imgStatus === 'finished') { this.submit() } } }
上述代碼中,使用upload方法更新了上傳狀態(tài),讓圖片開始執(zhí)行上傳操作,使用watch進(jìn)行上傳狀態(tài)的監(jiān)視,當(dāng)上傳完成(img_status狀態(tài)變?yōu)閒inished),執(zhí)行回調(diào)函數(shù)submit。
源文件如下:
// Created by quanzaiyu on 2017/10/25 0025. var state = { img_upload_cache: [], img_paths: [], img_status: 'ready' // 上傳狀態(tài) ready selected uploading finished } const actions = {} const getters = {} const mutations = { set_img_upload_cache (state, arg) { state.img_upload_cache = arg }, set_img_paths (state, arg) { state.img_paths = arg }, set_img_status (state, arg) { state.img_status = arg } } export default { state, mutations, actions, getters }
uploader.vue
先看源代碼(為了節(jié)省空間,未貼出style部分的代碼):
<template> <div class="imgUploader"> <div class="file-list"> <section v-for="(file, index) in imgStore" :key="index" class="file-item draggable-item" > <img :src="file.src" alt="" ondragstart="return false;"> <span class="file-remove" @click="remove(index)">+</span> </section> <section class="file-item" v-if="imgStatus !== 'finished'"> <div class="add"> <span>+</span> <input type="file" pictype='30010003' multiple data-role="none" accept="image/*" @change="selectImgs" ref="file" > </div> </section> </div> <div class="uploadBtn"> <section> <span v-if="imgStore.length > 0" class="empty" @click="empty"> {{imgStatus === 'finished' ? '重新上傳' : '清空'}} </span> </section> </div> </div> </template> <script> import { mapState } from 'vuex' export default { props: ['url'], data () { return { files: [], // 文件緩存 index: 0 // 序列號(hào) } }, computed: { ...mapState({ imgStore: state => state.imgstore.img_upload_cache, imgPaths: state => state.imgstore.img_paths, imgStatus: state => state.imgstore.img_status }) }, methods: { // 選擇圖片 selectImgs () { # ① let fileList = this.$refs.file.files for (let i = 0; i < fileList.length; i++) { // 文件過濾 if (fileList[i].name.match(/.jpg|.gif|.png|.bmp/i)) { let item = { key: this.index++, name: fileList[i].name, size: fileList[i].size, file: fileList[i] } // 將圖片文件轉(zhuǎn)成BASE64格式 let reader = new FileReader() # ② reader.onload = (e) => { this.$set(item, 'src', e.target.result) } reader.readAsDataURL(fileList[i]) this.files.push(item) this.$store.commit('set_img_upload_cache', this.files) // 存儲(chǔ)文件緩存 this.$store.commit('set_img_status', 'selected') // 更新文件上傳狀態(tài) } } }, // 上傳圖片 submit () { let formData = new FormData() # ③ this.imgStore.forEach((item, index) => { item.name = 'imgFiles[' + index + ']' # ④ formData.append(item.name, item.file) }) formData.forEach((v, k) => console.log(k, ' => ', v)) // 新建請(qǐng)求 const xhr = new XMLHttpRequest() # ⑤ xhr.open('POST', this.url, true) xhr.send(formData) xhr.onload = () => { if (xhr.status === 200 || xhr.status === 304) { let datas = JSON.parse(xhr.responseText) console.log('response: ', datas) // 存儲(chǔ)返回的地址 let imgUrlPaths = new Set() # ⑥ datas.forEach(e => { // error === 0為成功狀態(tài) e.error === 0 && imgUrlPaths.add(e.url) }) this.$store.commit('set_img_paths', imgUrlPaths) // 存儲(chǔ)返回的地址 this.files = [] // 清空文件緩存 this.index = 0 // 初始化序列號(hào) this.$store.commit('set_img_status', 'finished') // 更新文件上傳狀態(tài) } else { alert(`${xhr.status} 請(qǐng)求錯(cuò)誤!`) } } }, // 移除圖片 remove (index) { this.files.splice(index, 1) this.$store.commit('set_img_upload_cache', this.files) // 更新存儲(chǔ)文件緩存 }, // 清空?qǐng)D片 empty () { this.files.splice(0, this.files.length) this.$store.commit('set_img_upload_cache', this.files) // 更新存儲(chǔ)文件緩存 this.$store.commit('set_img_paths', []) } }, beforeCreate () { this.$store.commit('set_img_status', 'ready') // 更新文件上傳狀態(tài) }, destroyed () { this.$store.commit('set_img_upload_cache', []) this.$store.commit('set_img_paths', []) }, watch: { imgStatus () { if (this.imgStatus === 'uploading') { this.submit() # ⑦ } }, imgStore () { if (this.imgStore.length <= 0) { this.$store.commit('set_img_status', 'ready') // 更新文件上傳狀態(tài) } } } } </script> <style lang="less" scoped> ... </style>
以上代碼中有一些注釋序號(hào),是此插件設(shè)計(jì)的主要思路,其他代碼都比較容易理解,分別說下
① 選擇文件后執(zhí)行,img_status狀態(tài)變?yōu)閟elected。
② 將帶上傳的圖片文件轉(zhuǎn)化為Base64格式,用于縮略圖顯示。
③ 創(chuàng)建一個(gè)表單對(duì)象,用于存儲(chǔ)待上傳的文件。
④ 注意這里的name屬性值,暫時(shí)寫死,后面設(shè)計(jì)打算從組件中指定name屬性,如果是多文件的話,name屬性的數(shù)組序號(hào)從0開始遞增。
⑤ 未依賴任何Ajax請(qǐng)求插件,使用原生的XMLHttpRequest對(duì)象創(chuàng)建請(qǐng)求。
⑥ 存儲(chǔ)上傳成功后服務(wù)器返回的上傳路徑。
⑦ 檢測(cè)上傳狀態(tài),當(dāng)在使用此插件時(shí)將img_status的狀態(tài)設(shè)置為uploading時(shí)執(zhí)行上傳操作。
使用
注意
使用此插件時(shí),需要與后端約定返回的數(shù)據(jù)格式,如下:
[{"error":0,"url":"\/uploads\/api\/201711\/25\/fde412bd83d3ec5d6a49769bd0c143cd.jpg"},{"error":0,"url":"\/uploads\/api\/201711\/25\/c6fd51f0388c63a0b6d350331c945fb1.jpg"}]
預(yù)覽如下:
返回的是一個(gè)上傳后的路徑數(shù)組,包括error和url字段,每個(gè)文件有自己的上傳狀態(tài),當(dāng)error為0的時(shí)候?yàn)樯蟼鞒晒Γ⒎祷厣蟼骱蟮穆窂絬rl
改進(jìn)
后續(xù)版本打算進(jìn)行如下改進(jìn)
- 把表單的name屬性名稱通過組件傳遞。
- 自定義上傳成功后服務(wù)器響應(yīng)的數(shù)據(jù)格式,比如自定義error的名稱和其值所表示的狀態(tài)。
- 支持其他類型文件的上傳,可以在組件中自行制定上傳的文件類型,及其預(yù)覽方式。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Vue移動(dòng)端實(shí)現(xiàn)圖片上傳及超過1M壓縮上傳
- vue實(shí)現(xiàn)移動(dòng)端圖片上傳功能
- 移動(dòng)端 Vue+Vant 的Uploader 實(shí)現(xiàn)上傳、壓縮、旋轉(zhuǎn)圖片功能
- Vue2.0 實(shí)現(xiàn)移動(dòng)端圖片上傳功能
- vue實(shí)現(xiàn)移動(dòng)端圖片裁剪上傳功能
- vue+vux實(shí)現(xiàn)移動(dòng)端文件上傳樣式
- Vue.js 2.0 移動(dòng)端拍照壓縮圖片預(yù)覽及上傳實(shí)例
- vue2實(shí)現(xiàn)移動(dòng)端上傳、預(yù)覽、壓縮圖片解決拍照旋轉(zhuǎn)問題
- Vue.js 2.0 移動(dòng)端拍照壓縮圖片上傳預(yù)覽功能
- vue實(shí)現(xiàn)移動(dòng)端input上傳視頻、音頻
相關(guān)文章
vue+tsc+noEmit導(dǎo)致打包報(bào)TS類型錯(cuò)誤問題及解決方法
當(dāng)我們新建vue3項(xiàng)目,package.json文件會(huì)自動(dòng)給我添加一些配置選項(xiàng),這寫選項(xiàng)基本沒有問題,但是在實(shí)際操作過程中,當(dāng)項(xiàng)目越來越復(fù)雜就會(huì)出現(xiàn)問題,本文給大家分享vue+tsc+noEmit導(dǎo)致打包報(bào)TS類型錯(cuò)誤問題及解決方法,感興趣的朋友一起看看吧2023-10-10解決Vue在封裝了Axios后手動(dòng)刷新頁面攔截器無效的問題
這篇文章主要介紹了解決VUE在封裝了Axios后手動(dòng)刷新頁面攔截器無效的問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11