查看: 100|回复: 0

一个纯前端的 Tier List 排序页面,支持分享!

[复制链接]

89

主题

8

回帖

31

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
31
发表于 2025-10-3 21:16:18 | 显示全部楼层 |阅读模式
GitHub:https://github.com/IIIStudio/FreeTierListHTML
CNB:https://cnb.cool/IIIStudio/HTML/Game/FreeTierListHTML
演示:https://iiistudio.github.io/FreeTierListHTML/
分享链接:https://iiistudio.github.io/Free ... stHTML/txt/test.txt


简单说明

[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tier List 排序</title>
    <style>
        :root {
            --tier-S: #FF5252;
            --tier-A: #FF9800;
            --tier-B: #FFEB3B;
            --tier-C: #4CAF50;
            --tier-D: #2196F3;
            --tier-E: #9C27B0;
            --tier-F: #607D8B;
        }
         
        * {
            box-sizing: border-box;
        }
         
        body {
            font-family: 'Arial', sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
            color: #333;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }
         
        .app-container {
            max-width: 1200px;
            margin: 20px auto;
            padding: 0px 20px 0px 20px;
            flex: 1;
            flex-direction: column;
            width: 100%;
            padding: 0 20px;
            flex-direction: column;
        }
         
        .tier-list-container {
            flex: 1;
            display: flex;
            flex-direction: column;
        }
         
        h1 {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
            cursor: pointer;
            padding: 10px;
            border-radius: 4px;
        }
         
        h1:hover {
            background-color: #f0f0f0;
        }
         
        .tier-list {
            display: flex;
            flex-direction: column;
            background-color: #000000;
            flex: 1;
        }
         
        .tier-row {
            display: flex;
            margin-bottom: -4px;
            min-height: 100px;
            position: relative;
            border: 5px solid #000;
        }
         
        .tier-label {
            width: 110px;
            min-width: 110px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            color: white;
            font-size: 30px;
            text-align: center;
            padding: 10px;
            position: relative;
            cursor: pointer;
        }
         
        .tier-controls {
            display: none;
            justify-content: center;
            margin-top: 5px;
        }
         
        .tier-label:hover .tier-controls {
            display: flex;
        }
         
        .tier-control-btn {
                width: 18.5px;
                height: 18.5px;
                border: 2px solid #000;
                background-color: white;
                color: black;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                font-size: 17px;
                /* margin: 0 1px; */
                transition: all 0.2s;
        }
         
        .tier-control-btn:hover {
            background-color: black;
            color: white;
            border-color: white;
        }
         
        .tier-content {
            flex-grow: 1;
            background-color: #2f2b2b;
            display: flex;
            flex-wrap: wrap;
            align-items: flex-start;
            min-height: 100px;
            align-content: flex-start;
            min-width: 0;
            margin-left: 5px;
            flex: 1;
            min-width: 0;
        }
         
        .item {
            height: 90px;
            margin: 5px;
            background-color: #ddd;
            border-radius: 5px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: grab;
            position: relative;
            transition: transform 0.1s;
            overflow: hidden;
            flex-shrink: 0;
        }
         
        .item.dragging {
            opacity: 0.5;
            transform: scale(0.9);
        }
         
        .item.placeholder {
            background-color: rgba(0,0,0,0.1);
            border: 2px dashed #666;
        }
         
        .item:hover {
            transform: scale(1.05);
            box-shadow: 0 0 10px rgba(0,0,0,0.2);
        }
         
        .item img {
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
            pointer-events: none;
        }
         
        .bottom-panel {
            margin-top: 10px;
            transition: opacity 0.3s, transform 0.3s;
            transform: translateY(20px);
            opacity: 0;
        }
         
        .bottom-panel.visible {
            transform: translateY(0);
            opacity: 1;
        }
         
        .controls {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }
         
        .item-pool {
            background-color: #f0f0f0;
            border-radius: 5px;
            padding: 15px;
            margin-top: 10px;
            display: flex;
            flex-wrap: wrap;
            min-height: 150px;
        }
         
        button {
            padding: 8px 8px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        }
         
        button:hover {
            background-color: #45a049;
        }
         
        .upload-btn {
            background-color: #2196F3;
        }
         
        .upload-btn:hover {
            background-color: #0b7dda;
        }
         
        .delete-btn {
            background-color: #f44336;
        }
         
        .delete-btn:hover {
            background-color: #d32f2f;
        }
         
        #file-input {
            display: none;
        }
         
        #txt-input {
            display: none;
        }
         
        .item .delete-btn {
            position: absolute;
            top: 5px;
            right: 5px;
            background-color: #f44336;
            color: white;
            border: none;
            border-radius: 50%;
            width: 20px;
            height: 20px;
            font-size: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            opacity: 0;
            transition: opacity 0.2s;
        }
         
        .item:hover .delete-btn {
            opacity: 1;
        }
         
        .tier-S { background-color: var(--tier-S); }
        .tier-A { background-color: var(--tier-A); }
        .tier-B { background-color: var(--tier-B); }
        .tier-C { background-color: var(--tier-C); }
        .tier-D { background-color: var(--tier-D); }
        .tier-E { background-color: var(--tier-E); }
        .tier-F { background-color: var(--tier-F); }
         
        .title-edit {
            display: none;
            width: 100%;
            padding: 5px;
            margin-top: 5px;
            font-size: 16px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
         
        .tier-label-edit {
            display: none;
            width: 100%;
            padding: 5px;
            margin-top: 5px;
            font-size: 14px;
            border: 1px solid rgba(255,255,255,0.5);
            border-radius: 4px;
            background-color: rgba(0,0,0,0.2);
            color: white;
        }
         
        .color-picker-popup {
            position: absolute;
            top: 100%;
            left: 50%;
            transform: translateX(-50%);
            background: white;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            z-index: 100;
            display: none;
        }
         
        .color-option {
            width: 20px;
            height: 20px;
            display: inline-block;
            margin: 2px;
            cursor: pointer;
            border: 1px solid #ddd;
        }
         
        .screen-bottom-detector {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            height: 30px;
            z-index: 10;
        }
 
        .load-default-btn {
            background-color: #9C27B0; 
        }
        .load-default-btn:hover {
            background-color: #7B1FA2; 
        }
         
        .screenshot-btn {
            background-color: #FF9800; 
        }
        .screenshot-btn:hover {
            background-color: #F57C00; 
        }
        .hidden-for-screenshot {
            display: none !important;
        }
         
        .loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            color: white;
            font-size: 24px;
        }
    </style>
