<template>
	<div class="upload-area" @click="handleUpload">
		<a-icon type="cloud-upload" style="font-size: 60px; color: #3ca9f5" />
		<p>点击上传</p>
		<p>按住Ctrl可同时多选，支持上传PPT/word/excel/pdf/mp4/zip/rar，单个文件不能超过2G</p>
	</div>
	<!-- 上传组件 -->
	<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" @click="handleDeletePasteImg" />
			<CloseCircleOutlined class="close-icon" @click="dropBoxShow = false" />
		</uploader-drop>

		<!-- 上传列表 -->
		<uploader-list v-show="true">
			<template #default="props">
				<div class="file-panel">
					<div class="file-title">
						<span class="title-span">
							上传列表 <span class="count">（{{ props.fileList.length }}）</span>
						</span>
					</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>
</template>

<script setup>
	import { ref, reactive, computed, nextTick, getCurrentInstance } from 'vue'
	import { message } from 'ant-design-vue'
	import { useMyResourceStore } from '@/store/myResource'
	import SparkMD5 from 'spark-md5'
	import tool from '@/utils/tool'
	const props = defineProps({
		visible: {
			type: Boolean,
			default: true
		}
	})

	const emit = defineEmits(['update:visible', 'success'])

	const { proxy } = getCurrentInstance()
	const store = useMyResourceStore()

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

	// 表单数据
	const formState = reactive({
		title: '',
		description: '',
		category: undefined,
		fileIds: []
	})

	const rules = {
		title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
		category: [{ required: true, message: '请选择分类', trigger: 'change' }]
	}

	// 上传相关数据
	const options = ref({
		target: `${proxy.$RESOURCE_CONFIG.baseContext}/resourceFile/uploadfile`,
		chunkSize: 1024 * 1024,
		fileParameterName: 'file',
		maxChunkRetries: 3,
		simultaneousUploads: 5, // 控制并发上传数
		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: '.doc,.docx,.ppt,.pptx,.xls,.xlsx'
		accept: '*'
	})

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

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

	const remainderStorageValue = computed(() => {
		return store.remainderStorageValue
	})

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

	const handleUpload = () => {
		if (uploadBtn.value?.$el) {
			uploadBtn.value.$el.click()
		}
	}

	const handleUploadDir = () => {
		if (uploadDirBtn.value?.$el) {
			uploadDirBtn.value.$el.click()
		}
	}

	const handlePasteUpload = () => {
		pasteImg.value.src = ''
		pasteImg.value.name = ''
		pasteImgObj.value = null
		dropBoxShow.value = true
	}
	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) => {
		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
			// 手动控制上传队列，每次只处理5个文件
			const batchSize = 5
			for (let i = 0; i < filesSource.length; i += batchSize) {
				const batch = filesSource.slice(i, i + batchSize)
				batch.forEach((file) => {
					computeMD5(file)
				})
			}
		}
	}

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

		const result = JSON.parse(response)
		if (result.success) {
			file.statusStr = ''
			// 将上传成功的文件ID添加到表单数据中
			formState.fileIds.push(result.data.userFileId)
			emit('success', formState.fileIds)
		} else {
			message.error(result.msg)
			uploadStatus.value[file.id] = '上传失败'
		}
		filesLength.value--
		// 所有文件上传完成后，允许关闭弹窗
		canClose.value = filesLength.value === 0
		// 当一个文件完成时，检查是否有待上传文件
		if (uploaderInstance.value.files.some((f) => !f.isComplete)) {
			uploaderInstance.value.upload()
		}
		console.log(formState.fileIds, response, result, 'formState.fileIdsformState.fileIdsformState.fileIds')
	}

	const handleFileError = (rootFile, file, response) => {
		message.error(response)
		filesLength.value--
		canClose.value = filesLength.value === 0
	}

	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()
			filesLength.value--
			canClose.value = filesLength.value === 0
		}

		loadNext()
	}
	const uploadFileParams = computed(() => {
		return {
			filePath: '/',
			isDir: 0,
			funcType: 0
		}
	})
	const calculateFileMD5End = (md5, file) => {
		Object.assign(uploaderInstance.value.opts, {
			query: uploadFileParams.value
		})
		file.uniqueIdentifier = md5
		file.resume()
		file.statusStr = ''
		// 文件开始上传时，禁止关闭弹窗
		canClose.value = false
	}

	const handleCancel = () => {
		if (!canClose.value) {
			message.warning('文件正在上传中，请等待上传完成后再关闭')
			return
		}
		// 重置表单和上传状态
		formRef.value?.resetFields()
		formState.fileIds = []
		if (uploaderInstance.value) {
			uploaderInstance.value.cancel()
		}
		emit('update:visible', false)
	}

	const handleSubmit = () => {
		formRef.value
			.validate()
			.then(() => {
				if (formState.fileIds.length === 0) {
					message.warning('请上传文件')
					return
				}

				if (filesLength.value > 0) {
					message.warning('文件正在上传中，请等待上传完成后再提交')
					return
				}

				submitting.value = true

				// 模拟提交数据
				setTimeout(() => {
					message.success('提交成功')
					submitting.value = false
					emit('success', formState)
					// 重置表单和上传状态
					formRef.value?.resetFields()
					formState.fileIds = []
					emit('update:visible', false)
				}, 1000)
			})
			.catch(() => {
				// 表单验证失败
			})
	}

	// 暴露方法给父组件
	defineExpose({
		handleUpload
	})
