<template>
	<transition name="fade">
		<div class="img-preview-wrapper" v-show="visible">
			<!-- 顶部信息栏 & 工具栏 -->
			<div class="tip-wrapper" v-if="visible">
				<span
					class="fold-icon"
					:class="isShowMinImgList ? 'no-fold' : 'fold'"
					:title="isShowMinImgList ? '折叠缩略图' : '展开缩略图'"
					@click="isShowMinImgList = !isShowMinImgList"
				>
					<left-outlined v-if="isShowMinImgList" />
					<right-outlined v-else />
				</span>
				<div class="name" :title="activeImageName">
					{{ activeImageName }}
				</div>
				<div class="opera-btn-group">
					<a-input-number
						v-model:value="inputActiveIndex"
						:min="1"
						:max="imgList.length"
						size="small"
						@change="handleChangeInputIndex"
					/>
					<span class="split-line">/</span>{{ imgList.length }}
				</div>
				<div class="tool-wrapper">
					<span class="item" title="向右旋转" @click="handleRotateImg">
						<redo-outlined />
					</span>
					<a class="item download-link" target="_blank" :href="activeDownloadLink" :download="activeImageName">
						<download-outlined title="保存到本地" />
					</a>
					<a-tooltip placement="bottom">
						<template #title>
							<div style="line-height: 1.5">
								1. 点击图片以外的区域可退出预览<br />
								2. 按 Escape 键可退出预览<br />
								3. 按左、右方向键可切换为上一张、下一张图片<br />
								4. 鼠标滚轮可放大、缩小图片<br />
								5. 点击左上角
								<left-outlined /> 图标可折叠、展开缩略图<br />
							</div>
						</template>
						<div class="item text-wrapper">
							<span class="text">操作提示</span>
							<bulb-outlined />
						</div>
					</a-tooltip>
					<span class="item close-icon" title="关闭预览" @click="handleCloseImgPreview">
						<close-outlined />
					</span>
				</div>
			</div>
			<!-- 左侧缩略图 -->
			<ul class="min-img-list" ref="minImgListRef" v-show="isShowMinImgList">
				<li
					class="min-img-item"
					v-for="(item, index) in imgList"
					:key="index"
					:class="{ active: activeIndex === index }"
					:title="$file.getFileNameComplete(item)"
					ref="minImgRef"
					@click="activeIndex = index"
				>
					<img class="min-img" :src="$file.setFileImg(item)" :alt="`${item.fileName} 缩略图`" />
				</li>
			</ul>
			<!-- 右侧预览区域 -->
			<div
				class="img-wrapper"
				:class="{ 'full-width': !isShowMinImgList }"
				@wheel.prevent="handleMouseWheel"
				@click.self="handleCloseImgPreview"
			>
				<!-- 大图查看 -->
				<img
					class="img-large"
					ref="imgLarge"
					v-for="(item, index) in imgList"
					:key="index"
					:src="item.fileUrl"
					v-show="index === activeIndex"
				/>
				<!-- 左右切换图标 -->
				<span
					class="pre-icon"
					title="上一张"
					v-show="activeIndex > 0"
					@click.stop="handleChangeActiveImg(activeIndex - 1)"
				>
					<left-outlined />
				</span>
				<span
					class="next-icon"
					title="下一张"
					v-show="activeIndex < imgList.length - 1"
					@click.stop="handleChangeActiveImg(activeIndex + 1)"
				>
					<right-outlined />
				</span>
				<!-- 底部显示放大缩小比例 -->
				<div class="zoom-bar">
					<a-slider
						v-model:value="imgZoom"
						:min="imgZoomMin"
						:max="imgZoomMax"
						:tip-formatter="formatZoomSize"
						@update:value="handleZoomImg"
					/>
					<div class="zoom-count">{{ imgZoom }}%</div>
				</div>
			</div>
		</div>
	</transition>
</template>

