<template>
	<a-modal
		v-model:open="visible"
		:wrap-class-name="fixedWrapClassName"
		:title="title"
		:confirm-loading="confirmLoading"
		:width="width"
		force-render
		@ok="handleOk"
		@cancel="reset"
	>
		<div
			ref="modalContentWrapperRef"
			:class="`modal-content-wrapper ${modalContentWrapperClassName}`"
			:style="{ maxHeight }"
		>
			<div :class="fixedScrollContentClassName">
				<form-render
					ref="formRender"
					v-model="bindValue"
					:config="config"
					:popup-container-id="wrapClassId"
					:items="formItems"
					:disabled-fields="disabledFields"
					:hidden-fields="hiddenFields"
					v-bind="$attrs"
				>
					<!-- 透传插槽 -->
					<template v-for="(item, key, index) in $slots" :key="index" #[key]>
						<template v-if="key != 'modal-footer'"><slot :name="key" :item="item" /></template>
					</template>
				</form-render>
			</div>
		</div>
	</a-modal>
</template>
<script lang="ts" setup>
	import XEUtils from 'xe-utils'
	import { FormConfig, FormItem, FormValueType } from './form-render-types'
	import FormRender from './form-render.vue'
	import { NamePath } from 'ant-design-vue/es/form/interface'
	const props = defineProps({
		wrapClassName: { type: String, default: '' },
		modalContentWrapperClassName: { type: String, default: '' },
		modelValue: {
			type: Object as PropType<Record<string, FormValueType>>,
			required: true,
		},
		title: {
			type: String,
			default: () => {
				return undefined
			},
		},
		// 表单项
		formItems: {
			type: Array as PropType<Array<FormItem>>,
			default: () => {
				return []
			},
		},
		config: {
			type: Object as PropType<FormConfig>,
			required: false,
			default: () => {
				return {
					layout: 'horizontal',
					colon: true,
					hideRequiredMark: false,
					labelAlign: 'right',
					labelType: 'fixed',
					scrollToFirstError: false,
					validateOnRuleChange: true,
					labelCol: { span: 4 },
				}
			},
		},
		disabledFields: {
			type: Array as PropType<Array<string>>,
			required: false,
			default: () => {
				return []
			},
		},
		// 要隐藏的表单项
		hiddenFields: {
			type: Array as PropType<Array<string>>,
			required: false,
			default: () => {
				return []
			},
		},
		// 最大高度
		maxHeight: {
			type: [String, Number],
			required: false,
			default: 'none',
		},
		// 弹框的宽
		width: {
			type: String,
			required: false,
			default: '550px',
		},
	})
	const { modelValue, title, wrapClassName, maxHeight, width, config, disabledFields, hiddenFields } = toRefs(props)

	const wrapClassId = ref(XEUtils.uniqueId())
	// 是否设置了最大高度
	const hasMaxHeight = computed(() => {
		return maxHeight.value != 'none'
	})

	// 滚动部分的 class
	const fixedScrollContentClassName = computed(() => {
		return 'scroll-content ' + (hasMaxHeight.value ? wrapClassId.value : '')
	})
	// 滚动外部的 class
	const fixedWrapClassName = computed(() => {
		/**
		 * 没有最大高度，不会滚动，隐藏 select 等下拉弹框挂载到滚动外边
		 * 避免下拉菜单被弹框的 header 和 footer 挡住
		 */
		const maxHeightClassName = hasMaxHeight.value ? ' max-height-modal' : ` ${wrapClassId.value}`
		return wrapClassName.value + maxHeightClassName
	})
	const emit = defineEmits<{
		'update:model-value': [value: Record<string, FormValueType>]
		ok: [value: Record<string, FormValueType>]
	}>()

	// 表单数据的双向绑定
	const bindValue = computed({
		get: function () {
			return modelValue.value
		},
		set: function (value) {
			emit('update:model-value', value)
		},
	})
	const visible = ref(false)
	const confirmLoading = ref(false)

	const formRender = ref<typeof FormRender>()

	// 清空表单状态
	const reset = () => {
		bindValue.value = {}
		confirmLoading.value = false
		formRender.value?.clearValidate()
	}

	// 打开弹框
	const open = () => {
		visible.value = true
	}

	// 关闭弹框，并清空表单状态
	const close = () => {
		reset()
		visible.value = false
		confirmLoading.value = false
	}

	const handleOk = async () => {
		if (!formRender.value) {
			throw new Error('获取 formRender 失败')
		}
		try {
			confirmLoading.value = true
			await formRender.value.validateFields()
			emit('ok', bindValue.value)
		} catch (errorInfo) {
			console.error('errorInfo', errorInfo)
			confirmLoading.value = false
		}
	}

	// 校验表单
	const validateFields = async (nameList?: Array<NamePath>) => {
		if (!formRender.value) {
			throw new Error('获取 formRender 失败')
		}
		return await formRender.value.validateFields(nameList)
	}
	// 重置表单
	const resetFields = () => {
		formRender.value?.resetFields()
	}

	const modalContentWrapperRef = ref()
	const getModalContentWrapper = () => {
		return modalContentWrapperRef.value
	}

	const getPopupContainerId = () => {
		return wrapClassId.value
	}

	// 关闭loading
	const closeLoading = () => {
		confirmLoading.value = false
	}

	defineExpose({
		open,
		close,
		reset,
		validateFields,
		resetFields,
		getModalContentWrapper,
		getPopupContainerId,
		closeLoading,
	})
</script>
<style lang="css" scoped>
	/* 有最大高度时的滚动内容区域，用于挂载 select 等的弹出框组件 */
	.scroll-content {
		position: relative;
	}
</style>