</script>

<style lang="less" scoped>
	@import '@/style/myResource/varibles.less';
	.upload-btn-wrapper {
		display: flex;
		align-items: center;
	}
	.uploader-file {
		width: 560px;
	}
	.select-file-btn {
		display: none;
	}

	.drop-box {
		position: relative;
		width: 100%;
		height: 200px;
		border: 2px dashed #e9e9e9;
		border-radius: 6px;
		background-color: #fafafa;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		margin-top: 10px;

		.text {
			font-size: 16px;
			color: #999;
		}

		.paste-img-wrapper {
			width: 100%;
			height: 100%;
			display: flex;
			flex-direction: column;
			justify-content: center;
			align-items: center;

			.paste-name {
				font-size: 14px;
				margin-bottom: 10px;
			}

			.paste-img {
				max-width: 80%;
				max-height: 120px;
			}
		}

		.upload-icon,
		.delete-icon {
			position: absolute;
			bottom: 10px;
			font-size: 20px;
			cursor: pointer;
		}

		.upload-icon {
			right: 40px;
			color: #1890ff;
		}

		.delete-icon {
			right: 10px;
			color: #ff4d4f;
		}

		.close-icon {
			position: absolute;
			top: 10px;
			right: 10px;
			font-size: 16px;
			cursor: pointer;
			color: #999;
		}
	}
	.uploader-app {
		width: 560px;
	}
	.file-panel {
		width: 100%;
		margin-top: 15px;
		border: 1px solid #e9e9e9;
		border-radius: 4px;
		overflow: hidden;

		.file-title {
			display: flex;
			justify-content: space-between;
			align-items: center;
			padding: 8px 12px;
			background-color: #f5f5f5;
			border-bottom: 1px solid #e9e9e9;

			.title-span {
				font-size: 14px;
				font-weight: 500;

				.count {
					color: #999;
					font-weight: normal;
				}
			}
		}

		.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;
				padding: 8px 12px;
				width: 100%;
				border-bottom: 1px solid #f0f0f0;

				&:last-child {
					border-bottom: none;
				}

				.uploader-file-info {
					width: 540px;
				}

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

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

				&.custom-status-item {
					padding-right: 100px;
				}

				.custom-status {
					position: absolute;
					right: 12px;
					top: 50%;
					transform: translateY(-50%);
					color: #1878ff;
				}
			}

			.no-file {
				padding: 20px 0;
				text-align: center;
				color: #999;
				font-size: 14px;
			}
		}
	}

	.collapse-enter-active,
	.collapse-leave-active {
		transition: all 0.3s;
		max-height: 300px;
		overflow: hidden;
	}

	.collapse-enter-from,
	.collapse-leave-to {
		max-height: 0;
	}
	.upload-area {
		border: 2px dashed #3ca9f5;
		padding: 40px;
		text-align: center;
	}

	.upload-area p {
		margin: 10px 0;
	}

	.file-item {
		display: flex;
		align-items: center;
		margin: 10px 0;
	}

	.file-item .ant-progress {
		flex: 1;
		margin: 0 10px;
	}

	/* 新增表单样式 */
	.ant-form-item {
		margin-bottom: 16px;
	}
	.public-status-buttons {
		display: flex;
	}

	.status-button {
		padding: 5px 10px;
		/* margin-right: 10px; */
		border: 1px solid #ccc;
		/* border-radius: 3px; */
		cursor: pointer;
		background-color: #fff;
	}

	.status-button.active {
		background-color: #40a9ff;
		color: #fff;
		border-color: #40a9ff;
	}
	.upload-area {
		border: 2px dashed #3ca9f5;
		padding: 40px;
		text-align: center;
		transition: border-color 0.3s; /* 平滑过渡效果 */
	}

	.upload-area.drag-over {
		border-color: #1890ff;
		background-color: rgba(24, 144, 255, 0.05);
	}
</style>
