<template>
	<div class="upload-box">
		<el-upload
			v-model:file-list="_fileList"
			action=""
			list-type="picture-card"
			:class="['upload', self_disabled ? 'disabled' : '', drag ? 'no-border' : '', _fileList.length < limit ? '' : 'hide_upload']"
			:multiple="true"
			:disabled="self_disabled"
			:limit="limit"
			:auto-upload="true"
			:before-upload="beforeUpload"
			:http-request="handleHttpUpload"
			:on-exceed="handleExceed"
			:on-error="uploadError"
			:drag="drag"
			:accept="fileType.join(',')"
		>
			<div class="upload-empty">
				<slot name="empty">
					<el-icon><Plus /></el-icon>
					<span>上传视频</span>
				</slot>
			</div>
			<template #file="{ file }">
				<video :src="file.url" class="upload-video" />
				<div class="uploading" v-if="progressType">
					<el-progress class="custom-width" width="90" :stroke-width="3" type="circle" :percentage="fileData.progress" />
				</div>
				<div class="upload-handle" @click.stop>
					<div class="handle-icon" @click="handlePictureCardPreview(file)">
						<el-icon><ZoomIn /></el-icon>
					</div>
					<div v-if="!self_disabled" class="handle-icon" @click="handleRemove(file)">
						<el-icon><Delete /></el-icon>
					</div>
				</div>
			</template>
		</el-upload>
		<div class="el-upload__tip">
			<slot name="tip"></slot>
		</div>
		<el-dialog v-model="dialogVisible" title="查看视频" width="500" :footer="false">
			<video :src="dialogVideoUrl" class="upload-video" controls />
		</el-dialog>
	</div>
</template>

<script setup lang="ts" name="UploadVideo">
import { ref, reactive, computed, inject, watch } from 'vue';
import OSS from 'ali-oss';
import { nanoid } from 'nanoid';
import { Plus } from '@element-plus/icons-vue';
import type { UploadProps, UploadFile, UploadUserFile } from 'element-plus';
import { getSsoUrl } from '/@/api/system';
import { ElNotification, formContextKey } from 'element-plus';

interface UploadFileProps {
	fileList: UploadUserFile[];
	api?: (params: any) => Promise<any>; // 上传视频的 api 方法，一般项目上传都是同一个 api 方法，在组件里直接引入即可 ==> 非必传
	fileCode?: string;
	drag?: boolean; // 是否支持拖拽上传 ==> 非必传（默认为 true）
	disabled?: boolean; // 是否禁用上传组件 ==> 非必传（默认为 false）
	limit?: number; // 最大视频上传数 ==> 非必传（默认为 1）
	fileSize?: number; // 视频大小限制 ==> 非必传（默认为 30M）
	fileType?: any[]; // 视频类型限制 ==> 非必传（默认为 ["video/mp4"]）
	height?: string; // 组件高度 ==> 非必传（默认为 150px）
	width?: string; // 组件宽度 ==> 非必传（默认为 150px）
}

const props = withDefaults(defineProps<UploadFileProps>(), {
	fileList: () => [],
	drag: true,
	disabled: false,
	limit: 1,
	fileSize: 30,
	fileType: () => ['video/mp4'],
	height: '120px',
	width: '120px',
	fileCode: 'product',
});

let ossData: any = reactive({
	accessKeyId: '',
	accessKeySecret: '',
	securityToken: '',
});

let ossClient: any;
const progressType = ref(false);
let fileData = reactive({
	progress: 0,
});

// 获取 el-form 组件上下文
const formContext = inject(formContextKey, void 0);
// 获取 el-form-item 组件上下文
// const formItemContext = inject(formItemContextKey, void 0);
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
	return props.disabled || formContext?.disabled;
});

// eslint-disable-next-line vue/no-setup-props-destructure
const _fileList = ref<UploadUserFile[]>(props.fileList);

