<template>
	<div class="pi-upload" :class="{'pi-upload-round':round}" :style="style">
		<div v-if="file && file.status != 'success'" class="pi-upload__uploading">
			<div class="pi-upload__progress">
				<el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16"/>
			</div>
			<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
		</div>
		<div v-if="file && file.status=='success'" class="pi-upload__img">
			<el-image class="image" :src="file.url" :preview-src-list="[file.url]" fit="cover" preview-teleported :z-index="9999">
				<template #placeholder>
					<div class="pi-upload__img-slot">
						Loading...
					</div>
				</template>
			</el-image>
			<div class="pi-upload__img-actions" v-if="!disabled">
				<span class="del" @click="handleRemove()"><el-icon><el-icon-delete /></el-icon></span>
			</div>
		</div>
		<el-upload v-if="!file" class="uploader" ref="uploaderRef"
				   :auto-upload="cropper?false:autoUpload"
				   :disabled="disabled"
				   :show-file-list="showFileList"
				   :action="action"
				   :name="name"
				   :data="data"
				   :accept="accept"
				   :limit="1"
				   :http-request="request"
				   :on-change="change"
				   :before-upload="before"
				   :on-success="success"
				   :on-error="error"
				   :on-exceed="handleExceed">
			<slot>
				<div class="el-upload--picture-card" :style="style">
					<div class="file-empty">
						<el-icon><component :is="icon" /></el-icon>
						<h4 v-if="title">{{title}}</h4>
					</div>
				</div>
			</slot>
		</el-upload>
		<span style="display:none!important"><el-input v-model="value"></el-input></span>
		<el-dialog title="剪裁" draggable v-model="cropperDialogVisible" :width="580" @closed="cropperClosed" destroy-on-close>
			<pi-cropper :src="cropperFile.tempCropperFile" :compress="compress" :aspectRatio="aspectRatio" ref="cropperRef"></pi-cropper>
			<template #footer>
				<el-button @click="cropperDialogVisible=false" >取 消</el-button>
				<el-button type="primary" @click="cropperSave">确 定</el-button>
			</template>
		</el-dialog>
	</div>
</template>

<script setup>
import {defineAsyncComponent, ref, watch, onMounted, getCurrentInstance, nextTick} from 'vue'
import { genFileId } from 'element-plus'
import config from "@/config/upload"
const piCropper = defineAsyncComponent(() => import('@/components/piCropper'))

const emit = defineEmits(['update:modelValue'])
const cropperRef = ref(null)
const uploaderRef = ref(null)
const {proxy} = getCurrentInstance()

const props = defineProps({
	modelValue: { type: String, default: "" },
	height: {type: Number, default: 148},
	width: {type: Number, default: 148},
	title: { type: String, default: "" },
	icon: { type: String, default: "el-icon-plus" },
	action: { type: String, default: "" },
	api: { type: Object, default: () => {} },
	name: { type: String, default: config.filename },
	data: { type: Object, default: () => {} },
	accept: { type: String, default: "image/gif, image/jpeg, image/png" },
	maxSize: { type: Number, default: config.maxSize },
	limit: { type: Number, default: 1 },
	autoUpload: { type: Boolean, default: true },
	showFileList: { type: Boolean, default: false },
	disabled: { type: Boolean, default: false },
	round: { type: Boolean, default: false },
	onSuccess: { type: Function, default: () => { return true } },
	cropper: { type: Boolean, default: false },
	compress: {type: Number, default: 1},
	aspectRatio:  {type: Number, default: NaN}
})

let value = ref("")
let file = ref(null)
let style = ref({
	width: props.width + "px",
	height: props.height + "px"
})
let cropperDialogVisible = ref(false)
let cropperFile = ref(null)

watch(() => props.modelValue, function (val) {
	value.value = val
	newFile(val)
})

watch(value, function (val) {
	emit('update:modelValue', val)
})

onMounted(() => {
	value.value = props.modelValue
	newFile(props.modelValue)
})

function newFile(url){
	if(url){
		file.value = {
			status: "success",
			url: url
		}
	}else{
		file.value = null
	}
}

function cropperSave(){
	cropperRef.value.getCropFile(f => {
		f.uid = cropperFile.value.uid
		cropperFile.value.raw = f
		file.value = cropperFile.value
		file.value.tempFile = URL.createObjectURL(file.value.raw)
		uploaderRef.value.submit()

	}, cropperFile.value.name, cropperFile.value.type)
	cropperDialogVisible.value = false
}

function cropperClosed(){
	URL.revokeObjectURL(cropperFile.value.tempCropperFile)
	delete cropperFile.value.tempCropperFile
}

function handleRemove(){
	clearFiles()
}