<script setup>
	import { ref, computed, watch, nextTick, getCurrentInstance } from 'vue'
	import { useMyResourceStore } from '@/store/myResource'
	import {
		LeftOutlined,
		RightOutlined,
		RedoOutlined,
		DownloadOutlined,
		BulbOutlined,
		CloseOutlined
	} from '@ant-design/icons-vue'

	// 获取全局变量
	const { proxy } = getCurrentInstance()

	// 获取store
	const myResourceStore = useMyResourceStore()

	// 定义props
	const props = defineProps({
		imgList: {
			type: Array,
			default: () => []
		},
		defaultIndex: {
			type: Number,
			default: 0
		},
		callback: {
			type: Function,
			default: () => {}
		}
	})

	// 响应式数据
	const visible = ref(false)
	const inputActiveIndex = ref(1)
	const rotate = ref(0)
	const activeIndex = ref(0)
	const imgZoom = ref(100)
	const imgZoomMin = ref(1)
	const imgZoomMax = ref(200)
	const isShowMinImgList = ref(true)

	// DOM引用
	const minImgListRef = ref(null)
	const minImgRef = ref(null)
	const imgLarge = ref(null)

	// 计算属性
	const activeImage = computed(() => props.imgList[activeIndex.value])
	const activeImageName = computed(() => proxy.$file.getFileNameComplete(activeImage.value))
	const imageHeight = computed(() => activeImage.value.imageHeight)
	const imageWidth = computed(() => activeImage.value.imageWidth)
	const activeDownloadLink = computed(() => activeImage.value.downloadLink)
	const screenWidth = computed(() => myResourceStore.screenWidth)
	// 监听器
	watch(visible, (val) => {
		if (val) {
			activeIndex.value = props.defaultIndex
			document.addEventListener('keyup', handleAddKeyupEvent) // 添加键盘相关事件
			// 获取用户是否显示缩略图的操作习惯设置
			isShowMinImgList.value = localStorage.getItem('qiwen_file_img_preview_show_min') === 'false' ? false : true
			nextTick(() => {
				handleCalculateImgZoom(activeIndex.value) // 确定图片缩放比例
				handleScrollMinImg() // 将当前查看图片对应的缩略图滚动到视野中
			})
		} else {
			// 移除键盘相关事件
			document.removeEventListener('keyup', handleAddKeyupEvent)
		}
	})

	watch(activeIndex, (newValue) => {
		rotate.value = 0
		nextTick(() => {
			if (imgLarge.value[newValue].style.zoom) {
				imgZoom.value = Number(imgLarge.value[newValue].style.zoom.split('%')[0])
			} else {
				// 确定图片缩放比例
				handleCalculateImgZoom(newValue)
			}
		})
	})

	watch(isShowMinImgList, (val) => {
		localStorage.setItem('qiwen_file_img_preview_show_min', val)
	})

	// 方法
	/**
	 * 顶部栏输入框绑定值被改变时触发
	 * @param {number} currentValue 当前值
	 */
	const handleChangeInputIndex = (currentValue) => {
		activeIndex.value = currentValue - 1
		handleChangeActiveImg(currentValue - 1)
	}

	/**
	 * DOM 绑定 Esc 键、左方向键、右方向键的键盘按下事件
	 * @param {event} event 事件
	 */
	const handleAddKeyupEvent = (event) => {
		switch (event.key) {
			case 'Escape': {
				handleCloseImgPreview() // 关闭预览
				break
			}
			case 'ArrowLeft': {
				handleChangeActiveImg(activeIndex.value - 1) // 切换到上一张
				break
			}
			case 'ArrowRight': {
				handleChangeActiveImg(activeIndex.value + 1) // 切换到下一张
				break
			}
		}
	}

	/**
	 * 计算图片缩放比例
	 */
	const handleCalculateImgZoom = (currentIndex) => {
		// 包裹元素
		const wrapperDom = document.querySelector('.img-wrapper')
		// 宽度原比例
		const widthRate = Number(((wrapperDom.clientWidth - 264) / imageWidth.value).toFixed(2))
		// 高度原比例
		const heightRate = Number(((wrapperDom.clientHeight - 80) / imageHeight.value).toFixed(2))
		let zoomRate = 1 // 缩放比例
		if (widthRate >= 1 && heightRate >= 1) {
			// 原图宽、高都小于/等于包裹元素
			zoomRate = 1
		} else if (widthRate >= 1 && heightRate < 1) {
			// 原图宽小于/等于显示区域，高大于包裹元素
			zoomRate = heightRate
		} else if (widthRate < 1 && heightRate >= 1) {
			// 原图宽大于显示区域，高小于/等于包裹元素
			zoomRate = widthRate
		} else if (widthRate < 1 && heightRate < 1) {
			// 原图宽、高都大于包裹元素
			zoomRate = Math.min(widthRate, heightRate)
		}
		imgZoom.value = Number(zoomRate.toFixed(2)) * 100 //  缩放百分比
		imgLarge.value[currentIndex].style.zoom = `${imgZoom.value}%`
	}

	/**
	 * 关闭图片预览，恢复旋转角度
	 */
	const handleCloseImgPreview = () => {
		rotate.value = 0
		imgLarge.value[activeIndex.value].style.transform = `rotate(${rotate.value}deg)`
		visible.value = false
		props.callback('cancel')
	}

	/**
	 * 格式化 tooltip message - 显示图片缩放比例
	 * @param {number} value 缩放数字
	 * @returns {string}  图片缩放比例
	 */
	const formatZoomSize = (value) => {
		return value + '%'
	}

	/**
	 * 数据改变时触发（使用鼠标拖曳时，活动过程实时触发）
	 * @param {number} value 缩放数字
	 */
	const handleZoomImg = (value) => {
		if (imgLarge.value) {
			imgLarge.value[activeIndex.value].style.zoom = value + '%' //  实时设置图片缩放比例
		}
	}

	/**
	 * 鼠标滚动事件
	 * @description 缩放图片
	 */
	const handleMouseWheel = (event) => {
		let zoom = parseInt(imgLarge.value[activeIndex.value].style.zoom) || 100
		zoom += event.wheelDelta / 12
		if (zoom >= imgZoomMin.value && zoom < imgZoomMax.value) {
			imgZoom.value = zoom
			imgLarge.value[activeIndex.value].style.zoom = zoom + '%'
		}
		return false
	}

	/**
	 * 旋转图片
	 */
	const handleRotateImg = () => {
		rotate.value += 90
		imgLarge.value[activeIndex.value].style.transform = `rotate(${rotate.value}deg)`
	}

	/**
	 * 改变当前查看的图片索引
	 */
	const handleChangeActiveImg = (index) => {
		if (index >= 0 && index < props.imgList.length) {
			activeIndex.value = index
			handleScrollMinImg()
		}
	}

	/**
	 * 将缩略图中当前查看的图片缩略图滚动到视野中
	 */
	const handleScrollMinImg = () => {
		const parentEle = minImgListRef.value //  包裹缩略图的父元素
		const activeEle = minImgRef.value[activeIndex.value] //  当前缩略图
		// 当前元素距离可视区域顶部的距离
		const currentEleTop = activeEle.offsetTop - activeEle.clientHeight
		// 当前元素距离可视区域底部的距离
		const currentEleBottom = parentEle.clientHeight - activeEle.offsetTop
		// 如果当前查看的图片缩略图有一部分在可视区域之外（之上或之下）
		if (currentEleBottom < activeEle.clientHeight || currentEleTop < activeEle.clientHeight) {
			// 将父元素的滚动条向可视区域方向滚动两个图片的高度
			parentEle.scrollTop = -currentEleBottom + activeEle.clientHeight * 2
		}
	}

	// 对外暴露方法
	defineExpose({
		visible
	})