// 监听 props.fileList 列表默认值改变
watch(
	() => props.fileList,
	(n: UploadUserFile[]) => {
		_fileList.value = n;
	}
);

/**
 * @description 文件上传之前判断
 * @param rawFile 选择的文件
 * */
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
	const videoSize = rawFile.size / 1024 / 1024 < props.fileSize;
	const videoType = props.fileType.includes(rawFile.type);
	if (!videoType)
		ElNotification({
			title: '温馨提示',
			message: '上传视频不符合所需的格式！',
			type: 'warning',
		});
	if (!videoSize)
		setTimeout(() => {
			ElNotification({
				title: '温馨提示',
				message: `上传视频大小不能超过 ${props.fileSize}M！`,
				type: 'warning',
			});
		}, 0);
	return videoType && videoSize;
};

/**
 * @description 视频上传
 * @param options upload 所有配置项
 * */
const handleHttpUpload = async (option: any) => {
	try {
		let res: any = await upload(option.file);
		if (res.res.status == 200) {
			const index = _fileList.value.findIndex((item) => item.uid === option.file.uid);
			_fileList.value.splice(index, 1, { url: res.res.requestUrls[0].split('?')[0], name: '' });
			emit('update:fileList', _fileList.value);
			option.onSuccess({ url: res.res.requestUrls[0].split('?')[0] });
		} else {
			option.onError('上传失败');
		}
	} catch (error) {
		option.onError(error as any);
	}
};

//获取oss临时上传
const getOSSUrl = async () => {
	progressType.value = false;
	await getSsoUrl({}).then((res: any) => {
		if (res.code == 200) {
			ossData = res.data;
			ossClient = new OSS({
				endPoint: ossData.endPoint,
				region: ossData.region, // 为存储空间对应的地域域名，根据你的实际情况填写，如https://oss-cn-hangzhou.aliyuncs.com
				accessKeyId: ossData.accessKeyId, //  你的阿里云accessKeyId
				accessKeySecret: ossData.accessKeySecret, //  临时token  accessKeySecret: credentials.accessKeySecret,  //  你的阿里云accessKeySecret
				stsToken: ossData.securityToken,
				bucket: ossData.bucket, //  oss存储桶的名称
				// secure: ossData.secure, //http是false，https是true
				secure: true,
			});
		}
	});
};

//oss切片上传
const upload = async (file: any) => {
	await getOSSUrl();
	const uuid = nanoid();
	const index = file.name.lastIndexOf('.');
	const suffix = file.name.substring(index + 1);
	const ossFileName = props.packageName + '/' + uuid + '.' + suffix;
	return ossClient.multipartUpload(ossFileName, file, {
		progress: function (p) {
			if (p == 0) {
				progressType.value = true;
			}
			if (p !== 1) {
				fileData.progress = Math.ceil(p * 100);
			} else {
				fileData.progress = 100;
				progressType.value = false;
			}
		},
		partSize: 20 * 1024 * 1024,
	});
};

/**
 * @description 视频上传成功
 * @param response 上传响应结果
 * @param uploadFile 上传的文件
 * */
interface UploadEmits {
	(e: 'update:fileList', value: UploadUserFile[]): void;
}
const emit = defineEmits<UploadEmits>();
// const uploadSuccess = (response: { fileUrl: string } | undefined, uploadFile: UploadFile) => {
//   if (!response) return;
//   uploadFile.url = response.fileUrl;
//   emit("update:fileList", _fileList.value);
//   // 调用 el-form 内部的校验方法（可自动校验）
//   formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
//   ElNotification({
//     title: "温馨提示",
//     message: "视频上传成功！",
//     type: "success"
//   });
// };

/**
 * @description 删除视频
 * @param file 删除的文件
 * */
const handleRemove = (file: UploadFile) => {
	// deleteOssFile(file.name);
	_fileList.value = _fileList.value.filter((item) => item.url !== file.url || item.name !== file.name);
	emit('update:fileList', _fileList.value);
};