</head>
<body>
    <div class="app-container">
        <div class="tier-list-container">
            <h1 id="main-title">Tier List 排序</h1>
            <input type="text" class="title-edit" id="title-edit" placeholder="输入标题">
             
            <div class="tier-list" id="tier-list">
                <!-- 默认行 -->
                <div class="tier-row" data-tier-id="S">
                    <div class="tier-label tier-S">
                        <span class="label-text">S</span>
                        <input type="text" class="tier-label-edit" placeholder="输入标签名称">
                        <div class="tier-controls">
                            <div class="tier-control-btn move-up" title="上移行">↑</div>
                            <div class="tier-control-btn move-down" title="下移行">↓</div>
                            <div class="tier-control-btn add-below" title="下方添加行">+</div>
                            <div class="tier-control-btn clear-row" title="清空行">□</div>
                            <div class="tier-control-btn delete-row" title="删除行">×</div>
                            <div class="tier-control-btn color-picker-btn" title="修改颜色">■</div>
                        </div>
                    </div>
                    <div class="tier-content" data-tier="S"></div>
                </div>
                <div class="tier-row" data-tier-id="A">
                    <div class="tier-label tier-A">
                        <span class="label-text">A</span>
                        <input type="text" class="tier-label-edit" placeholder="输入标签名称">
                        <div class="tier-controls">
                            <div class="tier-control-btn move-up" title="上移行">↑</div>
                            <div class="tier-control-btn move-down" title="下移行">↓</div>
                            <div class="tier-control-btn add-below" title="下方添加行">+</div>
                            <div class="tier-control-btn clear-row" title="清空行">□</div>
                            <div class="tier-control-btn delete-row" title="删除行">×</div>
                            <div class="tier-control-btn color-picker-btn" title="修改颜色">■</div>
                        </div>
                    </div>
                    <div class="tier-content" data-tier="A"></div>
                </div>
                <div class="tier-row" data-tier-id="B">
                    <div class="tier-label tier-B">
                        <span class="label-text">B</span>
                        <input type="text" class="tier-label-edit" placeholder="输入标签名称">
                        <div class="tier-controls">
                            <div class="tier-control-btn move-up" title="上移行">↑</div>
                            <div class="tier-control-btn move-down" title="下移行">↓</div>
                            <div class="tier-control-btn add-below" title="下方添加行">+</div>
                            <div class="tier-control-btn clear-row" title="清空行">□</div>
                            <div class="tier-control-btn delete-row" title="删除行">×</div>
                            <div class="tier-control-btn color-picker-btn" title="修改颜色">■</div>
                        </div>
                    </div>
                    <div class="tier-content" data-tier="B"></div>
                </div>
            </div>
             
            <div class="bottom-panel" id="bottom-panel">
                <div class="controls">
                    <button class="upload-btn" id="upload-btn">上传图片</button>
                    <input type="file" id="file-input" accept="image/*" multiple>
                    <button class="upload-btn" id="upload-txt-btn">导入TXT</button>
                    <input type="file" id="txt-input" accept=".txt">
                    <button id="export-txt-btn">导出TXT</button>
                    <button id="reset-list" class="delete-btn">重置</button>
                    <button id="screenshot-btn" class="screenshot-btn">生成图片</button>
                    <button id="load-default" class="load-default-btn">加载 Test</button>
                </div>
                <div class="item-pool" id="item-pool">
                    <!-- 图片将在这里显示 -->
                </div>
            </div>
        </div>
    </div>
 
    <div class="screen-bottom-detector" id="screen-bottom-detector"></div>
    <div class="loading-overlay" id="loading-overlay" style="display: none;">加载中,请稍候...</div>
 
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const itemPool = document.getElementById('item-pool');
            const tierList = document.getElementById('tier-list');
            const uploadBtn = document.getElementById('upload-btn');
            const uploadTxtBtn = document.getElementById('upload-txt-btn');
            const fileInput = document.getElementById('file-input');
            const txtInput = document.getElementById('txt-input');
            const exportTxtBtn = document.getElementById('export-txt-btn');
            const resetBtn = document.getElementById('reset-list');
            const mainTitle = document.getElementById('main-title');
            const titleEdit = document.getElementById('title-edit');
            const bottomPanel = document.getElementById('bottom-panel');
            const screenBottomDetector = document.getElementById('screen-bottom-detector');
            const loadDefaultBtn = document.getElementById('load-default');
            const screenshotBtn = document.getElementById('screenshot-btn');
            const loadingOverlay = document.getElementById('loading-overlay');
             
            // 检查URL参数
            function getUrlParameter(name) {
                name = name.replace(/[\[\]]/g, '\\$&');
                const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
                const results = regex.exec(window.location.href);
                if (!results) return null;
                if (!results[2]) return '';
                return decodeURIComponent(results[2].replace(/\+/g, ' '));
            }
             
            // 加载远程TXT文件
            async function loadRemoteTxt(url) {
                try {
                    loadingOverlay.style.display = 'flex';
                    const response = await fetch(url);
                    if (!response.ok) {
                        throw new Error('文件加载失败,请检查URL是否正确');
                    }
                    const content = await response.text();
                     
                    // 清空现有内容(包括默认行)
                    tierList.innerHTML = '';
                    itemPool.innerHTML = '';
                    itemCounter = 0;
                     
                    processTxtContent(content);
                     
                    // 设置标题为文件名(不含扩展名)
                    const fileName = url.split('/').pop().replace(/\.[^/.]+$/, "");
                    mainTitle.textContent = fileName;
                     
                    loadingOverlay.style.display = 'none';
                } catch (error) {
                    console.error('加载远程TXT文件出错:', error);
                    loadingOverlay.style.display = 'none';
                    alert('加载远程TXT文件失败: ' + error.message);
                }
            }
 
            // 检查是否有txt参数
            const txtUrl = getUrlParameter('txt');
            if (txtUrl) {
                loadRemoteTxt(txtUrl);
            }
                         
            // 新增加载默认文件函数
            async function 加载默认文件() {
                try {
                    loadingOverlay.style.display = 'flex';
                    // 从服务器获取test.txt文件
                    const 响应 = await fetch('./txt/test.txt');
                    if (!响应.ok) {
                        throw new Error('文件加载失败,请检查文件是否存在');
                    }
                    const 内容 = await 响应.text();
                     
                    // 清空现有内容
                    tierList.innerHTML = '';
                    itemPool.innerHTML = '';
                    itemCounter = 0;
                     
                    // 处理加载的内容
                    processTxtContent(内容);
                     
                    // 设置默认标题
                    mainTitle.textContent = 'Test Tier列表';
                     
                    loadingOverlay.style.display = 'none';
                } catch (错误) {
                    console.error('加载默认文件出错:', 错误);
                    loadingOverlay.style.display = 'none';
                    alert('加载默认文件失败: ' + 错误.message);
                }
            }
             
            // 为新按钮添加点击事件
            loadDefaultBtn.addEventListener('click', 加载默认文件);
             
            let itemCounter = 0;
            let dragItem = null;
            let placeholder = null;
            let colorPicker = null;
            let hideBottomPanelTimeout = null;
            let bottomPanelVisible = false;
            let currentColorPickerRow = null;
             
            const tierColors = ['tier-S', 'tier-A', 'tier-B', 'tier-C', 'tier-D', 'tier-E', 'tier-F'];
             
            // 初始化默认行
            initDefaultRows();
             
            // 显示底部面板
            function showBottomPanel() {
                if (hideBottomPanelTimeout) {
                    clearTimeout(hideBottomPanelTimeout);
                    hideBottomPanelTimeout = null;
                }
                 
                if (!bottomPanelVisible) {
                    bottomPanel.classList.add('visible');
                    bottomPanelVisible = true;
                }
            }
             
            // 隐藏底部面板
            function hideBottomPanel() {
                if (bottomPanelVisible) {
                    bottomPanel.classList.remove('visible');
                    bottomPanelVisible = false;
                }
            }
             
            // 设置5秒后隐藏底部面板
            function scheduleHideBottomPanel() {
                if (hideBottomPanelTimeout) {
                    clearTimeout(hideBottomPanelTimeout);
                }
                hideBottomPanelTimeout = setTimeout(hideBottomPanel, 2000);
            }
             
            // 初始化底部面板行为
            function initBottomPanelBehavior() {
                // 鼠标进入底部面板区域时显示
                bottomPanel.addEventListener('mouseenter', function() {
                    showBottomPanel();
                });
                 
                // 鼠标离开底部面板区域时设置5秒后隐藏
                bottomPanel.addEventListener('mouseleave', function() {
                    scheduleHideBottomPanel();
                });
                 
                // 鼠标进入屏幕底部检测区域时显示底部面板
                screenBottomDetector.addEventListener('mouseenter', function() {
                    showBottomPanel();
                });
                 
                // 鼠标离开屏幕底部检测区域时设置5秒后隐藏
                screenBottomDetector.addEventListener('mouseleave', function() {
                    scheduleHideBottomPanel();
                });
                 
                // 初始状态:隐藏底部面板
                hideBottomPanel();
            }
             
            // 初始化默认行
            function initDefaultRows() {
                // 初始化行控制按钮
                initRowControls();
                 
                // 添加标签编辑功能
                document.querySelectorAll('.tier-label').forEach(label => {
                    const textSpan = label.querySelector('.label-text');
                    const editInput = label.querySelector('.tier-label-edit');
                     
                    label.addEventListener('click', function(e) {
                        // 防止点击子元素时触发
                        if (e.target === label || e.target === textSpan) {
                            editInput.value = textSpan.textContent;
                            textSpan.style.display = 'none';
                            editInput.style.display = 'block';
                            editInput.focus();
                        }
                    });
                     
                    editInput.addEventListener('blur', function() {
                        if (editInput.value.trim() !== '') {
                            textSpan.textContent = editInput.value;
                        }
                        editInput.style.display = 'none';
                        textSpan.style.display = 'inline';
                    });
                     
                    editInput.addEventListener('keypress', function(e) {
                        if (e.key === 'Enter') {
                            editInput.blur();
                        }
                    });
                });
                 
                // 设置拖放区域
                document.querySelectorAll('.tier-content').forEach(area => {
                    area.addEventListener('dragover', dragOver);
                    area.addEventListener('drop', drop);
                });
            }
             
            // 创建颜色选择器
            function createColorPicker(row) {
                if (colorPicker) {
                    colorPicker.remove();
                }
                 
                currentColorPickerRow = row;
                 
                colorPicker = document.createElement('div');
                colorPicker.className = 'color-picker-popup';
                colorPicker.innerHTML = `
                    <div class="color-option" style="background-color: #FF5252;" data-color="var(--tier-S)" data-class="tier-S"></div>
                    <div class="color-option" style="background-color: #FF9800;" data-color="var(--tier-A)" data-class="tier-A"></div>
                    <div class="color-option" style="background-color: #FFEB3B;" data-color="var(--tier-B)" data-class="tier-B"></div>
                    <div class="color-option" style="background-color: #4CAF50;" data-color="var(--tier-C)" data-class="tier-C"></div>
                    <div class="color-option" style="background-color: #2196F3;" data-color="var(--tier-D)" data-class="tier-D"></div>
                    <div class="color-option" style="background-color: #9C27B0;" data-color="var(--tier-E)" data-class="tier-E"></div>
                    <div class="color-option" style="background-color: #607D8B;" data-color="var(--tier-F)" data-class="tier-F"></div>
                `;
                 
                document.body.appendChild(colorPicker);
                 
                // 定位颜色选择器 - 相对于按钮定位
                const btn = row.querySelector('.color-picker-btn');
                const btnRect = btn.getBoundingClientRect();
                 
                // 计算位置,确保不会超出屏幕
                const pickerWidth = 180; // 颜色选择器的宽度
                let left = btnRect.left + btnRect.width/2 - pickerWidth/2;
                 
                // 确保不会超出屏幕左侧
                if (left < 10) left = 10;
                // 确保不会超出屏幕右侧
                if (left + pickerWidth > window.innerWidth - 10) {
                    left = window.innerWidth - pickerWidth - 10;
                }
                 
                colorPicker.style.left = left + 'px';
                colorPicker.style.top = (btnRect.bottom + 5) + 'px';
                colorPicker.style.display = 'block';
                 
                // 颜色选择事件
                colorPicker.querySelectorAll('.color-option').forEach(option => {
                    option.addEventListener('click', function(e) {
                        e.stopPropagation();
                        const label = row.querySelector('.tier-label');
                         
                        // 移除所有tier-*类
                        label.className = label.className.replace(/\btier-\S+/g, '');
                        label.className = 'tier-label';
                         
                        // 添加新的类
                        if (this.dataset.class) {
                            label.classList.add(this.dataset.class);
                        }
                         
                        colorPicker.style.display = 'none';
                        currentColorPickerRow = null;
                    });
                });
                 
                return colorPicker;
            }
             
            // 标题编辑功能
            mainTitle.addEventListener('click', function() {
                titleEdit.value = mainTitle.textContent;
                titleEdit.style.display = 'block';
                mainTitle.style.display = 'none';
                titleEdit.focus();
            });
             
            titleEdit.addEventListener('blur', function() {
                if (titleEdit.value.trim() !== '') {
                    mainTitle.textContent = titleEdit.value;
                }
                titleEdit.style.display = 'none';
                mainTitle.style.display = 'block';
            });
             
            titleEdit.addEventListener('keypress', function(e) {
                if (e.key === 'Enter') {
                    titleEdit.blur();
                }
            });
             
            // 初始化行控制按钮
            function initRowControls() {
                document.querySelectorAll('.tier-control-btn').forEach(btn => {
                    // 确保事件只绑定一次
                    if (!btn.hasAttribute('data-initialized')) {
                        btn.setAttribute('data-initialized', 'true');
                         
                        btn.addEventListener('click', function(e) {
                            e.stopPropagation();
                            const row = this.closest('.tier-row');
                             
                            if (this.classList.contains('move-up')) {
                                moveRowUp(row);
                            } else if (this.classList.contains('move-down')) {
                                moveRowDown(row);
                            } else if (this.classList.contains('add-below')) {
                                addNewRow(row, false);
                            } else if (this.classList.contains('clear-row')) {
                                const content = row.querySelector('.tier-content');
                                while (content.firstChild) {
                                    itemPool.appendChild(content.firstChild);
                                }
                            } else if (this.classList.contains('delete-row')) {
                                // 直接删除行,不再提示确认
                                const content = row.querySelector('.tier-content');
                                while (content.firstChild) {
                                    itemPool.appendChild(content.firstChild);
                                }
                                row.remove();
                            } else if (this.classList.contains('color-picker-btn')) {
                                if (currentColorPickerRow === row) {
                                    if (colorPicker) {
                                        colorPicker.style.display = 'none';
                                        currentColorPickerRow = null;
                                    }
                                } else {
                                    createColorPicker(row);
                                }
                            }
                        });
                    }
                });
            }
             
            // 上移行
            function moveRowUp(row) {
                const prevRow = row.previousElementSibling;
                if (prevRow) {
                    row.parentNode.insertBefore(row, prevRow);
                }
            }
             
            // 下移行
            function moveRowDown(row) {
                const nextRow = row.nextElementSibling;
                if (nextRow) {
                    row.parentNode.insertBefore(nextRow, row);
                }
            }
             
            // 上传按钮点击事件
            uploadBtn.addEventListener('click', function() {
                fileInput.click();
            });
             
            // TXT导入按钮点击事件
            uploadTxtBtn.addEventListener('click', function() {
                txtInput.click();
            });
             
            // 文件选择事件 - 修改为在转换为base64前调整图片大小
            fileInput.addEventListener('change', function(e) {
                const files = e.target.files;
                 
                for (let i = 0; i < files.length; i++) {
                    const file = files[i];
                     
                    if (!file.type.match('image.*')) continue;
                     
                    const reader = new FileReader();
                     
                    reader.onload = function(e) {
                        const img = new Image();
                        img.onload = function() {
                            // 创建canvas来调整图片大小
                            const canvas = document.createElement('canvas');
                            const ctx = canvas.getContext('2d');
                             
                            // 计算新的宽度(保持比例)
                            const ratio = img.width / img.height;
                            const newHeight = 200;
                            const newWidth = newHeight * ratio;
                             
                            canvas.width = newWidth;
                            canvas.height = newHeight;
                             
                            // 绘制调整大小后的图片
                            ctx.drawImage(img, 0, 0, newWidth, newHeight);
                             
                            // 转换为base64
                            const resizedDataUrl = canvas.toDataURL('image/png');
                             
                            // 创建图片项
                            const item = createImageItem(resizedDataUrl, file.name);
                            itemPool.appendChild(item);
                        };
                        img.src = e.target.result;
                    }
                     
                    reader.readAsDataURL(file);
                }
                 
                fileInput.value = '';
            });
             
            // TXT文件导入处理
            txtInput.addEventListener('change', function(e) {
                const file = e.target.files[0];
                if (!file) return;
                 
                // 设置标题为文件名(不含扩展名)
                const fileName = file.name.replace(/\.[^/.]+$/, "");
                mainTitle.textContent = fileName;
                 
                const reader = new FileReader();
                reader.onload = function(e) {
                    const content = e.target.result;
                    // 清空现有内容(包括默认行)
                    tierList.innerHTML = '';
                    itemPool.innerHTML = '';
                    itemCounter = 0;
                     
                    processTxtContent(content);
                };
                reader.readAsText(file);
            });
             
            // 处理TXT内容
            function processTxtContent(content) {
                const lines = content.split('\n');
                let currentTier = null;
                let currentTierRow = null;
                let tierIndex = 0;
                 
                for (let line of lines) {
                    line = line.trim();
                    if (!line) continue;
                     
                    // 检查是否是标签行
                    if (line.startsWith('#')) {
                        const tierName = line.substring(1).trim();
                        if (tierName.toLowerCase() === 'img') {
                            // img 分组不创建新行,后续项进入 item-pool
                            currentTier = 'img';
                            currentTierRow = null;
                        } else {
                            currentTier = tierName;
                            // 创建新行
                            currentTierRow = createTierRow(tierName, tierIndex);
                            tierIndex++;
                        }
                    } else if (currentTier) {
                        // 处理图片链接
                        if (currentTier === 'img') {
                            // 添加到图片池
                            const item = createImageItem(line, line.split('/').pop());
                            itemPool.appendChild(item);
                        } else if (currentTierRow) {
                            // 添加到对应的tier行
                            const item = createImageItem(line, line.split('/').pop());
                            currentTierRow.querySelector('.tier-content').appendChild(item);
                        }
                    }
                }
                 
                // 初始化行控制按钮
                initRowControls();
            }
             
            // 创建tier行
            function createTierRow(tierName, index) {
                const colorClass = tierColors[Math.min(index, tierColors.length - 1)];
                 
                const newRow = document.createElement('div');
                newRow.className = 'tier-row';
                newRow.dataset.tierId = tierName;
                 
                // 标签
                const label = document.createElement('div');
                label.className = `tier-label ${colorClass}`;
                 
                const textSpan = document.createElement('span');
                textSpan.className = 'label-text';
                textSpan.textContent = tierName;
                 
                const editInput = document.createElement('input');
                editInput.type = 'text';
                editInput.className = 'tier-label-edit';
                editInput.placeholder = '输入标签名称';
                 
                // 控制按钮
                const controls = document.createElement('div');
                controls.className = 'tier-controls';
                controls.innerHTML = `
                    <div class="tier-control-btn move-up" title="上移行">↑</div>
                    <div class="tier-control-btn move-down" title="下移行">↓</div>
                    <div class="tier-control-btn add-below" title="下方添加行">+</div>
                    <div class="tier-control-btn clear-row" title="清空行">□</div>
                    <div class="tier-control-btn delete-row" title="删除行">×</div>
                    <div class="tier-control-btn color-picker-btn" title="修改颜色">■</div>
                `;
                 
                label.appendChild(textSpan);
                label.appendChild(editInput);
                label.appendChild(controls);
                 
                // 内容区域
                const content = document.createElement('div');
                content.className = 'tier-content';
                content.dataset.tier = tierName;
                 
                newRow.appendChild(label);
                newRow.appendChild(content);
                 
                // 添加到DOM
                tierList.appendChild(newRow);
                 
                // 添加标签编辑功能
                const handleLabelClick = function(e) {
                    if (e.target === label || e.target === textSpan) {
                        editInput.value = textSpan.textContent;
                        textSpan.style.display = 'none';
                        editInput.style.display = 'block';
                        editInput.focus();
                    }
                };
                 
                const handleEditBlur = function() {
                    if (editInput.value.trim() !== '') {
                        textSpan.textContent = editInput.value;
                    }
                    editInput.style.display = 'none';
                    textSpan.style.display = 'inline';
                };
                 
                const handleEditKeypress = function(e) {
                    if (e.key === 'Enter') {
                        editInput.blur();
                    }
                };
                 
                label.addEventListener('click', handleLabelClick);
                editInput.addEventListener('blur', handleEditBlur);
                editInput.addEventListener('keypress', handleEditKeypress);
                 
                // 设置拖放区域
                content.addEventListener('dragover', dragOver);
                content.addEventListener('drop', drop);
                 
                // 初始化控制按钮
                initRowControls();
                 
                return newRow;
            }
             
            // 添加新行
            function addNewRow(referenceRow, above) {
                // 计算新行的颜色类
                const existingRows = document.querySelectorAll('.tier-row');
                const colorIndex = Math.min(existingRows.length, tierColors.length - 1);
                const colorClass = tierColors[colorIndex];
                 
                const newRow = document.createElement('div');
                newRow.className = 'tier-row';
                const newId = 'custom-' + Date.now();
                newRow.dataset.tierId = newId;
                 
                // 标签
                const label = document.createElement('div');
                label.className = `tier-label ${colorClass}`;
                 
                const textSpan = document.createElement('span');
                textSpan.className = 'label-text';
                textSpan.textContent = '新行';
                 
                const editInput = document.createElement('input');
                editInput.type = 'text';
                editInput.className = 'tier-label-edit';
                editInput.placeholder = '输入标签名称';
                 
                // 控制按钮
                const controls = document.createElement('div');
                controls.className = 'tier-controls';
                controls.innerHTML = `
                    <div class="tier-control-btn move-up" title="上移行">↑</div>
                    <div class="tier-control-btn move-down" title="下移行">↓</div>
                    <div class="tier-control-btn add-below" title="下方添加行">+</div>
                    <div class="tier-control-btn clear-row" title="清空行">□</div>
                    <div class="tier-control-btn delete-row" title="删除行">×</div>
                    <div class="tier-control-btn color-picker-btn" title="修改颜色">■</div>
                `;
                 
                label.appendChild(textSpan);
                label.appendChild(editInput);
                label.appendChild(controls);
                 
                // 内容区域
                const content = document.createElement('div');
                content.className = 'tier-content';
                content.dataset.tier = newId;
                 
                newRow.appendChild(label);
                newRow.appendChild(content);
                 
                // 添加到DOM
                if (above) {
                    referenceRow.parentNode.insertBefore(newRow, referenceRow);
                } else {
                    referenceRow.parentNode.insertBefore(newRow, referenceRow.nextSibling);
                }
                 
                // 初始化控制按钮
                initRowControls();
                 
                // 添加标签编辑功能
                const handleLabelClick = function(e) {
                    if (e.target === label || e.target === textSpan) {
                        editInput.value = textSpan.textContent;
                        textSpan.style.display = 'none';
                        editInput.style.display = 'block';
                        editInput.focus();
                    }
                };
                 
                const handleEditBlur = function() {
                    if (editInput.value.trim() !== '') {
                        textSpan.textContent = editInput.value;
                    }
                    editInput.style.display = 'none';
                    textSpan.style.display = 'inline';
                };
                 
                const handleEditKeypress = function(e) {
                    if (e.key === 'Enter') {
                        editInput.blur();
                    }
                };
                 
                label.addEventListener('click', handleLabelClick);
                editInput.addEventListener('blur', handleEditBlur);
                editInput.addEventListener('keypress', handleEditKeypress);
                 
                // 设置拖放区域
                content.addEventListener('dragover', dragOver);
                content.addEventListener('drop', drop);
                 
                return newRow;
            }
             
            // 导出TXT
            exportTxtBtn.addEventListener('click', function() {
                const txtContent = generateTxtContent();
                downloadTxtFile(txtContent, mainTitle.textContent + '.txt');
            });
             
            // 生成TXT内容
            function generateTxtContent() {
                let txt = '';
                 
                // 处理tier行
                const tierRows = document.querySelectorAll('.tier-row');
                tierRows.forEach(row => {
                    const tierId = row.dataset.tierId;
                    const label = row.querySelector('.label-text').textContent;
                    txt += `#${label}\n`;
                     
                    const items = row.querySelectorAll('.tier-content .item img');
                    items.forEach(img => {
                        // 如果是base64图片,直接导出
                        if (img.src.startsWith('data:')) {
                            txt += `${img.src}\n`;
                        } else {
                            // 如果是外部图片URL,也导出
                            txt += `${img.src}\n`;
                        }
                    });
                });
                 
                // 处理图片池
                txt += '#img\n';
                const poolItems = itemPool.querySelectorAll('.item img');
                poolItems.forEach(img => {
                    if (img.src.startsWith('data:')) {
                        txt += `${img.src}\n`;
                    } else {
                        txt += `${img.src}\n`;
                    }
                });
                 
                return txt;
            }
             
            // 下载TXT文件
            function downloadTxtFile(content, filename) {
                const blob = new Blob([content], { type: 'text/plain' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = filename;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }
             
            // 创建图片项
            function createImageItem(src, filename) {
                itemCounter++;
                const itemId = 'item-' + itemCounter;
                 
                const item = document.createElement('div');
                item.className = 'item';
                item.draggable = true;
                item.dataset.itemId = itemId;
                 
                const img = document.createElement('img');
                img.src = src;
                img.alt = filename;
                img.style.height = '200px'; // 设置固定高度
                img.style.width = 'auto';  // 宽度自适应
                 
                const deleteBtn = document.createElement('button');
                deleteBtn.className = 'delete-btn';
                deleteBtn.innerHTML = '×';
                deleteBtn.addEventListener('click', function(e) {
                    e.stopPropagation();
                    item.remove();
                });
                 
                item.appendChild(img);
                item.appendChild(deleteBtn);
                 
                // 使项目可拖动
                item.addEventListener('dragstart', dragStart);
                item.addEventListener('dragend', dragEnd);
                 
                return item;
            }
             
            // 拖放功能
            function dragStart(e) {
                dragItem = e.target.closest('.item');
                if (!dragItem) return;
                 
                e.dataTransfer.effectAllowed = 'move';
                e.dataTransfer.setData('text/plain', dragItem.dataset.itemId);
                 
                setTimeout(() => {
                    dragItem.classList.add('dragging');
                }, 0);
                 
                // 创建占位符
                placeholder = document.createElement('div');
                placeholder.className = 'item placeholder';
                placeholder.style.width = dragItem.offsetWidth + 'px';
                placeholder.style.height = dragItem.offsetHeight + 'px';
            }
             
            function dragEnd() {
                if (!dragItem) return;
                 
                dragItem.classList.remove('dragging');
                if (placeholder && placeholder.parentNode) {
                    placeholder.parentNode.removeChild(placeholder);
                }
                dragItem = null;
                placeholder = null;
            }
             
            function dragOver(e) {
                e.preventDefault();
                if (!dragItem) return;
                 
                const dropTarget = getDropTarget(e.target);
                if (!dropTarget) return;
                 
                // 在内容区域上方显示占位符
                if (dropTarget.classList.contains('tier-content') || dropTarget.classList.contains('item-pool')) {
                    const closestItem = getClosestItem(dropTarget, e.clientX, e.clientY);
                     
                    if (closestItem) {
                        dropTarget.insertBefore(placeholder, closestItem);
                    } else {
                        dropTarget.appendChild(placeholder);
                    }
                }
            }
             
            function drop(e) {
                e.preventDefault();
                if (!dragItem) return;
                 
                const dropTarget = getDropTarget(e.target);
                if (!dropTarget) return;
                 
                if (placeholder && placeholder.parentNode) {
                    if (placeholder.nextSibling) {
                        dropTarget.insertBefore(dragItem, placeholder.nextSibling);
                    } else {
                        dropTarget.appendChild(dragItem);
                    }
                    placeholder.parentNode.removeChild(placeholder);
                }
            }
             
            function getDropTarget(element) {
                while (element && !element.classList.contains('tier-content') && !element.classList.contains('item-pool')) {
                    element = element.parentNode;
                }
                return (element.classList.contains('tier-content') || element.classList.contains('item-pool')) ? element : null;
            }
             
            // 改进后的 getClosestItem 函数 - 基于坐标判断
            function getClosestItem(container, x, y) {
                const items = Array.from(container.querySelectorAll('.item:not(.dragging)'));
                if (items.length === 0) return null;
                 
                let closestItem = null;
                let closestDistance = Infinity;
                 
                items.forEach(item => {
                    const rect = item.getBoundingClientRect();
                    const centerX = rect.left + rect.width / 2;
                    const centerY = rect.top + rect.height / 2;
                     
                    // 计算鼠标位置与项目中心的距离
                    const distance = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
                     
                    if (distance < closestDistance) {
                        closestDistance = distance;
                        closestItem = item;
                    }
                });
                 
                // 判断是否应该放在项目前面还是后面
                if (closestItem) {
                    const rect = closestItem.getBoundingClientRect();
                    const isBefore = (x < rect.left + rect.width / 2);
                    return isBefore ? closestItem : closestItem.nextElementSibling;
                }
                 
                return null;
            }
             
            // 重置列表
            function resetList(keepPool = false) {
                if (!keepPool) {
                    itemPool.innerHTML = '';
                }
                tierList.innerHTML = '';
                itemCounter = 0;
                 
                // 重新添加默认行
                const defaultTiers = ['S', 'A', 'B'];
                defaultTiers.forEach((tier, index) => {
                    createTierRow(tier, index);
                });
            }
             
            // 重置按钮 - 新功能:移动所有图片到图片区并删除空行
            resetBtn.addEventListener('click', function() {
                // 移动所有图片到图片区
                document.querySelectorAll('.tier-content .item').forEach(item => {
                    itemPool.appendChild(item);
                });
 
                // 检查是否存在默认层级
                const defaultTiers = ['S', 'A', 'B'];
                const existingTiers = Array.from(document.querySelectorAll('.tier-row')).map(row => row.dataset.tierId);
                 
                // 确保默认层级存在
                defaultTiers.forEach((tier, index) => {
                    if (!existingTiers.includes(tier)) {
                        createTierRow(tier, index);
                    }
                });
                 
                // 删除所有空行(保留有内容的行和默认行)
                document.querySelectorAll('.tier-row').forEach(row => {
                    const tierId = row.dataset.tierId;
                    const hasItems = row.querySelector('.tier-content .item') !== null;
                     
                    // 如果不是默认行且没有内容,则删除
                    if (!defaultTiers.includes(tierId) && !hasItems) {
                        row.remove();
                    }
                });
            });
             
            // 生成截图并下载
            screenshotBtn.addEventListener('click', function() {
                // 获取需要隐藏的元素
                const controls = document.querySelector('.controls');
                const itemPool = document.getElementById('item-pool');
                 
                // 添加隐藏类
                controls.classList.add('hidden-for-screenshot');
                itemPool.classList.add('hidden-for-screenshot');
                 
                // 获取tier-list-container元素
                const container = document.querySelector('.tier-list-container');
                 
                // 使用html2canvas库生成截图
                html2canvas(container, {
                    backgroundColor: '#f5f5f5',
                    scale: 2,
                    logging: false,
                    useCORS: true,
                    onclone: function(clonedDoc) {
                        // 确保克隆的文档中也隐藏这些元素
                        const clonedControls = clonedDoc.querySelector('.controls');
                        const clonedItemPool = clonedDoc.getElementById('item-pool');
                        if (clonedControls) clonedControls.classList.add('hidden-for-screenshot');
                        if (clonedItemPool) clonedItemPool.classList.add('hidden-for-screenshot');
                    }
                }).then(canvas => {
                    // 创建下载链接
                    const link = document.createElement('a');
                    link.download = mainTitle.textContent + '.png';
                    link.href = canvas.toDataURL('image/png');
                    link.click();
                     
                    // 截图完成后恢复显示
                    controls.classList.remove('hidden-for-screenshot');
                    itemPool.classList.remove('hidden-for-screenshot');
                }).catch(err => {
                    console.error('生成截图失败:', err);
                    alert('生成截图失败,请重试');
                     
                    // 出错时也恢复显示
                    controls.classList.remove('hidden-for-screenshot');
                    itemPool.classList.remove('hidden-for-screenshot');
                });
            });
             
            // 初始化底部面板行为
            initBottomPanelBehavior();
             
            // 点击页面其他地方关闭颜色选择器
            document.addEventListener('click', function(e) {
                if (colorPicker && !e.target.closest('.color-picker-btn') && !e.target.closest('.color-picker-popup')) {
                    colorPicker.style.display = 'none';
                    currentColorPickerRow = null;
                }
            });
             
            // 防止颜色选择器点击事件冒泡
            if (colorPicker) {
                colorPicker.addEventListener('click', function(e) {
                    e.stopPropagation();
                });
            }
        });
    </script>
    <!-- 引入html2canvas库 -->
    <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
    <style>
        .corner-links {
            position: fixed;
            right: 20px;
            bottom: 20px;
            display: flex;
            align-items: center;
            z-index: 9999;
        }
        .corner-link {
            display: inline-flex;
            align-items: center;
            gap: 8px;
            padding: 8px 10px;
            border-radius: 8px;
            color: #212529;
            text-decoration: none;
            transition: all 0.3s ease;
        }
        .corner-link:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 12px rgba(0,0,0,0.15);
        }
        .corner-link img,
        .corner-link svg {
            width: 22px;
            height: 22px;
        }
        .corner-link .label {
            font-weight: 600;
            font-size: 0.95rem;
        }
    </style>
    <div class="corner-links" aria-label="页面固定链接">
        <a class="corner-link" href="https://github.com/IIIStudio/FreeTierListHTML" target="_blank" rel="noopener noreferrer" aria-label="前往 GitHub 仓库">
            <!-- 内联 GitHub 图标,避免外部资源依赖 -->
            <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">
                <path fill="#24292F" d="M12 .5a12 12 0 0 0-3.79 23.41c.6.11.82-.26.82-.58v-2.02c-3.35.73-4.06-1.61-4.06-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.75.08-.74.08-.74 1.2.09 1.83 1.23 1.83 1.23 1.07 1.83 2.8 1.3 3.49.99.11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.51.12-3.15 0 0 1.01-.32 3.3 1.23.96-.27 1.99-.4 3.01-.4s2.05.14 3.01.4c2.29-1.55 3.3-1.23 3.3-1.23.66 1.64.25 2.85.12 3.15.77.84 1.24 1.91 1.24 3.22 0 4.61-2.8 5.63-5.47 5.93.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 12 .5Z"/>
            </svg>
            <span class="label">FreeTierListHTML</span>
        </a>
        <a class="corner-link" href="https://cnb.cool/IIIStudio/HTML/Game/FreeTierListHTML/" target="_blank" rel="noopener noreferrer" aria-label="前往 FreeTierListHTML 文档页面">
            <img src="https://docs.cnb.cool/images/logo/svg/LogoColorfulIcon.svg" alt="CNB Logo">
        </a>
    </div>
</body>
</html>



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表