首頁 > 軟體

封裝一個Vue檔案上傳元件案例詳情

2022-08-15 10:02:10

前言

在面向特定使用者的專案中,引 其他ui元件庫導致打包體積過大,首屏載入緩慢,還需要根據UI設計師設計的樣式,重寫大量的樣式覆蓋引入的元件庫的樣式。因此嘗試自己封裝一個自己的元件,程式碼參考了好多前輩的文章

1. 子元件

<template>
    <div class="digital_upload">
        <input
            style="display: none"
            @change="addFile"
            :multiple="multiple"
            type="file"
            :name="name"
            :id="id"
            :accept="accept"
        />
        <label :for="id">
            <slot></slot>
        </label>
    </div>
</template>

<script>
export default {
    name: 'my-upload',
    props: {
        name: String,
        action: {
            type: String,
            required: true
        },
        fileList: {
            type: Array,
            default () {
                return []
            }
        },
        accept: {
            type: String,
            require: true
        },
        id: {
            type: String,
            default: 'my-upload'
        },
        data: Object,
        multiple: Boolean,
        limit: Number,
        onChange: Function,
        onBefore: Function,
        onProgress: Function,
        onSuccess: Function,
        onFailed: Function,
        onFinished: Function
    },
    methods: {
        // input的 chang事件處理方法
        addFile ({ target: { files } }) { // input標籤觸發onchange事件時,將檔案加入待上傳列表
            for (let i = 0, l = files.length; i < l; i++) {
                files[i].url = URL.createObjectURL(files[i])// 建立blob地址,不然圖片怎麼展示?
                files[i].status = 'ready'// 開始想給檔案一個欄位表示上傳進行的步驟的,後面好像也沒去用......
            }
            let fileList = [...this.fileList]
            if (this.multiple) { // 多選時,檔案全部壓如列表末尾
                fileList = [...fileList, ...files]
                const l = fileList.length
                let limit = this.limit
                if (limit && typeof limit === 'number' && Math.ceil(limit) > 0 && l > limit) { // 有數目限制時,取後面limit個檔案
                    limit = Math.ceil(limit)
                    // limit = limit > 10 ? 10 : limit;
                    fileList = fileList.slice(l - limit)
                }
            } else { // 單選時,只取最後一個檔案。注意這裡沒寫成fileList = files;是因為files本身就有多個元素(比如選擇檔案時一下子框了一堆)時,也只要一個
                fileList = [files[0]]
            }
            this.onChange(fileList)// 呼叫父元件方法,將列表快取到上一級data中的fileList屬性
        },
        // 移除某一個檔案
        remove (index) {
            const fileList = [...this.fileList]
            if (fileList.length) {
                fileList.splice(index, 1)
                this.onChange(fileList)
            }
        },
        // 檢測是否可以提交
        checkIfCanUpload () {
            console.log(this.fileList.length)
            return this.fileList.length ? ((this.onBefore && this.onBefore()) || !this.onBefore) : false
        },
        // 根據情況使用不同的提交的方法
        submit () {
            console.log('開始提交')
            if (this.checkIfCanUpload()) {
                // console.log('開始提交2')
                if (this.onProgress && typeof XMLHttpRequest !== 'undefined') {
                    this.xhrSubmit()
                } else {
                    this.fetchSubmit()
                }
            }
        },
        // fethc 提交
        fetchSubmit () {
            const keys = Object.keys(this.data); const values = Object.values(this.data); const action = this.action
            const promises = this.fileList.map(each => {
                each.status = 'uploading'
                const data = new FormData()
                data.append(this.name || 'file', each)
                keys.forEach((one, index) => data.append(one, values[index]))
                return fetch(action, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    body: data
                }).then(res => res.text()).then(res => JSON.parse(res))// 這裡res.text()是根據返回值型別使用的,應該視情況而定
            })
            Promise.all(promises).then(resArray => { // 多執行緒同時開始,如果並行數有限制,可以使用同步的方式一個一個傳,這裡不再贅述。
                let success = 0; let failed = 0
                resArray.forEach((res, index) => {
                    if (res.code === 1) {
                        success++ // 統計上傳成功的個數,由索引可以知道哪些成功了
                        this.onSuccess(index, res)
                    } else if (res.code === 520) { // 約定失敗的返回值是520
                        failed++ // 統計上傳失敗的個數,由索引可以知道哪些失敗了
                        this.onFailed(index, res)
                    }
                })
                return { success, failed } // 上傳結束,將結果傳遞到下文
            }).then(this.onFinished) // 把上傳總結果返回
        },
        // xhr 提交
        // xhrSubmit () {
        //     const _this = this
        //     const options = this.fileList.map((rawFile, index) => ({
        //         file: rawFile,
        //         data: _this.data,
        //         filename: _this.name || 'file',
        //         action: _this.action,
        //         headers: {
        //             Authorization: window.sessionStorage.getItem('token')
        //         },
        //         onProgress (e) {
        //             _this.onProgress(index, e)// 閉包,將index存住
        //         },
        //         onSuccess (res) {
        //             _this.onSuccess(index, res)
        //         },
        //         onError (err) {
        //             _this.onFailed(index, err)
        //         },
        //         onFinished (res) { // 

IT145.com E-mail:sddin#qq.com