使用vue多組圖片上傳
阿新 • • 發佈:2019-02-15
本文使用原生的上傳有侷限性,ios手機能同時選擇多張圖片,而安卓手機只能一張一張上傳,不能一次性選擇多張。所以建議呼叫 微信JS SDK。
程式碼如下:
<template> <div class="start-work viewport"> <div class="hd tc"> <div class="inner-wrap"> <div class="task-name">張三 - 幸福花園 19#404</div> <div class="time">2018-08-08</div> </div> </div> <!-- <div class="bd"> <dl class="start-pic"> <dt>開工照片</dt> <dd class="cf"> <div class="add-pic fl"> <i class="icon iconfont icon-jia"></i> </div> <div class="pic-item fl" v-for="(item,index) in 1" :key="index"> <img src="" alt=""> </div> </dd> </dl> </div> --> <!--收貨照片--> <dl class="receipt-pic pub-section"> <dt> <h3>收貨照片 <span class="c-999">(限9張)</span></h3> </dt> <dd class="cf"> <div class="add-pic fl" v-if="formDatas.receive_imgs.length < 9"> <input name="files" type="file" id="file" accept="image/*" multiple="multiple" @change="imgChange($event)" /> <i class="icon iconfont icon-jia"></i> <label for="file"></label> </div> <div class="pic-item fl" v-for="(item,index) in formDatas.receive_imgs" :key="index"> <!--<span v-if="item === true"> <mt-spinner type="snake" class="loading-more"></mt-spinner> </span>--> <img :src="item" alt=""> <span class="delete-pic" @click="deletePic(index)"> <i class="icon iconfont icon-icon-test"></i> </span> </div> </dd> </dl> <div class="ft"> <!--允許開工--> <div class="allow-start"> <div class="label"> 週末是否允許開工 </div> <div class="v"> <div class="j-radio l-txt" v-for="(type,index) in allowStartData" :key="index"> <label> <span class="txt">{{type.label}}</span> <input type="radio" name="houseType" v-model="allowStartType" :value="type.value" :checked="type.value === allowStartType ? true : false"> <span class="icon"></span> </label> </div> </div> </div> <div class="start-btn"> <span class="btn" @click="strtWorkFn"> 開工 </span> </div> </div> </div> </template> <script> import Vue from 'vue' import { Toast, Spinner} from 'mint-ui' import Oss from '@/assets/js/util/aliyunOss.js' Vue.component(Spinner.name, Spinner) export default { data () { return { allowStartType: '1', allowStartData: [ { label: '允許', value: '1' }, { label: '不允許', value: '2' } ], sign: { accessid: '', dir: '', expire: '', host: '', policy: '', signature: '' }, formDatas: { purchase_id: '', receive_imgs: [], products: [], remark: '' } } }, created () { }, mounted () { let _this = this Oss.getSign(200).then((res) => { // 獲取簽名 _this.sign = res.data console.log(_this.sign) // console.log('this.sign', _this.sign) }).catch((err) => { console.log('err', err) }) }, methods: { apiUploadOss (ossData) { let _this = this // this.$http({ // url: this.sign.host, // method: 'post', // data: ossData, // onUploadProgress (progressEvent) { // 原生獲取上傳進度的事件 // if (progressEvent.lengthComputable) { // console.log(progressEvent) // console.log('百分比', (progressEvent.loaded / progressEvent.total) * 100) // } // } // }).then(function (res) { // _this.formDatas.receive_imgs.push(_this.sign.host + '/' + ossData.get('key')) // console.log('路勁:', _this.sign.host + '/' + ossData.get('key')) // }).catch(function (err) { // console.log(err) // }) this.$http.post(_this.sign.host, ossData).then(function (res) { _this.formDatas.receive_imgs.push(_this.sign.host + '/' + ossData.get('key')) console.log('路勁:', _this.sign.host + '/' + ossData.get('key')) }).catch(function (err) { console.log(err) }) }, imgChange (e) { console.log('初次點選') let _this = this let files = e.target.files console.log(files) // 驗證數量 if ((this.formDatas.receive_imgs.length + files.length) > 9) { Toast('最多隻能上傳9張圖片') return } // 驗證格式 for (let i = 0; i < files.length; i++) { if (!/.(jpg|jpeg|png)$/.test(e.target.files[i].name)) { Toast('檔案必須是jpeg,jpg,png中的一種') return } // else { // this.formDatas.receive_imgs.push(true) // 表示載入中 // } } for (let i = 0; i < files.length; i++) { let file = e.target.files[i] // 獲取圖片資源 let filename = file.name // 獲取最終需要上傳的引數 let ossData = Oss.getFormDataParams(_this.sign, filename) // 新增檔案 let reader = new FileReader() let oImg = new Image() let uploadInfo = {} console.log('ossData', ossData) reader.onload = function (e) { uploadInfo.imageBase64 = e.target.result let src = uploadInfo.imageBase64 // $('#thumbnail-list').append("1->:"+getSizeByBase64(uploadInfo.imageBase64)) oImg.onload = function () { src = Oss.compress(oImg, 0.8) // 新增最後需要的引數 ossData.append('file', Oss.convertBase64UrlToBlob(src), filename) // 開始上傳 _this.apiUploadOss(ossData) } oImg.src = src return true } reader.readAsDataURL(file) } }, deletePic (index) { this.formDatas.receive_imgs.splice(index, 1) console.log(this.formDatas.receive_imgs) }, strtWorkFn () { let _this = this let work_on_weekend = '1' if (this.allowStartType === '1') { work_on_weekend = true } else if (this.allowStartType === '2') { work_on_weekend = false } this.$http.post('/api/steward/projects/' + _this.$route.query.contract_id + '/start', {contract_id: _this.$route.query.contract_id, work_on_weekend: work_on_weekend, work_images: _this.formDatas.receive_imgs}).then((res) => { let datas = res.data if (datas.code === 0) { console.log(datas) } else { Toast({ message: datas.msg, duration: 2000 }) } }) } } } </script> <style lang="less" scoped > @import (reference) "~less/base.less"; .start-work{ font-size:.28rem; .hd{ background:#fff; padding-left:.38rem; .inner-wrap{ padding:.5rem .38rem .2rem 0; } } // 收貨圖片 .receipt-pic{ padding: 0.5rem 0 0 0.4rem; border-bottom:.1rem solid @c-f5; h3{ border-bottom:1px solid @c-border; padding-bottom: 0.2rem; span{ font-size:.14rem; color: @c-999; } } dd { padding:.44rem 0 .6rem; .add-pic { position:relative; border: 2px dashed @c-999; width: 1.34rem; height: 1.34rem; float: left; text-align: center; margin-right:.2rem; box-sizing: border-box; .icon-jia{ font-size:.54rem; color:@c-999; line-height:1.32rem; } label{ position:absolute; top:0; left:0; z-index:1; width:100%; height:100%; } input{ width: 0.1px; height: 0.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1; } } .pic-item { position:relative; width: 1.34rem; height: 1.34rem; margin:0 .2rem .2rem 0; img { width: 100%; height: 100%; } .delete-pic{ position:absolute; top:-.15rem; right:-.15rem; background:@c-high; border-radius: 50%; z-index:1; width:.38rem; height:.38rem; text-align: center; line-height:.38rem; .tc; } .icon-icon-test{ font-size:.12rem; color:#fff; display:inline-block; transform: scale(0.8,0.8); } } } } .ft{ background:#fff; padding:.4rem .38rem .88rem; .allow-start{ .display-flex; line-height:.4rem; .v{ flex: 1; .tr; } .j-radio:last-child{ margin-left:.4rem; } } .start-btn{ margin-top:2.5rem; } .btn{ .btn-main(100%,1rem,.28rem, 5px); background:@c-main; color:#fff; } } } </style>
aliyunOss.js檔案,需要獲取後臺簽名。
import Axios from 'axios' export default { getSign (module) { return new Promise(function (resolve, reject) { // Axios.post('http://test.enlife.jjcclife.com/api/user/upload/sign?token=e1411e831c3a054610427bca05385583', { Axios.post('/api/images/sign', { module: module }).then(function (res) { let datas = res.data if (datas.code === SUCCESS_CODE) { resolve(datas) } else { reject(datas) } }).catch(function (err) { reject(err) }) }) }, randomString (len) { len = len || 32 let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' let maxPos = chars.length let pwd = '' for (let i = 0; i < len; i++) { pwd += chars.charAt(Math.floor(Math.random() * maxPos)) } return pwd }, convertBase64UrlToBlob (urlData) { let bytes = window.atob(urlData.split(',')[1]) // 去掉url的頭,並轉換為byte // 處理異常,將ascii碼小於0的轉換為大於0 let ab = new ArrayBuffer(bytes.length) let ia = new Uint8Array(ab) let mimeString = urlData.split(',')[0].split(':')[1].split(';')[0] for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i) } return new Blob([ab], {type: mimeString}) }, getFormDataParams (sign, fileName) { // 構造formdata表單資料 let ossData = new FormData() // let key = sign.dir + '/' + this.randomString() + '.' + fileName.split('.')[1] let key = '' if (/\/$/.test(sign.dir)) { key = sign.dir + this.randomString() + '.' + fileName.split('.')[1] } else { key = sign.dir + '/' + this.randomString() + '.' + fileName.split('.')[1] } console.log('key', key) ossData.append('OSSAccessKeyId', sign.accessid) ossData.append('policy', sign.policy) ossData.append('Signature', sign.signature) ossData.append('success_action_status', 200) ossData.append('key', key) return ossData }, compress (img, quality) { let canvas = document.createElement('canvas') let ctx = canvas.getContext('2d') // 瓦片canvas let tCanvas = document.createElement('canvas') let tctx = tCanvas.getContext('2d') // let maxSize = 200 * 1024 // 200KB quality = quality || 0.8 // 圖片壓縮質量 let initSize = img.src.length let width = img.width let height = img.height // 如果圖片大於四百萬畫素,計算壓縮比並將大小壓至400萬以下 let ratio if ((ratio = width * height / 4000000) > 1) { ratio = Math.sqrt(ratio) width /= ratio height /= ratio } else { ratio = 1 } canvas.width = width canvas.height = height // 鋪底色 ctx.fillStyle = '#fff' ctx.fillRect(0, 0, canvas.width, canvas.height) // 如果圖片畫素大於100萬則使用瓦片繪製 let count if ((count = width * height / 1000000) > 1) { count = ~~(Math.sqrt(count) + 1) // 計算要分成多少塊瓦片 // 計算每塊瓦片的寬和高 let nw = ~~(width / count) let nh = ~~(height / count) tCanvas.width = nw tCanvas.height = nh for (let i = 0; i < count; i++) { for (let j = 0; j < count; j++) { tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh) ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh) } } } else { ctx.drawImage(img, 0, 0, width, height) } // 進行最小壓縮 let ndata = canvas.toDataURL('image/jpeg', quality) // console.log('壓縮前:' + initSize) // console.log('壓縮後:' + ndata.length) // console.log('壓縮率:' + ~~(100 * (initSize - ndata.length) / initSize) + '%') tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0 // document.write(ndata); if (initSize > ndata.length) { // 壓縮後比原來還大,就使用原圖 return ndata } else { // return img.src return ndata } } }