<template>
	<div class="upload-file-wrapper">
		<!-- 上传文件组件 -->
		<uploader
			class="uploader-app"
			ref="uploaderRef"
			:options="options"
			:autoStart="false"
			:fileStatusText="fileStatusText"
			@files-added="handleFilesAdded"
			@file-success="handleFileSuccess"
			@file-error="handleFileError"
			@dragleave="hideUploadMask"
		>
			<uploader-unsupport></uploader-unsupport>
			<!-- 选择按钮 在这里隐藏 -->
			<uploader-btn class="select-file-btn" :attrs="attrs" ref="uploadBtn"> 选择文件 </uploader-btn>
			<uploader-btn class="select-file-btn" :attrs="attrs" :directory="true" ref="uploadDirBtn">
				选择目录
			</uploader-btn>

			<!-- 拖拽上传 -->
			<uploader-drop class="drop-box" id="dropBox" @paste="handlePaste" v-show="dropBoxShow">
				<div class="paste-img-wrapper" v-show="pasteImg.src">
					<div class="paste-name">{{ pasteImg.name }}</div>
					<img class="paste-img" :src="pasteImg.src" :alt="pasteImg.name" v-if="pasteImg.src" />
				</div>
				<span class="text" v-show="!pasteImg.src"> 截图粘贴或将文件拖拽至此区域上传 </span>
				<UploadOutlined class="upload-icon" v-show="pasteImg.src" @click="handleUploadPasteImg" />
				<DeleteOutlined class="delete-icon" v-show="pasteImg.src" @click="handleDeletePasteImg" />
				<CloseCircleOutlined class="close-icon" @click="dropBoxShow = false" />
			</uploader-drop>
			<!-- 上传列表 -->
			<uploader-list v-show="panelShow">
				<template #default="props">
					<div class="file-panel">
						<div class="file-title">
							<span class="title-span">
								上传列表 <span class="count">（{{ props.fileList.length }}）</span>
							</span>
							<div class="operate">
								<a-button type="link" :title="collapse ? '展开' : '折叠'" @click="handleCollapse">
									<template #icon>
										<FullscreenOutlined v-if="collapse" />
										<MinusOutlined v-else />
									</template>
								</a-button>
								<a-button type="link" title="关闭" @click="handleClosePanel">
									<template #icon>
										<CloseOutlined />
									</template>
								</a-button>
							</div>
						</div>

						<transition name="collapse">
							<ul class="file-list" v-show="!collapse">
								<li
									v-for="file in props.fileList"
									:key="file.id"
									class="file-item"
									:class="{ 'custom-status-item': file.statusStr !== '' }"
								>
									<uploader-file ref="fileItem" :file="file" :list="true" />
									<span class="custom-status">{{ file.statusStr }}</span>
								</li>
								<div class="no-file" v-if="!props.fileList.length"><FileExclamationOutlined /> 暂无待上传文件</div>
							</ul>
						</transition>
					</div>
				</template>
			</uploader-list>
		</uploader>
	</div>
</template>

