文件内容 lib.js 11 KB 10897 字符
class ColumnDragSorter {
    // 使用示例
    // var COLUMN_ORDER_KEY="table_order"
    constructor(tableId) {
        if (tableId) {
            this.tableId = tableId;
            this.table = document.getElementById(tableId);
            this.draggedColumn = null;
            this.draggedColumnIndex = -1;
            this.isInitialized = false;//是否初始化
            // this.orginginColumnHeaders =null;//原始头部
            this.originalHeaders=null;//保存表头信息,用于查看第二页的时候,表头已经变更顺序了,但是tbody的顺序还是旧的,做一个描点
            this.init();
        }
        //可能只是用于清理
    }

    //初始化
    init() {
        if (this.isInitialized) return;
        this.saveOriginalOrder();
        var load = this.loadColumnOrder(true);
        if (!load) {
            this.bindDragEvents();
        }
        this.isInitialized = true;
    }

    //重新绑定顺序并绑定事件 外部表格数据重新载入后,重新执行绑定顺序
    rebind() {
        setTimeout(() => {
            var load = this.loadColumnOrder(false);
            if (!load) {
                this.bindDragEvents();
            } else {
                // console.log('从新加载了顺序');
            }
        }, 0);
    }

    // 生成基于页面路径的MD5加密key
    generateStorageKey() {
        const path = window.location.pathname + location.search;
        return 'table_column_order_' + this.md5(path);
    }

    // 简单的MD5实现(适用于这个场景)
    md5(input) {
        let hash = 0;
        if (input.length === 0) return hash.toString(16);
        for (let i = 0; i < input.length; i++) {
            const char = input.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash; // Convert to 32bit integer
        }
        return Math.abs(hash).toString(16);
    }

    /**
     * 清理当前页面缓存
     */
    cleanOrderCache() {
        var key = this.generateStorageKey();
        localStorage.removeItem(key);
        //清理后,UI动了没动?
        location.reload();
    }

    // 保存原始列顺序
    saveOriginalOrder() {
        const headers = this.getColumnHeaders();
        this.originalHeaders = headers;
        this.originalColumnOrder = headers.map(th => th.dataset.column);
    }

    // 获取所有表头
    getColumnHeaders() {
        return Array.from(this.table.querySelectorAll('thead th'));
    }

    // 获取所有数据行
    getDataRows() {
        return Array.from(this.table.querySelectorAll('tbody tr'));
    }

    // 加载保存的列顺序
    loadColumnOrder(isall) {
        const savedOrder = localStorage.getItem(this.generateStorageKey());
        if (savedOrder) {
            try {
                const order = JSON.parse(savedOrder);
                this.applyColumnOrder(order, isall);
                this.showStatus('已加载您保存的列顺序');
                return true;
            } catch (e) {
                console.error('加载列顺序失败:', e);
            }
        }
        return false;
    }

    // 应用列顺序
    applyColumnOrder(columnOrder, isall) {
        
        var headers =null;

        if(isall || this.originalHeaders==null){
            headers = this.getColumnHeaders();
        }else{
            headers = this.originalHeaders;
        }
        // const headers = this.getColumnHeaders();

        const rows = this.getDataRows();
        // console.log(headers);

        // 创建当前列的索引映射
        const currentIndexMap = new Map();
        headers.forEach((th, index) => {
            currentIndexMap.set(th.dataset.column, index);
        });

        if (isall) {
            // 重新构建表头
            const theadRow = this.table.querySelector('thead tr');
            const newHeaders = [];

            columnOrder.forEach(column => {
                const originalIndex = currentIndexMap.get(column);
                if (originalIndex !== undefined) {
                    newHeaders.push(headers[originalIndex].cloneNode(true));
                }
            });

            // 清空并重新添加表头
            theadRow.innerHTML = '';
            newHeaders.forEach(th => theadRow.appendChild(th));
        }
        // 重新构建数据行
        rows.forEach(row => {
            const cells = Array.from(row.querySelectorAll('td'));
            const newCells = [];

            columnOrder.forEach(column => {
                const originalIndex = currentIndexMap.get(column);
                if (originalIndex !== undefined && cells[originalIndex]) {
                    newCells.push(cells[originalIndex].cloneNode(true));
                }
            });

            // 替换行内容
            row.innerHTML = '';
            newCells.forEach(td => row.appendChild(td));
            // console.log('写入了新的行',row.innerHTML);
        });

        // 重新绑定事件
        this.bindDragEvents();
    }

    // 绑定拖拽事件 - 修复版本
    bindDragEvents() {
        const headers = this.getColumnHeaders();
        const dragHandles = this.table.querySelectorAll('.drag-handle');

        // 为拖拽手柄绑定事件
        dragHandles.forEach(handle => {
            // 移除旧事件,绑定新事件
            const newHandle = handle.cloneNode(true);
            handle.parentNode.replaceChild(newHandle, handle);

            newHandle.addEventListener('mousedown', (e) => {
                e.stopPropagation(); // 阻止事件冒泡到表头
                this.startDrag(e);
            });

            // 阻止拖拽手柄的点击事件冒泡到表头
            newHandle.addEventListener('click', (e) => {
                e.stopPropagation();
            });
        });

        // 表头点击事件(用于排序)
        headers.forEach(th => {
            th.addEventListener('click', (e) => {
                // 如果点击的是拖拽手柄,不触发排序
                if (e.target.classList.contains('drag-handle')) {
                    return;
                }
                // 这里可以添加表头排序逻辑
                // console.log('点击表头排序:', th.dataset.column);
            });
        });

        // 全局拖拽事件
        document.addEventListener('mousemove', (e) => {
            this.handleDrag(e);
        });

        document.addEventListener('mouseup', () => {
            this.endDrag();
        });
    }

