<template>
    <div class="upload">
        <el-upload
            ref="upload"
            action="#"
            :http-request="uploadFile"
            :before-upload="beforeUpload"
            :on-exceed="handleExceed"
            :show-file-list="false"
            :multiple="multiple"
            :limit="limit"
            :accept="accept"
            :disabled="uploading"
        >
        <slot>
            <template v-if="btnType !== 'icon-plus'">
                <el-button
                    type="primary"
                    :loading="true"
                    v-if="!multiple && uploading"
                >努力上传中：{{ mathProgress }}%</el-button>
                <el-button size="small" type="primary" v-else>{{ btnText }}</el-button>
            </template>
            <template v-else>
                <i
                    class="el-icon-plus"
                    :style="btnStyle"
                    v-if="!multiple && uploading"
                    v-loading="true"
                    :element-loading-text="`努力上传中: ${mathProgress}%`"
                    element-loading-spinner="el-icon-loading"
                    element-loading-background="rgba(0, 0, 0, 0.8)"
                ></i>
                <i class="el-icon-plus" :style="btnStyle" v-else></i>
            </template>
        </slot>    
        </el-upload>
        <!-- <el-button size="small" type="primary" @click="handleDeleteMultiFile(['/10086/TestUpload/20190702/笨办法学Python第四版_1562031010235.pdf'])">删除文件</el-button>
        <el-button size="small" type="primary" @click="handleDownload('/10086/TestUpload/20190702/笨办法学Python第四版_1562033860757.pdf')">下载文件</el-button>-->
        <!-- vueCropper 剪裁图片实现-->
        <el-dialog ref="dialog" title="图片剪裁" :visible.sync="dialogVisible" append-to-body>
            <div class="cropper-content">
                <div
                    class="cropper"
                    style="text-align:center"
                    :style="{width:(option.autoCropWidth+100) + 'px',height:(option.autoCropHeight+100) + 'px'}"
                >
                    <vueCropper
                        ref="cropper"
                        :img="option.img"
                        :outputSize="option.size"
                        :outputType="option.outputType"
                        :info="true"
                        :full="option.full"
                        :autoCropWidth="option.autoCropWidth"
                        :autoCropHeight="option.autoCropHeight"
                        :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="dialogVisible = false">取 消</el-button>
                <el-button type="primary" @click="finish">确认</el-button>
            </div>
        </el-dialog>
    </div>
</template>

<script>
import client from '@/assets/oss'
import store from '@/store/index.js'
import { formatDate } from '@/assets/public'