/**
 * @description 视频上传错误
 * */
const uploadError = () => {
	ElNotification({
		title: '温馨提示',
		message: '视频上传失败，请您重新上传！',
		type: 'error',
	});
};

/**
 * @description 文件数超出
 * */
const handleExceed = () => {
	ElNotification({
		title: '温馨提示',
		message: `当前最多只能上传 ${props.limit} 个视频，请移除后上传！`,
		type: 'warning',
	});
};

/**
 * @description 视频预览
 * @param file 预览的文件
 * */
const dialogVideoUrl = ref('');
const dialogVisible = ref(false);
const handlePictureCardPreview: UploadProps['onPreview'] = (file) => {
	dialogVideoUrl.value = file.url!;
	dialogVisible.value = true;
};
</script>

<style scoped lang="scss">
.is-error {
	.upload {
		:deep(.el-upload--picture-card),
		:deep(.el-upload-dragger) {
			border: 1px dashed var(--el-color-danger) !important;
			&:hover {
				border-color: var(--el-color-primary) !important;
			}
		}
	}
}
:deep(.disabled) {
	.el-upload--picture-card,
	.el-upload-dragger {
		cursor: not-allowed;
		background: var(--el-disabled-bg-color) !important;
		border: 1px dashed var(--el-border-color-darker);
		&:hover {
			border-color: var(--el-border-color-darker) !important;
		}
	}
}
.upload-box {
	.no-border {
		:deep(.el-upload--picture-card) {
			border: none !important;
		}
	}
	:deep(.upload) {
		.el-upload-dragger {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 100%;
			height: 100%;
			padding: 0;
			overflow: hidden;
			border: 1px dashed var(--el-border-color-darker);
			border-radius: val(--el-border-radius-base);
			&:hover {
				border: 1px dashed var(--el-color-primary);
			}
		}
		.el-upload-dragger.is-dragover {
			background-color: var(--el-color-primary-light-9);
			border: 2px dashed var(--el-color-primary) !important;
		}
		.el-upload-list__item,
		.el-upload--picture-card {
			width: v-bind(width);
			height: v-bind(height);
			background-color: transparent;
			border-radius: val(--el-border-radius-base);
		}

		.uploading {
			position: absolute;
			top: 0;
			right: 0;
			box-sizing: border-box;
			display: flex;
			align-items: center;
			justify-content: center;
			width: 100%;
			height: 100%;
			cursor: pointer;
			background: rgb(0 0 0 / 60%);
			padding: 15px;
			box-sizing: border-box;
			.el-upload-list--picture-card .el-progress {
				width: 90px;
			}
		}
		.upload-handle {
			position: absolute;
			top: 0;
			right: 0;
			box-sizing: border-box;
			display: flex;
			align-items: center;
			justify-content: center;
			width: 100%;
			height: 100%;
			cursor: pointer;
			background: rgb(0 0 0 / 60%);
			opacity: 0;
			transition: var(--el-transition-duration-fast);
			.handle-icon {
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				padding: 0 6%;
				color: aliceblue;
				.el-icon {
					margin-bottom: 15%;
					font-size: 140%;
				}
				span {
					font-size: 100%;
				}
			}
		}
		.el-upload-list__item {
			&:hover {
				.upload-handle {
					opacity: 1;
				}
			}
		}
		.upload-empty {
			display: flex;
			flex-direction: column;
			align-items: center;
			font-size: 12px;
			line-height: 30px;
			color: var(--el-color-info);
			.el-icon {
				font-size: 28px;
				color: var(--el-text-color-secondary);
			}
		}
	}
	.el-upload__tip {
		line-height: 15px;
		text-align: left;
	}
}
.upload-video {
	width: 100%;
	height: 100%;
	object-fit: contain;
}
:deep(.hide_upload) {
	.el-upload--picture-card {
		display: none;
	}
}
</style>
<style lang="scss">
.custom-width.el-progress {
	width: 90px;
}
</style>
