MediaWiki

MediaWiki:Gadget-TierListMaker.js

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献2025年10月6日 (一) 18:08的版本

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
/**
 * 战斗员图鉴分级表制作工具
 * 支持拖放卡片到表格并导出为PNG
 */
(function() {
    'use strict';

    // 仅在特定页面启用
    if (mw.config.get('wgPageName') !== '节奏榜') {
        return;
    }

    // 等待页面加载完成
    mw.loader.using(['mediawiki.util', 'mediawiki.api']).then(function() {
        init();
    });

    function init() {
        // 添加控制按钮
        addControlPanel();
        
        // 使卡片可拖动
        makeCardsDraggable();
        
        // 使表格单元格可接收
        makeTableDroppable();
        
        // 添加样式
        addStyles();
        
        // 恢复面板状态
        restorePanelState();
    }

    function addControlPanel() {
        var $panel = $('<div>')
            .attr('id', 'tierlist-control-panel')
            .css({
                'position': 'fixed',
                'top': '100px',
                'right': '20px',
                'z-index': '9999',
                'background': '#fff',
                'border': '2px solid #0645ad',
                'border-radius': '8px',
                'box-shadow': '0 4px 6px rgba(0,0,0,0.1)',
                'min-width': '200px',
                'transition': 'transform 0.3s ease'
            });

        // 标题栏(可点击折叠)
        var $header = $('<div>')
            .attr('id', 'tierlist-panel-header')
            .css({
                'background': '#0645ad',
                'color': '#fff',
                'padding': '10px 15px',
                'border-radius': '6px 6px 0 0',
                'cursor': 'pointer',
                'user-select': 'none',
                'display': 'flex',
                'justify-content': 'space-between',
                'align-items': 'center'
            })
            .hover(
                function() { $(this).css('background', '#0b5394'); },
                function() { $(this).css('background', '#0645ad'); }
            );

        var $title = $('<h3>')
            .text('分级表工具')
            .css({
                'margin': '0',
                'font-size': '16px',
                'flex': '1'
            });

        var $toggleIcon = $('<span>')
            .attr('id', 'tierlist-toggle-icon')
            .text('▼')
            .css({
                'font-size': '12px',
                'transition': 'transform 0.3s ease'
            });

        $header.append($title, $toggleIcon);
        $header.click(togglePanel);

        // 内容区域
        var $content = $('<div>')
            .attr('id', 'tierlist-panel-content')
            .css({
                'padding': '15px'
            });

        var $exportBtn = $('<button>')
            .text('📸 导出为PNG')
            .css({
                'width': '100%',
                'padding': '10px',
                'margin': '5px 0',
                'background': '#0645ad',
                'color': '#fff',
                'border': 'none',
                'border-radius': '4px',
                'cursor': 'pointer',
                'font-size': '14px',
                'font-weight': 'bold',
                'transition': 'background 0.2s'
            })
            .hover(
                function() { $(this).css('background', '#0b5394'); },
                function() { $(this).css('background', '#0645ad'); }
            )
            .click(exportToPNG);

        var $clearBtn = $('<button>')
            .text('🗑️ 清空表格')
            .css({
                'width': '100%',
                'padding': '10px',
                'margin': '5px 0',
                'background': '#d33',
                'color': '#fff',
                'border': 'none',
                'border-radius': '4px',
                'cursor': 'pointer',
                'font-size': '14px',
                'font-weight': 'bold',
                'transition': 'background 0.2s'
            })
            .hover(
                function() { $(this).css('background', '#a00'); },
                function() { $(this).css('background', '#d33'); }
            )
            .click(clearTable);

        var $toggleBtn = $('<button>')
            .text('👁️ 切换预览模式')
            .attr('id', 'toggle-edit-mode')
            .css({
                'width': '100%',
                'padding': '10px',
                'margin': '5px 0',
                'background': '#36c',
                'color': '#fff',
                'border': 'none',
                'border-radius': '4px',
                'cursor': 'pointer',
                'font-size': '14px',
                'font-weight': 'bold',
                'transition': 'background 0.2s'
            })
            .hover(
                function() { $(this).css('background', '#258'); },
                function() { $(this).css('background', '#36c'); }
            )
            .click(toggleEditMode);

        var $info = $('<div>')
            .css({
                'margin-top': '10px',
                'padding': '10px',
                'background': '#f8f9fa',
                'border-radius': '4px',
                'font-size': '12px',
                'color': '#666',
                'line-height': '1.6'
            })
            .html('<strong>使用说明:</strong><br>1. 拖动卡片到表格<br>2. 右键点击可删除<br>3. 点击标题栏折叠<br>4. 导出前切换预览');

        $content.append($exportBtn, $clearBtn, $toggleBtn, $info);
        $panel.append($header, $content);
        $('body').append($panel);

        // 添加最小化按钮(在面板外)
        var $minimizeBtn = $('<button>')
            .attr('id', 'tierlist-minimize-btn')
            .text('📋')
            .css({
                'position': 'fixed',
                'top': '100px',
                'right': '20px',
                'z-index': '9998',
                'width': '50px',
                'height': '50px',
                'background': '#0645ad',
                'color': '#fff',
                'border': '2px solid #0645ad',
                'border-radius': '50%',
                'cursor': 'pointer',
                'font-size': '24px',
                'display': 'none',
                'box-shadow': '0 4px 6px rgba(0,0,0,0.1)',
                'transition': 'all 0.2s'
            })
            .hover(
                function() { 
                    $(this).css({
                        'background': '#0b5394',
                        'transform': 'scale(1.1)'
                    }); 
                },
                function() { 
                    $(this).css({
                        'background': '#0645ad',
                        'transform': 'scale(1)'
                    }); 
                }
            )
            .click(function() {
                showPanel();
            });

        $('body').append($minimizeBtn);
    }

    function togglePanel() {
        var $content = $('#tierlist-panel-content');
        var $icon = $('#tierlist-toggle-icon');
        var $panel = $('#tierlist-control-panel');
        
        if ($content.is(':visible')) {
            // 折叠
            $content.slideUp(300);
            $icon.css('transform', 'rotate(-90deg)');
            localStorage.setItem('tierlist-panel-collapsed', 'true');
        } else {
            // 展开
            $content.slideDown(300);
            $icon.css('transform', 'rotate(0deg)');
            localStorage.setItem('tierlist-panel-collapsed', 'false');
        }
    }

    function hidePanel() {
        var $panel = $('#tierlist-control-panel');
        var $minimizeBtn = $('#tierlist-minimize-btn');
        
        $panel.fadeOut(300);
        $minimizeBtn.fadeIn(300);
        localStorage.setItem('tierlist-panel-hidden', 'true');
    }

    function showPanel() {
        var $panel = $('#tierlist-control-panel');
        var $minimizeBtn = $('#tierlist-minimize-btn');
        
        $minimizeBtn.fadeOut(300);
        $panel.fadeIn(300);
        localStorage.setItem('tierlist-panel-hidden', 'false');
    }

    function restorePanelState() {
        var isCollapsed = localStorage.getItem('tierlist-panel-collapsed') === 'true';
        var isHidden = localStorage.getItem('tierlist-panel-hidden') === 'true';
        
        if (isCollapsed) {
            $('#tierlist-panel-content').hide();
            $('#tierlist-toggle-icon').css('transform', 'rotate(-90deg)');
        }
        
        if (isHidden) {
            $('#tierlist-control-panel').hide();
            $('#tierlist-minimize-btn').show();
        }
    }

    function makeCardsDraggable() {
        $('.战斗员卡片').each(function() {
            var $card = $(this);
            
            // 如果已经是可拖动的,跳过
            if ($card.attr('data-draggable-init')) {
                return;
            }
            
            $card.attr('draggable', 'true')
                .attr('data-draggable-init', 'true')
                .css('cursor', 'move')
                .on('dragstart', function(e) {
                    // 克隆卡片HTML,确保包含所有结构
                    var $clone = $card.clone();
                    $clone.find('*').each(function() {
                        // 移除可能导致样式问题的内联样式
                        var $el = $(this);
                        var style = $el.attr('style');
                        if (style) {
                            // 保留关键样式,移除 opacity 等
                            style = style.replace(/opacity\s*:\s*[^;]+;?/gi, '');
                            $el.attr('style', style);
                        }
                    });
                    
                    var cardHTML = $clone[0].outerHTML;
                    e.originalEvent.dataTransfer.setData('text/html', cardHTML);
                    e.originalEvent.dataTransfer.effectAllowed = 'copy';
                    
                    // 只让原始卡片变透明,不影响拖动的副本
                    $card.css('opacity', '0.5');
                })
                .on('dragend', function() {
                    $card.css('opacity', '1');
                });
        });
    }

    function makeTableDroppable() {
        $('.wikitable td').each(function() {
            var $cell = $(this);
            
            $cell.addClass('tierlist-dropzone')
                .css({
                    'min-height': '280px',
                    'vertical-align': 'top',
                    'padding': '10px',
                    'position': 'relative'
                })
                .on('dragover', function(e) {
                    e.preventDefault();
                    e.originalEvent.dataTransfer.dropEffect = 'copy';
                    $cell.addClass('drag-over');
                })
                .on('dragleave', function() {
                    $cell.removeClass('drag-over');
                })
                .on('drop', function(e) {
                    e.preventDefault();
                    $cell.removeClass('drag-over');
                    
                    var cardHTML = e.originalEvent.dataTransfer.getData('text/html');
                    if (cardHTML) {
                        var $newCard = $(cardHTML);
                        
                        // 确保新卡片的样式正确
                        $newCard.addClass('placed-card')
                            .css({
                                'margin': '5px',
                                'opacity': '1',  // 明确设置为不透明
                                'filter': 'none' // 移除任何滤镜
                            })
                            .attr('draggable', 'true')
                            .removeAttr('data-draggable-init') // 允许重新初始化
                            .on('contextmenu', function(e) {
                                e.preventDefault();
                                if (confirm('确定要删除这张卡片吗?')) {
                                    $newCard.remove();
                                }
                            })
                            .on('dblclick', function() {
                                if (confirm('确定要删除这张卡片吗?')) {
                                    $newCard.remove();
                                }
                            });
                        
                        // 确保内部元素也没有 opacity 问题
                        $newCard.find('*').css('opacity', '');
                        
                        $cell.append($newCard);
                        
                        // 重新使新卡片可拖动
                        makeCardsDraggable();
                    }
                });
        });
    }

    function toggleEditMode() {
        $('body').toggleClass('tierlist-preview-mode');
        var isPreview = $('body').hasClass('tierlist-preview-mode');
        $('#toggle-edit-mode').text(isPreview ? '✏️ 切换编辑模式' : '👁️ 切换预览模式');
        
        if (isPreview) {
            $('.placed-card').css('pointer-events', 'none');
        } else {
            $('.placed-card').css('pointer-events', 'auto');
        }
    }

    function clearTable() {
        if (confirm('确定要清空表格中的所有卡片吗?此操作不可撤销!')) {
            $('.wikitable .placed-card').remove();
            mw.notify('表格已清空', { type: 'success' });
        }
    }

    function exportToPNG() {
        var $btn = $(event.target);
        $btn.prop('disabled', true).text('⏳ 正在生成...');

        // 加载 html2canvas 库
        if (typeof html2canvas === 'undefined') {
            $.getScript('https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js')
                .done(function() {
                    performExport($btn);
                })
                .fail(function() {
                    mw.notify('加载导出库失败,请检查网络连接', { type: 'error' });
                    $btn.prop('disabled', false).text('📸 导出为PNG');
                });
        } else {
            performExport($btn);
        }
    }

    function performExport($btn) {
        var $table = $('.wikitable').first();
        
        // 临时隐藏控制面板和最小化按钮
        var $panel = $('#tierlist-control-panel');
        var $minimizeBtn = $('#tierlist-minimize-btn');
        var panelVisible = $panel.is(':visible');
        var btnVisible = $minimizeBtn.is(':visible');
        
        $panel.hide();
        $minimizeBtn.hide();
        
        // 进入预览模式
        var wasPreview = $('body').hasClass('tierlist-preview-mode');
        if (!wasPreview) {
            $('body').addClass('tierlist-preview-mode');
        }

        // 确保所有卡片都是完全不透明的
        var $cards = $('.placed-card');
        $cards.css({
            'opacity': '1',
            'filter': 'none'
        });

        html2canvas($table[0], {
            backgroundColor: '#ffffff',
            scale: 2,
            logging: false,
            useCORS: true,
            allowTaint: true,
            imageTimeout: 0,
            removeContainer: true
        }).then(function(canvas) {
            // 恢复界面
            if (panelVisible) $panel.show();
            if (btnVisible) $minimizeBtn.show();
            if (!wasPreview) {
                $('body').removeClass('tierlist-preview-mode');
            }
            
            // 下载图片
            var link = document.createElement('a');
            var timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
            link.download = '战斗员分级表_' + timestamp + '.png';
            link.href = canvas.toDataURL('image/png');
            link.click();
            
            $btn.prop('disabled', false).text('📸 导出为PNG');
            
            mw.notify('分级表已成功导出!', { type: 'success' });
        }).catch(function(error) {
            console.error('导出失败:', error);
            if (panelVisible) $panel.show();
            if (btnVisible) $minimizeBtn.show();
            $btn.prop('disabled', false).text('📸 导出为PNG');
            mw.notify('导出失败: ' + error.message, { type: 'error' });
        });
    }

    function addStyles() {
        var styles = `
            .tierlist-dropzone {
                transition: background-color 0.3s, border 0.3s;
            }
            
            .tierlist-dropzone.drag-over {
                background-color: #e6f3ff !important;
                border: 2px dashed #0645ad !important;
            }
            
            .placed-card {
                transition: transform 0.2s, box-shadow 0.2s;
                opacity: 1 !important;
                filter: none !important;
            }
            
            .placed-card * {
                opacity: 1 !important;
                filter: none !important;
            }
            
            body:not(.tierlist-preview-mode) .placed-card:hover {
                transform: scale(1.05);
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
                z-index: 10;
            }
            
            .tierlist-preview-mode .placed-card {
                pointer-events: none;
            }
            
            .tierlist-preview-mode .placed-card:hover {
                transform: none;
                box-shadow: none;
            }
            
            #tierlist-control-panel button:active {
                transform: scale(0.95);
            }
            
            #tierlist-panel-header {
                position: relative;
            }
            
            /* 添加一个关闭按钮到标题栏 */
            #tierlist-panel-header::after {
                content: '✕';
                position: absolute;
                right: 40px;
                top: 50%;
                transform: translateY(-50%);
                cursor: pointer;
                font-size: 18px;
                opacity: 0.8;
                transition: opacity 0.2s;
            }
            
            #tierlist-panel-header:hover::after {
                opacity: 1;
            }
            
            @media print {
                #tierlist-control-panel,
                #tierlist-minimize-btn {
                    display: none !important;
                }
            }
        `;
        
        $('<style>').text(styles).appendTo('head');
    }

    // 为标题栏添加关闭功能
    $(document).on('click', '#tierlist-panel-header', function(e) {
        var $header = $(this);
        var clickX = e.pageX - $header.offset().left;
        var headerWidth = $header.outerWidth();
        
        // 如果点击的是右侧的关闭区域(最右边40px)
        if (clickX > headerWidth - 40 && clickX < headerWidth - 10) {
            e.stopPropagation();
            hidePanel();
        }
    });

})();