<script setup>
	import { ref, computed, defineExpose, nextTick, inject, getCurrentInstance, onMounted } from 'vue'
	import { message } from 'ant-design-vue'
	import { useMyResourceStore } from '@/store/myResource'
	import SparkMD5 from 'spark-md5'
	import tool from '@/utils/tool'
	import {
		UploadOutlined,
		DeleteOutlined,
		CloseCircleOutlined,
		FullscreenOutlined,
		MinusOutlined,
		CloseOutlined,
		FileExclamationOutlined
	} from '@ant-design/icons-vue'
	// 使用 inject 接收响应式数据
	const uploadProps = inject('uploadProps', {
		params: {},
		uploadWay: 1,
		serviceEl: null,
		callType: 1,
		callback: () => {}
	})
	const { proxy } = getCurrentInstance()
	const store = useMyResourceStore()

	// refs
	const uploaderRef = ref(null)
	const uploadBtn = ref(null)
	const uploadDirBtn = ref(null)
	const fileItem = ref(null)

	// 响应式数据
	const imageTypes = ref(['image/gif', 'image/jpg', 'image/jpeg', 'image/png', 'image/bmp', 'image/webp'])
	const documentTypes = ref([
		'application/msword',
		'text/plain',
		'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
		'application/pdf',
		'application/vnd.ms-excel',
		'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
		'application/vnd.ms-powerpoint',
		'application/vnd.openxmlformats-officedocument.presentationml.presentation'
	])
	const opts = ref({
		categoryMap: {
			image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
			document: ['doc', 'txt', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx']
		}
	})

	const options = ref({
		target: `${proxy.$RESOURCE_CONFIG.baseContext}/filetransfer/uploadfile`,
		chunkSize: 1024 * 1024,
		fileParameterName: 'file',
		maxChunkRetries: 3,
		testChunks: true,
		checkChunkUploadedByResponse: (chunk, message) => {
			let objMessage = JSON.parse(message)
			if (objMessage.success) {
				let data = objMessage.data
				if (data.skipUpload) {
					return true
				}
				return (data.uploaded || []).indexOf(chunk.offset + 1) >= 0
			} else {
				return true
			}
		},
		headers: {
			token: tool.data.get('TOKEN')
		},
		query: () => {}
	})

	const fileStatusText = ref({
		success: '上传成功',
		error: 'error',
		uploading: '上传中',
		paused: '暂停中',
		waiting: '等待中'
	})

	const attrs = ref({
		accept: '*'
	})

	const panelShow = ref(false)
	const collapse = ref(false)
	const dropBoxShow = ref(false)
	const pasteImg = ref({
		src: '',
		name: ''
	})
	const pasteImgObj = ref(null)
	const filesLength = ref(0)
	const uploadStatus = ref({})

	// 计算属性
	const uploaderInstance = computed(() => {
		return uploaderRef.value.uploader
	})

	const remainderStorageValue = computed(() => {
		return store.remainderStorageValue
	})
	// 方法
	const hideUploadMask = (e) => {
		e.stopPropagation()
		e.preventDefault()
		dropBoxShow.value = false
	}

	const handlePrepareUpload = async () => {
		// options.value.headers.token = getCookies(getConfig().tokenKeyName)
		await nextTick()
		switch (uploadProps.uploadWay) {
			case 1: {
				if (uploadBtn.value?.$el) {
					uploadBtn.value.$el.click()
				}
				break
			}
			case 2: {
				if (uploadDirBtn.value?.$el) {
					uploadDirBtn.value.$el.click()
				}
				break
			}
			case 3: {
				pasteImg.value.src = ''
				pasteImg.value.name = ''
				pasteImgObj.value = null
				dropBoxShow.value = true
				break
			}
		}
	}

	const handlePaste = (event) => {
		let pasteItems = (event.clipboardData || window.clipboardData).items
		if (pasteItems && pasteItems.length) {
			let imgObj = pasteItems[0].getAsFile()
			pasteImgObj.value =
				imgObj !== null
					? new File([imgObj], `qiwenshare_${new Date().valueOf()}.${imgObj.name.split('.')[1]}`, { type: imgObj.type })
					: null
		} else {
			message.error('当前浏览器不支持')
			return false
		}

		if (!pasteImgObj.value) {
			message.error('粘贴内容非图片')
			return false
		}

		pasteImg.value.name = pasteImgObj.value.name

		let reader = new FileReader()
		reader.onload = (event) => {
			pasteImg.value.src = event.target.result
		}
		reader.readAsDataURL(pasteImgObj.value)
	}

	const handleUploadPasteImg = () => {
		uploaderInstance.value.addFile(pasteImgObj.value)
	}

	const handleDeletePasteImg = () => {
		pasteImg.value.src = ''
		pasteImg.value.name = ''
		pasteImgObj.value = null
	}

	const handleFilesAdded = (filesSource) => {
		console.log('filesSource.filesSource', filesSource)
		// const inconformityFileArr = []
		// const files = filesSource.filter((file) => {
		// 	const isFile = isImageOrDocument(file)
		// 	if (isFile) {
		// 		return file
		// 	} else {
		// 		inconformityFileArr.push(file)
		// 	}
		// })

		// if (inconformityFileArr.length > 0) {
		// 	filesSource.ignored = true
		// 	message.warning('文件夹中有不符合要求的文件', {
		// 		duration: 0,
		// 		content: '无'
		// 		// content: () => {
		// 		// 	let warringHtml = ''
		// 		// 	inconformityFileArr.forEach((item, index) => {
		// 		// 		warringHtml += `<p><i style="margin-right:5px">${index + 1}.</i>${item.name}</p>`
		// 		// 	})
		// 		// 	return (
		// 		// 		<div>
		// 		// 			<div style="color:#E6A23C;width:290px">{warringHtml}</div>
		// 		// 			<p style="color:#f56c6c">只可以上传图片和文档!文件大小不能为0！</p>
		// 		// 		</div>
		// 		// 	)
		// 		// }
		// 	})
		// 	return false
		// }

		// const filesTotalSize = files.reduce((pre, next) => pre + next.size, 0)
		const filesTotalSize = filesSource
			.map((item) => item.size)
			.reduce((pre, next) => {
				return pre + next
			}, 0)

		if (remainderStorageValue.value < filesTotalSize) {
			// 批量选择的文件超出剩余存储空间
			message.warning(`剩余存储空间不足，请重新选择${filesSource.length > 1 ? '批量' : ''}文件`)
			filesSource.ignored = true // 本次选择的文件过滤掉
		} else {
			// 批量或单个选择的文件未超出剩余存储空间，正常上传
			filesLength.value += filesSource.length
			filesSource.forEach((file) => {
				dropBoxShow.value = false
				panelShow.value = true
				collapse.value = false
				computeMD5(file)
			})
		}
	}

	const handleFileSuccess = (rootFile, file, response) => {
		if (response === '') {
			uploadStatus.value[file.id] = '上传失败'
			uploadProps.callback(false)
			return
		}

		const result = JSON.parse(response)
		if (result.success) {
			file.statusStr = ''
			if (filesLength.value === 1) {
				message.success('上传完毕')
				if (uploadProps.callType === 1) {
					uploadProps.serviceEl.$emit('getTableDataByType')
				} else {
					// uploadProps.serviceEl.getTableDataByType()
				}
				store.showStorage()
				uploadProps.callback(true)
			}
		} else {
			message.error(result.message)
			uploadStatus.value[file.id] = '上传失败'
		}
		filesLength.value--
	}

	const handleFileError = (rootFile, file, response) => {
		message.error(response)
	}

	const computeMD5 = (file) => {
		let fileReader = new FileReader()
		let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
		let currentChunk = 0
		const chunkSize = 1024 * 1024
		let chunks = Math.ceil(file.size / chunkSize)
		let spark = new SparkMD5.ArrayBuffer()

		file.statusStr = '计算MD5'
		file.pause()

		const loadNext = () => {
			let start = currentChunk * chunkSize
			let end = start + chunkSize >= file.size ? file.size : start + chunkSize
			fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
		}

		fileReader.onload = (e) => {
			spark.append(e.target.result)
			if (currentChunk < chunks) {
				currentChunk++
				loadNext()
				file.statusStr = `校验MD5 ${((currentChunk / chunks) * 100).toFixed(0)}%`
			} else {
				let md5 = spark.end()
				calculateFileMD5End(md5, file)
			}
		}

		fileReader.onerror = () => {
			message.error({
				content: `文件${file.name}读取出错，请检查该文件`,
				duration: 2
			})
			file.cancel()
		}

		loadNext()
	}

	const calculateFileMD5End = (md5, file) => {
		Object.assign(uploaderInstance.value.opts, {
			query: {
				...uploadProps.params
			}
		})
		file.uniqueIdentifier = md5
		file.resume()
		file.statusStr = ''
	}

	const handleClosePanel = () => {
		uploaderInstance.value.cancel()
		panelShow.value = false
		uploadProps.callback('cancel')
	}

	const handleCollapse = () => {
		collapse.value = !collapse.value
	}

	const isImageOrDocument = (file) => {
		const isImage = imageTypes.value.includes(file.fileType)
		const isDocument = documentTypes.value.includes(file.fileType)
		const fileSize = file.size
		return (isImage || isDocument) && fileSize !== 0
	}
	// 暴露方法给父组件
	defineExpose({
		handlePrepareUpload
	})
</script>

<style lang="less" scoped>
	@import '@/style/myResource/varibles.less';

	.upload-file-wrapper {
		position: fixed;
		z-index: 20;
		right: 16px;
		bottom: 16px;

		.drop-box {
			position: fixed;
			z-index: 19;
			top: 0;
			left: 0;
			border: 5px dashed #8091a5 !important;
			background: #ffffffd9;
			color: #8091a5 !important;
			text-align: center;
			box-sizing: border-box;
			height: 100%;
			line-height: 100%;
			width: 100%;

			.text {
				position: absolute;
				top: 50%;
				left: 50%;
				width: 100%;
				transform: translate(-50%, -50%);
				font-size: 30px;
			}

			.upload-icon {
				position: absolute;
				right: 176px;
				top: 16px;
				cursor: pointer;
				&:hover {
					color: @primary-color;
				}
			}

			.delete-icon {
				position: absolute;
				right: 80px;
				top: 16px;
				cursor: pointer;
				&:hover {
					color: @error-color;
				}
			}

			.close-icon {
				position: absolute;
				right: 16px;
				top: 16px;
				cursor: pointer;
				&:hover {
					color: @success-color;
				}
			}

			.paste-img-wrapper {
				width: 100%;
				height: 100%;
			}

			.paste-img {
				margin-top: 16px;
				max-width: 90%;
				max-height: 80%;
			}

			.paste-name {
				height: 24px;
				line-height: 24px;
				font-size: 18px;
				color: @text-color;
			}
		}

		.uploader-app {
			width: 560px;
		}

		.file-panel {
			background-color: #fff;
			border: 1px solid #e2e2e2;
			border-radius: 7px 7px 0 0;
			box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);

			.file-title {
				display: flex;
				height: 40px;
				line-height: 40px;
				padding: 0 15px;
				border-bottom: 1px solid #ddd;

				.title-span {
					padding-left: 0;
					margin-bottom: 0;
					font-size: 16px;

					.count {
						color: @text-color-secondary;
					}
				}

				.operate {
					flex: 1;
					text-align: right;

					:deep(.ant-btn-link) {
						color: @text-color;

						&:hover {
							.anticon-fullscreen,
							.anticon-minus {
								color: @success-color;
							}
							.anticon-close {
								color: @error-color;
							}
						}
					}
				}
			}

			.file-list {
				position: relative;
				height: 240px;
				overflow-x: hidden;
				overflow-y: auto;
				background-color: #fff;
				font-size: 12px;
				list-style: none;

				&::-webkit-scrollbar {
					width: 6px;
				}

				&::-webkit-scrollbar-thumb {
					background: @scrollbar-thumb-color;
					border-radius: 4px;
				}

				&::-webkit-scrollbar-track {
					background: @scrollbar-track-color;
				}

				.file-item {
					position: relative;
					background-color: #fff;

					:deep(.uploader-file) {
						height: 40px;
						line-height: 40px;

						.uploader-file-progress {
							border: 1px solid @success-color;
							border-right: none;
							border-left: none;
							background: #e1f3d8;
						}

						.uploader-file-name {
							width: 44%;
						}

						.uploader-file-size {
							width: 16%;
						}

						.uploader-file-meta {
							display: none;
						}

						.uploader-file-status {
							width: 30%;
							text-indent: 0;
						}

						.uploader-file-actions > span {
							margin-top: 12px;
						}
					}

					:deep(.uploader-file[status='success']) {
						.uploader-file-progress {
							border: none;
						}
					}
				}

				.file-item.custom-status-item {
					:deep(.uploader-file-status) {
						visibility: hidden;
					}

					.custom-status {
						position: absolute;
						top: 0;
						right: 10%;
						width: 24%;
						height: 40px;
						line-height: 40px;
					}
				}
			}

			&.collapse {
				.file-title {
					background-color: #e7ecf2;
				}
			}
		}

		.no-file {
			position: absolute;
			top: 50%;
			left: 50%;
			transform: translate(-50%, -50%);
			font-size: 16px;
		}

		:deep(.uploader-file-icon) {
			display: none;
		}

		:deep(.uploader-file-actions > span) {
			margin-right: 6px;
		}
	}

	.select-file-btn {
		display: none;
	}
	.collapse-enter-active,
	.collapse-leave-active {
		transition: all 0.3s ease-in-out;
		max-height: 1000px;
		overflow: hidden;
	}

	.collapse-enter-from,
	.collapse-leave-to {
		max-height: 0;
		opacity: 0;
		overflow: hidden;
	}
</style>
