<!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7166068828202336263 -->
<template>
	<!-- 表格主体 -->
	<div class="card table-main">
		<!-- 表格头部 操作按钮 -->
		<div class="table-header">
			<div class="header-button-lf">
				<slot name="tableHeader" :selected-list="selectedList" :selected-list-ids="selectedListIds" :is-selected="isSelected" />
			</div>
			<div v-if="toolButton" class="header-button-ri">
				<slot name="toolButton">
					<el-button v-if="showToolButton('refresh')" circle @click="onRefresh" title="刷新表格">
						<SvgIcon name="ele-Refresh" />
					</el-button>
					<el-button v-if="showToolButton('setting') && columns.length" circle @click="openColSetting" title="列设置">
						<SvgIcon name="ele-Setting" />
					</el-button>
					<el-button v-if="showToolButton('exportCurrent') && columns.length" circle @click="exportCurrent" title="导出当前页">
						<SvgIcon name="iconfont icon-daochu" />
					</el-button>
					<el-button v-if="showToolButton('exportAll') && columns.length" circle @click="exportAll" title="导出全部">
						<SvgIcon name="iconfont icon-export" />
					</el-button>
				</slot>
			</div>
		</div>
		<!-- 表格主体 -->
		<el-table
			ref="tableRef"
			v-bind="$attrs"
			:data="processTableData"
			:border="border"
			:row-key="rowKey"
			@selection-change="selectionChange"
			v-loading="loading"
		>
			<!-- 默认插槽 -->
			<slot />
			<template v-for="item in tableColumns" :key="item">
				<!-- selection || radio || index || expand || sort -->
				<el-table-column
					v-if="item.type && columnTypes.includes(item.type)"
					v-bind="item"
					:align="item.align ?? 'left'"
					:reserve-selection="item.type == 'selection'"
				>
					<template #default="scope">
						<!-- expand -->
						<template v-if="item.type == 'expand'">
							<component :is="item.render" v-bind="scope" v-if="item.render" />
							<slot v-else :name="item.type" v-bind="scope" />
						</template>
						<!-- radio -->
						<el-radio v-if="item.type == 'radio'" v-model="radio" :label="scope.row[rowKey]">
							<i></i>
						</el-radio>
						<!-- sort -->
						<el-tag v-if="item.type == 'sort'" class="move">
							<SvgIcon name="ele-Rank" />
						</el-tag>
					</template>
				</el-table-column>
				<!-- other -->
				<TableColumn v-if="!item.type && item.prop && item.isShow" :column="item" col-setting="">
					<template v-for="slot in Object.keys($slots)" #[slot]="scope">
						<slot :name="slot" v-bind="scope" />
					</template>
				</TableColumn>
			</template>
			<!-- 插入表格最后一行之后的插槽 -->
			<template #append>
				<slot name="append" />
			</template>
			<!-- 无数据 -->
			<template #empty>
				<Empty />
			</template>
		</el-table>
		<!-- 分页组件 -->
		<slot name="pagination">
			<PaginationEl v-if="pagination" @pagination="onRefresh" :total="total" v-model:current-page="pageIndex" v-model:page-size="pageSize" />
		</slot>
	</div>
	<!-- 列设置 -->
	<ColSetting v-if="toolButton" ref="colRef" v-model:col-setting="colSetting" @update:colSetting="updateColSetting" />
</template>

<script setup lang="ts" name="ProTable">
import { ref, provide, onMounted, unref, computed, reactive, PropType, watch } from 'vue';
import { ElTable } from 'element-plus';
import { useSelection } from '@/hooks/useSelection';
import { ColumnProps, TypeProps } from '@/components/ProTable/interface';
import PaginationEl from './components/Pagination.vue';
import ColSetting from './components/ColSetting.vue';
import TableColumn from './components/TableColumn.vue';
import Sortable from 'sortablejs';
import { handleProp } from '@/utils/tools';

// 接受父组件参数，配置默认值
const props = defineProps({
	columns: {
		// 列配置项  ==> 必传
		type: Array as PropType<ColumnProps[]>,
		required: true,
	},
	data: {
		// 静态 table data 数据
		type: Array,
		default: () => [],
		required: true,
	},
	pagination: {
		// 是否需要分页组件 ==> 非必传（默认为true）
		type: Boolean,
		default: true,
	},
	total: {
		type: Number,
		default: 0,
	},
	pageIndex: {
		type: Number,
		default: 1,
	},
	pageSize: {
		type: Number,
		default: 10,
	},
	border: {
		// 是否带有纵向边框 ==> 非必传（默认为false）
		type: Boolean,
		default: false,
	},
	toolButton: {
		// 是否显示表格功能按钮 ==> 非必传（默认为true）
		type: [Array, Boolean],
		// default: true,
		default: ['refresh', 'setting'],
	},
	rowKey: {
		// 行数据的 Key，用来优化 Table 的渲染，当表格数据多选时，所指定的 id ==> 非必传（默认为 id）
		type: String,
		default: 'id',
	},
	loading: {
		// 是否显示加载中
		type: Boolean,
		default: false,
	},
	radio: {
		type: String,
		default: '',
	},
});
const emit = defineEmits([
	'dargSort',
	'updateColSetting',
	'selectChange',
	'updateTable',
	'update:pagination',
	'exportCurrent',
	'exportAll',
	'update:pageSize',
	'update:pageIndex',
	'update:radio',
]);
const pageSize = computed({
	get() {
		return props.pageSize;
	},
	set(val) {
		emit('update:pageSize', val);
	},
});
const pageIndex = computed({
	get() {
		return props.pageIndex;
	},
	set(val) {
		emit('update:pageIndex', val);
	},
});
const radio = computed({
	get() {
		return props.radio;
	},
	set(val) {
		emit('update:radio', val);
	},
});
// table 实例
const tableRef = ref<InstanceType<typeof ElTable>>();

