MediaWiki:TierListMaker.js
来自卡厄思梦境WIKI
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
(function() {
'use strict';
// 仅在 TierListMaker 页面运行
if (mw.config.get('wgPageName') !== '节奏榜') {
return;
}
// 等待页面加载完成
$(function() {
initTierListMaker();
});
function initTierListMaker() {
// 添加控制按钮
addControlButtons();
// 初始化拖拽功能
initDragAndDrop();
// 添加样式
addCustomStyles();
// 检查URL中是否有ID参数
checkAndLoadFromURL();
}
function addControlButtons() {
var $buttonContainer = $('<div>')
.attr('id', 'tier-list-controls')
.css({
'margin': '10px 0',
'padding': '10px',
'background': '#f0f0f0',
'border-radius': '5px'
});
var $saveButton = $('<button>')
.text('保存为PNG')
.addClass('tier-list-save-btn')
.click(saveTierListAsPNG);
var $clearButton = $('<button>')
.text('清空所有')
.addClass('tier-list-clear-btn')
.click(clearAllTiers);
var $resetButton = $('<button>')
.text('重置')
.addClass('tier-list-reset-btn')
.click(function() {
location.reload();
});
var $saveListButton = $('<button>')
.text('保存榜单')
.addClass('tier-list-save-list-btn')
.click(saveTierListData);
var $loadListButton = $('<button>')
.text('加载榜单')
.addClass('tier-list-load-list-btn')
.click(showLoadDialog);
var $shareButton = $('<button>')
.text('分享榜单')
.addClass('tier-list-share-btn')
.click(shareTierList);
$buttonContainer.append(
$saveButton, ' ',
$saveListButton, ' ',
$loadListButton, ' ',
$shareButton, ' ',
$clearButton, ' ',
$resetButton
);
$('.wikitable').before($buttonContainer);
}
function addCustomStyles() {
var styles = `
<style>
.tier-list-save-btn, .tier-list-clear-btn, .tier-list-reset-btn,
.tier-list-save-list-btn, .tier-list-load-list-btn, .tier-list-share-btn {
padding: 8px 16px;
margin: 0 5px;
font-size: 14px;
font-weight: bold;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.tier-list-save-btn {
background: #4CAF50;
color: white;
}
.tier-list-save-btn:hover {
background: #45a049;
}
.tier-list-clear-btn {
background: #f44336;
color: white;
}
.tier-list-clear-btn:hover {
background: #da190b;
}
.tier-list-reset-btn {
background: #2196F3;
color: white;
}
.tier-list-reset-btn:hover {
background: #0b7dda;
}
.tier-list-save-list-btn {
background: #FF9800;
color: white;
}
.tier-list-save-list-btn:hover {
background: #e68900;
}
.tier-list-load-list-btn {
background: #9C27B0;
color: white;
}
.tier-list-load-list-btn:hover {
background: #7B1FA2;
}
.tier-list-share-btn {
background: #00BCD4;
color: white;
}
.tier-list-share-btn:hover {
background: #0097A7;
}
.wikitable td {
min-height: 120px;
min-width: 300px;
padding: 10px;
vertical-align: top;
position: relative;
}
.tier-row-drop-zone {
border: 2px dashed transparent;
transition: all 0.3s;
}
.tier-row-drop-zone.drag-over {
border-color: #2196F3;
background-color: rgba(33, 150, 243, 0.1);
}
.avatar-frame {
cursor: move;
transition: transform 0.2s, opacity 0.2s;
user-select: none;
}
.avatar-frame:hover {
transform: scale(1.05);
}
.avatar-frame.dragging {
opacity: 0.5;
}
.avatar-frame.in-tier {
margin: 5px;
}
#avatar-pool {
border: 2px solid #ddd;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
background: #fafafa;
min-height: 150px;
}
#avatar-pool-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.tier-dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
align-items: center;
justify-content: center;
}
.tier-dialog {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
max-width: 500px;
width: 90%;
z-index: 9999;
}
.tier-dialog h3 {
margin-top: 0;
margin-bottom: 20px;
color: #333;
}
.tier-dialog label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #555;
}
.tier-dialog input[type="text"] {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 14px;
box-sizing: border-box;
margin-bottom: 15px;
}
.tier-dialog input[type="text"]:focus {
outline: none;
border-color: #2196F3;
}
.tier-dialog-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
}
.tier-dialog-btn {
padding: 10px 20px;
border: none;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
}
.tier-dialog-btn-primary {
background: #2196F3;
color: white;
}
.tier-dialog-btn-primary:hover {
background: #0b7dda;
}
.tier-dialog-btn-secondary {
background: #ddd;
color: #333;
}
.tier-dialog-btn-secondary:hover {
background: #ccc;
}
.tier-id-display {
background: #f0f8ff;
padding: 15px;
border-radius: 5px;
border: 2px solid #2196F3;
margin: 15px 0;
word-break: break-all;
}
.tier-id-label {
font-weight: bold;
color: #2196F3;
margin-bottom: 5px;
}
.tier-id-value {
font-family: 'Courier New', monospace;
font-size: 16px;
color: #333;
user-select: all;
}
.tier-url-display {
background: #f0f8ff;
padding: 15px;
border-radius: 5px;
border: 2px solid #00BCD4;
margin: 15px 0;
word-break: break-all;
}
.copy-btn {
background: #4CAF50;
color: white;
padding: 5px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
margin-left: 10px;
}
.copy-btn:hover {
background: #45a049;
}
.success-message {
color: #4CAF50;
font-weight: bold;
margin-top: 10px;
}
</style>
`;
$('head').append(styles);
}
function initDragAndDrop() {
// 创建头像池容器
var $avatarPool = $('<div>')
.attr('id', 'avatar-pool')
.addClass('tier-row-drop-zone');
var $poolTitle = $('<div>')
.attr('id', 'avatar-pool-title')
.text('角色池(拖动到上方表格)');
$avatarPool.append($poolTitle);
// 将所有头像移动到头像池
var $avatars = $('.avatar-frame');
$avatars.each(function() {
$(this).addClass('avatar-item').appendTo($avatarPool);
});
$('.wikitable').after($avatarPool);
// 为表格单元格添加drop-zone类
$('.wikitable td').addClass('tier-row-drop-zone');
// 初始化所有头像的拖拽
initAvatarDrag();
// 初始化所有drop zone
initDropZones();
}
function initAvatarDrag() {
$('.avatar-frame').each(function() {
var $avatar = $(this);
$avatar.attr('draggable', 'true');
$avatar.on('dragstart', function(e) {
$(this).addClass('dragging');
e.originalEvent.dataTransfer.effectAllowed = 'move';
e.originalEvent.dataTransfer.setData('text/html', this.outerHTML);
});
$avatar.on('dragend', function(e) {
$(this).removeClass('dragging');
});
});
}
function initDropZones() {
$('.tier-row-drop-zone').each(function() {
var $zone = $(this);
$zone.on('dragover', function(e) {
e.preventDefault();
e.originalEvent.dataTransfer.dropEffect = 'move';
$(this).addClass('drag-over');
});
$zone.on('dragleave', function(e) {
$(this).removeClass('drag-over');
});
$zone.on('drop', function(e) {
e.preventDefault();
$(this).removeClass('drag-over');
var $dragging = $('.avatar-frame.dragging');
if ($dragging.length) {
// 移动现有元素
$dragging.removeClass('dragging');
// 如果是在tier中,添加样式
if ($(this).hasClass('wikitable')) {
$dragging.addClass('in-tier');
} else if ($(this).attr('id') === 'avatar-pool') {
$dragging.removeClass('in-tier');
} else {
$dragging.addClass('in-tier');
}
$(this).append($dragging);
}
});
});
}
function clearAllTiers() {
if (!confirm('确定要清空所有分级吗?')) {
return;
}
// 将所有tier中的头像移回头像池
$('.wikitable td .avatar-frame').each(function() {
$(this).removeClass('in-tier');
$('#avatar-pool').append($(this));
});
}
// 保存榜单数据
function saveTierListData() {
var tierData = collectTierData();
if (tierData.tiers.length === 0 && tierData.pool.length === $('.avatar-frame').length) {
alert('您还没有放置任何角色到榜单中!');
return;
}
var listId = generateUniqueId();
var timestamp = new Date().toISOString();
var saveData = {
id: listId,
timestamp: timestamp,
data: tierData
};
// 保存到 localStorage
localStorage.setItem('tierlist_' + listId, JSON.stringify(saveData));
// 显示保存成功对话框
showSaveSuccessDialog(listId);
}
// 收集榜单数据
function collectTierData() {
var data = {
tiers: [],
pool: []
};
// 收集表格中的数据
$('.wikitable tr').each(function(rowIndex) {
var $cells = $(this).find('td');
if ($cells.length > 0) {
var tierName = $cells.eq(0).text().trim();
var avatars = [];
$cells.eq(1).find('.avatar-frame').each(function() {
var avatarId = getAvatarIdentifier($(this));
if (avatarId) {
avatars.push(avatarId);
}
});
if (avatars.length > 0) {
data.tiers.push({
name: tierName,
avatars: avatars
});
}
}
});
// 收集头像池中的数据
$('#avatar-pool .avatar-frame').each(function() {
var avatarId = getAvatarIdentifier($(this));
if (avatarId) {
data.pool.push(avatarId);
}
});
return data;
}
// 获取头像的唯一标识符
function getAvatarIdentifier($avatar) {
// 尝试多种方式获取头像标识
var identifier = null;
// 方法1: 通过图片src
var $img = $avatar.find('img');
if ($img.length > 0) {
identifier = $img.attr('src');
}
// 方法2: 通过data属性
if (!identifier && $avatar.attr('data-avatar-id')) {
identifier = $avatar.attr('data-avatar-id');
}
// 方法3: 通过alt或title
if (!identifier && $img.attr('alt')) {
identifier = $img.attr('alt');
}
// 方法4: 通过完整HTML(作为后备)
if (!identifier) {
identifier = $avatar.prop('outerHTML');
}
return identifier;
}
// 生成唯一ID
function generateUniqueId() {
var timestamp = Date.now().toString(36);
var randomStr = Math.random().toString(36).substr(2, 9);
return timestamp + randomStr;
}
// 显示保存成功对话框
function showSaveSuccessDialog(listId) {
var shareUrl = window.location.href.split('?')[0] + '?tierid=' + listId;
var $overlay = $('<div>').addClass('tier-dialog-overlay');
var $dialog = $('<div>').addClass('tier-dialog');
var $title = $('<h3>').text('榜单保存成功!');
var $idDisplay = $('<div>').addClass('tier-id-display');
var $idLabel = $('<div>').addClass('tier-id-label').text('榜单ID:');
var $idValue = $('<div>').addClass('tier-id-value').text(listId);
var $copyIdBtn = $('<button>').addClass('copy-btn').text('复制ID')
.click(function() {
copyToClipboard(listId);
$(this).text('已复制!').css('background', '#4CAF50');
setTimeout(() => {
$(this).text('复制ID').css('background', '');
}, 2000);
});
$idDisplay.append($idLabel, $idValue, $copyIdBtn);
var $urlDisplay = $('<div>').addClass('tier-url-display');
var $urlLabel = $('<div>').addClass('tier-id-label').text('分享链接:');
var $urlValue = $('<div>').addClass('tier-id-value').text(shareUrl);
var $copyUrlBtn = $('<button>').addClass('copy-btn').text('复制链接')
.click(function() {
copyToClipboard(shareUrl);
$(this).text('已复制!').css('background', '#4CAF50');
setTimeout(() => {
$(this).text('复制链接').css('background', '');
}, 2000);
});
$urlDisplay.append($urlLabel, $urlValue, $copyUrlBtn);
var $info = $('<p>').css({'color': '#666', 'margin': '15px 0'})
.text('保存的榜单将在本地存储,您可以通过ID或链接访问。');
var $buttons = $('<div>').addClass('tier-dialog-buttons');
var $closeBtn = $('<button>')
.addClass('tier-dialog-btn tier-dialog-btn-primary')
.text('关闭')
.click(function() {
$overlay.remove();
});
$buttons.append($closeBtn);
$dialog.append($title, $idDisplay, $urlDisplay, $info, $buttons);
$overlay.append($dialog);
$('body').append($overlay);
}
// 显示加载对话框
function showLoadDialog() {
var $overlay = $('<div>').addClass('tier-dialog-overlay');
var $dialog = $('<div>').addClass('tier-dialog');
var $title = $('<h3>').text('加载榜单');
var $label = $('<label>').text('请输入榜单ID:');
var $input = $('<input>')
.attr('type', 'text')
.attr('placeholder', '输入榜单ID')
.attr('id', 'tier-id-input');
var $errorMsg = $('<div>')
.css({'color': '#f44336', 'margin-top': '10px', 'display': 'none'})
.attr('id', 'load-error-msg');
var $buttons = $('<div>').addClass('tier-dialog-buttons');
var $loadBtn = $('<button>')
.addClass('tier-dialog-btn tier-dialog-btn-primary')
.text('加载')
.click(function() {
var listId = $input.val().trim();
if (!listId) {
$errorMsg.text('请输入榜单ID!').show();
return;
}
if (loadTierListData(listId)) {
$overlay.remove();
} else {
$errorMsg.text('未找到该榜单,请检查ID是否正确!').show();
}
});
var $cancelBtn = $('<button>')
.addClass('tier-dialog-btn tier-dialog-btn-secondary')
.text('取消')
.click(function() {
$overlay.remove();
});
$buttons.append($cancelBtn, $loadBtn);
$dialog.append($title, $label, $input, $errorMsg, $buttons);
$overlay.append($dialog);
$('body').append($overlay);
// 聚焦输入框
setTimeout(() => $input.focus(), 100);
// 支持回车键加载
$input.on('keypress', function(e) {
if (e.which === 13) {
$loadBtn.click();
}
});
}
// 加载榜单数据
function loadTierListData(listId) {
var savedData = localStorage.getItem('tierlist_' + listId);
if (!savedData) {
return false;
}
try {
var saveData = JSON.parse(savedData);
applyTierData(saveData.data);
// 显示加载成功提示
showNotification('榜单加载成功!', 'success');
return true;
} catch (e) {
console.error('加载榜单失败:', e);
return false;
}
}
// 应用榜单数据
function applyTierData(tierData) {
// 首先清空所有层级
$('.wikitable td .avatar-frame').each(function() {
$(this).removeClass('in-tier');
$('#avatar-pool').append($(this));
});
// 应用tier数据
tierData.tiers.forEach(function(tier) {
var $targetCell = null;
// 查找对应的tier单元格
$('.wikitable tr').each(function() {
var $cells = $(this).find('td');
if ($cells.length > 0) {
var tierName = $cells.eq(0).text().trim();
if (tierName === tier.name) {
$targetCell = $cells.eq(1);
return false;
}
}
});
if ($targetCell) {
tier.avatars.forEach(function(avatarId) {
var $avatar = findAvatarByIdentifier(avatarId);
if ($avatar) {
$avatar.addClass('in-tier');
$targetCell.append($avatar);
}
});
}
});
}
// 根据标识符查找头像
function findAvatarByIdentifier(identifier) {
var $found = null;
$('.avatar-frame').each(function() {
var $avatar = $(this);
var currentId = getAvatarIdentifier($avatar);
if (currentId === identifier) {
$found = $avatar;
return false;
}
});
return $found;
}
// 分享榜单
function shareTierList() {
var tierData = collectTierData();
if (tierData.tiers.length === 0 && tierData.pool.length === $('.avatar-frame').length) {
alert('您还没有放置任何角色到榜单中!');
return;
}
// 检查是否已经保存
var currentUrl = window.location.href;
var urlParams = new URLSearchParams(window.location.search);
var existingId = urlParams.get('tierid');
if (existingId) {
// 已有ID,直接分享
var shareUrl = window.location.href.split('?')[0] + '?tierid=' + existingId;
showShareDialog(shareUrl, existingId);
} else {
// 先保存再分享
var listId = generateUniqueId();
var timestamp = new Date().toISOString();
var saveData = {
id: listId,
timestamp: timestamp,
data: tierData
};
localStorage.setItem('tierlist_' + listId, JSON.stringify(saveData));
var shareUrl = window.location.href.split('?')[0] + '?tierid=' + listId;
showShareDialog(shareUrl, listId);
}
}
// 显示分享对话框
function showShareDialog(shareUrl, listId) {
var $overlay = $('<div>').addClass('tier-dialog-overlay');
var $dialog = $('<div>').addClass('tier-dialog');
var $title = $('<h3>').text('分享榜单');
var $urlDisplay = $('<div>').addClass('tier-url-display');
var $urlLabel = $('<div>').addClass('tier-id-label').text('分享链接:');
var $urlValue = $('<div>').addClass('tier-id-value').text(shareUrl);
var $copyUrlBtn = $('<button>').addClass('copy-btn').text('复制链接')
.click(function() {
copyToClipboard(shareUrl);
$(this).text('已复制!').css('background', '#4CAF50');
setTimeout(() => {
$(this).text('复制链接').css('background', '');
}, 2000);
});
$urlDisplay.append($urlLabel, $urlValue, $copyUrlBtn);
var $idDisplay = $('<div>').addClass('tier-id-display');
var $idLabel = $('<div>').addClass('tier-id-label').text('榜单ID:');
var $idValue = $('<div>').addClass('tier-id-value').text(listId);
var $copyIdBtn = $('<button>').addClass('copy-btn').text('复制ID')
.click(function() {
copyToClipboard(listId);
$(this).text('已复制!').css('background', '#4CAF50');
setTimeout(() => {
$(this).text('复制ID').css('background', '');
}, 2000);
});
$idDisplay.append($idLabel, $idValue, $copyIdBtn);
var $info = $('<p>').css({'color': '#666', 'margin': '15px 0'})
.text('将此链接分享给其他人,他们可以查看您的榜单!');
var $buttons = $('<div>').addClass('tier-dialog-buttons');
var $closeBtn = $('<button>')
.addClass('tier-dialog-btn tier-dialog-btn-primary')
.text('关闭')
.click(function() {
$overlay.remove();
});
$buttons.append($closeBtn);
$dialog.append($title, $urlDisplay, $idDisplay, $info, $buttons);
$overlay.append($dialog);
$('body').append($overlay);
}
// 检查URL并加载榜单
function checkAndLoadFromURL() {
var urlParams = new URLSearchParams(window.location.search);
var tierid = urlParams.get('tierid');
if (tierid) {
setTimeout(function() {
if (loadTierListData(tierid)) {
showNotification('已加载分享的榜单!ID: ' + tierid, 'success');
} else {
showNotification('无法加载榜单,ID可能无效或已过期', 'error');
}
}, 500);
}
}
// 复制到剪贴板
function copyToClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text);
} else {
// 后备方案
var $temp = $('<textarea>');
$('body').append($temp);
$temp.val(text).select();
document.execCommand('copy');
$temp.remove();
}
}
// 显示通知
function showNotification(message, type) {
var bgColor = type === 'success' ? '#4CAF50' : '#f44336';
var $notification = $('<div>')
.css({
'position': 'fixed',
'top': '20px',
'right': '20px',
'background': bgColor,
'color': 'white',
'padding': '15px 25px',
'border-radius': '5px',
'box-shadow': '0 4px 12px rgba(0,0,0,0.3)',
'z-index': 10000,
'font-size': '14px',
'font-weight': 'bold',
'animation': 'slideIn 0.3s ease-out'
})
.text(message);
$('body').append($notification);
setTimeout(function() {
$notification.fadeOut(300, function() {
$(this).remove();
});
}, 3000);
}
function saveTierListAsPNG() {
// 检查是否已加载html2canvas库
if (typeof html2canvas === 'undefined') {
loadHtml2Canvas(function() {
captureTierList();
});
} else {
captureTierList();
}
}
function loadHtml2Canvas(callback) {
mw.loader.using('jquery', function() {
$.getScript('https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js')
.done(function() {
callback();
})
.fail(function() {
alert('加载截图库失败,请检查网络连接');
});
});
}
function captureTierList() {
var $table = $('.wikitable');
if ($table.length === 0) {
alert('未找到表格');
return;
}
// 显示加载提示
var $loading = $('<div>')
.css({
'position': 'fixed',
'top': '50%',
'left': '50%',
'transform': 'translate(-50%, -50%)',
'background': 'rgba(0,0,0,0.8)',
'color': 'white',
'padding': '20px 40px',
'border-radius': '10px',
'z-index': 9999,
'font-size': '18px'
})
.text('正在生成图片...')
.appendTo('body');
html2canvas($table[0], {
backgroundColor: '#ffffff',
scale: 2,
logging: false,
useCORS: true,
allowTaint: true
}).then(function(canvas) {
$loading.remove();
// 转换为blob并下载
canvas.toBlob(function(blob) {
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
var timestamp = new Date().toISOString().slice(0,19).replace(/:/g,'-');
link.download = 'tier-list-' + timestamp + '.png';
link.href = url;
link.click();
URL.revokeObjectURL(url);
});
}).catch(function(error) {
$loading.remove();
console.error('截图失败:', error);
alert('生成图片失败,请重试');
});
}
})();