<template>
    <component :is="render" />
</template>
<script setup lang="jsx">
    import { ElTable } from 'element-plus/es/components/table/index';
    import 'element-plus/es/components/table/style/css';
    import Sortable from 'sortablejs';
    const props = defineProps({
        /**
         * 是否可拖拽排序
         */
        sortable: {
            type: Boolean,
            default: false,
        },
        /**
         * 拖拽表格唯一标识，用于区分多个拖拽区域互相拖拽时数据来源
         */
        code: {
            type: String,
            default: '',
        },
        /**
         * el-table原生属性，表格数据，代理element原生属性，用于修改数据为排序后的数据
         */
        data: {
            type: Array,
            required: true,
        },
        /**
         * el-table原生属性，唯一标识属性字段名，默认id
         */
        rowKey: {
            type: String,
            default: 'id',
        },
        /**
         * 排序属性字段名，默认sort
         */
        sortKey: {
            type: String,
            default: 'sort',
        },
        /**
         * 控制是否修改表格数据排序，默认修改，如果不修改则只改变表格渲染顺序但是真实数据顺序不变
         */
        modifyData: {
            type: Boolean,
            default: true,
        },
        /**
         * sortablejs原生属性，分组，详细用法见官方文档
         * http://www.sortablejs.com/options.html#:~:text=group%EF%BC%9Astring%20or%20object
         */
        group: {
            type: [String, Object],
            default: '',
        },
        /**
         * sortablejs原生属性，是否列表单元
         */
        sort: {
            type: Boolean,
            default: true,
        },
        /**
         * sortablejs原生属性，是否此sortable对象是否可用
         */
        disabled: {
            type: Boolean,
            default: false,
        },
        /**
         * sortablejs原生属性，动画持续时间
         */
        animation: {
            type: Number,
            default: 150,
        },
        /**
         * sortablejs原生属性，可拖拽元素
         */
        draggable: {
            type: String,
            default: 'el-table__row',
        },
        /**
         * sortablejs原生属性，元素可拖拽部分选择器，默认为空表示元素任意部分都可拖拽
         */
        handle: {
            type: String,
            default: '',
        },
        /**
         * sortablejs原生属性，幽灵元素
         */
        ghostClass: {
            type: String,
            default: 'ghost-row',
        },
        /**
         * sortablejs原生属性，选中的元素
         */
        chosenClass: {
            type: String,
            default: 'chosen-row',
        },
        /**
         * sortablejs原生属性，拖拽的元素
         */
        dragClass: {
            type: String,
            default: 'drag-row',
        },
        /**
         * sortablejs原生属性，不允许拖拽元素选择器
         */
        filter: {
            type: String,
            default: '.ignore-drag-sort',
        },
    });
    const attrs = useAttrs();
    const slots = useSlots();
    const emits = defineEmits(['row-sort', 'row-add', 'row-remove', 'row-clone', 'expand-change']);

    // 可拖拽表格唯一标识
    const uid = 'sortableTable' + getCurrentInstance().uid;
    const { proxy } = getCurrentInstance();
    // 表格ref
    const sortting = ref(false);
    const refsTable = ref(null);
    const expandRowKeys = ref([]);
    const handleExpandChange = (row, expandRows) => {
        expandRowKeys.value = expandRows.map((item) => item[props.rowKey]);
        emits('expand-change', row, expandRows);
    };
    // sortablejs实例
    let sortable = ref(null);
    // 初始化sortablejs
    const handleInit = () => {
        // 获取拖拽区域元素
        const el = document.querySelector(`[sort-id='${uid}'] :not(.el-table) tbody`);
        if (el) {
            sortable.value = new Sortable(el, {
                group: props.group,
                sort: props.sort,
                disabled: !props.sortable || props.disabled,
                animation: props.animation,
                draggable: '.' + props.draggable,
                handle: props.handle,
                ghostClass: props.ghostClass,
                chosenClass: props.chosenClass,
                dragClass: props.dragClass,
                filter: props.filter,
                onUpdate(e) {
                    if (typeof e.newIndex === 'number') {
                        if (props.modifyData) {
                            // 开始对数据进行重新排序
                            const row = unref(props.data).splice(e.oldIndex, 1)[0];
                            unref(props.data).splice(e.newIndex, 0, row);
                            // 刷新排序属性
                            unref(props.data).forEach((item, index) => {
                                item[props.sortKey] = index + 1;
                            });
                            proxy.$nextTick(() => {
                                // 排序完成
                                proxy.$nextTick(() => {
                                    proxy.$forceUpdate();
                                    // 触发排序完成事件
                                    emits('row-sort', e.newIndex, e.oldIndex, e);
                                });
                            });
                        } else {
                            // 触发排序完成事件
                            emits('row-sort', e.newIndex, e.oldIndex, e);
                        }
                    }
                },
                setData(dataTransfer) {
                    dataTransfer.setData('code', props.code);
                },
                onAdd(e) {
                    // 从sort-id为这个的表格里面拖过来的元素，需要自行根据表格元素判断数据来源
                    let code = e.originalEvent.dataTransfer.getData('code');
                    // 触发添加元素事件
                    emits(
                        'row-add',
                        e.newIndex,
                        e.oldIndex,
                        code,
                        (row) => {
                            if (props.modifyData && row) {
                                sortting.value = true;
                                unref(props.data).splice(e.newIndex, 0, row);
                                // 通过v-if强行重新渲染表格，否则表格可能渲染错误
                                proxy.$nextTick(() => {
                                    sortting.value = false;
                                    proxy.$nextTick(() => {
                                        handleInit();
                                    });
                                });
                            }
                        },
                        e
                    );
                },
                onRemove(e) {
                    if (typeof e.newIndex === 'number') {
                        if (props.modifyData && e.pullMode !== 'clone') {
                            unref(props.data).splice(e.oldIndex, 1);
                            proxy.$forceUpdate();
                        }
                        // 触发添加元素事件
                        emits('row-remove', e.newIndex, e.oldIndex, e);
                    }
                },
                onClone(e) {
                    if (typeof e.newIndex === 'number') {
                        // 触发赋值元素事件
                        emits('row-clone', e.newIndex, e.oldIndex, e);
                    }
                },
            });
        } else {
            console.error('可拖拽表格ID不存在');
        }
    };
    // 元素实例化后初始化sortablejs
    onMounted(handleInit);
    // 代理原生函数
    const handleProxy = (fnName, args) => {
        return unref(refsTable)[fnName]?.apply(unref(refsTable), args);
    };
    const clearSelection = function () {
        return handleProxy('clearSelection', arguments);
    };
    const toggleRowSelection = function () {
        return handleProxy('toggleRowSelection', arguments);
    };
    const toggleAllSelection = function () {
        return handleProxy('toggleAllSelection', arguments);
    };
    const toggleRowExpansion = function () {
        return handleProxy('toggleRowExpansion', arguments);
    };
    const setCurrentRow = function () {
        return handleProxy('setCurrentRow', arguments);
    };
    const clearSort = function () {
        return handleProxy('clearSort', arguments);
    };
    const clearFilter = function () {
        return handleProxy('clearFilter', arguments);
    };
    const doLayout = function () {
        return handleProxy('doLayout', arguments);
    };
    const sort = function () {
        return handleProxy('sort', arguments);
    };
    // 抛出
    defineExpose({
        refsTable,
        clearSelection,
        toggleRowSelection,
        toggleAllSelection,
        toggleRowExpansion,
        setCurrentRow,
        clearSort,
        clearFilter,
        doLayout,
        sort,
    });
    const render = () => (
        <ElTable
            ref={refsTable}
            {...props}
            {...attrs}
            class={{ 'sortable-table': true, [props.group]: true }}
            expand-row-keys={unref(expandRowKeys)}
            row-key={props.rowKey}
            sort-id={unref(uid)}
            onExxpandChange={() => handleExpandChange}
            v-slots={unref(sortting) ? {} : slots}
        />
    );
</script>

<style lang="less" scoped>
    :deep(.ghost-row) {
        background: #ddd;
    }
    :deep(.chosen-row) {
        background: #eee;
    }
    :deep(.drag-row) {
        background: #ccc;
    }
</style>
