From 1336512244bc1353ce08c77698c73c18d21e2b91 Mon Sep 17 00:00:00 2001
From: hxuanyu <2252193204@qq.com>
Date: Thu, 3 Apr 2025 10:56:55 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A8=A1=E6=80=81=E6=A1=86?=
=?UTF-8?q?=E7=9A=84=E6=A0=B7=E5=BC=8F=E5=92=8C=E4=BA=A4=E4=BA=92=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=8A=A0=E8=BD=BD=E5=8A=A8?=
=?UTF-8?q?=E7=94=BB=E6=95=88=E6=9E=9C=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8?=
=?UTF-8?q?=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82=E9=87=8D=E6=9E=84=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3JavaScript=E9=80=BB=E8=BE=91=E4=BB=A5=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E6=96=B0=E7=9A=84=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?=
=?UTF-8?q?=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8=E4=B8=8D=E5=90=8C=E8=AE=BE?=
=?UTF-8?q?=E5=A4=87=E4=B8=8A=E8=89=AF=E5=A5=BD=E6=98=BE=E7=A4=BA=E5=92=8C?=
=?UTF-8?q?=E6=93=8D=E4=BD=9C=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/web/static/css/style.css | 125 ++++++----
src/web/static/js/main.js | 431 +++++++++++++++++++++++------------
src/web/templates/index.html | 2 +-
3 files changed, 365 insertions(+), 193 deletions(-)
diff --git a/src/web/static/css/style.css b/src/web/static/css/style.css
index bd003c6..27313e3 100644
--- a/src/web/static/css/style.css
+++ b/src/web/static/css/style.css
@@ -63,18 +63,20 @@
z-index: 50;
display: none;
backdrop-filter: blur(2px);
- animation: backdropFadeIn 0.3s ease;
+ transition: opacity 0.25s ease;
+ opacity: 0;
}
#tft-modal-backdrop.open {
display: block;
+ opacity: 1;
}
#tft-modal {
position: fixed;
top: 50%;
left: 50%;
- transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%) scale(0.98);
z-index: 51;
max-width: 90%;
width: 450px;
@@ -85,12 +87,15 @@
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; /* 设置模态框内默认文字颜色为浅灰色 */
+ opacity: 0;
+ transition: all 0.25s ease;
}
#tft-modal.open {
display: block;
+ opacity: 1;
+ transform: translate(-50%, -50%) scale(1);
}
/* 模态框内部包装容器 */
@@ -110,17 +115,6 @@
to { opacity: 1; }
}
-@keyframes modalFadeIn {
- from {
- opacity: 0;
- transform: translate(-50%, -48%) scale(0.96);
- }
- to {
- opacity: 1;
- transform: translate(-50%, -50%) scale(1);
- }
-}
-
/* 自定义滚动条样式 */
.modal-wrapper::-webkit-scrollbar {
width: 6px;
@@ -174,9 +168,6 @@
/* 模态框内容淡入效果 */
#tft-modal .modal-content {
word-break: break-word;
- opacity: 0;
- animation: contentFadeIn 0.2s ease forwards;
- animation-delay: 0.1s;
}
/* 模态框中的标题和文本颜色 */
@@ -206,17 +197,6 @@
background-color: #4b5563;
}
-@keyframes contentFadeIn {
- from {
- opacity: 0;
- transform: translateY(5px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
/* 提示框中的费用标识 */
#tft-modal .cost-1 { border-left: 3px solid #94a3b8; } /* 1费 - 灰色 */
#tft-modal .cost-2 { border-left: 3px solid #65a30d; } /* 2费 - 绿色 */
@@ -252,36 +232,37 @@
#tft-modal .modal-close-btn {
position: absolute;
top: 12px;
- right: 12px;
- width: 36px;
- height: 36px;
- background-color: #374151;
- color: #9ca3af;
- border-radius: 9999px;
+ left: 12px;
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
+ background-color: rgba(55, 65, 81, 0.9);
+ color: rgb(209, 213, 219);
+ border: none;
cursor: pointer;
+ z-index: 5;
transition: all 0.2s ease;
- opacity: 0.9;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
- z-index: 2;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
#tft-modal .modal-close-btn:hover {
- opacity: 1;
- color: #ffffff;
- background-color: #4b5563;
+ background-color: rgba(99, 102, 241, 0.9);
+ color: white;
transform: scale(1.05);
}
/* 模态框返回按钮样式 */
#tft-modal .modal-back-btn {
- background-color: #4f46e5;
+ background-color: rgba(79, 70, 229, 0.9);
+ color: white;
}
#tft-modal .modal-back-btn:hover {
- background-color: #4338ca;
+ background-color: rgba(99, 102, 241, 0.9);
+ transform: scale(1.05);
}
/* 特殊介绍文本样式 */
@@ -292,8 +273,56 @@
padding-left: 8px;
}
+/* 加载动画样式 */
#tft-modal .loading-spinner {
- opacity: 0.8;
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba(31, 41, 55, 0.85); /* 深灰色背景,半透明 */
+ z-index: 2;
+ backdrop-filter: blur(1px);
+ border-radius: 0.5rem;
+ transition: opacity 0.2s ease, visibility 0.2s ease;
+}
+
+#tft-modal .loading-spinner.hidden {
+ opacity: 0;
+ visibility: hidden;
+}
+
+#tft-modal .loading-spinner.visible {
+ opacity: 1;
+ visibility: visible;
+}
+
+#tft-modal .loading-spinner .animate-spin {
+ height: 2.5rem;
+ width: 2.5rem;
+ border-width: 3px;
+ border-color: transparent;
+ border-top-color: rgba(99, 102, 241, 0.7);
+ border-bottom-color: rgba(99, 102, 241, 0.7);
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+}
+
+#tft-modal .loading-spinner .text-xs {
+ margin-top: 0.75rem;
+ color: rgba(165, 180, 252, 0.8);
+ animation: pulse 1.5s ease infinite;
+}
+
+@keyframes pulse {
+ 0% { opacity: 0.6; }
+ 50% { opacity: 1; }
+ 100% { opacity: 0.6; }
}
/* 优化悬停标识 */
@@ -510,4 +539,14 @@
flex-wrap: wrap;
gap: 0.3rem;
}
+}
+
+/* 模态框内容淡入淡出动画 */
+@keyframes fadeInOut {
+ 0% { opacity: 0; transform: translateY(10px); }
+ 100% { opacity: 1; transform: translateY(0); }
+}
+
+.fade-in-content {
+ animation: fadeInOut 0.3s ease forwards;
}
\ No newline at end of file
diff --git a/src/web/static/js/main.js b/src/web/static/js/main.js
index 27d60dd..9d835c3 100644
--- a/src/web/static/js/main.js
+++ b/src/web/static/js/main.js
@@ -687,17 +687,23 @@ function createTooltip() {
* 关闭模态框
*/
function closeModal() {
- $('#tft-modal').removeClass('open');
- $('#tft-modal-backdrop').removeClass('open');
+ const modal = $('#tft-modal');
+ const modalBackdrop = $('#tft-modal-backdrop');
+
+ // 关闭模态框
+ modal.removeClass('open');
+ modalBackdrop.removeClass('open');
+
+ // 重置历史记录和状态
currentHoveredId = null;
$('.tooltip-active').removeClass('tooltip-active');
- // 重置历史记录
tooltipHistory = [];
- // 重置滚动位置
+ // 延迟后重置滚动位置
setTimeout(() => {
$('.modal-wrapper').scrollTop(0);
- }, 300); // 等待模态框关闭动画完成
+ modal.find('.modal-content').empty();
+ }, 250); // 等待模态框关闭动画完成
}
/**
@@ -840,24 +846,15 @@ function showChessTooltipContent(chessId, element, modalContent) {
('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0);
- // 先打开模态框,再加载内容,这样可以避免滚动条突然出现
+ // 先打开模态框,再加载内容
$('#tft-modal').addClass('open');
$('#tft-modal-backdrop').addClass('open');
// 滚动到顶部
$('.modal-wrapper').scrollTop(0);
- // 重置内容并显示加载中 - 添加占位符内容以维持高度
- modalContent.empty();
- modalContent.html(`
-
- `);
- loadingSpinner.removeClass('hidden');
+ // 显示加载动画
+ loadingSpinner.removeClass('hidden').addClass('visible');
// 获取棋子详情
fetchItemDetails('chess', chessId).then(response => {
@@ -975,7 +972,29 @@ function showChessTooltipContent(chessId, element, modalContent) {
}
// 技能信息
- if (details.skill_name) {
+ if (details.skill) {
+ content += `
+
+
${details.skill.name || '技能'}
+
+
+
+ ${details.skill.detail ? `
+
+ ${details.skill.detail}
+
+ ` : ''}
+
+ ${details.skill.introduce ? `
+
+ ${details.skill.introduce}
+
+ ` : ''}
+
+
+ `;
+ } else if (details.skill_name) {
+ // 兼容原有数据结构
content += `
@@ -1005,49 +1024,107 @@ function showChessTooltipContent(chessId, element, modalContent) {
}
// 羁绊信息
- if (details.synergies && details.synergies.length > 0) {
- content += `
羁绊:
`;
- const synergyTypes = {
- 'job': '职业',
- 'race': '特质'
- };
-
- details.synergies.forEach(synergy => {
- content += `
-
- ${synergy.name}
- ${synergyTypes[synergy.type] || synergy.type}
-
- `;
- });
+ let hasSynergies = (details.jobs && details.jobs.length) || (details.races && details.races.length);
+ if (!hasSynergies && details.synergies && details.synergies.length > 0) {
+ hasSynergies = true;
}
- // 使用淡入效果更新内容,避免内容突然变化
- setTimeout(() => {
- // 隐藏加载动画
- loadingSpinner.addClass('hidden');
+ if (hasSynergies) {
+ content += `
+
羁绊
+
`;
- // 模拟内容加载完成的淡入效果
- modalContent.fadeOut(100, function() {
- $(this).html(content).fadeIn(150);
+ // 处理新的数据结构
+ // 职业羁绊
+ if (details.jobs && details.jobs.length) {
+ for (const job of details.jobs) {
+ content += `
+
+
+
${job.name}
+
${job.description || ''}
+
+
职业
+
+ `;
+ }
+ }
+
+ // 种族羁绊
+ if (details.races && details.races.length) {
+ for (const race of details.races) {
+ content += `
+
+
+
${race.name}
+
${race.description || ''}
+
+
种族
+
+ `;
+ }
+ }
+
+ // 兼容原有数据结构
+ if (details.synergies && details.synergies.length > 0) {
+ const synergyTypes = {
+ 'job': '职业',
+ 'race': '特质'
+ };
- // 显示返回按钮(如果有历史记录)
- showTooltipBackButton(modalContent);
-
- // 绑定交互事件
- bindTooltipInteractions(modalContent);
- });
- }, 100);
+ details.synergies.forEach(synergy => {
+ content += `
+
+
+
${synergy.name}
+
${synergy.description || ''}
+
+
${synergyTypes[synergy.type] || synergy.type}
+
+ `;
+ });
+ }
+
+ content += `
`;
+ }
+
+ // 淡出加载动画
+ loadingSpinner.removeClass('visible').addClass('hidden');
+
+ // 直接更新内容,减少不必要的过渡
+ modalContent.html(content);
+
+ // 设置返回按钮
+ showTooltipBackButton(modalContent);
+
+ // 绑定新内容的交互事件
+ bindTooltipInteractions(modalContent);
+
+ } else {
+ // 加载失败
+ loadingSpinner.removeClass('visible').addClass('hidden');
+
+ modalContent.html(`
+
+
+
加载失败
+
无法获取棋子信息,请稍后重试
+
+
+ `);
}
}).catch(error => {
- // 处理错误
- setTimeout(() => {
- loadingSpinner.addClass('hidden');
- modalContent.html(`
请求出错: ${error.message || '未知错误'}
`);
- modalContent.fadeIn(150);
- }, 100);
+ // 加载失败
+ loadingSpinner.removeClass('visible').addClass('hidden');
- console.error('获取棋子详情失败:', error);
+ modalContent.html(`
+
+
+
加载失败
+
发生错误: ${error.message || '未知错误'}
+
+
+ `);
});
}
@@ -1061,43 +1138,42 @@ function showSynergyTooltipContent(synergyId, element, modalContent) {
const modal = $('#tft-modal');
const loadingSpinner = modal.find('.loading-spinner');
- // 先打开模态框,再加载内容,这样可以避免滚动条突然出现
+ // 先打开模态框,再加载内容
$('#tft-modal').addClass('open');
$('#tft-modal-backdrop').addClass('open');
// 滚动到顶部
$('.modal-wrapper').scrollTop(0);
- // 重置内容并显示加载中 - 添加占位符内容以维持高度
- modalContent.empty();
- modalContent.html(`
-
- `);
- loadingSpinner.removeClass('hidden');
+ // 显示加载动画
+ loadingSpinner.removeClass('hidden').addClass('visible');
// 获取羁绊详情
fetchItemDetails('synergy', synergyId).then(response => {
if (response.status === 'success') {
const details = response.details;
- console.log("羁绊详情:", details); // 调试用,查看响应数据结构
- // 构建提示内容
+ // 构建提示内容,添加左侧内边距给返回按钮留出空间
let content = `
-
+
${details.name}
- ${details.type === 'job' ? '职业' : '种族'}
+
+
+ ${details.jobId ? '职业' : (details.raceId ? '种族' : (details.type === 'job' ? '职业' : '种族'))}
`;
- // 羁绊介绍
- if (details.introduce) {
+ // 羁绊描述
+ if (details.description) {
+ content += `
+
+ ${details.description}
+
+ `;
+ } else if (details.introduce) {
+ // 兼容原有数据结构
content += `
${details.introduce}
@@ -1105,99 +1181,156 @@ function showSynergyTooltipContent(synergyId, element, modalContent) {
`;
}
- // 羁绊激活条件
- if (details.levels && details.levels.length > 0) {
- content += `
激活条件:
`;
-
- // 添加激活条件卡片
- 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 += `
-
-
- ${activateCount}个
-
- ${level.level ? `等级 ${level.level}` : ''}
-
-
-
${level.effect}
-
- `;
- });
-
- content += `
`;
+ // 羁绊效果
+ let hasLevels = details.setEffects && details.setEffects.length;
+ if (!hasLevels && details.levels && details.levels.length) {
+ hasLevels = true;
}
- // 相关棋子
- if (details.related_chess && details.related_chess.length > 0) {
- content += `
相关棋子:
`;
+ if (hasLevels) {
+ content += `
+
+
激活效果
+
+ `;
- // 根据费用对棋子进行分组
- const chessByPrice = {};
- details.related_chess.forEach(chess => {
- const price = chess.cost || 0;
- if (!chessByPrice[price]) {
- chessByPrice[price] = [];
- }
- chessByPrice[price].push(chess);
- });
-
- // 按费用从低到高排序并添加棋子
- Object.keys(chessByPrice).sort((a, b) => Number(a) - Number(b)).forEach(price => {
- const priceChess = chessByPrice[price];
+ // 处理新的数据结构
+ if (details.setEffects && details.setEffects.length) {
+ // 按照人口从小到大排序
+ const sortedEffects = [...details.setEffects].sort((a, b) => a.count - b.count);
- content += `
`;
- content += `
`;
-
- priceChess.forEach(chess => {
+ for (const effect of sortedEffects) {
content += `
-
-
${chess.cost}
-
${chess.name}
+
+
${effect.count}人口:
+
${effect.effect}
+
+ `;
+ }
+ }
+
+ // 兼容原有数据结构
+ if (details.levels && details.levels.length > 0) {
+ 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 += `
+
+
${activateCount}个:
+
${level.effect}
`;
});
-
- content += `
`;
- });
+ }
+
+ content += `
`;
}
- // 隐藏加载动画并更新内容
- setTimeout(() => {
- loadingSpinner.addClass('hidden');
+ // 拥有此羁绊的棋子列表
+ let hasChess = details.heros && details.heros.length;
+ if (!hasChess && details.related_chess && details.related_chess.length) {
+ hasChess = true;
+ }
+
+ if (hasChess) {
+ content += `
+
+
拥有该羁绊的棋子
+
+ `;
- // 模拟内容加载完成的淡入效果
- modalContent.fadeOut(100, function() {
- $(this).html(content).fadeIn(150);
+ // 处理新的数据结构
+ if (details.heros && details.heros.length) {
+ // 按照费用从小到大排序
+ const sortedHeros = [...details.heros].sort((a, b) => a.cost - b.cost);
- // 显示返回按钮(如果有历史记录)
- showTooltipBackButton(modalContent);
+ for (const hero of sortedHeros) {
+ content += `
+
+
${hero.name}
+
${hero.cost}费
+
+ `;
+ }
+ }
+
+ // 兼容原有数据结构
+ if (details.related_chess && details.related_chess.length > 0) {
+ // 根据费用对棋子进行分组
+ const chessByPrice = {};
+ details.related_chess.forEach(chess => {
+ const price = chess.cost || 0;
+ if (!chessByPrice[price]) {
+ chessByPrice[price] = [];
+ }
+ chessByPrice[price].push(chess);
+ });
- // 绑定棋子点击事件
- bindTooltipInteractions(modalContent);
- });
- }, 100);
+ // 按费用从低到高排序并添加棋子
+ Object.keys(chessByPrice).sort((a, b) => Number(a) - Number(b)).forEach(price => {
+ const priceChess = chessByPrice[price];
+
+ priceChess.forEach(chess => {
+ content += `
+
+
${chess.name}
+
${chess.cost}费
+
+ `;
+ });
+ });
+ }
+
+ content += `
`;
+ }
+
+ // 淡出加载动画
+ loadingSpinner.removeClass('visible').addClass('hidden');
+
+ // 直接更新内容,减少不必要的过渡
+ modalContent.html(content);
+
+ // 设置返回按钮
+ showTooltipBackButton(modalContent);
+
+ // 绑定新内容的交互事件
+ bindTooltipInteractions(modalContent);
+
+ } else {
+ // 加载失败
+ loadingSpinner.removeClass('visible').addClass('hidden');
+
+ modalContent.html(`
+
+
+
加载失败
+
无法获取羁绊信息,请稍后重试
+
+
+ `);
}
}).catch(error => {
- // 处理错误
- setTimeout(() => {
- loadingSpinner.addClass('hidden');
- modalContent.html(`
请求出错: ${error.message || '未知错误'}
`);
- modalContent.fadeIn(150);
- }, 100);
+ // 加载失败
+ loadingSpinner.removeClass('visible').addClass('hidden');
- console.error('获取羁绊详情失败:', error);
+ modalContent.html(`
+
+
+
加载失败
+
发生错误: ${error.message || '未知错误'}
+
+
+ `);
});
}
diff --git a/src/web/templates/index.html b/src/web/templates/index.html
index bebd70e..e45a75d 100644
--- a/src/web/templates/index.html
+++ b/src/web/templates/index.html
@@ -145,7 +145,7 @@