<template>
	<el-dialog
		v-model="localVisible"
		:title="title"
		:width="width"
		:close-on-click-modal="false"
		@closed="handleCancel"
		@open="handleOpen"
		v-bind="$attrs"
	>
		<el-form
			ref="formRef"
			:model="modelValue"
			:rules="rules"
			:label-width="labelWidth"
			:label-position="labelPosition"
		>
			<el-row :gutter="gutter">
				<template v-for="(item, index) in fields" :key="item.prop">
					<el-col :span="item.span || 24">
						<el-form-item :label="item.label" :prop="item.prop" :rules="item.rules">
							<!-- 插槽优先 -->
							<slot :name="item.prop" :item="item" :form="modelValue">
								<component
									:is="getComponent(item.type)"
									v-model="modelValue[item.prop]"
									v-bind="getProps(item)"
								>
									<!-- 下拉选项 -->
									<template v-if="item.type === 'select'" #default>
										<el-option
											v-for="opt in item.options || []"
											:key="opt.value"
											:label="opt.label"
											:value="opt.value"
										/>
									</template>
									<template v-if="item.type === 'radio'">
										<el-radio
											v-model="modelValue[item.prop]"
											v-for="opt in item.options"
											:key="opt.value"
											:value="opt.value"
											v-bind="getProps(item)"
										>
											{{ opt.label }}
										</el-radio>
									</template>
								</component>
							</slot>
						</el-form-item>
					</el-col>
				</template>
			</el-row>
		</el-form>

		<!-- 操作按钮 -->
		<template #footer v-if="showFooter">
			<slot name="footer">
				<el-button @click="handleCancel">取消</el-button>
				<el-button type="primary" @click="handleSubmit">确定</el-button>
			</slot>
		</template>
	</el-dialog>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { PopupFormField } from '../type';
import UploadImg from '/@/components/uploadImg/index.vue'
defineOptions({ name: 'ModalForm' });

const props = defineProps<{
	visible: boolean;
	title?: string;
	width?: string;
	modelValue: Record<string, any>;
	fields: PopupFormField[];
	labelWidth?: string;
	labelPosition?: 'left' | 'right' | 'top';
	gutter?: number;
	showFooter?: boolean;
}>();

const emit = defineEmits<{
	(e: 'update:visible', val: boolean): void;
	(e: 'update:modelValue', val: Record<string, any>): void;
	(e: 'submit', val: any): void;
	(e: 'cancel'): void;
	(e: 'open'): void;
}>();

const formRef = ref<FormInstance>();
const localVisible = ref(props.visible);

watch(
	() => props.visible,
	(val) => (localVisible.value = val)
);
watch(localVisible, (val) => emit('update:visible', val));

const labelWidth = computed(() => props.labelWidth || '100px');
const labelPosition = computed(() => props.labelPosition || 'right');
const gutter = computed(() => props.gutter || 10);
const showFooter = computed(() => props.showFooter !== false);

const rules = computed<FormRules>(() => {
	const map: FormRules = {};
	props.fields.forEach((field) => {
		if (field.rules) map[field.prop] = field.rules;
	});
	return map;
});

const componentMap: Record<string, any> = {
	input: 'el-input',
	select: 'el-select',
	date: 'el-date-picker',
	dateTime: 'el-date-picker',
	daterange: 'el-date-picker',
	datetimerange: 'el-date-picker',
	switch: 'el-switch',
	radio: 'el-radio-group',
	checkbox: 'el-checkbox-group',
	upload: UploadImg,
	cascader: 'el-cascader',
	rate: 'el-rate',
	slider: 'el-slider',
	colorPicker: 'el-color-picker',
};

const getComponent = (type?: string) => componentMap[type || 'input'] || 'el-input';

const getProps = (item: PopupFormField) => {
	const props = item.componentProps || {};
	if (item.type === 'select') {
		return { ...props, filterable: true };
	}
	if (item.type === 'daterange' || item.type === 'datetimerange') {
		return {
			...props,
			type: item.type,
			'value-format': 'YYYY-MM-DD',
			'range-separator': '至',
			'start-placeholder': '开始日期',
			'end-placeholder': '结束日期',
		};
	}
	if (item.type === 'radio') {
		return {
			...props,
			size: props.size || 'default',
			disabled: props.disabled || false,
			type: props.button ? 'button' : props.type,
		};
	}

	return props;
};

const handleSubmit = async () => {
	if (!formRef.value) return;
	await formRef.value.validate((valid) => {
		const model = { ...props.modelValue };
		for (const key in model) {
			if (model[key] instanceof Date) {
				model[key] = model[key].toLocaleString();
			}
		}
		if (valid) emit('submit', model);
	});
};

const handleCancel = () => {
	emit('cancel');
	localVisible.value = false;
	formRef.value?.resetFields();
};

const handleOpen = () => {
	emit('open');
};

const loadOptions = async () => {
	const loaded = new Set();
	for (const field of props.fields) {
		if (field.fetchOptions && !loaded.has(field.fetchOptions)) {
			field.options = await field.fetchOptions();
			loaded.add(field.fetchOptions);
		}
	}
};

onMounted(loadOptions);
</script>

<style scoped>
.form-item-wrapper {
	display: block;
}
</style>
