diff --git a/src/web/app.py b/src/web/app.py
index e6125f7..8d60895 100644
--- a/src/web/app.py
+++ b/src/web/app.py
@@ -243,15 +243,21 @@ def create_app(config_path: Optional[str] = None):
'name': chess.get('displayName', ''),
'cost': chess.get('price', 0),
'skill_name': chess.get('skillName', ''),
- 'skill_description': chess.get('skillDescription', ''),
+ 'skill_description': chess.get('skillDetail', ''),
'skill_introduce': chess.get('skillIntroduce', ''),
- # 添加更多棋子属性
- 'health': chess.get('health', ''),
- 'attack_damage': chess.get('attackDamage', ''),
- 'attack_speed': chess.get('attackSpeed', ''),
- 'attack_range': chess.get('range', ''),
+ 'skill_type': chess.get('skillType', ''),
+ 'skill_mana': chess.get('magic', ''),
+ # 添加更多棋子属性 - 使用与 chess.json 一致的字段名
+ 'life': chess.get('life', ''),
+ 'attack': chess.get('attack', ''),
+ 'attackSpeed': chess.get('attackSpeed', ''),
+ 'attackRange': chess.get('attackRange', ''),
'armor': chess.get('armor', ''),
- 'magic_resist': chess.get('magicResist', ''),
+ 'spellBlock': chess.get('spellBlock', ''),
+ 'magic': chess.get('magic', ''),
+ 'startMagic': chess.get('startMagic', ''),
+ 'crit': chess.get('crit', ''),
+ 'attackMag': chess.get('attackMag', ''),
'synergies': [{
'id': synergy.get('jobId') or synergy.get('raceId'),
'name': synergy.get('name', ''),
diff --git a/src/web/static/css/style.css b/src/web/static/css/style.css
index 565c02d..0a69693 100644
--- a/src/web/static/css/style.css
+++ b/src/web/static/css/style.css
@@ -54,13 +54,66 @@
/* 增强提示框样式 */
#tft-tooltip {
- transition: opacity 0.2s, transform 0.2s;
+ transition: all 0.3s ease;
max-height: 80vh;
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.2s ease-out;
+ 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);
+}
+
+/* 自定义滚动条样式 */
+#tft-tooltip::-webkit-scrollbar {
+ width: 6px;
+}
+
+#tft-tooltip::-webkit-scrollbar-track {
+ background: rgba(17, 24, 39, 0.1);
+ border-radius: 3px;
+}
+
+#tft-tooltip::-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 {
+ background-color: rgba(79, 70, 229, 0.6);
+}
+
+/* 提示框内容淡入效果 */
+#tft-tooltip .tooltip-content {
+ word-break: break-word;
+ opacity: 0;
+ animation: contentFadeIn 0.2s ease forwards;
+ animation-delay: 0.1s;
+}
+
+@keyframes contentFadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(5px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes tooltipFadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(-5px) scale(0.97);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
}
/* 提示框中的费用标识 */
@@ -94,33 +147,23 @@
border-radius: 2px;
}
-@keyframes tooltipFadeIn {
- from {
- opacity: 0;
- transform: translateY(-5px) scale(0.98);
- }
- to {
- opacity: 1;
- transform: translateY(0) scale(1);
- }
-}
-
/* 提示框中的各部分样式 */
#tft-tooltip .tooltip-content {
word-break: break-word;
+ padding-top: 5px;
+ padding-right: 36px; /* 为右侧关闭按钮留出空间 */
}
-/* 提示框返回按钮 */
-.tooltip-back-button {
- opacity: 0.7;
+/* 关闭按钮样式 */
+#tft-tooltip .tooltip-close-btn {
transition: all 0.2s ease;
- z-index: 15;
+ opacity: 0.9;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
-.tooltip-back-button:hover {
+#tft-tooltip .tooltip-close-btn:hover {
opacity: 1;
- transform: scale(1.1);
- background-color: rgba(79, 70, 229, 0.9);
+ transform: scale(1.05);
}
/* 特殊介绍文本样式 */
@@ -212,29 +255,38 @@
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.2s ease-out;
+ 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-tooltip .close-tooltip {
- width: 30px;
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(79, 70, 229, 0.2);
- border-radius: 50%;
- opacity: 0.9;
+ #tft-tooltip .tooltip-close-btn {
+ width: 38px;
+ height: 38px;
+ top: 10px;
+ right: 10px;
}
#tft-tooltip .tooltip-content {
- margin-top: 5px;
- padding-right: 15px;
+ margin-top: 10px;
+ padding-right: 20px;
+ padding-bottom: 5px;
}
/* 增大移动端触摸目标大小 */
.chess-item-mini, .synergy-item-mini {
- padding: 10px;
- margin-bottom: 8px;
+ padding: 12px;
+ margin-bottom: 10px;
+ position: relative;
+ overflow: hidden;
+ /* 增加右侧间距,避免文本与按钮重叠 */
+ padding-right: 80px;
}
/* 移动端使用更明显的交互提示 */
@@ -244,27 +296,74 @@
top: 50%;
right: 10px;
transform: translateY(-50%);
- color: rgba(99, 102, 241, 0.8);
+ color: white;
font-size: 10px;
- opacity: 0.8;
+ background-color: rgba(79, 70, 229, 0.5);
+ padding: 3px 8px;
+ border-radius: 10px;
+ white-space: nowrap;
+ z-index: 5;
}
- /* 提示框返回按钮优化 */
- .tooltip-back-button {
- width: 36px;
- height: 36px;
- opacity: 0.9;
- background-color: rgba(79, 70, 229, 0.8);
+ /* 防止棋子费用与点击按钮重叠 */
+ .chess-item-mini .text-xs.px-1\.5.py-0\.5.bg-gray-600.rounded {
+ margin-right: 65px;
}
+ /* 防止羁绊类型与点击按钮重叠 */
+ .synergy-item-mini .bg-gray-600.text-xs.px-1\.5.py-0\.5.rounded {
+ margin-right: 65px;
+ }
+
+ /* 移动端提示框内部元素间距优化 */
+ #tft-tooltip .mb-2 {
+ margin-bottom: 10px;
+ }
+
+ #tft-tooltip .mb-3 {
+ margin-bottom: 15px;
+ }
+
+ #tft-tooltip .p-2 {
+ padding: 10px;
+ }
+
+ /* 移动端动画效果 */
@keyframes mobileTooltipFadeIn {
from {
opacity: 0;
- transform: translateY(10px) scale(0.95);
+ transform: translate(-50%, -45%);
}
to {
opacity: 1;
- transform: translateY(0) scale(1);
+ transform: translate(-50%, -50%);
}
}
+
+ /* 提示框内部滚动优化 */
+ #tft-tooltip::-webkit-scrollbar {
+ width: 5px;
+ }
+
+ /* 移动端加载状态优化 */
+ #tft-tooltip .loading-spinner .animate-spin {
+ height: 45px;
+ width: 45px;
+ border-width: 3px;
+ }
+
+ #tft-tooltip .loading-spinner .text-xs {
+ font-size: 12px;
+ margin-top: 8px;
+ }
+
+ /* 移动端表格布局优化 */
+ #tft-tooltip .grid.grid-cols-2 {
+ grid-template-columns: 1fr;
+ }
+
+ /* 改进相关棋子列表显示 */
+ #tft-tooltip .grid.grid-cols-2.gap-1.mt-1 {
+ grid-template-columns: 1fr 1fr;
+ }
}
\ No newline at end of file
diff --git a/src/web/static/js/main.js b/src/web/static/js/main.js
index 8ff5c43..fb5fbb3 100644
--- a/src/web/static/js/main.js
+++ b/src/web/static/js/main.js
@@ -668,13 +668,16 @@ function createTooltip() {
const tooltip = $(`
-
-
@@ -684,11 +687,13 @@ function createTooltip() {
$('body').append(tooltip);
// 绑定关闭按钮事件
- tooltip.find('.close-tooltip').on('click', function(e) {
+ tooltip.find('.tooltip-close-btn').on('click', function(e) {
e.stopPropagation();
tooltip.addClass('hidden');
currentHoveredId = null;
$('.tooltip-active').removeClass('tooltip-active');
+ // 重置历史记录
+ tooltipHistory = [];
});
// 防止点击提示框本身触发关闭
@@ -698,24 +703,29 @@ function createTooltip() {
}
/**
- * 显示提示的返回按钮
+ * 显示提示的返回按钮(重构为返回功能)
* @param {jQuery} tooltipContent 提示内容元素
*/
function showTooltipBackButton(tooltipContent) {
- // 如果历史记录中有内容,则显示返回按钮
+ const tooltip = tooltipContent.closest('#tft-tooltip');
+
+ // 获取关闭按钮
+ const closeButton = tooltip.find('.tooltip-close-btn');
+
+ // 如果历史记录中有内容,则显示返回功能
if (tooltipHistory.length > 0) {
- const backButton = $(`
-
+ // 修改按钮图标为返回箭头
+ closeButton.html(`
+
`);
- tooltipContent.append(backButton);
+ // 移除之前的事件处理程序
+ closeButton.off('click');
- // 添加返回按钮点击事件
- backButton.on('click', function(e) {
+ // 绑定返回按钮事件
+ closeButton.on('click', function(e) {
e.stopPropagation();
// 弹出上一个提示内容
@@ -725,18 +735,57 @@ function showTooltipBackButton(tooltipContent) {
currentHoveredId = prevItem.id;
tooltipContent.html(prevItem.content);
- // 如果仍有历史记录,恢复返回按钮
+ // 如果仍有历史记录,保持返回按钮
if (tooltipHistory.length > 0) {
showTooltipBackButton(tooltipContent);
+ } else {
+ // 没有更多历史,切换回关闭按钮
+ resetCloseButton(closeButton, tooltip);
}
// 重新绑定事件处理程序
bindTooltipInteractions(tooltipContent);
}
});
+
+ // 使用返回样式
+ closeButton.removeClass('bg-gray-700 hover:bg-gray-600').addClass('bg-indigo-600 hover:bg-indigo-700');
+ } else {
+ // 没有历史,重置为关闭按钮
+ resetCloseButton(closeButton, tooltip);
}
}
+/**
+ * 重置为关闭按钮
+ * @param {jQuery} closeButton 按钮元素
+ * @param {jQuery} tooltip 提示框元素
+ */
+function resetCloseButton(closeButton, tooltip) {
+ // 修改按钮图标为关闭图标
+ closeButton.html(`
+
+ `);
+
+ // 移除之前的事件处理程序
+ closeButton.off('click');
+
+ // 绑定关闭按钮事件
+ closeButton.on('click', function(e) {
+ e.stopPropagation();
+ tooltip.addClass('hidden');
+ currentHoveredId = null;
+ $('.tooltip-active').removeClass('tooltip-active');
+ // 重置历史记录
+ tooltipHistory = [];
+ });
+
+ // 使用关闭样式
+ closeButton.removeClass('bg-indigo-600 hover:bg-indigo-700').addClass('bg-gray-700 hover:bg-gray-600');
+}
+
/**
* 绑定提示内容中的交互事件
* @param {jQuery} tooltipContent 提示内容元素
@@ -786,8 +835,14 @@ function bindTooltipInteractions(tooltipContent) {
function showChessTooltipContent(chessId, 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);
- // 显示加载中
+ // 先定位提示框但不显示
+ positionTooltip(tooltip, element);
+
+ // 重置内容并显示加载中
tooltipContent.empty();
loadingSpinner.removeClass('hidden');
@@ -796,9 +851,9 @@ function showChessTooltipContent(chessId, element, tooltipContent) {
if (response.status === 'success') {
const details = response.details;
- // 构建提示内容,与showChessTooltip相同的内容构建逻辑
+ // 构建提示内容,添加左侧内边距给返回按钮留出空间
let content = `
-
+
${details.name}
${details.title ? `
${details.title}` : ''}
@@ -816,51 +871,93 @@ function showChessTooltipContent(chessId, element, tooltipContent) {
`;
}
- // 添加属性信息
- const attributes = {
- 'health': '生命值',
- 'attack_damage': '攻击力',
- 'attack_speed': '攻速',
- 'attack_range': '攻击范围',
- 'armor': '护甲',
- 'magic_resist': '魔抗'
- };
+ // 将属性分组显示
+ // 判断是否有任何属性数据可以显示
+ const hasAnyAttribute =
+ details.life || details.attack || details.attackSpeed || details.attackRange ||
+ details.armor || details.spellBlock || details.crit ||
+ details.magic || details.startMagic || details.attackMag;
- // 检查是否有属性数据
- const hasAttributes = Object.keys(attributes).some(key => details[key]);
-
- if (hasAttributes) {
- content += `
`;
- for (const [key, label] of Object.entries(attributes)) {
- if (details[key]) {
- content += `
-
- ${label}:
- ${details[key]}
-
- `;
- }
- }
-
- // 附加属性(如果有)
- const extraAttributes = {
- 'crit_rate': '暴击率',
- 'crit_damage': '暴击伤害',
- 'dodge': '闪避',
- 'speed': '移速'
- };
-
- for (const [key, label] of Object.entries(extraAttributes)) {
- if (details[key]) {
- content += `
-
- ${label}:
- ${details[key]}
-
- `;
- }
+ if (hasAnyAttribute) {
+ content += `
`;
+
+ // 所有属性都归类到基础属性组
+ content += `
+
基础属性
+
`;
+
+ if (details.life) {
+ content += `
+
+ 生命值:
+ ${details.life}
+
`;
+ }
+ if (details.attack) {
+ content += `
+
+ 攻击力:
+ ${details.attack}
+
`;
+ }
+ if (details.attackSpeed) {
+ content += `
+
+ 攻速:
+ ${details.attackSpeed}
+
`;
+ }
+ if (details.attackRange) {
+ content += `
+
+ 攻击范围:
+ ${details.attackRange}
+
`;
+ }
+ if (details.armor) {
+ content += `
+
+ 护甲:
+ ${details.armor}
+
`;
+ }
+ if (details.spellBlock) {
+ content += `
+
+ 魔抗:
+ ${details.spellBlock}
+
`;
+ }
+ if (details.crit) {
+ content += `
+
+ 暴击率:
+ ${details.crit}%
+
`;
+ }
+ if (details.magic) {
+ content += `
+
+ 法力值:
+ ${details.magic}
+
`;
+ }
+ if (details.startMagic) {
+ content += `
+
+ 初始法力值:
+ ${details.startMagic}
+
`;
+ }
+ if (details.attackMag) {
+ content += `
+
+ 法术强度倍率:
+ ${details.attackMag}
+
`;
}
+ content += `
`;
content += `
`;
}
@@ -905,13 +1002,17 @@ function showChessTooltipContent(chessId, element, tooltipContent) {
details.synergies.forEach(synergy => {
content += `
- ${synergy.name}
+ ${synergy.name}
${synergyTypes[synergy.type] || synergy.type}
`;
});
}
+ // 隐藏加载中状态
+ loadingSpinner.addClass('hidden');
+
+ // 设置内容并显示提示框
tooltipContent.html(content);
// 显示返回按钮
@@ -919,14 +1020,20 @@ function showChessTooltipContent(chessId, element, tooltipContent) {
// 绑定交互事件
bindTooltipInteractions(tooltipContent);
+
+ // 所有内容准备好后显示提示框
+ tooltip.removeClass('hidden');
} else {
+ // 处理错误
tooltipContent.html(`
获取详情失败
`);
+ loadingSpinner.addClass('hidden');
+ tooltip.removeClass('hidden');
}
-
- loadingSpinner.addClass('hidden');
}).catch(error => {
+ // 处理错误
tooltipContent.html(`
请求出错
`);
loadingSpinner.addClass('hidden');
+ tooltip.removeClass('hidden');
console.error('获取棋子详情失败:', error);
});
}
@@ -940,6 +1047,12 @@ function showChessTooltipContent(chessId, element, tooltipContent) {
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);
+
+ // 先定位提示框但不显示
+ positionTooltip(tooltip, element);
// 显示加载中
tooltipContent.empty();
@@ -950,9 +1063,9 @@ function showSynergyTooltipContent(synergyId, element, tooltipContent) {
if (response.status === 'success') {
const details = response.details;
- // 构建提示内容,与showSynergyTooltip相同的内容构建逻辑
+ // 构建提示内容,添加左侧内边距给返回按钮留出空间
let content = `
-
+
${details.name}
${details.type === 'job' ? '职业' : '特质'}
@@ -1002,13 +1115,13 @@ function showSynergyTooltipContent(synergyId, element, tooltipContent) {
return parseInt(a.cost) - parseInt(b.cost);
});
+ // 保持两列布局,因为棋子卡片较小
content += ``;
sortedChess.forEach(chess => {
const costClass = `cost-${chess.cost}`;
content += `
${chess.name}
- ${chess.title ? `${chess.title}` : ''}
${chess.cost}费
`;
@@ -1016,6 +1129,10 @@ function showSynergyTooltipContent(synergyId, element, tooltipContent) {
content += `
`;
}
+ // 隐藏加载中状态
+ loadingSpinner.addClass('hidden');
+
+ // 设置内容并显示提示框
tooltipContent.html(content);
// 显示返回按钮
@@ -1023,14 +1140,20 @@ function showSynergyTooltipContent(synergyId, element, tooltipContent) {
// 绑定交互事件
bindTooltipInteractions(tooltipContent);
+
+ // 所有内容准备好后显示提示框
+ tooltip.removeClass('hidden');
} else {
+ // 处理错误
tooltipContent.html(`获取详情失败
`);
+ loadingSpinner.addClass('hidden');
+ tooltip.removeClass('hidden');
}
-
- loadingSpinner.addClass('hidden');
}).catch(error => {
+ // 处理错误
tooltipContent.html(`请求出错
`);
loadingSpinner.addClass('hidden');
+ tooltip.removeClass('hidden');
console.error('获取羁绊详情失败:', error);
});
}
@@ -1045,17 +1168,12 @@ function showChessTooltip(chessId, element) {
const tooltipContent = tooltip.find('.tooltip-content');
const loadingSpinner = tooltip.find('.loading-spinner');
- // 定位提示框
- positionTooltip(tooltip, element);
+ // 先隐藏提示框,在数据加载完成后再显示
+ tooltip.addClass('hidden');
// 重置历史记录
tooltipHistory = [];
- // 显示加载中
- tooltipContent.empty();
- loadingSpinner.removeClass('hidden');
- tooltip.removeClass('hidden');
-
// 使用共享方法显示内容
showChessTooltipContent(chessId, element, tooltipContent);
}
@@ -1070,17 +1188,12 @@ function showSynergyTooltip(synergyId, element) {
const tooltipContent = tooltip.find('.tooltip-content');
const loadingSpinner = tooltip.find('.loading-spinner');
- // 定位提示框
- positionTooltip(tooltip, element);
+ // 先隐藏提示框,在数据加载完成后再显示
+ tooltip.addClass('hidden');
// 重置历史记录
tooltipHistory = [];
- // 显示加载中
- tooltipContent.empty();
- loadingSpinner.removeClass('hidden');
- tooltip.removeClass('hidden');
-
// 使用共享方法显示内容
showSynergyTooltipContent(synergyId, element, tooltipContent);
}
@@ -1096,28 +1209,15 @@ function positionTooltip(tooltip, element) {
('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0);
- // 移动设备优先考虑居中显示
+ // 移动设备始终居中显示
if (isMobile) {
- const tooltipWidth = Math.min(320, window.innerWidth - 40); // 在小屏幕上适当缩小
- let left = Math.max(20, (window.innerWidth - tooltipWidth) / 2);
-
- // 移动设备上优先显示在元素下方,占据更多空间
- let top = rect.bottom + 10;
-
- // 如果元素已经在视窗下方,则显示在元素上方
- if (top > window.innerHeight / 2) {
- top = Math.max(20, rect.top - 300); // 预留足够空间显示内容
- }
-
- // 设置最大高度,确保提示框不会占满整个屏幕
- const maxHeight = window.innerHeight - top - 20;
-
+ // 移动设备上使用CSS定位在屏幕中央
+ // 样式表中已经设置了固定位置,这里不需要设置left和top
tooltip.css({
- left: `${left}px`,
- top: `${top}px`,
- width: `${tooltipWidth}px`,
- maxHeight: `${maxHeight}px`,
- zIndex: 1000 // 确保在移动设备上能覆盖其他元素
+ // 重置定位,让CSS的固定定位生效
+ transform: '',
+ maxHeight: `${window.innerHeight * 0.7}px`,
+ zIndex: 1000
});
} else {
// 桌面设备的定位逻辑