// column 列类型
const columnTypes: TypeProps[] = ['selection', 'radio', 'index', 'expand', 'sort'];

// 控制 ToolButton 显示
const showToolButton = (key: 'refresh' | 'setting' | 'exportCurrent' | 'exportAll') => {
	return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton;
};

// 表格多选 Hooks
const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey);

// 清空选中数据列表
const clearSelection = () => tableRef.value!.clearSelection();

// 初始化表格数据 && 拖拽排序
onMounted(() => {
	dragSort();
	getSelectColumns();
});

// 处理表格数据
const processTableData = computed(() => {
	if (!props.pagination) return props.data;
	return props.data;
});

// 接收 columns 并设置为响应式
const tableColumns = reactive<ColumnProps[]>(props.columns);

// 扁平化 columns
const flatColumns = computed(() => flatColumnsFunc(tableColumns));
// 定义 enumMap 存储 enum 值（避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择）
const enumMap = ref(new Map<string, { [key: string]: any }[]>());
const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => {
	if (!enumValue) return;

	// 如果当前 enumMap 存在相同的值 return
	if (enumMap.value.has(prop!) && (typeof enumValue === 'function' || enumMap.value.get(prop!) === enumValue)) return;

	// 当前 enum 为静态数据，则直接存储到 enumMap
	if (typeof enumValue !== 'function') return enumMap.value.set(prop!, unref(enumValue!));

	// 为了防止接口执行慢，而存储慢，导致重复请求，所以预先存储为[]，接口返回后再二次存储
	enumMap.value.set(prop!, []);

	// 当前 enum 为后台数据需要请求数据，则调用该请求接口，并存储到 enumMap
	const { data } = await enumValue();
	enumMap.value.set(prop!, data);
};

// 注入 enumMap
provide('enumMap', enumMap);

// 扁平化 columns 的方法
const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => {
	columns.forEach(async (col) => {
		if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
		flatArr.push(col);

		// column 添加默认 isShow
		col.isShow = col.isShow ?? true;
		// 设置 enumMap
		await setEnumMap(col);
	});
	return flatArr.filter((item) => !item._children?.length);
};

// 过滤需要搜索的配置项 && 排序
const searchColumns = computed(() => {
	return flatColumns.value?.filter((item) => item.search?.el || item.search?.render).sort((a, b) => a.search!.order! - b.search!.order!);
});

// 设置 搜索表单默认排序 && 搜索表单项的默认值
searchColumns.value?.forEach((column, index) => {
	column.search!.order = column.search?.order ?? index + 2;
	const key = column.search?.key ?? handleProp(column.prop!);
	const defaultValue = column.search?.defaultValue;
});

// 列设置 ==> 需要过滤掉不需要设置的列
const colRef = ref();
const colSetting = tableColumns!.filter((item) => {
	const { type, prop, isShow } = item;
	return !columnTypes.includes(type!) && prop !== 'operation' && isShow;
});
const openColSetting = () => colRef.value.openColSetting();

// 拖拽排序
const dragSort = () => {
	const tbody = document.querySelector('.el-table__body-wrapper tbody') as HTMLElement;
	Sortable.create(tbody, {
		handle: '.move',
		animation: 300,
		onEnd({ newIndex, oldIndex }) {
			const [removedItem] = processTableData.value.splice(oldIndex!, 1);
			processTableData.value.splice(newIndex!, 0, removedItem);
			emit('dargSort', { newIndex, oldIndex });
		},
	});
};
// 刷新事件
const onRefresh = () => {
	clearSelection();
	emit('updateTable');
};
const filterColumns = ref([]);
const filterColumnsProp = ref([]);
// 获取需要显示的列
const getSelectColumns = () => {
	const flatColumns = flatColumnsFunc(tableColumns);
	filterColumns.value = flatColumns.filter((item) => item.isShow && item.prop !== 'operation' && item.prop);
	filterColumnsProp.value = filterColumns.value.map((item) => item.prop);
};
// 更新列设置
const updateColSetting = () => {
	getSelectColumns();
	emit('updateColSetting');
};
// 导出当前页
const exportCurrent = () => {
	emit('exportCurrent');
};
// 导出全部
const exportAll = () => {
	emit('exportAll');
};
watch(
	// 监听table数据改变后重置选择
	() => props.data,
	() => {
		clearSelection();
	}
);
// 暴露给父组件的参数和方法 (外部需要什么，都可以从这里暴露出去)
defineExpose({
	element: tableRef,
	tableData: processTableData,
	radio,
	clearSelection,
	enumMap,
	isSelected,
	selectedList,
	selectedListIds,
	filterColumns,
	filterColumnsProp,
});
</script>
<style scoped lang="scss">
.table-header {
	margin-bottom: 15px;
	display: flex;
	justify-content: space-between;
}
.header-button-lf {
	flex: 1;
}
.header-button-ri {
}
</style>
