diff --git a/src/web/static/css/style.css b/src/web/static/css/style.css index 0a69693..bd003c6 100644 --- a/src/web/static/css/style.css +++ b/src/web/static/css/style.css @@ -40,7 +40,7 @@ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } -/* 悬停提示样式 */ +/* 悬停提示样式 - 改为点击样式 */ .chess-item, .synergy-item { cursor: pointer; position: relative; @@ -52,48 +52,160 @@ z-index: 5; } -/* 增强提示框样式 */ -#tft-tooltip { - transition: all 0.3s ease; - max-height: 80vh; +/* 模态框样式 */ +#tft-modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); + z-index: 50; + display: none; + backdrop-filter: blur(2px); + animation: backdropFadeIn 0.3s ease; +} + +#tft-modal-backdrop.open { + display: block; +} + +#tft-modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 51; + max-width: 90%; + width: 450px; + max-height: 85vh; + background-color: #1f2937; + border-radius: 0.5rem; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); + display: none; + overflow: hidden; /* 改为hidden,让滚动发生在内部容器 */ + border: 1px solid rgba(99, 102, 241, 0.3); + animation: modalFadeIn 0.3s cubic-bezier(0.16, 1, 0.3, 1); + color: #f3f4f6; /* 设置模态框内默认文字颜色为浅灰色 */ +} + +#tft-modal.open { + display: block; +} + +/* 模态框内部包装容器 */ +.modal-wrapper { + width: 100%; + height: 100%; + max-height: 85vh; overflow-y: auto; overflow-x: hidden; - border: 1px solid rgba(99, 102, 241, 0.3); - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); - transform-origin: top left; - animation: tooltipFadeIn 0.35s cubic-bezier(0.16, 1, 0.3, 1); scrollbar-width: thin; scrollbar-color: rgba(79, 70, 229, 0.4) rgba(17, 24, 39, 0.2); + position: relative; +} + +@keyframes backdropFadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes modalFadeIn { + from { + opacity: 0; + transform: translate(-50%, -48%) scale(0.96); + } + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } } /* 自定义滚动条样式 */ -#tft-tooltip::-webkit-scrollbar { +.modal-wrapper::-webkit-scrollbar { width: 6px; } -#tft-tooltip::-webkit-scrollbar-track { +.modal-wrapper::-webkit-scrollbar-track { background: rgba(17, 24, 39, 0.1); border-radius: 3px; } -#tft-tooltip::-webkit-scrollbar-thumb { +.modal-wrapper::-webkit-scrollbar-thumb { background-color: rgba(79, 70, 229, 0.4); border-radius: 3px; transition: background-color 0.3s; } -#tft-tooltip::-webkit-scrollbar-thumb:hover { +.modal-wrapper::-webkit-scrollbar-thumb:hover { background-color: rgba(79, 70, 229, 0.6); } -/* 提示框内容淡入效果 */ -#tft-tooltip .tooltip-content { +/* 仅在滚动时显示滚动条 */ +.modal-wrapper { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.modal-wrapper::-webkit-scrollbar { + width: 6px; + /* 初始隐藏滚动条 */ + opacity: 0; + transition: opacity 0.3s ease; +} + +.modal-wrapper:hover::-webkit-scrollbar, +.modal-wrapper:focus::-webkit-scrollbar, +.modal-wrapper:active::-webkit-scrollbar { + /* 悬停、聚焦或激活时显示滚动条 */ + opacity: 1; +} + +/* 解决模态框内容加载时可能导致的布局偏移 */ +.modal-content { + min-height: 150px; /* 设置最小高度,减少加载时的布局变化 */ + position: relative; + /* 保持右侧边距一致,不管是否有滚动条 */ + padding: 20px; + padding-right: 26px; /* 16px基础 + 10px给滚动条预留 */ + box-sizing: border-box; +} + +/* 模态框内容淡入效果 */ +#tft-modal .modal-content { word-break: break-word; opacity: 0; animation: contentFadeIn 0.2s ease forwards; animation-delay: 0.1s; } +/* 模态框中的标题和文本颜色 */ +#tft-modal .font-bold { + color: #ffffff; +} + +#tft-modal .text-gray-800, +#tft-modal .text-gray-700 { + color: #e5e7eb; +} + +#tft-modal .text-gray-500, +#tft-modal .text-gray-400 { + color: #9ca3af; +} + +#tft-modal .text-indigo-300 { + color: #a5b4fc; +} + +#tft-modal .bg-gray-700 { + background-color: #374151; +} + +#tft-modal .bg-gray-600 { + background-color: #4b5563; +} + @keyframes contentFadeIn { from { opacity: 0; @@ -105,23 +217,12 @@ } } -@keyframes tooltipFadeIn { - from { - opacity: 0; - transform: translateY(-5px) scale(0.97); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - /* 提示框中的费用标识 */ -#tft-tooltip .cost-1 { border-left: 3px solid #94a3b8; } /* 1费 - 灰色 */ -#tft-tooltip .cost-2 { border-left: 3px solid #65a30d; } /* 2费 - 绿色 */ -#tft-tooltip .cost-3 { border-left: 3px solid #2563eb; } /* 3费 - 蓝色 */ -#tft-tooltip .cost-4 { border-left: 3px solid #7e22ce; } /* 4费 - 紫色 */ -#tft-tooltip .cost-5 { border-left: 3px solid #f59e0b; } /* 5费 - 金色 */ +#tft-modal .cost-1 { border-left: 3px solid #94a3b8; } /* 1费 - 灰色 */ +#tft-modal .cost-2 { border-left: 3px solid #65a30d; } /* 2费 - 绿色 */ +#tft-modal .cost-3 { border-left: 3px solid #2563eb; } /* 3费 - 蓝色 */ +#tft-modal .cost-4 { border-left: 3px solid #7e22ce; } /* 4费 - 紫色 */ +#tft-modal .cost-5 { border-left: 3px solid #f59e0b; } /* 5费 - 金色 */ /* 提示框内交互元素样式 */ .chess-item-mini, .synergy-item-mini { @@ -147,34 +248,51 @@ border-radius: 2px; } -/* 提示框中的各部分样式 */ -#tft-tooltip .tooltip-content { - word-break: break-word; - padding-top: 5px; - padding-right: 36px; /* 为右侧关闭按钮留出空间 */ -} - -/* 关闭按钮样式 */ -#tft-tooltip .tooltip-close-btn { +/* 模态框关闭按钮样式 */ +#tft-modal .modal-close-btn { + position: absolute; + top: 12px; + right: 12px; + width: 36px; + height: 36px; + background-color: #374151; + color: #9ca3af; + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; transition: all 0.2s ease; opacity: 0.9; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 2; } -#tft-tooltip .tooltip-close-btn:hover { +#tft-modal .modal-close-btn:hover { opacity: 1; + color: #ffffff; + background-color: #4b5563; transform: scale(1.05); } +/* 模态框返回按钮样式 */ +#tft-modal .modal-back-btn { + background-color: #4f46e5; +} + +#tft-modal .modal-back-btn:hover { + background-color: #4338ca; +} + /* 特殊介绍文本样式 */ -#tft-tooltip .skill-introduce, -#tft-tooltip .synergy-introduce { +#tft-modal .skill-introduce, +#tft-modal .synergy-introduce { border-left: 3px solid rgba(79, 70, 229, 0.7); background-color: rgba(79, 70, 229, 0.1); padding-left: 8px; } -#tft-tooltip .loading-spinner { +#tft-modal .loading-spinner { opacity: 0.8; } @@ -249,121 +367,147 @@ border-radius: 4px; } -/* 移动设备上的提示框样式优化 */ +/* 模态框额外样式 - 文字颜色优化 */ +#tft-modal .text-sm { + color: #e5e7eb; +} + +#tft-modal .text-xs { + color: #d1d5db; +} + +#tft-modal .font-medium { + color: #f3f4f6; +} + +#tft-modal .font-semibold { + color: #f9fafb; +} + +/* 模态框中棋子列表项 */ +#tft-modal .chess-item-mini { + background-color: #374151; + border-radius: 4px; +} + +#tft-modal .chess-item-mini .text-sm { + color: #f3f4f6; +} + +/* 模态框中羁绊列表项 */ +#tft-modal .synergy-item-mini { + background-color: #374151; + border-radius: 4px; +} + +#tft-modal .synergy-item-mini .text-sm { + color: #f3f4f6; +} + +/* 移动端样式优化 */ @media (max-width: 768px) { - #tft-tooltip { - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); - border: 1px solid rgba(99, 102, 241, 0.5); - max-width: calc(100vw - 40px); - animation: mobileTooltipFadeIn 0.3s cubic-bezier(0.16, 1, 0.3, 1); - max-height: 70vh; - /* 固定位置在屏幕中央 */ - position: fixed; - top: 50% !important; - left: 50% !important; - transform: translate(-50%, -50%); - margin: 0 auto; - width: calc(100vw - 40px) !important; + #tft-modal { + width: 95%; + max-height: 80vh; } - - #tft-tooltip .tooltip-close-btn { - width: 38px; - height: 38px; - top: 10px; - right: 10px; + + #tft-modal .modal-content { + padding: 16px; } - - #tft-tooltip .tooltip-content { - margin-top: 10px; - padding-right: 20px; - padding-bottom: 5px; - } - - /* 增大移动端触摸目标大小 */ + .chess-item-mini, .synergy-item-mini { - padding: 12px; - margin-bottom: 10px; - position: relative; - overflow: hidden; - /* 增加右侧间距,避免文本与按钮重叠 */ - padding-right: 80px; + margin-bottom: 5px; } - - /* 移动端使用更明显的交互提示 */ + .chess-item-mini::after, .synergy-item-mini::after { - content: '点击查看'; - position: absolute; - top: 50%; - right: 10px; - transform: translateY(-50%); - color: white; - font-size: 10px; - background-color: rgba(79, 70, 229, 0.5); - padding: 3px 8px; - border-radius: 10px; - white-space: nowrap; - z-index: 5; + display: none; } - - /* 防止棋子费用与点击按钮重叠 */ + + /* 移动端字体和边距调整 */ + #tft-modal .text-xs { + font-size: 0.7rem; + } + + #tft-modal .text-sm { + font-size: 0.8rem; + } + + #tft-modal .px-2 { + padding-left: 0.4rem; + padding-right: 0.4rem; + } + + #tft-modal .py-1 { + padding-top: 0.2rem; + padding-bottom: 0.2rem; + } + + /* 为移动端优化小标签 */ .chess-item-mini .text-xs.px-1\.5.py-0\.5.bg-gray-600.rounded { - margin-right: 65px; + font-size: 0.65rem; + padding: 0.1rem 0.25rem; } - - /* 防止羁绊类型与点击按钮重叠 */ + .synergy-item-mini .bg-gray-600.text-xs.px-1\.5.py-0\.5.rounded { - margin-right: 65px; + font-size: 0.65rem; + padding: 0.1rem 0.25rem; } - - /* 移动端提示框内部元素间距优化 */ - #tft-tooltip .mb-2 { - margin-bottom: 10px; + + /* 移动端间距调整 */ + #tft-modal .mb-2 { + margin-bottom: 0.4rem; } - - #tft-tooltip .mb-3 { - margin-bottom: 15px; + + #tft-modal .mb-3 { + margin-bottom: 0.6rem; } - - #tft-tooltip .p-2 { - padding: 10px; + + #tft-modal .p-2 { + padding: 0.4rem; } - - /* 移动端动画效果 */ - @keyframes mobileTooltipFadeIn { + + /* 移动端动画优化 */ + @keyframes mobileModalFadeIn { from { opacity: 0; - transform: translate(-50%, -45%); + transform: translate(-50%, -46%); } to { opacity: 1; transform: translate(-50%, -50%); } } - - /* 提示框内部滚动优化 */ - #tft-tooltip::-webkit-scrollbar { - width: 5px; + + #tft-modal { + animation: mobileModalFadeIn 0.25s ease; } - - /* 移动端加载状态优化 */ - #tft-tooltip .loading-spinner .animate-spin { - height: 45px; - width: 45px; - border-width: 3px; + + /* 移动端滚动条优化 */ + #tft-modal::-webkit-scrollbar { + width: 4px; } - - #tft-tooltip .loading-spinner .text-xs { - font-size: 12px; - margin-top: 8px; + + /* 移动端加载动画优化 */ + #tft-modal .loading-spinner .animate-spin { + height: 8vw; + width: 8vw; + border-width: 2px; + margin-bottom: 0.3rem; } - - /* 移动端表格布局优化 */ - #tft-tooltip .grid.grid-cols-2 { + + #tft-modal .loading-spinner .text-xs { + font-size: 0.65rem; + } + + /* 移动端网格布局优化 */ + #tft-modal .grid.grid-cols-2 { grid-template-columns: 1fr; + grid-gap: 0.3rem; } - - /* 改进相关棋子列表显示 */ - #tft-tooltip .grid.grid-cols-2.gap-1.mt-1 { - grid-template-columns: 1fr 1fr; + + #tft-modal .grid.grid-cols-2.gap-1.mt-1 { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; } } \ No newline at end of file diff --git a/src/web/static/js/main.js b/src/web/static/js/main.js index fb5fbb3..27d60dd 100644 --- a/src/web/static/js/main.js +++ b/src/web/static/js/main.js @@ -658,59 +658,57 @@ function toggleActiveChessVisibility(showOnlyActive) { let tooltipHistory = []; /** - * 创建提示框 + * 创建模态框 */ function createTooltip() { - // 如果已经存在提示框,不重复创建 - if ($('#tft-tooltip').length) { - return; - } - - const tooltip = $(` - - `); - - // 添加到页面 - $('body').append(tooltip); + // 已经在HTML中创建了模态框结构,不需要动态创建 + // 为模态框按钮绑定事件 + const modal = $('#tft-modal'); + const modalBackdrop = $('#tft-modal-backdrop'); // 绑定关闭按钮事件 - tooltip.find('.tooltip-close-btn').on('click', function(e) { + modal.find('.modal-close-btn').on('click', function(e) { e.stopPropagation(); - tooltip.addClass('hidden'); - currentHoveredId = null; - $('.tooltip-active').removeClass('tooltip-active'); - // 重置历史记录 - tooltipHistory = []; + closeModal(); }); - // 防止点击提示框本身触发关闭 - tooltip.on('click', function(e) { + // 点击背景关闭模态框 + modalBackdrop.on('click', function() { + closeModal(); + }); + + // 防止点击模态框本身触发关闭 + modal.on('click', function(e) { e.stopPropagation(); }); } /** - * 显示提示的返回按钮(重构为返回功能) - * @param {jQuery} tooltipContent 提示内容元素 + * 关闭模态框 */ -function showTooltipBackButton(tooltipContent) { - const tooltip = tooltipContent.closest('#tft-tooltip'); +function closeModal() { + $('#tft-modal').removeClass('open'); + $('#tft-modal-backdrop').removeClass('open'); + currentHoveredId = null; + $('.tooltip-active').removeClass('tooltip-active'); + // 重置历史记录 + tooltipHistory = []; + + // 重置滚动位置 + setTimeout(() => { + $('.modal-wrapper').scrollTop(0); + }, 300); // 等待模态框关闭动画完成 +} + +/** + * 显示模态框的返回按钮(重构为返回功能) + * @param {jQuery} modalContent 模态框内容元素 + */ +function showTooltipBackButton(modalContent) { + const modal = $('#tft-modal'); // 获取关闭按钮 - const closeButton = tooltip.find('.tooltip-close-btn'); + const closeButton = modal.find('.modal-close-btn'); // 如果历史记录中有内容,则显示返回功能 if (tooltipHistory.length > 0) { @@ -731,37 +729,44 @@ function showTooltipBackButton(tooltipContent) { // 弹出上一个提示内容 const prevItem = tooltipHistory.pop(); if (prevItem) { + // 滚动到顶部 + $('.modal-wrapper').scrollTop(0); + // 恢复ID和内容 currentHoveredId = prevItem.id; - tooltipContent.html(prevItem.content); - // 如果仍有历史记录,保持返回按钮 - if (tooltipHistory.length > 0) { - showTooltipBackButton(tooltipContent); - } else { - // 没有更多历史,切换回关闭按钮 - resetCloseButton(closeButton, tooltip); - } - - // 重新绑定事件处理程序 - bindTooltipInteractions(tooltipContent); + // 使用淡入效果显示内容 + modalContent.fadeOut(100, function() { + $(this).html(prevItem.content).fadeIn(100); + + // 如果仍有历史记录,保持返回按钮 + if (tooltipHistory.length > 0) { + showTooltipBackButton(modalContent); + } else { + // 没有更多历史,切换回关闭按钮 + resetCloseButton(closeButton); + } + + // 重新绑定事件处理程序 + bindTooltipInteractions(modalContent); + }); } }); // 使用返回样式 - closeButton.removeClass('bg-gray-700 hover:bg-gray-600').addClass('bg-indigo-600 hover:bg-indigo-700'); + closeButton.removeClass('bg-gray-700 hover:bg-gray-600') + .addClass('modal-back-btn'); } else { // 没有历史,重置为关闭按钮 - resetCloseButton(closeButton, tooltip); + resetCloseButton(closeButton); } } /** * 重置为关闭按钮 * @param {jQuery} closeButton 按钮元素 - * @param {jQuery} tooltip 提示框元素 */ -function resetCloseButton(closeButton, tooltip) { +function resetCloseButton(closeButton) { // 修改按钮图标为关闭图标 closeButton.html(` @@ -775,75 +780,83 @@ function resetCloseButton(closeButton, tooltip) { // 绑定关闭按钮事件 closeButton.on('click', function(e) { e.stopPropagation(); - tooltip.addClass('hidden'); - currentHoveredId = null; - $('.tooltip-active').removeClass('tooltip-active'); - // 重置历史记录 - tooltipHistory = []; + closeModal(); }); // 使用关闭样式 - closeButton.removeClass('bg-indigo-600 hover:bg-indigo-700').addClass('bg-gray-700 hover:bg-gray-600'); + closeButton.removeClass('modal-back-btn'); } /** - * 绑定提示内容中的交互事件 - * @param {jQuery} tooltipContent 提示内容元素 + * 绑定模态框内容中的交互事件 + * @param {jQuery} modalContent 模态框内容元素 */ -function bindTooltipInteractions(tooltipContent) { +function bindTooltipInteractions(modalContent) { // 为羁绊小标签添加点击事件,显示对应的羁绊详情 - tooltipContent.find('.synergy-item-mini').on('click', function(e) { + modalContent.find('.synergy-item-mini').on('click', function(e) { e.stopPropagation(); const synergyId = $(this).data('synergy-id'); if (synergyId && synergyId !== currentHoveredId) { // 保存当前的提示内容到历史记录 tooltipHistory.push({ id: currentHoveredId, - content: tooltipContent.html() + content: modalContent.html() }); // 更新当前悬停ID并显示新的提示 currentHoveredId = synergyId; - showSynergyTooltipContent(synergyId, this, tooltipContent); + showSynergyTooltipContent(synergyId, this, modalContent); } }); // 为棋子小标签添加点击事件,显示对应的棋子详情 - tooltipContent.find('.chess-item-mini').on('click', function(e) { + modalContent.find('.chess-item-mini').on('click', function(e) { e.stopPropagation(); const chessId = $(this).data('chess-id'); if (chessId && chessId !== currentHoveredId) { // 保存当前的提示内容到历史记录 tooltipHistory.push({ id: currentHoveredId, - content: tooltipContent.html() + content: modalContent.html() }); // 更新当前悬停ID并显示新的提示 currentHoveredId = chessId; - showChessTooltipContent(chessId, this, tooltipContent); + showChessTooltipContent(chessId, this, modalContent); } }); } /** - * 显示棋子提示内容(不重新创建提示框) + * 显示棋子提示内容(在模态框中显示) * @param {string} chessId 棋子ID * @param {Element} element 触发元素 - * @param {jQuery} tooltipContent 提示内容元素 + * @param {jQuery} modalContent 模态框内容元素 */ -function showChessTooltipContent(chessId, element, tooltipContent) { - const tooltip = tooltipContent.closest('#tft-tooltip'); - const loadingSpinner = tooltip.find('.loading-spinner'); +function showChessTooltipContent(chessId, element, modalContent) { + const modal = $('#tft-modal'); + const loadingSpinner = modal.find('.loading-spinner'); const isMobile = window.matchMedia("(max-width: 768px)").matches || ('ontouchstart' in window) || (navigator.maxTouchPoints > 0); - // 先定位提示框但不显示 - positionTooltip(tooltip, element); + // 先打开模态框,再加载内容,这样可以避免滚动条突然出现 + $('#tft-modal').addClass('open'); + $('#tft-modal-backdrop').addClass('open'); - // 重置内容并显示加载中 - tooltipContent.empty(); + // 滚动到顶部 + $('.modal-wrapper').scrollTop(0); + + // 重置内容并显示加载中 - 添加占位符内容以维持高度 + modalContent.empty(); + modalContent.html(` +
+
+
加载中...
+
正在获取棋子信息
+
+
+ `); loadingSpinner.removeClass('hidden'); // 获取棋子详情 @@ -855,9 +868,9 @@ function showChessTooltipContent(chessId, element, tooltipContent) { let content = `
- ${details.name} + ${details.name} ${details.title ? `${details.title}` : ''} - ${details.cost}费 + ${details.cost}费
`; @@ -890,70 +903,70 @@ function showChessTooltipContent(chessId, element, tooltipContent) { content += `
生命值: - ${details.life} + ${details.life}
`; } if (details.attack) { content += `
攻击力: - ${details.attack} + ${details.attack}
`; } if (details.attackSpeed) { content += `
攻速: - ${details.attackSpeed} + ${details.attackSpeed}
`; } if (details.attackRange) { content += `
攻击范围: - ${details.attackRange} + ${details.attackRange}
`; } if (details.armor) { content += `
护甲: - ${details.armor} + ${details.armor}
`; } if (details.spellBlock) { content += `
魔抗: - ${details.spellBlock} + ${details.spellBlock}
`; } if (details.crit) { content += `
暴击率: - ${details.crit}% + ${details.crit}%
`; } if (details.magic) { content += `
法力值: - ${details.magic} + ${details.magic}
`; } if (details.startMagic) { content += `
初始法力值: - ${details.startMagic} + ${details.startMagic}
`; } if (details.attackMag) { content += `
法术强度倍率: - ${details.attackMag} + ${details.attackMag}
`; } @@ -967,7 +980,7 @@ function showChessTooltipContent(chessId, element, tooltipContent) {
${details.skill_name}
- ${details.skill_mana ? `${details.skill_mana}法力` : ''} + ${details.skill_mana ? `${details.skill_mana}法力` : ''}
${details.skill_type ? `
${details.skill_type}
` : ''} @@ -1002,265 +1015,323 @@ function showChessTooltipContent(chessId, element, tooltipContent) { details.synergies.forEach(synergy => { content += `
- ${synergy.name} - ${synergyTypes[synergy.type] || synergy.type} + ${synergy.name} + ${synergyTypes[synergy.type] || synergy.type}
`; }); } - // 隐藏加载中状态 - loadingSpinner.addClass('hidden'); - - // 设置内容并显示提示框 - tooltipContent.html(content); - - // 显示返回按钮 - showTooltipBackButton(tooltipContent); - - // 绑定交互事件 - bindTooltipInteractions(tooltipContent); - - // 所有内容准备好后显示提示框 - tooltip.removeClass('hidden'); - } else { - // 处理错误 - tooltipContent.html(`
获取详情失败
`); - loadingSpinner.addClass('hidden'); - tooltip.removeClass('hidden'); + // 使用淡入效果更新内容,避免内容突然变化 + setTimeout(() => { + // 隐藏加载动画 + loadingSpinner.addClass('hidden'); + + // 模拟内容加载完成的淡入效果 + modalContent.fadeOut(100, function() { + $(this).html(content).fadeIn(150); + + // 显示返回按钮(如果有历史记录) + showTooltipBackButton(modalContent); + + // 绑定交互事件 + bindTooltipInteractions(modalContent); + }); + }, 100); } }).catch(error => { // 处理错误 - tooltipContent.html(`
请求出错
`); - loadingSpinner.addClass('hidden'); - tooltip.removeClass('hidden'); + setTimeout(() => { + loadingSpinner.addClass('hidden'); + modalContent.html(`
请求出错: ${error.message || '未知错误'}
`); + modalContent.fadeIn(150); + }, 100); + console.error('获取棋子详情失败:', error); }); } /** - * 显示羁绊提示内容(不重新创建提示框) + * 显示羁绊提示内容(在模态框中显示) * @param {string} synergyId 羁绊ID * @param {Element} element 触发元素 - * @param {jQuery} tooltipContent 提示内容元素 + * @param {jQuery} modalContent 模态框内容元素 */ -function showSynergyTooltipContent(synergyId, element, tooltipContent) { - const tooltip = tooltipContent.closest('#tft-tooltip'); - const loadingSpinner = tooltip.find('.loading-spinner'); - const isMobile = window.matchMedia("(max-width: 768px)").matches || - ('ontouchstart' in window) || - (navigator.maxTouchPoints > 0); +function showSynergyTooltipContent(synergyId, element, modalContent) { + const modal = $('#tft-modal'); + const loadingSpinner = modal.find('.loading-spinner'); - // 先定位提示框但不显示 - positionTooltip(tooltip, element); + // 先打开模态框,再加载内容,这样可以避免滚动条突然出现 + $('#tft-modal').addClass('open'); + $('#tft-modal-backdrop').addClass('open'); - // 显示加载中 - tooltipContent.empty(); + // 滚动到顶部 + $('.modal-wrapper').scrollTop(0); + + // 重置内容并显示加载中 - 添加占位符内容以维持高度 + modalContent.empty(); + modalContent.html(` +
+
+
加载中...
+
正在获取羁绊信息
+
+
+ `); loadingSpinner.removeClass('hidden'); // 获取羁绊详情 fetchItemDetails('synergy', synergyId).then(response => { if (response.status === 'success') { const details = response.details; + console.log("羁绊详情:", details); // 调试用,查看响应数据结构 - // 构建提示内容,添加左侧内边距给返回按钮留出空间 + // 构建提示内容 let content = ` -
- ${details.name} - - ${details.type === 'job' ? '职业' : '特质'} - +
+
+ ${details.name} + ${details.type === 'job' ? '职业' : '种族'} +
`; - // 羁绊介绍 - 优先使用 introduce 字段 + // 羁绊介绍 if (details.introduce) { content += ` -
+
${details.introduce}
`; } - // 羁绊描述作为补充信息 - 如果有额外的描述信息 - if (details.description && details.description !== details.introduce) { - content += `
${details.description}
`; - } - - // 等级效果 + // 羁绊激活条件 if (details.levels && details.levels.length > 0) { - content += `
等级效果:
`; + content += `
激活条件:
`; - // 按等级排序 - const sortedLevels = [...details.levels].sort((a, b) => { - return parseInt(a.level) - parseInt(b.level); - }); - - sortedLevels.forEach(levelInfo => { + // 添加激活条件卡片 + details.levels.forEach(level => { + // 检查激活数量可能的字段名 + let activateCount = null; + if (level.level) { + activateCount = level.level; + } else if (level.count) { + activateCount = level.count; + } else if (level.num) { + activateCount = level.num; + } else if (level.number) { + activateCount = level.number; + } + content += ` -
-
${levelInfo.level}人口:
-
${levelInfo.effect}
+
+
+ ${activateCount}个 + + ${level.level ? `等级 ${level.level}` : ''} + +
+
${level.effect}
`; }); + + content += `
`; } // 相关棋子 if (details.related_chess && details.related_chess.length > 0) { - content += `
相关棋子 (${details.related_chess.length}):
`; + content += `
相关棋子:
`; - // 按费用排序 - const sortedChess = [...details.related_chess].sort((a, b) => { - return parseInt(a.cost) - parseInt(b.cost); + // 根据费用对棋子进行分组 + const chessByPrice = {}; + details.related_chess.forEach(chess => { + const price = chess.cost || 0; + if (!chessByPrice[price]) { + chessByPrice[price] = []; + } + chessByPrice[price].push(chess); }); - // 保持两列布局,因为棋子卡片较小 - content += `
`; - sortedChess.forEach(chess => { - const costClass = `cost-${chess.cost}`; - content += ` -
- ${chess.name} - ${chess.cost}费 -
- `; + // 按费用从低到高排序并添加棋子 + Object.keys(chessByPrice).sort((a, b) => Number(a) - Number(b)).forEach(price => { + const priceChess = chessByPrice[price]; + + content += `
`; + content += `
`; + + priceChess.forEach(chess => { + content += ` +
+
${chess.cost}
+ ${chess.name} +
+ `; + }); + + content += `
`; }); - content += `
`; } - // 隐藏加载中状态 - loadingSpinner.addClass('hidden'); - - // 设置内容并显示提示框 - tooltipContent.html(content); - - // 显示返回按钮 - showTooltipBackButton(tooltipContent); - - // 绑定交互事件 - bindTooltipInteractions(tooltipContent); - - // 所有内容准备好后显示提示框 - tooltip.removeClass('hidden'); - } else { - // 处理错误 - tooltipContent.html(`
获取详情失败
`); - loadingSpinner.addClass('hidden'); - tooltip.removeClass('hidden'); + // 隐藏加载动画并更新内容 + setTimeout(() => { + loadingSpinner.addClass('hidden'); + + // 模拟内容加载完成的淡入效果 + modalContent.fadeOut(100, function() { + $(this).html(content).fadeIn(150); + + // 显示返回按钮(如果有历史记录) + showTooltipBackButton(modalContent); + + // 绑定棋子点击事件 + bindTooltipInteractions(modalContent); + }); + }, 100); } }).catch(error => { // 处理错误 - tooltipContent.html(`
请求出错
`); - loadingSpinner.addClass('hidden'); - tooltip.removeClass('hidden'); + setTimeout(() => { + loadingSpinner.addClass('hidden'); + modalContent.html(`
请求出错: ${error.message || '未知错误'}
`); + modalContent.fadeIn(150); + }, 100); + console.error('获取羁绊详情失败:', error); }); } /** - * 显示棋子悬停提示 + * 显示棋子模态框 * @param {string} chessId 棋子ID * @param {Element} element 触发元素 */ function showChessTooltip(chessId, element) { - const tooltip = $('#tft-tooltip'); - const tooltipContent = tooltip.find('.tooltip-content'); - const loadingSpinner = tooltip.find('.loading-spinner'); - - // 先隐藏提示框,在数据加载完成后再显示 - tooltip.addClass('hidden'); + const modal = $('#tft-modal'); + const modalContent = modal.find('.modal-content'); + const loadingSpinner = modal.find('.loading-spinner'); // 重置历史记录 tooltipHistory = []; // 使用共享方法显示内容 - showChessTooltipContent(chessId, element, tooltipContent); + showChessTooltipContent(chessId, element, modalContent); } /** - * 显示羁绊悬停提示 + * 显示羁绊模态框 * @param {string} synergyId 羁绊ID * @param {Element} element 触发元素 */ function showSynergyTooltip(synergyId, element) { - const tooltip = $('#tft-tooltip'); - const tooltipContent = tooltip.find('.tooltip-content'); - const loadingSpinner = tooltip.find('.loading-spinner'); - - // 先隐藏提示框,在数据加载完成后再显示 - tooltip.addClass('hidden'); + const modal = $('#tft-modal'); + const modalContent = modal.find('.modal-content'); + const loadingSpinner = modal.find('.loading-spinner'); // 重置历史记录 tooltipHistory = []; // 使用共享方法显示内容 - showSynergyTooltipContent(synergyId, element, tooltipContent); + showSynergyTooltipContent(synergyId, element, modalContent); } +// 存储当前悬停元素ID +let currentHoveredId = null; + /** - * 定位提示框 - * @param {jQuery} tooltip 提示元素 - * @param {Element} element 触发元素 + * 初始化模态框交互 */ -function positionTooltip(tooltip, element) { - const rect = element.getBoundingClientRect(); +function initTooltips() { + // 初始化模态框 + createTooltip(); + + // 检测是否为移动设备 const isMobile = window.matchMedia("(max-width: 768px)").matches || ('ontouchstart' in window) || (navigator.maxTouchPoints > 0); - // 移动设备始终居中显示 - if (isMobile) { - // 移动设备上使用CSS定位在屏幕中央 - // 样式表中已经设置了固定位置,这里不需要设置left和top - tooltip.css({ - // 重置定位,让CSS的固定定位生效 - transform: '', - maxHeight: `${window.innerHeight * 0.7}px`, - zIndex: 1000 - }); - } else { - // 桌面设备的定位逻辑 - const tooltipWidth = 320; // 提示框宽度 - - // 计算水平位置,优先显示在右侧 - let left = rect.right + 10; - if (left + tooltipWidth > window.innerWidth) { - // 右侧空间不足,显示在左侧 - left = rect.left - tooltipWidth - 10; - // 如果左侧也没有足够空间 - if (left < 0) { - left = Math.max(10, (window.innerWidth - tooltipWidth) / 2); // 居中显示 - } + // 处理棋子触发事件 - 统一使用点击事件 + $(document).on('click', '.chess-item', function(e) { + // 避免在点击移除按钮时显示提示 + if ($(e.target).closest('.remove-chess').length) { + return; } - // 计算垂直位置,考虑提示框不超出屏幕 - let top = rect.top; - const tooltipHeight = Math.min(500, window.innerHeight * 0.8); // 预估高度 + e.stopPropagation(); - if (top + tooltipHeight > window.innerHeight) { - // 尝试显示在元素上方 - top = Math.max(10, rect.top - tooltipHeight); + // 如果已经点击过同一个元素,则关闭模态框 + if (currentHoveredId === $(this).data('chess-id') && $('#tft-modal').hasClass('open')) { + closeModal(); + return; } - // 设置位置 - tooltip.css({ - left: `${left}px`, - top: `${top}px`, - width: 'auto', - maxHeight: `${window.innerHeight - 20}px` // 防止溢出屏幕 - }); - } -} - -// 防抖函数 -function debounce(func, wait) { - let timeout; - return function(...args) { - const context = this; - clearTimeout(timeout); - timeout = setTimeout(() => func.apply(context, args), wait); - }; + const chessId = $(this).data('chess-id'); + if (chessId) { + // 关闭之前可能打开的提示 + $('.tooltip-active').removeClass('tooltip-active'); + + currentHoveredId = chessId; + showChessTooltip(chessId, this); + + // 添加活跃状态 + $(this).addClass('tooltip-active'); + } + }); + + // 处理羁绊触发事件 - 统一使用点击事件 + $(document).on('click', '.synergy-item', function(e) { + // 避免在点击移除按钮时显示提示 + if ($(e.target).closest('.remove-synergy').length) { + return; + } + + e.stopPropagation(); + + // 如果已经点击过同一个元素,则关闭模态框 + if (currentHoveredId === $(this).data('synergy-id') && $('#tft-modal').hasClass('open')) { + closeModal(); + return; + } + + const synergyId = $(this).data('synergy-id'); + if (synergyId) { + // 关闭之前可能打开的提示 + $('.tooltip-active').removeClass('tooltip-active'); + + currentHoveredId = synergyId; + showSynergyTooltip(synergyId, this); + + // 添加活跃状态 + $(this).addClass('tooltip-active'); + } + }); + + // 点击文档其它位置关闭模态框 + $(document).on('click', function(e) { + if (!$(e.target).closest('#tft-modal, .chess-item, .synergy-item').length) { + closeModal(); + } + }); + + // ESC键关闭模态框 + $(document).on('keydown', function(e) { + if (e.key === 'Escape' && $('#tft-modal').hasClass('open')) { + closeModal(); + } + }); + + // 处理点击事件,避免与模态框冲突 + $(document).on('click', '.remove-chess, .remove-synergy', function(e) { + e.stopPropagation(); + closeModal(); + }); + + // 处理窗口滚动时隐藏模态框 + $(window).on('scroll', function() { + if ($('#tft-modal').hasClass('open')) { + closeModal(); + } + }); } /** @@ -1295,153 +1366,4 @@ function fetchItemDetails(type, id) { error: reject }); }); -} - -// 存储当前悬停元素ID -let currentHoveredId = null; - -/** - * 初始化悬停提示 - */ -function initTooltips() { - // 创建提示框 - createTooltip(); - - const tooltipElement = $('#tft-tooltip'); - - // 检测是否为移动设备 - const isMobile = window.matchMedia("(max-width: 768px)").matches || - ('ontouchstart' in window) || - (navigator.maxTouchPoints > 0); - - // 点击文档其它位置关闭提示框 - $(document).on('click touchend', function(e) { - if (!$(e.target).closest('#tft-tooltip, .chess-item, .synergy-item').length) { - tooltipElement.addClass('hidden'); - currentHoveredId = null; - $('.tooltip-active').removeClass('tooltip-active'); - } - }); - - // 处理棋子触发事件 - $(document).on(isMobile ? 'click' : 'mouseenter', '.chess-item', function(e) { - // 避免在点击移除按钮时显示提示 - if ($(e.target).closest('.remove-chess').length) { - return; - } - - // 在移动设备上点击时阻止冒泡 - if (isMobile) { - e.stopPropagation(); - // 如果已经点击过同一个元素,则关闭提示 - if (currentHoveredId === $(this).data('chess-id') && tooltipElement.is(':visible')) { - tooltipElement.addClass('hidden'); - currentHoveredId = null; - $(this).removeClass('tooltip-active'); - return; - } - } - - const chessId = $(this).data('chess-id'); - if (chessId) { - // 关闭之前可能打开的提示 - $('.tooltip-active').removeClass('tooltip-active'); - - currentHoveredId = chessId; - - // 在移动设备上直接显示,非移动设备使用防抖 - if (isMobile) { - showChessTooltip(chessId, this); - } else { - // 使用防抖函数延迟显示提示 - debounce((id, element) => { - if (currentHoveredId === id) { - showChessTooltip(id, element); - } - }, 50)(chessId, this); - } - - // 添加活跃状态 - $(this).addClass('tooltip-active'); - } - }); - - // 处理羁绊触发事件 - $(document).on(isMobile ? 'click' : 'mouseenter', '.synergy-item', function(e) { - // 避免在点击移除按钮时显示提示 - if ($(e.target).closest('.remove-synergy').length) { - return; - } - - // 在移动设备上点击时阻止冒泡 - if (isMobile) { - e.stopPropagation(); - // 如果已经点击过同一个元素,则关闭提示 - if (currentHoveredId === $(this).data('synergy-id') && tooltipElement.is(':visible')) { - tooltipElement.addClass('hidden'); - currentHoveredId = null; - $(this).removeClass('tooltip-active'); - return; - } - } - - const synergyId = $(this).data('synergy-id'); - if (synergyId) { - // 关闭之前可能打开的提示 - $('.tooltip-active').removeClass('tooltip-active'); - - currentHoveredId = synergyId; - - // 在移动设备上直接显示,非移动设备使用防抖 - if (isMobile) { - showSynergyTooltip(synergyId, this); - } else { - // 使用防抖函数延迟显示提示 - debounce((id, element) => { - if (currentHoveredId === id) { - showSynergyTooltip(id, element); - } - }, 50)(synergyId, this); - } - - // 添加活跃状态 - $(this).addClass('tooltip-active'); - } - }); - - // 非移动设备的鼠标离开事件 - if (!isMobile) { - $(document).on('mouseleave', '.chess-item, .synergy-item', function() { - currentHoveredId = null; - // 移除活跃状态 - $(this).removeClass('tooltip-active'); - - // 检查鼠标是否在提示框上 - if (!tooltipElement.is(':hover')) { - tooltipElement.addClass('hidden'); - } - }); - - // 避免提示框本身触发悬停事件 - tooltipElement.on('mouseenter', function() { - // 保持显示 - }).on('mouseleave', function() { - $(this).addClass('hidden'); - currentHoveredId = null; - $('.tooltip-active').removeClass('tooltip-active'); - }); - } - - // 处理点击事件,避免与提示框冲突 - $(document).on('click', '.remove-chess, .remove-synergy', function(e) { - e.stopPropagation(); - tooltipElement.addClass('hidden'); - currentHoveredId = null; - }); - - // 处理窗口滚动时隐藏提示 - $(window).on('scroll', function() { - tooltipElement.addClass('hidden'); - currentHoveredId = null; - }); } \ No newline at end of file diff --git a/src/web/templates/index.html b/src/web/templates/index.html index 61bf1c3..bebd70e 100644 --- a/src/web/templates/index.html +++ b/src/web/templates/index.html @@ -140,6 +140,25 @@
+ +
+
+ +
+

云顶之弈阵容推荐器