文件内容 index.html 17 KB 17409 字符
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表格列拖拽排序</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            padding: 20px;
            background: #f5f5f5;
        }

        .container {
            max-width: 1000px;
            margin: 0 auto;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            padding: 20px;
        }

        h1 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 20px;
        }

        th,
        td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #ddd;
            position: relative;
        }

        th {
            background-color: #f8f9fa;
            font-weight: 600;
            user-select: none;
            cursor: pointer;
            /* 表头整体可以点击排序 */
        }

        th:hover {
            background-color: #e9ecef;
        }

        th.dragging {
            opacity: 0.6;
            background: #e3f2fd;
        }

        th.drop-target::before {
            content: '';
            position: absolute;
            top: 0;
            bottom: 0;
            width: 2px;
            background: #007bff;
        }

        th.drop-target-left::before {
            left: -1px;
        }

        th.drop-target-right::before {
            right: -1px;
        }

        .drag-handle {
            position: absolute;
            right: 8px;
            top: 50%;
            transform: translateY(-50%);
            color: #666;
            cursor: grab;
            padding: 4px;
        }

        .drag-handle:active {
            cursor: grabbing;
        }

        .drag-handle:hover {
            background: #e9ecef;
            border-radius: 3px;
        }

        .actions {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        button {
            padding: 8px 16px;
            border: 1px solid #ddd;
            background: white;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.2s;
        }

        button:hover {
            background: #f0f0f0;
        }

        .status {
            padding: 10px;
            background: #e8f5e9;
            border-radius: 4px;
            margin-top: 10px;
            display: none;
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>表格列拖拽排序</h1>

        <div class="actions">
            <button onclick="saveColumnOrder()">保存列顺序</button>
            <button onclick="resetColumnOrder()">重置列顺序</button>
            <button onclick="clearColumnOrder()">清除本地设置</button>
        </div>

        <table id="data-table">
            <thead>
                <tr>
                    <th data-column="name">
                        姓名
                        <span class="drag-handle">⋮⋮</span>
                    </th>
                    <th data-column="age">
                        年龄
                        <span class="drag-handle">⋮⋮</span>
                    </th>
                    <th data-column="height">
                        身高
                        <span class="drag-handle">⋮⋮</span>
                    </th>
                    <th data-column="department">
                        部门
                        <span class="drag-handle">⋮⋮</span>
                    </th>
                    <th data-column="position">
                        职位
                        <span class="drag-handle">⋮⋮</span>
                    </th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>张三</td>
                    <td>28</td>
                    <td>175cm</td>
                    <td>技术部</td>
                    <td>前端工程师</td>
                </tr>
                <tr>
                    <td>李四</td>
                    <td>32</td>
                    <td>180cm</td>
                    <td>设计部</td>
                    <td>UI设计师</td>
                </tr>
                <tr>
                    <td>王五</td>
                    <td>25</td>
                    <td>172cm</td>
                    <td>技术部</td>
                    <td>后端工程师</td>
                </tr>
                <tr>
                    <td>赵六</td>
                    <td>35</td>
                    <td>178cm</td>
                    <td>产品部</td>
                    <td>产品经理</td>
                </tr>
            </tbody>
        </table>

        <div id="status" class="status"></div>
    </div>

    <script>

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

        // 简单的MD5实现(适用于这个场景)
        function 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);
        }

        // 使用示例
        const COLUMN_ORDER_KEY = generateStorageKey();

        // 存储键名
        // const COLUMN_ORDER_KEY = 'pasteform_column_order';

        class ColumnDragSorter {
            constructor(tableId) {
                this.tableId = tableId;
                this.table = document.getElementById(tableId);
                this.draggedColumn = null;
                this.draggedColumnIndex = -1;
                this.isInitialized = false;

                this.init();
            }

            init() {
                if (this.isInitialized) return;

                this.saveOriginalOrder();
                this.loadColumnOrder();
                this.bindDragEvents();
                this.isInitialized = true;
            }

            // 保存原始列顺序
            saveOriginalOrder() {
                const headers = this.getColumnHeaders();
                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() {
                const savedOrder = localStorage.getItem(COLUMN_ORDER_KEY);
                if (savedOrder) {
                    try {
                        const order = JSON.parse(savedOrder);
                        this.applyColumnOrder(order);
                        this.showStatus('已加载您保存的列顺序');
                    } catch (e) {
                        console.error('加载列顺序失败:', e);
                    }
                }
            }

            // 应用列顺序
            applyColumnOrder(columnOrder) {
                const headers = this.getColumnHeaders();
                const rows = this.getDataRows();

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

                // 重新构建表头
                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));
                });

                // 重新绑定事件
                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(COLUMN_ORDER_KEY, JSON.stringify(currentOrder));
                this.showStatus('列顺序已保存到本地');
            }

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

            // 显示状态信息
            showStatus(message) {
                const statusEl = document.getElementById('status');
                statusEl.textContent = message;
                statusEl.style.display = 'block';

                setTimeout(() => {
                    statusEl.style.display = 'none';
                }, 3000);
            }
        }

        // 全局函数
        let columnSorter;

        function saveColumnOrder() {
            if (columnSorter) {
                columnSorter.saveColumnOrder();
            }
        }

        function resetColumnOrder() {
            if (columnSorter) {
                columnSorter.resetColumnOrder();
            }
        }

        function clearColumnOrder() {
            localStorage.removeItem(COLUMN_ORDER_KEY);
            if (columnSorter) {
                columnSorter.resetColumnOrder();
            }
        }

        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            // columnSorter = new ColumnDragSorter('data-table');
            //启用这个规则
        });
    </script>
</body>

</html>
​ ​