    // 开始拖拽
    startDrag(e) {
        const handle = e.target;
        const th = handle.closest('th');
        const headers = this.getColumnHeaders();
        this.draggedColumn = th;
        this.draggedColumnIndex = headers.indexOf(th);

        th.classList.add('dragging');

        // 创建拖拽镜像
        this.dragGhost = th.cloneNode(true);
        this.dragGhost.style.position = 'absolute';
        this.dragGhost.style.opacity = '0.8';
        this.dragGhost.style.pointerEvents = 'none';
        this.dragGhost.style.zIndex = '1000';
        this.dragGhost.style.width = th.offsetWidth + 'px';
        document.body.appendChild(this.dragGhost);

        this.updateDragGhost(e);
    }

    // 处理拖拽
    handleDrag(e) {
        if (!this.draggedColumn) return;

        this.updateDragGhost(e);
        this.updateDropTarget(e);
    }

    // 更新拖拽镜像位置
    updateDragGhost(e) {
        if (!this.dragGhost) return;

        this.dragGhost.style.left = (e.clientX - this.dragGhost.offsetWidth / 2) + 'px';
        this.dragGhost.style.top = (e.clientY - 10) + 'px';
    }

    // 更新放置目标
    updateDropTarget(e) {
        const headers = this.getColumnHeaders();

        // 移除所有drop-target样式
        headers.forEach(header => {
            header.classList.remove('drop-target', 'drop-target-left', 'drop-target-right');
        });

        // 找到最近的表头
        const targetTh = document.elementFromPoint(e.clientX, e.clientY)?.closest('th');
        if (!targetTh || targetTh === this.draggedColumn) return;

        const targetRect = targetTh.getBoundingClientRect();
        const targetCenterX = targetRect.left + targetRect.width / 2;

        if (e.clientX < targetCenterX) {
            targetTh.classList.add('drop-target', 'drop-target-left');
        } else {
            targetTh.classList.add('drop-target', 'drop-target-right');
        }
    }

    // 结束拖拽
    endDrag() {
        if (!this.draggedColumn) return;

        const headers = this.getColumnHeaders();
        const targetTh = headers.find(th => th.classList.contains('drop-target'));

        if (targetTh && targetTh !== this.draggedColumn) {
            const targetIndex = headers.indexOf(targetTh);
            const targetRect = targetTh.getBoundingClientRect();
            const dropPosition = targetTh.classList.contains('drop-target-left') ? 'left' : 'right';

            this.moveColumn(this.draggedColumnIndex, targetIndex, dropPosition);
            this.saveColumnOrder();
        }

        // 清理
        this.draggedColumn.classList.remove('dragging');
        headers.forEach(header => {
            header.classList.remove('drop-target', 'drop-target-left', 'drop-target-right');
        });

        if (this.dragGhost) {
            this.dragGhost.remove();
            this.dragGhost = null;
        }

        this.draggedColumn = null;
        this.draggedColumnIndex = -1;
    }

    // 移动列 - 修复最后一列问题
    moveColumn(fromIndex, toIndex, dropPosition) {
        if (fromIndex === toIndex) return;

        const headers = this.getColumnHeaders();
        const rows = this.getDataRows();
        const theadRow = this.table.querySelector('thead tr');

        // 计算实际的目标位置
        let actualTargetIndex = toIndex;
        if (dropPosition === 'right' && toIndex < headers.length - 1) {
            actualTargetIndex = toIndex + 1;
        }

        // 如果是拖拽到最后一个位置的右边,直接放到最后
        if (dropPosition === 'right' && toIndex === headers.length - 1) {
            actualTargetIndex = headers.length;
        }

        const thToMove = headers[fromIndex];

        // 移动表头
        if (actualTargetIndex >= headers.length) {
            // 放到最后
            theadRow.appendChild(thToMove);
        } else {
            const thTarget = headers[actualTargetIndex];
            theadRow.insertBefore(thToMove, thTarget);
        }

        // 移动所有数据行的单元格
        rows.forEach(row => {
            const cells = Array.from(row.cells);
            const cellToMove = cells[fromIndex];

            if (actualTargetIndex >= cells.length) {
                // 放到最后
                row.appendChild(cellToMove);
            } else {
                const cellTarget = cells[actualTargetIndex];
                row.insertBefore(cellToMove, cellTarget);
            }
        });
    }

    // 保存列顺序
    saveColumnOrder() {
        const headers = this.getColumnHeaders();
        const currentOrder = headers.map(th => th.dataset.column);

        localStorage.setItem(this.generateStorageKey(), JSON.stringify(currentOrder));
        this.showStatus('列顺序已保存到本地');
    }

    // 重置列顺序
    resetColumnOrder() {
        this.applyColumnOrder(this.originalColumnOrder, true);
        this.showStatus('已重置为默认列顺序');
    }

    // 显示状态信息
    showStatus(message) {
        const statusEl = document.getElementById('status');
        if (statusEl) {
            statusEl.textContent = message;
            statusEl.style.display = 'block';
            setTimeout(() => {
                statusEl.style.display = 'none';
            }, 3000);
        }
    }
}
​ ​