import { Loading } from 'element-ui'
export default {
    name: 'ObjectStorageService',
    props: {
        // 根据业务需求传递，已规定默认值
        ossCof: {
            type: Object,
            default: () => ({
                domain: 'https://file.vschool100.com',
                endpoint: 'oss-cn-shenzhen.aliyuncs.com',
                bucket: 'bussiness-file-bucket',
                region: 'oss-cn-shenzhen.aliyuncs.com'
            })
        },
        // 上传文件类型，默认 default 图片images、文件files、
        uploadType: {
            type: String,
            default: 'default'
        },
        // 上传按钮的样式类型 默认default 按钮， 或者icon-plus 为带加号的按钮
        btnType: {
            type: String,
            default: ''
        },
        // 按钮显示的文案 默认点击上传
        btnText: {
            type: String,
            default: '点击上传'
        },
        // icon-plus 按钮的样式
        btnStyle: {
            type: Object,
            default: () => ({
                width: '178px',
                height: '178px',
                fontSize: '30px'
            })
        },
        // 是否可以多选图片
        multiple: {
            type: Boolean,
            default: false
        },
        // 允许上传的数量  默认9999 不传不限制  (根据需求限制的总数量 - 已经上传的数量 = 允许上传的数量 )
        // eg 业务需求上传 9个  现已上传 7个 则现在允许上传的数量为 2 个，如果继续上传两个 则允许上传的数量为 0个
        limit: {
            type: Number,
            default: 9999
        },
        // 已上传的数量
        uploadedNum: {
            type: Number,
            default: 0
        },
        // 接受文件类型 有逗号分隔
        accept: {
            type: String,
            default: '.jpg,.jpeg,.png,.gif,.bmp,.zip,.rar,.xlsx,.xls,.xsl,.html,.pdf,.txt,.json,.xml,.docx,.pptx,.doc,.ppt'
        },
        // 文件存储路径 必传
        filePath: {
            type: String,
            default: `${store.state.shcoolCode}/TestUpload`
        },
        cropperOption: {// 裁剪的宽高
            type: Object,
            default: () => ({
                width: 300,
                height: 300
            })
        },
        needCropper: {// 是否需要裁剪
            type: Boolean,
            default: false
        }
    },
    data () {
        return {
            ossParams: {
                accessKeyId: '',
                accessKeySecret: '',
                stsToken: '',
                endpoint: this.ossCof.endpoint,
                bucket: this.ossCof.bucket,
                region: this.ossCof.region
            },
            mathProgress: 0,
            uploading: false,

            dialogVisible: false, // 显示裁剪框
            // 裁剪组件的基础配置option
            option: {
                img: '', // 裁剪图片的地址
                info: true, // 裁剪框的大小信息
                outputSize: 0.8, // 裁剪生成图片的质量
                outputType: 'jpeg', // 裁剪生成图片的格式
                canScale: false, // 图片是否允许滚轮缩放
                autoCrop: true, // 是否默认生成截图框
                autoCropWidth: this.cropperOption.width, // 默认生成截图框宽度
                autoCropHeight: this.cropperOption.height, // 默认生成截图框高度
                fixedBox: true, // 固定截图框大小 不允许改变
                fixed: true, // 是否开启截图框宽高固定比例
                fixedNumber: [this.cropperOption.width, this.cropperOption.height], // 截图框的宽高比例
                full: false, // 是否输出原图比例的截图
                canMoveBox: false, // 截图框能否拖动
                original: false, // 上传图片按照原始比例渲染
                centerBox: false, // 截图框是否被限制在图片里面
                infoTrue: true // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
            },
            // needCropper:true,
            isCropper: false// 是否已经裁剪

        }
    },
    methods: {
        // 上传图片之前先获取sts秘钥
        beforeUpload (files) {
            return new Promise((resolve, reject) => {
                // getAliOssToken 获取OSS秘钥的接口地址
                let point = files.name.lastIndexOf('.')
                let suffix = files.name.substr(point)// 文件后缀名
                console.log(suffix)
                if (!this.accept.includes(suffix)) {
                    this.$message.warning(`上传格式必须为${this.accept}`)
                    // eslint-disable-next-line prefer-promise-reject-errors
                    reject(false)
                    return
                }

                this.$api.aliOssApi
                    .getToken()
                    .then(res => {
                        let data = res.data
                        if (data.Status) {
                            this.ossParams.accessKeyId = data.Data.AccessKeyId
                            this.ossParams.accessKeySecret = data.Data.AccessKeySecret
                            this.ossParams.stsToken = data.Data.SecurityToken
                            resolve(true)
                        } else {
                            this.$message.warning(data.ResponseError.LongMessage)
                            // eslint-disable-next-line prefer-promise-reject-errors
                            reject(false)
                        }
                    })
                    .catch(err => {
                        console.log(err)
                        // eslint-disable-next-line prefer-promise-reject-errors
                        reject(false)
                    })
            })
        },
        // 上传文件
        uploadFile (fileParams) {
            this.uploading = true
            let loadingInstance
            let file = fileParams.file
            let point = file.name.lastIndexOf('.')
            let suffix = file.name.substr(point)// 文件后缀名
            let fileName = file.name.substr(0, point) // 去掉后缀名的文件名
            let fileAliasName = `pc_file_${this.randomNum(10)}`
            // eslint-disable-next-line no-useless-escape
            let date = formatDate(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()).replace(/\-/g, '')
            let uid = file.uid
            let size = file.size // 单位B
            let storeAs = `${this.filePath}/${date}/${fileAliasName}_${uid}${suffix}`
            // 如果是单文件上传 则在相应的按钮处增加loading和上传进度，如果不是则全局增加loading没有进度
            let options = !this.multiple
                ? {
                    progress: p => {
                        this.mathProgress = Math.floor(p * 100)
                    }
                }
                : {}
            if (this.multiple) {
                loadingInstance = Loading.service({
                    lock: true,
                    text: '努力上传中',
                    spinner: 'el-icon-loading',
                    background: 'rgba(0, 0, 0, 0.7)'
                })
            }
            let ossClient = client(this.ossParams)
            ossClient
                .multipartUpload(storeAs, file, options)
                .then(result => {
                    // 下面是如果对返回结果再进行处理，根据项目需要
                    if (result.res && result.res.status === 200) {
                        let obj = {
                            objectKey: result.name,
                            filename: fileName,
                            filesize: size,
                            filesuffix: suffix,
                            fileUrl: `/${result.name}`
                        }

                        if (this.needCropper && !this.isCropper) { // 需要裁剪且未裁剪的显示裁剪框
                            // this.$refs.dialog.style.width = this.options.autoCropWidth + 140 + 'px';
                            this.$nextTick(() => {
                                this.option.img = this.ossCof.domain + obj.fileUrl// 需要上传成功后拿到路径才能裁剪
                                this.dialogVisible = true
                            })
                        } else { // 不需要裁剪或已裁剪的返回数据
                            this.isCropper = false
                            this.$emit('uploadSucc', obj)
                        }
                    }
                    this.uploading = false
                    if (this.multiple) {
                        loadingInstance.close()
                    }
                })
                // eslint-disable-next-line handle-callback-err
                .catch(err => {
                    this.uploading = false
                    if (this.multiple) {
                        loadingInstance.close()
                    }
                })
        },
        /**
    * 获取指定位数的随机数
    * @param {Number} n = [随机数的位数]
    **/
        randomNum (n) {
            var rnd = ''
            for (var i = 0; i < n; i++) {
                rnd += Math.floor(Math.random() * 10)
            }
            return rnd
        },
        /*
     *  删除单个文件
     *  @params  objectKey  String 文件的object-key  必传
     *  @params  name  String 文件的名字 可传空
     * */
        handleDeleteFile (objectKey, name = '') {
            if (objectKey === undefined) {
                this.$message.warning('objectKey不能为空！')
                return
            }
            try {
                if (Object.prototype.toString.call(objectKey) !== '[object String]') {
                    this.$message.warning('objectKey 必须为字符串！')
                    return
                }
            } catch (e) {
                // TODO handle the exception
            }
            this.$confirm(`此操作将永久删除${name}, 是否继续?`, '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            })
                .then(() => {
                    this.$api.aliOssApi
                        .getToken()
                        .then(res => {
                            let data = res.data
                            if (data.Status) {
                                this.ossParams.accessKeyId = data.Data.AccessKeyId
                                this.ossParams.accessKeySecret = data.Data.AccessKeySecret
                                this.ossParams.stsToken = data.Data.SecurityToken
                                let ossClient = client(this.ossParams)
                                ossClient
                                    .delete(objectKey)
                                    .then(result => {
                                        console.log(result)
                                        if (result.res.status === 204) {
                                            this.$message.success('删除成功')
                                            // 导出删除文件成功方法，父组件做其他逻辑处理，如在视图上删除文件
                                            this.$emit('deleteFileSucc')
                                        }
                                    })
                                    .catch(err => {
                                        console.log(err)
                                    })
                            } else {
                                this.$message.warning(data.ResponseError.LongMessage)
                            }
                        })
                })
                .catch(() => { })
        },
        /*
     * 删除多个文件
     * @params  objectKeys  Array 所有文件的object-key组成的数组 必传
     *
     * */
        handleDeleteMultiFile (objectKeys) {
            if (objectKeys === undefined) {
                this.$message.warning('objectKey不能为空！')
                return
            }
            try {
                if (Object.prototype.toString.call(objectKeys) !== '[object Array]') {
                    this.$message.warning('objectKeys 必须为数组！')
                    return
                }
            } catch (e) {
                // TODO handle the exception
            }

            this.$confirm(`此操作将永久删除${objectKeys.length}文件, 是否继续?`, '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            })
                .then(() => {
                    this.$api.aliOssApi
                        .getToken()
                        .then(res => {
                            let data = res.data
                            if (data.Status) {
                                this.ossParams.accessKeyId = data.Data.AccessKeyId
                                this.ossParams.accessKeySecret = data.Data.AccessKeySecret
                                this.ossParams.stsToken = data.Data.SecurityToken
                                let ossClient = client(this.ossParams)
                                ossClient
                                    .deleteMulti(objectKeys)
                                    .then(result => {
                                        if (result.res.status === 200) {
                                            // 导出删除多文件成功方法，父组件做其他逻辑处理，如在视图上删除文件
                                            this.$emit('deleteMultiFileSucc')
                                            this.$message.success('删除成功')
                                        }
                                    })
                                    .catch(err => {
                                        console.log(err)
                                    })
                            } else {
                                this.$message.warning(data.ResponseError.LongMessage)
                            }
                        })
                })
                .catch(() => { })
        },
        /*
     * 下载文件
     * @params  objectKey String 文件的object-key 必传
     * @params name String 文件的名字 必传
     **/
        handleDownload (objectKey, name = '文件下载', type = 'download') {
            if (objectKey === undefined) {
                this.$message.warning('objectKey不能为空！')
                return
            }
            try {
                if (Object.prototype.toString.call(objectKey) !== '[object String]') {
                    this.$message.warning('objectKey 必须为字符串！')
                    return
                }
            } catch (e) {
                // TODO handle the exception
            }
            let options = (type === 'download'
                ? {
                    expires: 3600,
                    response: {
                        'Content-Disposition': 'attachment; filename="' + name + '"'
                    }
                }
                : {})
            this.$api.aliOssApi
                .getToken()
                .then(res => {
                    let data = res.data
                    if (data.Status) {
                        this.ossParams.accessKeyId = data.Data.AccessKeyId
                        this.ossParams.accessKeySecret = data.Data.AccessKeySecret
                        this.ossParams.stsToken = data.Data.SecurityToken
                        let ossClient = client(this.ossParams)
                        let downloadUrl = ossClient.signatureUrl(objectKey, options)
                        window.location = downloadUrl
                    } else {
                        this.$message.warning(data.ResponseError.LongMessage)
                    }
                })
        },
        // 判断文件数量限制的钩子
        handleExceed (files, fileList) {
            this.$message.warning(`您还可以上传 ${this.limit} 个文件,但是您本次选择了 ${files.length} 个文件, 请重新选择文件`)
        },
        // 裁剪完成时将bolb转成file上传
        finish () {
            this.$refs.cropper.getCropBlob((data) => {
                // console.log(data);
                this.isCropper = true
                this.dialogVisible = false
                var self = this
                this.blobToDataURL(data, function (e) {
                    var file = self.dataURLtoFile(e, 'aa')
                    // console.log('base64:',e);
                    console.log('file:', file)
                    self.uploadFile({ 'file': file })
                })
            })
        },
        // 将截图后的bolb对象转成base64
        blobToDataURL (blob, callback) {
            let a = new FileReader()
            a.onload = function (e) { callback(e.target.result) }
            a.readAsDataURL(blob)
        },
        // 将base64转成file
        dataURLtoFile (dataurl, filename) { // 将base64转换为文件
            let arr = dataurl.split(',')
            let mime = arr[0].match(/:(.*?);/)[1]
            let bstr = atob(arr[1])
            let n = bstr.length
            let u8arr = new Uint8Array(n)
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n)
            }
            return new File([u8arr], filename, { type: mime })
        }
    }
}
</script>

<style lang="less" scoped>
.upload {
    display: inline-block;
    /deep/.el-loading-spinner {
        font-size: 22px;
    }
    .el-icon-plus {
        overflow: hidden;
        position: relative;
        display: flex;
        justify-content: center;
        align-items: center;
        border: 1px dashed #d9d9d9;
        border-radius: 6px;
        cursor: pointer;
    }
}
/deep/ .el-dialog {
    width: 930px;
}
</style>
