微件

TierListMaker:修订间差异

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献
无编辑摘要
律Rhyme留言 | 贡献
无编辑摘要
 
(未显示同一用户的19个中间版本)
第1行: 第1行:
<includeonly>
<includeonly>
<style>
<style>
.tier-row {
.tierlist-controls {
    position: relative;
  display: flex;
    background-color: #fafafa;
  gap: 8px;
    border: 2px dashed transparent;
  margin: 8px 0 12px 0;
    transition: all 0.2s ease;
    min-height: 120px;
}
 
.tier-row.drag-over {
    background-color: #e3f2fd;
    border-color: #2196F3;
}
 
#character-pool {
    position: relative;
    min-height: 150px;
}
}
 
.btn {
#character-pool.drag-over {
  display: inline-block;
    background-color: #fff3e0;
  padding: 6px 12px;
  background: #f0f0f0;
  border: 1px solid #ccc;
  color: #333;
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
}
}
 
.btn:hover { background: #e6e6e6; }
.btn.primary { background: #4a8cf6; color: #fff; border-color: #3b78de; }
.btn.primary:hover { background: #3b78de; }
.tierlist-table {
.tierlist-table {
    width: 100%;
  width: 100%;
    table-layout: fixed;
  table-layout: fixed;
}
}
 
.tier-row .tier-head {
.tierlist-table th {
  position: relative;
    width: 100px;
  width: 120px;
    position: relative;
  color: #fff;
  text-align: center;
  font-size: 18px;
  font-weight: bold;
  padding: 8px;
  white-space: nowrap;
  border: 1px solid #ccc;
  background: #555;
}
}
 
.tier-row .tier-cell {
.tierlist-controls {
  border: 1px solid #ccc;
    margin-bottom: 20px;
  padding: 0px;
    display: flex;
    gap: 10px;
}
}
 
.tier-tools {
.tierlist-btn {
  position: absolute;
    padding: 10px 20px;
  right: 6px;
    font-size: 16px;
  top: 6px;
    color: white;
  display: flex;
    border: none;
  gap: 6px;
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: inline-block;
    text-align: center;
    user-select: none;
}
}
 
/* 导出时隐藏工具按钮 */
.tierlist-btn-save {
.exporting .tier-tools {
    background-color: #4CAF50;
  display: none !important;
}
}
 
.color-toggle {
.tierlist-btn-save:hover {
  width: 18px;
    background-color: #45a049;
  height: 18px;
  border-radius: 3px;
  border: 1px solid rgba(0,0,0,0.2);
  cursor: pointer;
  background: rgba(255,255,255,0.6);
}
}
 
.delete-row {
.tierlist-btn-reset {
  font-size: 12px;
    background-color: #f44336;
  padding: 2px 6px;
  border-radius: 3px;
  background: rgba(0,0,0,0.1);
  cursor: pointer;
}
}
 
.delete-row:hover { background: rgba(0,0,0,0.2); }
.tierlist-btn-reset:hover {
.tier-dropzone {
    background-color: #da190b;
  min-height: 112px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: flex-start;
  padding: 4px;
}
}
 
.tier-dropzone.pool {
.tierlist-btn-add {
  border: 2px dashed #ccc;
    background-color: #2196F3;
  padding: 8px;
  border-radius: 6px;
  background: #fafafa;
}
}
 
/* 导出时隐藏角色池 */
.tierlist-btn-add:hover {
.exporting .pool-wrapper {
    background-color: #0b7dda;
  display: none !important;
}
}
 
.pool-wrapper { margin-top: 12px; }
.tierlist-btn-edit {
.pool-header {
    background-color: #ff9800;
  font-weight: bold;
  margin-bottom: 6px;
}
}
 
.avatar-frame {
.tierlist-btn-edit:hover {
  position: relative;
    background-color: #e68900;
  display: inline-block;
  vertical-align: top;
  border: 3px solid #ccc;
  border-radius: 5px;
  overflow: hidden;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  background: #f5f5f5;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
  cursor: move;
}
}
 
.avatar-frame.dragging {
.tierlist-btn-edit.active {
  opacity: 0.7;
    background-color: #ff5722;
  transform: scale(1.03);
  border-color: #4a8cf6;
  z-index: 10;
}
}
 
.avatar-frame img {
.tierlist-btn:active {
  display: block;
    transform: scale(0.98);
  width: 100px;
  height: 100px;
  object-fit: cover;
  pointer-events: none;
  -webkit-user-drag: none;
}
}
 
.avatar-name {
.character-pool-container {
  position: absolute;
    background-color: #f9f9f9;
  left: 0;
    padding: 15px;
  bottom: 0;
    border: 2px dashed #ccc;
  padding: 2px 8px;
    border-radius: 5px;
  color: white;
    margin-top: 20px;
  font-size: 12px;
  font-weight: bold;
  text-shadow: 0 0 2px black, 0 0 2px black;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  border-top-right-radius: 3px;
}
}
 
.color-palette {
.character-pool-container h3 {
  position: absolute;
    margin-top: 0;
  display: none;
    color: #333;
  gap: 6px;
  flex-wrap: wrap;
  width: 210px;
  padding: 8px;
  background: #fff;
  border: 1px solid #ccc;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
  border-radius: 6px;
  z-index: 9999;
}
}
 
.color-swatch {
/* 编辑模式下的样式 */
  width: 24px; height: 24px;
.tier-header.editable {
  border-radius: 4px;
    cursor: pointer;
  border: 1px solid rgba(0,0,0,0.2);
    position: relative;
  cursor: pointer;
}
}
 
.color-swatch:hover { outline: 2px solid rgba(0,0,0,0.2); }
.tier-header.editable:hover::after {
.tier-label {
    content: '✎';
  display: inline-block;
    position: absolute;
  padding: 2px 6px;
    right: 5px;
  border-radius: 3px;
    top: 50%;
  background: rgba(255,255,255,0.15);
    transform: translateY(-50%);
    font-size: 16px;
}
}
 
.tier-label[contenteditable="true"] {
.delete-row-btn {
  outline: none;
    position: absolute;
  cursor: text;
    right: 5px;
    top: 50%;
    transform: translateY(-50%);
    background-color: #f44336;
    color: white;
    border: none;
    border-radius: 3px;
    padding: 3px 8px;
    cursor: pointer;
    font-size: 12px;
    display: none;
    z-index: 100;
}
}
</style>


.edit-mode .delete-row-btn {
<script>
     display: block;
(function() {
}
  function ready(fn) {
    if (document.readyState !== 'loading') fn();
    else document.addEventListener('DOMContentLoaded', fn);
  }
  function contrastColor(hex) {
    hex = (hex || '').replace('#','');
    if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
    if (!/^[0-9a-fA-F]{6}$/.test(hex)) return '#fff';
    var r = parseInt(hex.substr(0,2), 16);
    var g = parseInt(hex.substr(2,2), 16);
    var b = parseInt(hex.substr(4,2), 16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    return yiq >= 128 ? '#000' : '#fff';
  }
  function getDragAfterElement(container, y) {
    const elements = [...container.querySelectorAll('.avatar-frame:not(.dragging)')];
    return elements.reduce((closest, child) => {
      const box = child.getBoundingClientRect();
      const offset = y - box.top - box.height/2;
      if (offset < 0 && offset > closest.offset) {
        return { offset: offset, element: child };
      } else {
        return closest;
      }
     }, { offset: Number.NEGATIVE_INFINITY, element: null }).element;
  }
  function ensureCrossOrigin(img) {
    try {
      if (!img) return;
      if (!img.crossOrigin) img.crossOrigin = 'anonymous';
      if (!img.referrerPolicy) img.referrerPolicy = 'no-referrer';
    } catch(e) {}
  }
  function makeDraggable(avatar) {
    if (!avatar || avatar._draggableInit) return;
    avatar._draggableInit = true;
    avatar.setAttribute('draggable', 'true');
    avatar.classList.add('draggable-avatar');
    avatar.querySelectorAll('img').forEach(ensureCrossOrigin);


.delete-row-btn:hover {
    avatar.addEventListener('dragstart', function(e) {
     background-color: #da190b;
      avatar.classList.add('dragging');
}
      avatar._prevParent = avatar.parentNode;
      e.dataTransfer.setData('text/plain', avatar.dataset.id || avatar.querySelector('.avatar-name')?.textContent || '');
      e.dataTransfer.effectAllowed = 'move';
    });
    avatar.addEventListener('dragend', function() {
      avatar.classList.remove('dragging');
      cleanupEmptyWrapper(avatar._prevParent);
      avatar._prevParent = null;
    });
  }
  function initAvatars(root) {
    if (!root) return;
    const avatars = root.querySelectorAll('.avatar-frame');
    avatars.forEach(function(av) {
      if (!av.dataset.id) {
        const nameEl = av.querySelector('.avatar-name');
        av.dataset.name = nameEl ? nameEl.textContent.trim() : '';
      }
      makeDraggable(av);
    });
  }
  function cleanupEmptyWrapper(node) {
     if (!node || node.nodeType !== 1) return;
    if (node.classList && node.classList.contains('tier-dropzone')) return;
    const hasElementChild = node.querySelector('.avatar-frame');
    const onlyWhitespace = !node.textContent || node.textContent.trim().length === 0;
    if (!hasElementChild && node.childElementCount === 0 && onlyWhitespace) {
      node.parentNode && node.parentNode.removeChild(node);
    }
  }
  function cleanupEmptyPlaceholdersIn(container) {
    if (!container) return;
    Array.from(container.children).forEach(function(child) {
      if (child.nodeType !== 1) return;
      if (child.classList.contains('avatar-frame')) return;
      const hasAvatarInside = !!child.querySelector('.avatar-frame');
      const onlyWhitespace = !child.textContent || child.textContent.trim().length === 0;
      if (!hasAvatarInside && child.childElementCount === 0 && onlyWhitespace) {
        child.remove();
      }
    });
  }
  function initDropzone(zone) {
    if (!zone || zone._dropzoneInit) return;
    zone._dropzoneInit = true;


/* 颜色选择器容器 */
    zone.addEventListener('dragover', function(e) {
.color-picker-popup {
      e.preventDefault();
    position: fixed;
      e.dataTransfer.dropEffect = 'move';
    top: 50%;
      const afterElement = getDragAfterElement(zone, e.clientY);
    left: 50%;
      const dragging = document.querySelector('.avatar-frame.dragging');
    transform: translate(-50%, -50%);
      if (!dragging) return;
    background: white;
      if (afterElement == null) {
    padding: 20px;
        if (dragging.parentNode !== zone) zone.appendChild(dragging);
    border-radius: 8px;
      } else {
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
        if (afterElement !== dragging) zone.insertBefore(dragging, afterElement);
    z-index: 1000;
      }
     min-width: 300px;
     });
}


.color-picker-popup h4 {
    zone.addEventListener('drop', function(e) {
     margin-top: 0;
      e.preventDefault();
}
      e.stopPropagation();
 
      const dragging = document.querySelector('.avatar-frame.dragging');
.color-picker-popup label {
      if (dragging) {
     display: block;
        if (dragging.parentNode !== zone) zone.appendChild(dragging);
     margin: 10px 0 5px 0;
        cleanupEmptyWrapper(dragging._prevParent);
}
        dragging._prevParent = null;
 
      }
.color-picker-popup input[type="text"],
      if (zone.classList.contains('pool')) {
.color-picker-popup input[type="color"] {
        cleanupEmptyPlaceholdersIn(zone);
     width: 100%;
      }
     padding: 5px;
    });
     margin-bottom: 10px;
  }
     box-sizing: border-box;
  function buildEditableHead(th) {
}
    const currentText = (th.childNodes[0] && th.childNodes[0].nodeType === 3)
 
      ? th.childNodes[0].nodeValue.trim()
.color-picker-popup .button-group {
      : th.textContent.trim();
     display: flex;
    const tools = th.querySelector('.tier-tools');
     gap: 10px;
    th.innerHTML = '';
     margin-top: 15px;
    const label = document.createElement('div');
}
     label.className = 'tier-label';
 
    label.setAttribute('contenteditable', 'true');
.color-picker-popup button {
    label.textContent = currentText || '未命名';
     flex: 1;
    const toolsWrap = tools || (function() {
    padding: 8px;
      const t = document.createElement('div');
     border: none;
      t.className = 'tier-tools';
     border-radius: 4px;
      const color = document.createElement('div');
     cursor: pointer;
      color.className = 'color-toggle';
     font-size: 14px;
      color.title = '更改颜色';
}
      const del = document.createElement('div');
      del.className = 'delete-row';
      del.textContent = '删除';
      del.title = '删除该行';
      t.appendChild(color); t.appendChild(del);
      return t;
    })();
    th.appendChild(label);
     th.appendChild(toolsWrap);
     const initialBg = th.getAttribute('data-initial-bg') || '#555';
    th.style.background = initialBg;
    th.style.color = contrastColor(initialBg);
    attachColorPalette(toolsWrap.querySelector('.color-toggle'), th);
    const delBtn = toolsWrap.querySelector('.delete-row');
    delBtn.addEventListener('click', function() {
      const tr = th.closest('tr');
      const dropzone = tr.querySelector('.tier-dropzone');
      const pool = document.getElementById('character-pool');
      Array.from(dropzone.querySelectorAll('.avatar-frame')).forEach(function(av) {
        pool.appendChild(av);
      });
      tr.parentNode.removeChild(tr);
    });
  }
  function attachColorPalette(toggle, th) {
    if (!toggle) return;
    const palette = document.createElement('div');
    palette.className = 'color-palette';
    const colors = [
      '#e53935','#d81b60','#8e24aa','#5e35b1','#3949ab','#1e88e5','#039be5','#00acc1',
      '#00897b','#43a047','#7cb342','#c0ca33','#fdd835','#fb8c00','#f4511e','#6d4c41',
      '#546e7a','#9e9e9e','#000000','#ffffff'
    ];
    colors.forEach(function(c) {
      const sw = document.createElement('div');
      sw.className = 'color-swatch';
      sw.style.background = c;
      sw.addEventListener('click', function() {
        th.style.background = c;
        th.style.color = contrastColor(c);
        palette.style.display = 'none';
      });
      palette.appendChild(sw);
    });
    document.body.appendChild(palette);
    function placePalette() {
      const rect = toggle.getBoundingClientRect();
      palette.style.left = (window.scrollX + rect.left) + 'px';
      palette.style.top  = (window.scrollY + rect.bottom + 6) + 'px';
    }
    toggle.addEventListener('click', function(e) {
      e.stopPropagation();
      if (palette.style.display === 'block') {
        palette.style.display = 'none';
      } else {
        placePalette();
        palette.style.display = 'block';
      }
    });
    document.addEventListener('click', function(e) {
      if (e.target === toggle || palette.contains(e.target)) return;
      palette.style.display = 'none';
    });
    window.addEventListener('scroll', function() {
      if (palette.style.display === 'block') placePalette();
    });
    window.addEventListener('resize', function() {
      if (palette.style.display === 'block') placePalette();
    });
  }
  function addNewRow() {
     const tbody = document.querySelector('#tierlist-table tbody') || document.querySelector('#tierlist-table');
    const tr = document.createElement('tr');
    tr.className = 'tier-row';
     const th = document.createElement('th');
     th.className = 'tier-head';
     th.setAttribute('data-initial-bg', '#8888ff');
    th.textContent = '新行';
    const td = document.createElement('td');
    td.className = 'tier-cell';
    const dz = document.createElement('div');
    dz.className = 'tier-dropzone';
    dz.setAttribute('data-tier', 'CUSTOM');
    td.appendChild(dz);
    tr.appendChild(th);
    tr.appendChild(td);
    tbody.appendChild(tr);
    initDropzone(dz);
    buildEditableHead(th);
  }
  function ensureHtml2Canvas(cb) {
    if (window.html2canvas) { cb(); return; }
     var s = document.createElement('script');
    s.src = 'https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js';
    s.onload = cb;
    s.onerror = function() {
      alert('加载截图库失败,请检查网络或跨域策略。');
    };
    document.body.appendChild(s);
  }
  function downloadCanvas(canvas) {
     function done(blobOrUrl) {
      try {
        const link = document.createElement('a');
        const isBlob = blobOrUrl instanceof Blob;
        const url = isBlob ? (URL.createObjectURL(blobOrUrl)) : blobOrUrl;
        link.href = url;
        link.download = 'tierlist.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        if (isBlob) setTimeout(() => URL.revokeObjectURL(url), 0);
      } catch (e) {
        const dataUrl = canvas.toDataURL('image/png');
        window.open(dataUrl, '_blank');
      }
    }
    if (canvas.toBlob) {
      canvas.toBlob(function(blob) {
        if (blob) done(blob);
        else done(canvas.toDataURL('image/png'));
      }, 'image/png');
    } else {
      done(canvas.toDataURL('image/png'));
    }
  }
  function prepareImagesForExport(container) {
     if (!container) return;
    container.querySelectorAll('img').forEach(ensureCrossOrigin);
  }
  function savePNG() {
    const maker = document.getElementById('tierlist-maker');
    const table = document.getElementById('tierlist-table');
    if (!table) {
      alert('未找到要导出的表格。');
      return;
    }
      
    // 添加导出样式类
    maker.classList.add('exporting');
    prepareImagesForExport(table);
   
    ensureHtml2Canvas(function() {
      // 稍微延迟以确保CSS生效
      setTimeout(function() {
        html2canvas(table, {
          backgroundColor: '#ffffff',
          scale: 2,
          useCORS: true,
          allowTaint: false,
          imageTimeout: 15000
        }).then(function(canvas) {
          downloadCanvas(canvas);
        }).catch(function(err) {
          console.error('html2canvas 失败', err);
          alert('保存PNG失败:' + (err && err.message ? err.message : '未知错误'));
        }).finally(function() {
          maker.classList.remove('exporting');
        });
      }, 100);
     });
  }
  function resetAll() {
     const pool = document.getElementById('character-pool');
     if (!pool) return;
     const rows = document.querySelectorAll('#tierlist-table .tier-dropzone');
    rows.forEach(function(zone) {
      if (zone.classList.contains('pool')) return;
      Array.from(zone.querySelectorAll('.avatar-frame')).forEach(function(av) {
        pool.appendChild(av);
      });
      cleanupEmptyPlaceholdersIn(zone);
    });
    cleanupEmptyPlaceholdersIn(pool);
  }


.color-picker-popup .btn-confirm {
  ready(function() {
     background-color: #4CAF50;
    document.querySelectorAll('#tierlist-table .tier-head').forEach(buildEditableHead);
     color: white;
     document.querySelectorAll('.tier-dropzone').forEach(initDropzone);
}
     initAvatars(document.getElementById('character-pool'));


.color-picker-popup .btn-cancel {
    var addBtn = document.getElementById('add-row');
    background-color: #f44336;
     if (addBtn) addBtn.addEventListener('click', addNewRow);
     color: white;
     var saveBtn = document.getElementById('save-png');
}
     if (saveBtn) saveBtn.addEventListener('click', savePNG);
 
.overlay {
    position: fixed;
     top: 0;
     left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0,0,0,0.5);
    z-index: 999;
}
</style>


<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
    (function insertResetButton() {
      const controls = document.querySelector('#tierlist-maker .tierlist-controls');
      if (!controls || document.getElementById('reset-roles')) return;
      const resetBtn = document.createElement('div');
      resetBtn.id = 'reset-roles';
      resetBtn.className = 'btn';
      resetBtn.textContent = '重置';
      controls.insertBefore(resetBtn, controls.querySelector('#save-png') || null);
      resetBtn.addEventListener('click', resetAll);
    })();


<script>
    const pool = document.getElementById('character-pool');
(function() {
     if (pool) {
    'use strict';
      const obs = new MutationObserver(function(muts) {
   
         muts.forEach(function(m) {
    // 防止重复初始化
          if (m.addedNodes && m.addedNodes.length) {
     if (window.TierlistMakerInitialized) {
             m.addedNodes.forEach(function(n) {
        console.log('TierlistMaker already initialized');
              if (n.nodeType === 1) {
        return;
                 if (n.classList.contains('avatar-frame')) {
    }
                  makeDraggable(n);
   
    const TierlistMaker = {
         draggedElement: null,
        sourceContainer: null,
        editMode: false,
       
        init: function() {
            // 等待DOM完全加载
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', () => this.setup());
            } else {
                setTimeout(() => this.setup(), 100);
            }
        },
       
        setup: function() {
             this.initDragAndDrop();
            this.initButtons();
            this.loadState();
        },
       
        initDragAndDrop: function() {
            const avatars = document.querySelectorAll('.avatar-frame');
           
            avatars.forEach(avatar => {
                 if (!avatar.getAttribute('data-drag-initialized')) {
                    avatar.setAttribute('draggable', 'true');
                    avatar.setAttribute('data-drag-initialized', 'true');
                   
                    avatar.addEventListener('dragstart', (e) => this.handleDragStart(e, avatar));
                    avatar.addEventListener('dragend', (e) => this.handleDragEnd(e, avatar));
                 }
                 }
                n.querySelectorAll && n.querySelectorAll('.avatar-frame').forEach(makeDraggable);
                if (pool.contains(n)) cleanupEmptyPlaceholdersIn(pool);
              }
             });
             });
           
          }
            // 为tier行添加拖放事件
         });
            const tierRows = document.querySelectorAll('.tier-row');
      });
            tierRows.forEach(row => {
      obs.observe(pool, { childList: true, subtree: true });
                if (!row.getAttribute('data-drop-initialized')) {
    }
                    row.setAttribute('data-drop-initialized', 'true');
  });
                    row.addEventListener('dragover', (e) => this.handleDragOver(e, row));
                    row.addEventListener('drop', (e) => this.handleDrop(e, row));
                    row.addEventListener('dragleave', (e) => this.handleDragLeave(e, row));
                }
            });
           
            // 为角色池添加拖放事件
            const characterPool = document.getElementById('character-pool');
            if (characterPool && !characterPool.getAttribute('data-drop-initialized')) {
                characterPool.setAttribute('data-drop-initialized', 'true');
                characterPool.addEventListener('dragover', (e) => this.handleDragOver(e, characterPool));
                characterPool.addEventListener('drop', (e) => this.handleDrop(e, characterPool));
                characterPool.addEventListener('dragleave', (e) => this.handleDragLeave(e, characterPool));
            }
        },
       
        handleDragStart: function(e, element) {
            this.draggedElement = element;
            this.sourceContainer = element.parentElement;
            element.classList.add('dragging');
            e.dataTransfer.effectAllowed = 'move';
            e.dataTransfer.setData('text/plain', element.getAttribute('data-character-id') || 'drag');
        },
       
        handleDragEnd: function(e, element) {
            element.classList.remove('dragging');
           
            // 移除所有drag-over类
            document.querySelectorAll('.drag-over').forEach(el => {
                el.classList.remove('drag-over');
            });
           
            // 保存状态
            this.saveState();
           
            this.draggedElement = null;
            this.sourceContainer = null;
        },
       
        handleDragOver: function(e, element) {
            e.preventDefault();
            e.stopPropagation();
           
            e.dataTransfer.dropEffect = 'move';
           
            if (!element.classList.contains('drag-over')) {
                element.classList.add('drag-over');
            }
           
            return false;
        },
       
        handleDragLeave: function(e, element) {
            if (e.target === element) {
                element.classList.remove('drag-over');
            }
        },
       
        handleDrop: function(e, element) {
            e.preventDefault();
            e.stopPropagation();
           
            element.classList.remove('drag-over');
           
            if (this.draggedElement && this.draggedElement.parentElement !== element) {
                // 从原位置移除
                if (this.draggedElement.parentElement) {
                    this.draggedElement.parentElement.removeChild(this.draggedElement);
                }
               
                // 添加到新位置
                element.appendChild(this.draggedElement);
               
                // 确保图片透明度正常
                const img = this.draggedElement.querySelector('img');
                if (img) {
                    img.style.opacity = '1';
                }
            }
           
            return false;
        },
       
        initButtons: function() {
            const saveBtn = document.getElementById('save-tierlist-btn');
            const resetBtn = document.getElementById('reset-tierlist-btn');
            const addRowBtn = document.getElementById('add-row-btn');
            const editModeBtn = document.getElementById('edit-mode-btn');
           
            if (saveBtn) {
                saveBtn.onclick = () => this.saveTierlist();
            }
           
            if (resetBtn) {
                resetBtn.onclick = () => this.resetTierlist();
            }
           
            if (addRowBtn) {
                addRowBtn.onclick = () => this.addRow();
            }
           
            if (editModeBtn) {
                editModeBtn.onclick = () => this.toggleEditMode();
            }
        },
       
        toggleEditMode: function() {
            this.editMode = !this.editMode;
            const btn = document.getElementById('edit-mode-btn');
            const table = document.getElementById('tierlist-table');
           
            if (this.editMode) {
                btn.classList.add('active');
                btn.textContent = '完成编辑';
                table.classList.add('edit-mode');
               
                // 为所有tier header添加编辑功能
                document.querySelectorAll('.tier-header').forEach(header => {
                    header.classList.add('editable');
                    if (!header.querySelector('.delete-row-btn')) {
                        const deleteBtn = document.createElement('button');
                        deleteBtn.className = 'delete-row-btn';
                        deleteBtn.textContent = '删除';
                        deleteBtn.onclick = (e) => {
                            e.stopPropagation();
                            this.deleteRow(header.parentElement);
                        };
                        header.appendChild(deleteBtn);
                    }
                   
                    header.onclick = () => this.editTierHeader(header);
                });
            } else {
                btn.classList.remove('active');
                btn.textContent = '编辑模式';
                table.classList.remove('edit-mode');
               
                document.querySelectorAll('.tier-header').forEach(header => {
                    header.classList.remove('editable');
                    header.onclick = null;
                });
            }
        },
       
        editTierHeader: function(header) {
            const currentText = header.textContent.replace('删除', '').trim();
            const currentBgColor = header.style.backgroundColor || '#000000';
           
            // 创建弹窗
            const overlay = document.createElement('div');
            overlay.className = 'overlay';
           
            const popup = document.createElement('div');
            popup.className = 'color-picker-popup';
            popup.innerHTML = `
                <h4>编辑Tier</h4>
                <label>Tier名称:</label>
                <input type="text" id="tier-name-input" value="${currentText}" maxlength="10">
                <label>背景颜色:</label>
                <input type="color" id="tier-color-input" value="${this.rgbToHex(currentBgColor)}">
                <div class="button-group">
                    <button class="btn-confirm">确定</button>
                    <button class="btn-cancel">取消</button>
                </div>
            `;
           
            document.body.appendChild(overlay);
            document.body.appendChild(popup);
           
            const nameInput = popup.querySelector('#tier-name-input');
            const colorInput = popup.querySelector('#tier-color-input');
           
            popup.querySelector('.btn-confirm').onclick = () => {
                const newName = nameInput.value.trim();
                const newColor = colorInput.value;
               
                if (newName) {
                    header.setAttribute('data-tier', newName);
                    header.querySelector('.delete-row-btn').remove();
                    header.textContent = newName;
                    header.style.backgroundColor = newColor;
                   
                    // 重新添加删除按钮
                    const deleteBtn = document.createElement('button');
                    deleteBtn.className = 'delete-row-btn';
                    deleteBtn.textContent = '删除';
                    deleteBtn.onclick = (e) => {
                        e.stopPropagation();
                        this.deleteRow(header.parentElement);
                    };
                    header.appendChild(deleteBtn);
                   
                    // 更新对应的tier-row
                    const row = header.parentElement.querySelector('.tier-row');
                    if (row) {
                        row.setAttribute('data-tier', newName);
                    }
                   
                    this.saveState();
                }
               
                document.body.removeChild(overlay);
                document.body.removeChild(popup);
            };
           
            popup.querySelector('.btn-cancel').onclick = () => {
                document.body.removeChild(overlay);
                document.body.removeChild(popup);
            };
           
            overlay.onclick = () => {
                document.body.removeChild(overlay);
                document.body.removeChild(popup);
            };
           
            nameInput.focus();
            nameInput.select();
        },
       
        rgbToHex: function(rgb) {
            if (rgb.startsWith('#')) return rgb;
           
            const values = rgb.match(/\d+/g);
            if (!values || values.length < 3) return '#000000';
           
            const r = parseInt(values[0]).toString(16).padStart(2, '0');
            const g = parseInt(values[1]).toString(16).padStart(2, '0');
            const b = parseInt(values[2]).toString(16).padStart(2, '0');
           
            return '#' + r + g + b;
        },
       
        addRow: function() {
            const table = document.getElementById('tierlist-table');
            if (!table) return;
           
            const tbody = table.querySelector('tbody');
            const newRow = document.createElement('tr');
           
            const tierCount = document.querySelectorAll('.tier-row').length;
            const tierName = 'T' + (tierCount + 1);
           
            newRow.innerHTML = `
                <th style="width: 100px; background-color: #9e9e9e; color: white; font-size: 20px; text-align: center" class="tier-header" data-tier="${tierName}">${tierName}</th>
                <td style="padding: 10px; min-height: 120px;" class="tier-row" data-tier="${tierName}"></td>
            `;
           
            tbody.appendChild(newRow);
           
            // 为新行添加拖放事件
            const newTierRow = newRow.querySelector('.tier-row');
            newTierRow.setAttribute('data-drop-initialized', 'true');
            newTierRow.addEventListener('dragover', (e) => this.handleDragOver(e, newTierRow));
            newTierRow.addEventListener('drop', (e) => this.handleDrop(e, newTierRow));
            newTierRow.addEventListener('dragleave', (e) => this.handleDragLeave(e, newTierRow));
           
            // 如果在编辑模式下,添加编辑功能
            if (this.editMode) {
                const header = newRow.querySelector('.tier-header');
                header.classList.add('editable');
               
                const deleteBtn = document.createElement('button');
                deleteBtn.className = 'delete-row-btn';
                deleteBtn.textContent = '删除';
                deleteBtn.onclick = (e) => {
                    e.stopPropagation();
                    this.deleteRow(newRow);
                };
                header.appendChild(deleteBtn);
               
                header.onclick = () => this.editTierHeader(header);
            }
           
            this.saveState();
         },
       
        deleteRow: function(row) {
            if (!confirm('确定要删除这一行吗?行内的角色将移回角色池。')) {
                return;
            }
           
            const tierRow = row.querySelector('.tier-row');
            const avatars = tierRow.querySelectorAll('.avatar-frame');
            const pool = document.getElementById('character-pool');
           
            // 将角色移回角色池
            avatars.forEach(avatar => {
                pool.appendChild(avatar);
            });
           
            // 删除行
            row.remove();
           
            this.saveState();
        },
       
        saveState: function() {
            const state = {
                rows: [],
                pool: []
            };
           
            // 保存表格行信息
            document.querySelectorAll('#tierlist-table tbody tr').forEach(row => {
                const header = row.querySelector('.tier-header');
                const tierRow = row.querySelector('.tier-row');
               
                if (!header || !tierRow) return;
               
                const rowData = {
                    name: header.getAttribute('data-tier'),
                    bgColor: header.style.backgroundColor,
                    characters: []
                };
               
                tierRow.querySelectorAll('.avatar-frame').forEach(avatar => {
                    const charId = this.getCharacterId(avatar);
                    if (charId) {
                        rowData.characters.push(charId);
                    }
                });
               
                state.rows.push(rowData);
            });
           
            // 保存角色池信息
            const pool = document.getElementById('character-pool');
            if (pool) {
                pool.querySelectorAll('.avatar-frame').forEach(avatar => {
                    const charId = this.getCharacterId(avatar);
                    if (charId) {
                        state.pool.push(charId);
                    }
                });
            }
           
            try {
                localStorage.setItem('tierlist-state', JSON.stringify(state));
            } catch (e) {
                console.warn('无法保存状态:', e);
            }
        },
       
        loadState: function() {
            try {
                const stateJson = localStorage.getItem('tierlist-state');
                if (!stateJson) return;
               
                const state = JSON.parse(stateJson);
               
                // 先清空所有行(保留第一行作为模板)
                const tbody = document.querySelector('#tierlist-table tbody');
                const rows = Array.from(tbody.querySelectorAll('tr'));
               
                // 恢复表格结构
                if (state.rows && state.rows.length > 0) {
                    rows.forEach((row, index) => {
                        if (index >= state.rows.length) {
                            row.remove();
                        } else {
                            const header = row.querySelector('.tier-header');
                            const savedRow = state.rows[index];
                           
                            if (header && savedRow) {
                                header.textContent = savedRow.name;
                                header.setAttribute('data-tier', savedRow.name);
                                header.style.backgroundColor = savedRow.bgColor;
                               
                                const tierRow = row.querySelector('.tier-row');
                                if (tierRow) {
                                    tierRow.setAttribute('data-tier', savedRow.name);
                                }
                            }
                        }
                    });
                   
                    // 如果保存的行数多于当前行数,添加新行
                    for (let i = rows.length; i < state.rows.length; i++) {
                        const savedRow = state.rows[i];
                        const newRow = document.createElement('tr');
                        newRow.innerHTML = `
                            <th style="width: 100px; background-color: ${savedRow.bgColor}; color: white; font-size: 20px; text-align: center" class="tier-header" data-tier="${savedRow.name}">${savedRow.name}</th>
                            <td style="padding: 10px; min-height: 120px;" class="tier-row" data-tier="${savedRow.name}"></td>
                        `;
                        tbody.appendChild(newRow);
                       
                        const newTierRow = newRow.querySelector('.tier-row');
                        newTierRow.setAttribute('data-drop-initialized', 'true');
                        newTierRow.addEventListener('dragover', (e) => this.handleDragOver(e, newTierRow));
                        newTierRow.addEventListener('drop', (e) => this.handleDrop(e, newTierRow));
                        newTierRow.addEventListener('dragleave', (e) => this.handleDragLeave(e, newTierRow));
                    }
                }
               
                // 恢复角色位置
                setTimeout(() => {
                    state.rows.forEach((rowData, index) => {
                        const tierRow = document.querySelectorAll('.tier-row')[index];
                        if (!tierRow) return;
                       
                        rowData.characters.forEach(charId => {
                            const avatar = this.findAvatarById(charId);
                            if (avatar) {
                                tierRow.appendChild(avatar);
                            }
                        });
                    });
                }, 100);
               
            } catch (e) {
                console.warn('无法加载状态:', e);
            }
        },
       
        getCharacterId: function(avatar) {
            // 尝试多种方式获取角色ID
            const img = avatar.querySelector('img');
            if (img) {
                const src = img.getAttribute('src');
                if (src) {
                    const match = src.match(/\/(\d+)\./);
                    if (match) return match[1];
                }
            }
           
            const name = avatar.querySelector('.avatar-name');
            if (name) {
                return name.textContent.trim();
            }
           
            return avatar.outerHTML;
        },
       
        findAvatarById: function(charId) {
            const allAvatars = document.querySelectorAll('.avatar-frame');
            for (let avatar of allAvatars) {
                if (this.getCharacterId(avatar) === charId) {
                    return avatar;
                }
            }
            return null;
        },
       
        saveTierlist: function() {
            const table = document.getElementById('tierlist-table');
            if (!table) {
                alert('未找到 Tierlist 表格!');
                return;
            }
           
            if (typeof html2canvas === 'undefined') {
                alert('正在加载图片生成库,请稍后再试...');
                return;
            }
           
            const controls = document.querySelector('.tierlist-controls');
            const poolContainer = document.querySelector('.character-pool-container');
           
            const originalControlsDisplay = controls ? controls.style.display : '';
            const originalPoolDisplay = poolContainer ? poolContainer.style.display : '';
           
            // 隐藏编辑按钮
            const deleteButtons = document.querySelectorAll('.delete-row-btn');
            deleteButtons.forEach(btn => btn.style.display = 'none');
           
            if (controls) controls.style.display = 'none';
            if (poolContainer) poolContainer.style.display = 'none';
           
            html2canvas(table, {
                scale: 2,
                backgroundColor: '#ffffff',
                logging: false,
                useCORS: true,
                allowTaint: true
            }).then(canvas => {
                if (controls) controls.style.display = originalControlsDisplay;
                if (poolContainer) poolContainer.style.display = originalPoolDisplay;
                deleteButtons.forEach(btn => btn.style.display = '');
               
                const link = document.createElement('a');
                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
                link.download = 'tierlist_' + timestamp + '.png';
                link.href = canvas.toDataURL('image/png');
                link.click();
            }).catch(err => {
                console.error('生成图片失败:', err);
                if (controls) controls.style.display = originalControlsDisplay;
                if (poolContainer) poolContainer.style.display = originalPoolDisplay;
                deleteButtons.forEach(btn => btn.style.display = '');
                alert('生成图片失败,请查看控制台了解详情');
            });
        },
       
        resetTierlist: function() {
            if (!confirm('确定要重置所有排列吗?此操作不可撤销。')) {
                return;
            }
           
            const pool = document.getElementById('character-pool');
            if (!pool) {
                alert('未找到角色池!');
                return;
            }
           
            const allAvatars = document.querySelectorAll('.avatar-frame');
           
            document.querySelectorAll('.tier-row').forEach(row => {
                row.innerHTML = '';
            });
           
            allAvatars.forEach(avatar => {
                pool.appendChild(avatar);
                const img = avatar.querySelector('img');
                if (img) {
                    img.style.opacity = '1';
                }
            });
           
            // 清除保存的状态
            try {
                localStorage.removeItem('tierlist-state');
            } catch (e) {
                console.warn('无法清除状态:', e);
            }
        }
    };
   
    // 标记已初始化
    window.TierlistMakerInitialized = true;
    window.TierlistMaker = TierlistMaker;
   
    // 自动初始化
    TierlistMaker.init();
})();
})();
</script>
</script>
</includeonly>
</includeonly>

2025年10月29日 (三) 14:50的最新版本