<template>
	<div class="sc-upload-multiple">
		<el-upload ref="uploader" list-type="picture-card"
			v-model:file-list="defaultFileList"
			action=""
			accept="image/gif, image/jpeg, image/png, video/mp4 , video/avi"
			:limit="limit"
			:multiple="multiple"
			:disabled="disabled"
			show-file-list
			:http-request="request"
			:before-upload="before"
			:on-success="success"
			:on-error="error"
			:on-exceed="handleExceed">
			<slot>
				<el-icon><el-icon-plus /></el-icon>
			</slot>
			<template #tip>
				<div v-if="tip" class="el-upload__tip">{{ tip }}</div>
			</template>
			<template #file="{ file }">
				<div class="sc-upload-list-item">
					<el-image v-if="isImage(file.mineType)" class="el-upload-list__item-thumbnail" :src="'/api/folder/' + file.path" :preview-src-list="preview" fit="cover" preview-teleported :z-index="9999">
						<template #placeholder>
							<div class="sc-upload-multiple-image-slot">Loading...</div>
						</template>
					</el-image>
					<sc-video v-if="isVideo(file.mineType)" :src="'/api/folder/' + file.path" showMask @play="videoPlay(file)"></sc-video>

					<div v-if="!disabled && file.status == 'success'" class="sc-upload__item-actions">
						<el-button class="download" :loading="loading" type="primary" @click="handleDownload(file)">
                            <sc-iconify icon="ant-design:download-outlined"></sc-iconify>
                        </el-button>
                        <el-button class="del" :loading="loading" type="danger" @click="handleRemove(file)">
                            <sc-iconify icon="ant-design:delete-outlined"></sc-iconify>
                        </el-button>
					</div>
					<div v-if="file.status == 'ready' || file.status == 'uploading'" class="sc-upload__item-progress">
						<el-progress :percentage="file.percentage" text-inside :stroke-width="16" />
					</div>
				</div>
			</template>
		</el-upload>
		<span style="display:none!important"><el-input v-model="value"></el-input></span>
	</div>

    <file-viewer v-if="showViewer" ref="fileViewer" @closed="showViewer = false"></file-viewer>
</template>

<script>
import { fileTypes } from "./main";

export default {
    props: {
        modelValue: { type: Array, default: () => [] },
        tip: { type: String, default: "" },
        maxSize: { type: Number, default: 50 },
        limit: { type: Number, default: 0 },
        multiple: { type: Boolean, default: true },
        disabled: { type: Boolean, default: false },
        onSuccess: { type: Function, default: () => { return true } }
    },

    data() {
        return {
            value: "",
            defaultFileList: [],

            loading: false,
            showViewer: false
        }
    },

    watch: {
        modelValue(val) {
            if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
                this.defaultFileList = val;
                this.value = val;
            }
        },

        defaultFileList: {
            deep: true,
            handler(val) {
                this.$emit("update:modelValue", this.formatArr(val));
                this.value = val.map(v => v.path).join(",");
            }
        }
    },

    computed: {
        preview() {
            return this.defaultFileList.map(v => "/api/folder/" + v.path);
        }
    },

    mounted() {
        this.defaultFileList = this.modelValue;
        this.value = this.modelValue;
    },

    methods: {
        isImage(type) {
            return fileTypes[type] == "image"
        },

        isVideo(type) {
            return fileTypes[type] == "video"
        },

        // 格式化数组值
        formatArr(arr) {
            return arr.map(item => ({ id: item.id, name: item.name, mineType: item.mineType, path: item.path }));
        },

        before(file) {
            if (!this.isImage(file.type) && !this.isVideo(file.type)) {
                this.$message.warning({ title: "上传文件警告", message: "选择的文件非图像类/视频类文件" });
                return false;
            }

            const maxSize = file.size / 1024 / 1024 < this.maxSize;
            if (!maxSize) {
                this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
                return false;
            }
        },

        success(res, file) {
            let os = this.onSuccess(res, file);
            if (os != undefined && os == false) return false;
            
            file.name = res.fileName;
            file.path = res.path;
            file.mineType = res.mineType;
        },

        error(message) {
            this.$notify.error({ title: "上传文件未成功", message });
        },

        beforeRemove({ id, name }) {
            return this.$confirm(`是否移除 ${name}? 此操作不可逆!`, "提示", {
                type: "warning",
                confirmButtonText: "移除"
            }).then(() => {
                if (id) {
                    this.$API.common.folder.rm(id).then(res => {
                        if (res.code == 200) return true;
                        else return false;
                    }).catch(() => {
                        return false;
                    });
                } return true;
            }).catch(() => {
                return false;
            });
        },
        
        handleRemove(file) {
            this.$refs.uploader.handleRemove(file);
        },

        handleExceed() {
            this.$message.warning(`当前设置最多上传 ${this.limit} 个文件，请移除后上传!`);
        },

        request(param) {
            const data = new FormData();
            data.append(param.filename, param.file);
            this.$API.common.folder.up(data, {
                onUploadProgress: e => {
                    const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10)
                    param.onProgress({ percent });
                }
            }).then(res => {
                if (res.code == 200) param.onSuccess({ path: res.expands.file, fileName: param.file.name, mineType: param.file.type })
                else param.onError(res.message || "未知错误");
            }).catch(err => param.onError(err));
        },

        videoPlay(file) {
            this.showViewer = true;
            nextTick(() => this.$refs.fileViewer.init(file));
        },

        handleDownload(file) {
            this.loading = true;
            this.$API.common.folder.download(file.path).then(res => {
                this.loading = false;
                const a = document.createElement("a");
                const blob = new Blob([res.data], { type: file.mineType });
                a.download = this.file.name;
                a.href = URL.createObjectURL(blob);
                a.click();
            }).catch(() => this.loading = false);
        }
    }
}
</script>

<style scoped>
.el-form-item.is-error .sc-upload-multiple:deep(.el-upload--picture-card) {
  border-color: var(--el-color-danger);
}
:deep(.el-upload-list__item) {
  transition: none;
  border-radius: 0;
}
.sc-upload-multiple:deep(.el-upload-list__item.el-list-leave-active) {
  position: static !important;
}
.sc-upload-multiple:deep(.el-upload--picture-card) {
  border-radius: 0;
}
.sc-upload-list-item {
  width: 100%;
  height: 100%;
  position: relative;
}
.sc-upload-multiple .el-image {
  display: block;
}
.sc-upload-multiple .el-image:deep(img) {
  -webkit-user-drag: none;
}
.sc-upload-multiple-image-slot {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  font-size: 12px;
}
.sc-upload-multiple .el-upload-list__item:hover .sc-upload__item-actions {
  display: block;
}
.sc-upload__item-actions {
  position: absolute;
  top: 0;
  right: 0;
  display: none;
}
.sc-upload__item-actions span {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 25px;
  height: 25px;
  cursor: pointer;
  color: #fff;
}
.sc-upload__item-actions span i {
  font-size: 12px;
}
.sc-upload__item-actions .del {
  background: #f56c6c;
}
.sc-upload__item-progress {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  background-color: var(--el-overlay-color-lighter);
}
</style>