</script>

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

	.img-preview-wrapper {
		position: fixed;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		overflow: auto;
		width: 100%;
		height: 100%;
		z-index: 2;
		text-align: center;
		display: flex;
		align-items: center;
		animation: imgPreviewAnimation 0.3s;
		animation-iteration-count: 0.3;
		animation-fill-mode: forwards;

		@keyframes imgPreviewAnimation {
			0% {
				background: transparent;
			}

			100% {
				background: rgba(0, 0, 0, 0.8);
			}
		}

		.tip-wrapper {
			position: fixed;
			top: 0;
			left: 0;
			z-index: 2;
			background: rgba(0, 0, 0, 0.5);
			padding-right: 48px;
			width: 100%;
			height: 48px;
			line-height: 48px;
			color: #fff;
			font-size: 16px;
			display: flex;
			justify-content: space-between;
			align-items: center;

			.fold-icon {
				margin-right: 16px;
				font-size: 24px;
				cursor: pointer;
				&:hover {
					opacity: 0.6;
				}
				&.no-fold {
					width: 120px;
				}
				&.fold {
					width: 64px;
				}
			}

			.name {
				flex: 1;
				padding-right: 16px;
				text-align: left;
				overflow: hidden;
				text-overflow: ellipsis;
				white-space: nowrap;
			}

			.opera-btn-group {
				width: 100px;
				display: flex;

				:deep(.ant-input-number) {
					width: 40px;

					.ant-input-number-handler-wrap {
						display: none;
					}

					.ant-input-number-input {
						margin-top: 14px;
						background: rgba(0, 0, 0, 0.5);
						height: 20px;
						line-height: 20px;
						padding: 0;
						font-size: 16px;
						color: #fff;
					}
				}

				.split-line {
					margin: 0 8px;
				}
			}

			.tool-wrapper {
				flex: 1;
				display: flex;
				justify-content: flex-end;
				align-items: center;

				.item {
					margin-left: 16px;
					cursor: pointer;

					&:hover {
						opacity: 0.7;
					}
				}

				.close-icon,
				.download-link {
					font-size: 18px;
				}

				.download-link {
					color: inherit;
				}

				.text-wrapper {
					margin-left: 32px;

					.text {
						margin-right: 8px;
					}
				}
			}
		}
		.min-img-list {
			position: fixed;
			top: 48px;
			left: 0;
			background: rgba(0, 0, 0, 0.5);
			height: calc(100vh - 48px);
			padding: 8px 16px;
			overflow-y: auto;
			list-style: none;
			&::-webkit-scrollbar {
				width: 8px;
				background: transparent;
			}
			&::-webkit-scrollbar-thumb {
				background: #c0c4cc;
			}

			.min-img-item {
				position: relative;
				margin-bottom: 4px;
				width: 80px;
				height: 80px;
				cursor: pointer;
				.min-img {
					width: 100%;
					height: 100%;
				}
				&:last-of-type {
					margin-bottom: 0;
				}
				&:not(.active)::after {
					content: '';
					position: absolute;
					top: 0;
					left: 0;
					display: block;
					width: 80px;
					height: 80px;
					background: #000;
					opacity: 0.4;
				}
				&:hover {
					&:not(.active)::after {
						opacity: 0.2;
					}
				}
			}
		}
		.img-wrapper {
			position: fixed;
			top: 48px;
			right: 0;
			left: 120px;
			overflow: auto;
			height: calc(100vh - 48px);
			display: flex;
			align-items: center;
			justify-content: center;
			&.full-width {
				left: 0;
			}
			.img-large {
				margin: 0 auto;
				transition: transform 0.5s;
			}

			.pre-icon,
			.next-icon {
				font-size: 60px;
				color: #fff;
				position: absolute;
				top: 50%;
				cursor: pointer;
				z-index: 3;

				&:hover {
					opacity: 0.7;
				}
			}

			.pre-icon {
				left: 64px;
			}

			.next-icon {
				right: 64px;
			}

			.zoom-bar {
				position: absolute;
				right: 0;
				bottom: 20px;
				left: 0;
				margin: 0 auto;
				width: 600px;
				display: flex;

				:deep(.ant-slider) {
					flex: 1;

					.ant-slider-track {
						background: @primary-text;
					}

					.ant-slider-handle {
						border-color: @primary-text;
					}
				}

				.zoom-count {
					width: 60px;
					height: 38px;
					line-height: 38px;
					text-align: right;
					color: #fff;
				}
			}
		}
	}

	.fade-enter-active,
	.fade-leave-active {
		transition: opacity 0.3s;
	}
	.fade-enter-from,
	.fade-leave-to {
		opacity: 0;
	}
</style>