function clearFiles(){
	URL.revokeObjectURL(file.value.tempFile)
	value.value = ""
	file.value = null
	nextTick(()=>{
		uploaderRef.value.clearFiles()
	})
}

function change(f, files){
	if(files.length > 1){
		files.splice(0, 1)
	}
	if(props.cropper && f.status=='ready'){
		const acceptIncludes = ["image/gif", "image/jpeg", "image/png"].includes(f.raw.type)
		if(!acceptIncludes){
			proxy.$notify.warning({
				title: '上传文件警告',
				message: '选择的文件非图像类文件'
			})
			return false
		}
		cropperFile.value = f
		cropperFile.value.tempCropperFile = URL.createObjectURL(f.raw)
		cropperDialogVisible.value = true
		return false
	}
	file.value = f
	if(f.status=='ready'){
		f.tempFile = URL.createObjectURL(f.raw)
	}
}

function before(file){
	const acceptIncludes = props.accept.replace(/\s/g,"").split(",").includes(file.type)
	if(!acceptIncludes){
		proxy.$notify.warning({
			title: '上传文件警告',
			message: '选择的文件非图像类文件'
		})
		clearFiles()
		return false
	}
	const maxSize = file.size / 1024 / 1024 < props.maxSize;
	if (!maxSize) {
		proxy.$message.warning(`上传文件大小不能超过 ${props.maxSize}MB!`);
		clearFiles()
		return false
	}
}

function handleExceed(files){
	const file = files[0]
	file.uid = genFileId()
	uploaderRef.value.handleStart(file)
}

function success(res, f){
	//释放内存删除blob
	URL.revokeObjectURL(f.tempFile)
	delete f.tempFile
	var os = props.onSuccess(res, f)
	if(os!=undefined && os==false){
		proxy.$nextTick(() => {
			file.value = null
			value.value = ""
		})
		return false
	}
	f.url = res.src
	value.value = f.url
}

function error(err){
	nextTick(()=>{
		clearFiles()
	})
	proxy.$notify.error({
		title: '上传文件未成功',
		message: err
	})
}

function request(param){
	var api = config.api;
	if(props.api){
		api = props.api;
	}
	const data = new FormData();
	data.append(param.filename, param.file);
	for (const key in param.data) {
		data.append(key, param.data[key]);
	}
	api(data, {
		onUploadProgress: e => {
			const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
			param.onProgress({percent: complete})
		}
	}).then(res => {
		if (res.code === config.successCode) {
			var response = config.parseData(res.data);
			param.onSuccess(response)
		}else {
			param.onError(res.msg)
		}
	}).catch(err => {
		param.onError(err)
	})
}

</script>

<style scoped>
.el-form-item.is-error .pi-upload .el-upload--picture-card {border-color: var(--el-color-danger);}
.pi-upload .el-upload--picture-card {border-radius: 0;}

.pi-upload .uploader,.pi-upload:deep(.el-upload) {width: 100%;height: 100%;}

.pi-upload__img {width: 100%;height: 100%;position: relative;}
.pi-upload__img .image {width: 100%;height: 100%;}
.pi-upload__img-actions {position: absolute;top:0;right: 0;display: none;}
.pi-upload__img-actions span {display: flex;justify-content: center;align-items: center;width: 25px;height:25px;cursor: pointer;color: #fff;}
.pi-upload__img-actions span i {font-size: 12px;}
.pi-upload__img-actions .del {background: #F56C6C;}
.pi-upload__img:hover .pi-upload__img-actions {display: block;}
.pi-upload__img-slot {display: flex;justify-content: center;align-items: center;width: 100%;height: 100%;font-size: 12px;background-color: var(--el-fill-color-lighter);}

.pi-upload__uploading {width: 100%;height: 100%;position: relative;}
.pi-upload__progress {position: absolute;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background-color: var(--el-overlay-color-lighter);z-index: 1;padding:10px;}
.pi-upload__progress .el-progress {width: 100%;}
.pi-upload__uploading .image {width: 100%;height: 100%;}

.pi-upload .file-empty {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;flex-direction: column;}
.pi-upload .file-empty i {font-size: 28px;}
.pi-upload .file-empty h4 {font-size: 12px;font-weight: normal;color: #8c939d;margin-top: 8px;}

.pi-upload.pi-upload-round {border-radius: 50%;overflow: hidden;}
.pi-upload.pi-upload-round .el-upload--picture-card {border-radius: 50%;}
.pi-upload.pi-upload-round .pi-upload__img-actions {top: auto;left: 0;right: 0;bottom: 0;}
.pi-upload.pi-upload-round .pi-upload__img-actions span {width: 100%;}
</style>
