1. 程式人生 > >使用vue多組圖片上傳

使用vue多組圖片上傳

本文使用原生的上傳有侷限性,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
        }
    }
}