实现权重配置更新和详细信息获取API,优化错误处理,新增棋子和羁绊的详细信息展示功能,提升用户体验和代码可维护性。

This commit is contained in:
hxuanyu 2025-04-02 15:48:36 +08:00
parent ee7daf9ad1
commit 79bd57b79d
3 changed files with 857 additions and 24 deletions

View File

@ -199,15 +199,120 @@ def create_app(config_path: Optional[str] = None):
@app.route('/api/weights', methods=['POST'])
def update_weights():
"""更新权重配置API - 已弃用仅保留API兼容性"""
"""更新权重配置API"""
try:
data = request.json
weights_config.update_config_data(data)
return jsonify({'status': 'success'})
except Exception as e:
logger.exception("更新权重配置失败")
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.route('/api/details', methods=['GET'])
def get_details():
"""获取棋子或羁绊的详细信息API"""
try:
item_type = request.args.get('type') # 'chess' 或 'synergy'
item_id = request.args.get('id')
if not item_type or not item_id:
return jsonify({
'status': 'error',
'message': '缺少必要参数'
}), 400
# 获取详细信息
if item_type == 'chess':
# 获取棋子详情
chess = data_api.get_chess_by_id(item_id)
if not chess:
return jsonify({
'status': 'error',
'message': f'未找到ID为{item_id}的棋子'
}), 404
# 获取棋子的所有羁绊
synergies = data_api.get_synergies_of_chess(item_id)
# 构建详细信息
details = {
'id': chess.get('chessId'),
'name': chess.get('displayName', ''),
'cost': chess.get('price', 0),
'skill_name': chess.get('skillName', ''),
'skill_description': chess.get('skillDescription', ''),
'skill_introduce': chess.get('skillIntroduce', ''),
# 添加更多棋子属性
'health': chess.get('health', ''),
'attack_damage': chess.get('attackDamage', ''),
'attack_speed': chess.get('attackSpeed', ''),
'attack_range': chess.get('range', ''),
'armor': chess.get('armor', ''),
'magic_resist': chess.get('magicResist', ''),
'synergies': [{
'id': synergy.get('jobId') or synergy.get('raceId'),
'name': synergy.get('name', ''),
'type': 'job' if 'jobId' in synergy else 'race'
} for synergy in synergies]
}
return jsonify({
'status': 'success',
'message': '权重配置已更新(本地存储模式)'
'type': 'chess',
'details': details
})
elif item_type == 'synergy':
# 获取羁绊详情
synergy = data_api.get_synergy_by_id(item_id)
if not synergy:
return jsonify({
'status': 'error',
'message': f'未找到ID为{item_id}的羁绊'
}), 404
# 获取羁绊的所有等级信息
synergy_levels = synergy.get('level', {})
# 获取该羁绊的所有棋子
related_chess = data_api.get_chess_by_synergy(item_id)
# 构建详细信息
details = {
'id': synergy.get('jobId') or synergy.get('raceId'),
'name': synergy.get('name', ''),
'description': synergy.get('description', ''),
'introduce': synergy.get('introduce', ''),
'type': 'job' if 'jobId' in synergy else 'race',
'levels': [{
'level': level,
'effect': effect
} for level, effect in synergy_levels.items()],
# 添加相关棋子
'related_chess': [{
'id': chess.get('chessId'),
'name': chess.get('displayName', ''),
'cost': chess.get('price', 0)
} for chess in related_chess]
}
return jsonify({
'status': 'success',
'type': 'synergy',
'details': details
})
else:
return jsonify({
'status': 'error',
'message': f'不支持的类型: {item_type}'
}), 400
except Exception as e:
logger.exception("更新权重配置时发生错误")
logger.exception("获取详细信息失败")
return jsonify({
'status': 'error',
'message': str(e)
@ -220,16 +325,16 @@ def create_temp_config(base_config: Dict[str, Any],
synergy_weights: Dict[str, float],
chess_weights: Dict[str, float]) -> Dict[str, Any]:
"""
创建临时配置对象不修改原始配置
创建临时权重配置
Args:
base_config: 基础配置
base_weights: 用户自定义的基础权重
synergy_weights: 用户自定义的羁绊权重
chess_weights: 用户自定义的棋子权重
base_weights: 基础权重
synergy_weights: 羁绊权重
chess_weights: 棋子权重
Returns:
Dict[str, Any]: 临时配置对象
Dict[str, Any]: 临时配置
"""
# 深拷贝基础配置,避免修改原始配置
temp_config = copy.deepcopy(base_config)

View File

@ -40,6 +40,127 @@
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;
}
/* 活跃状态样式 */
.chess-item.tooltip-active, .synergy-item.tooltip-active {
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.4);
z-index: 5;
}
/* 增强提示框样式 */
#tft-tooltip {
transition: opacity 0.2s, transform 0.2s;
max-height: 80vh;
overflow-y: auto;
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;
}
/* 提示框中的费用标识 */
#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费 - 金色 */
/* 提示框内交互元素样式 */
.chess-item-mini, .synergy-item-mini {
cursor: pointer;
transition: all 0.2s;
position: relative;
}
.chess-item-mini:hover, .synergy-item-mini:hover {
background-color: rgba(255, 255, 255, 0.1);
transform: translateY(-1px);
}
.chess-item-mini:hover::after, .synergy-item-mini:hover::after {
content: '查看详情';
position: absolute;
top: -18px;
right: 5px;
background: rgba(79, 70, 229, 0.9);
color: white;
font-size: 8px;
padding: 2px 4px;
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;
}
/* 提示框返回按钮 */
.tooltip-back-button {
opacity: 0.7;
transition: all 0.2s ease;
z-index: 15;
}
.tooltip-back-button:hover {
opacity: 1;
transform: scale(1.1);
background-color: rgba(79, 70, 229, 0.9);
}
/* 特殊介绍文本样式 */
#tft-tooltip .skill-introduce,
#tft-tooltip .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 {
opacity: 0.8;
}
/* 优化悬停标识 */
.chess-item::after, .synergy-item::after {
content: '?';
position: absolute;
top: -5px;
right: -5px;
width: 16px;
height: 16px;
background: rgba(79, 70, 229, 0.9);
color: white;
border-radius: 50%;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.2s, transform 0.2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 10;
}
.chess-item:hover::after, .synergy-item:hover::after {
opacity: 1;
transform: scale(1.1);
}
/* 羁绊标签样式 */
.synergy-tag {
background-color: #e0e7ff;

View File

@ -40,7 +40,7 @@ $(document).ready(function() {
// 添加到列表
const synergyItem = $(`
<div id="required-synergy-${synergyId}" class="flex items-center justify-between bg-indigo-50 p-2 rounded" data-synergy-id="${synergyId}">
<div id="required-synergy-${synergyId}" class="flex items-center justify-between bg-indigo-50 p-2 rounded synergy-item" data-synergy-id="${synergyId}">
<span class="text-gray-800">${synergyName}</span>
<button class="remove-synergy text-red-500 hover:text-red-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
@ -74,7 +74,7 @@ $(document).ready(function() {
// 添加到列表
const chessItem = $(`
<div id="required-chess-${chessId}" class="flex items-center justify-between bg-indigo-50 p-2 rounded" data-chess-id="${chessId}">
<div id="required-chess-${chessId}" class="flex items-center justify-between bg-indigo-50 p-2 rounded chess-item" data-chess-id="${chessId}">
<span class="text-gray-800">${chessName}</span>
<button class="remove-chess text-red-500 hover:text-red-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
@ -121,6 +121,9 @@ $(document).ready(function() {
$('#reset-to-default').on('click', function() {
resetToDefaultWeights();
});
// 初始化悬停提示
initTooltips();
});
/**
@ -506,7 +509,7 @@ function renderResults(results) {
const chessList = teamElement.find('.chess-list');
result.chess_list.forEach(chess => {
const chessCard = $(`
<div class="chess-card p-2 chess-cost-${chess.cost}">
<div class="chess-item chess-card p-2 chess-cost-${chess.cost}" data-chess-id="${chess.id}">
<div class="font-medium text-sm">${chess.name}</div>
<div class="text-xs text-gray-500">${chess.cost}</div>
</div>
@ -518,7 +521,7 @@ function renderResults(results) {
const synergyList = teamElement.find('.synergy-list');
result.active_synergies.forEach(synergy => {
const synergyTag = $(`
<div class="synergy-tag">
<div class="synergy-item synergy-tag" data-synergy-id="${synergy.id}">
<span>${synergy.name}</span>
<span class="ml-1 text-indigo-700">(${synergy.level})</span>
</div>
@ -613,18 +616,622 @@ function toggleActiveSynergiesVisibility(showOnlyActive) {
}
}
/**
* 获取当前激活的棋子ID列表
* @returns {Array} 激活的棋子ID列表
*/
function getActiveChessIds() {
const activeIds = [];
$('.chess-weight-item.active').each(function() {
const chessId = $(this).data('chess-id');
if (chessId) {
activeIds.push(chessId);
}
});
return activeIds;
}
/**
* 切换激活棋子的可见性
* @param {boolean} showOnlyActive 是否只显示激活项
*/
function toggleActiveChessVisibility(showOnlyActive) {
// 获取已激活的棋子ID列表
const activeChessIds = getActiveChessIds();
$('.chess-weight-item').each(function() {
const chessId = $(this).data('chess-id');
if (showOnlyActive) {
// 隐藏所有棋子权重项
$('.chess-weight-item').hide();
// 显示已激活棋子项
$('.chess-weight-item.active').show();
// 仅显示激活的棋子
if (activeChessIds.includes(chessId)) {
$(this).show();
} else {
// 显示所有棋子权重项
$('.chess-weight-item').show();
$(this).hide();
}
} else {
// 显示所有棋子
$(this).show();
}
});
}
let tooltipHistory = [];
/**
* 创建悬停提示
*/
function createTooltip() {
// 创建提示元素
if ($('#tft-tooltip').length === 0) {
$('body').append(`
<div id="tft-tooltip" class="hidden fixed z-50 p-4 bg-gray-800 text-white rounded-lg shadow-lg max-w-sm">
<div class="tooltip-content relative"></div>
<div class="loading-spinner hidden flex justify-center py-2">
<div class="animate-spin rounded-full h-6 w-6 border-t-2 border-b-2 border-white"></div>
</div>
</div>
`);
}
}
/**
* 显示提示的返回按钮
* @param {jQuery} tooltipContent 提示内容元素
*/
function showTooltipBackButton(tooltipContent) {
// 如果历史记录中有内容,则显示返回按钮
if (tooltipHistory.length > 0) {
const backButton = $(`
<div class="tooltip-back-button absolute top-1 left-1 bg-gray-600 rounded-full w-6 h-6 flex items-center justify-center cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</div>
`);
tooltipContent.append(backButton);
// 添加返回按钮点击事件
backButton.on('click', function(e) {
e.stopPropagation();
// 弹出上一个提示内容
const prevItem = tooltipHistory.pop();
if (prevItem) {
// 恢复ID和内容
currentHoveredId = prevItem.id;
tooltipContent.html(prevItem.content);
// 如果仍有历史记录,恢复返回按钮
if (tooltipHistory.length > 0) {
showTooltipBackButton(tooltipContent);
}
// 重新绑定事件处理程序
bindTooltipInteractions(tooltipContent);
}
});
}
}
/**
* 绑定提示内容中的交互事件
* @param {jQuery} tooltipContent 提示内容元素
*/
function bindTooltipInteractions(tooltipContent) {
// 为羁绊小标签添加点击事件,显示对应的羁绊详情
tooltipContent.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()
});
// 更新当前悬停ID并显示新的提示
currentHoveredId = synergyId;
showSynergyTooltipContent(synergyId, this, tooltipContent);
}
});
// 为棋子小标签添加点击事件,显示对应的棋子详情
tooltipContent.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()
});
// 更新当前悬停ID并显示新的提示
currentHoveredId = chessId;
showChessTooltipContent(chessId, this, tooltipContent);
}
});
}
/**
* 显示棋子提示内容不重新创建提示框
* @param {string} chessId 棋子ID
* @param {Element} element 触发元素
* @param {jQuery} tooltipContent 提示内容元素
*/
function showChessTooltipContent(chessId, element, tooltipContent) {
const tooltip = tooltipContent.closest('#tft-tooltip');
const loadingSpinner = tooltip.find('.loading-spinner');
// 显示加载中
tooltipContent.empty();
loadingSpinner.removeClass('hidden');
// 获取棋子详情
fetchItemDetails('chess', chessId).then(response => {
if (response.status === 'success') {
const details = response.details;
// 构建提示内容与showChessTooltip相同的内容构建逻辑
let content = `
<div class="mb-2 flex items-center justify-between">
<div>
<span class="font-bold text-lg">${details.name}</span>
${details.title ? `<span class="text-xs text-gray-300 ml-1">${details.title}</span>` : ''}
<span class="ml-2 px-2 py-1 bg-yellow-600 rounded-full text-xs">${details.cost}</span>
</div>
</div>
`;
// 棋子介绍
if (details.introduce) {
content += `
<div class="mb-3 text-sm italic text-gray-300 bg-gray-700 p-2 rounded">
${details.introduce}
</div>
`;
}
// 添加属性信息
const attributes = {
'health': '生命值',
'attack_damage': '攻击力',
'attack_speed': '攻速',
'attack_range': '攻击范围',
'armor': '护甲',
'magic_resist': '魔抗'
};
// 检查是否有属性数据
const hasAttributes = Object.keys(attributes).some(key => details[key]);
if (hasAttributes) {
content += `<div class="grid grid-cols-2 gap-2 mb-3 bg-gray-700 p-2 rounded">`;
for (const [key, label] of Object.entries(attributes)) {
if (details[key]) {
content += `
<div class="flex items-center">
<span class="text-xs text-gray-400">${label}:</span>
<span class="text-sm ml-1">${details[key]}</span>
</div>
`;
}
}
// 附加属性(如果有)
const extraAttributes = {
'crit_rate': '暴击率',
'crit_damage': '暴击伤害',
'dodge': '闪避',
'speed': '移速'
};
for (const [key, label] of Object.entries(extraAttributes)) {
if (details[key]) {
content += `
<div class="flex items-center">
<span class="text-xs text-gray-400">${label}:</span>
<span class="text-sm ml-1">${details[key]}</span>
</div>
`;
}
}
content += `</div>`;
}
// 技能信息
if (details.skill_name) {
content += `
<div class="mb-3 bg-gray-700 p-2 rounded">
<div class="flex items-center justify-between">
<div class="font-semibold text-indigo-300">${details.skill_name}</div>
${details.skill_mana ? `<span class="bg-blue-600 text-xs px-2 py-0.5 rounded">${details.skill_mana}法力</span>` : ''}
</div>
${details.skill_type ? `<div class="text-xs text-blue-300 mt-1">${details.skill_type}</div>` : ''}
<!-- 技能描述和介绍 -->
<div class="mt-2">
${details.skill_description ? `
<div class="text-sm text-gray-300">
<div class="text-xs text-gray-400 mb-1">技能效果</div>
${details.skill_description}
</div>
` : ''}
${details.skill_introduce ? `
<div class="text-sm italic text-gray-300 mt-2 pt-2 border-t border-gray-600 skill-introduce">
<div class="text-xs text-gray-400 mb-1">技能简介</div>
${details.skill_introduce}
</div>
` : ''}
</div>
</div>
`;
}
// 羁绊信息
if (details.synergies && details.synergies.length > 0) {
content += `<div class="text-sm font-semibold text-indigo-300 mb-1">羁绊:</div>`;
const synergyTypes = {
'job': '职业',
'race': '特质'
};
details.synergies.forEach(synergy => {
content += `
<div class="text-sm mb-1 px-2 py-1 bg-gray-700 rounded flex items-center justify-between synergy-item-mini" data-synergy-id="${synergy.id}">
<span>${synergy.name}</span>
<span class="bg-gray-600 text-xs px-1.5 py-0.5 rounded">${synergyTypes[synergy.type] || synergy.type}</span>
</div>
`;
});
}
tooltipContent.html(content);
// 显示返回按钮
showTooltipBackButton(tooltipContent);
// 绑定交互事件
bindTooltipInteractions(tooltipContent);
} else {
tooltipContent.html(`<div class="text-red-400">获取详情失败</div>`);
}
loadingSpinner.addClass('hidden');
}).catch(error => {
tooltipContent.html(`<div class="text-red-400">请求出错</div>`);
loadingSpinner.addClass('hidden');
console.error('获取棋子详情失败:', error);
});
}
/**
* 显示羁绊提示内容不重新创建提示框
* @param {string} synergyId 羁绊ID
* @param {Element} element 触发元素
* @param {jQuery} tooltipContent 提示内容元素
*/
function showSynergyTooltipContent(synergyId, element, tooltipContent) {
const tooltip = tooltipContent.closest('#tft-tooltip');
const loadingSpinner = tooltip.find('.loading-spinner');
// 显示加载中
tooltipContent.empty();
loadingSpinner.removeClass('hidden');
// 获取羁绊详情
fetchItemDetails('synergy', synergyId).then(response => {
if (response.status === 'success') {
const details = response.details;
// 构建提示内容与showSynergyTooltip相同的内容构建逻辑
let content = `
<div class="mb-2">
<span class="font-bold text-lg">${details.name}</span>
<span class="ml-2 px-2 py-1 bg-indigo-600 rounded-full text-xs">
${details.type === 'job' ? '职业' : '特质'}
</span>
</div>
`;
// 羁绊介绍 - 优先使用 introduce 字段
if (details.introduce) {
content += `
<div class="mb-3 text-sm italic text-gray-300 bg-gray-700 p-2 rounded font-medium synergy-introduce">
${details.introduce}
</div>
`;
}
// 羁绊描述作为补充信息 - 如果有额外的描述信息
if (details.description && details.description !== details.introduce) {
content += `<div class="mb-3 text-sm text-gray-300">${details.description}</div>`;
}
// 等级效果
if (details.levels && details.levels.length > 0) {
content += `<div class="text-sm font-semibold text-indigo-300 mb-1">等级效果:</div>`;
// 按等级排序
const sortedLevels = [...details.levels].sort((a, b) => {
return parseInt(a.level) - parseInt(b.level);
});
sortedLevels.forEach(levelInfo => {
content += `
<div class="mb-2 bg-gray-700 p-2 rounded">
<div class="text-xs text-yellow-400 font-medium">${levelInfo.level}人口</div>
<div class="text-sm mt-1">${levelInfo.effect}</div>
</div>
`;
});
}
// 相关棋子
if (details.related_chess && details.related_chess.length > 0) {
content += `<div class="text-sm font-semibold text-indigo-300 mt-4 mb-1">相关棋子 (${details.related_chess.length})</div>`;
// 按费用排序
const sortedChess = [...details.related_chess].sort((a, b) => {
return parseInt(a.cost) - parseInt(b.cost);
});
content += `<div class="grid grid-cols-2 gap-1 mt-1">`;
sortedChess.forEach(chess => {
const costClass = `cost-${chess.cost}`;
content += `
<div class="bg-gray-700 rounded p-1 flex items-center ${costClass} chess-item-mini" data-chess-id="${chess.id}">
<span class="text-sm flex-grow truncate">${chess.name}</span>
${chess.title ? `<span class="text-xs text-gray-400 mr-1 truncate" title="${chess.title}">${chess.title}</span>` : ''}
<span class="text-xs px-1.5 py-0.5 bg-gray-600 rounded">${chess.cost}</span>
</div>
`;
});
content += `</div>`;
}
tooltipContent.html(content);
// 显示返回按钮
showTooltipBackButton(tooltipContent);
// 绑定交互事件
bindTooltipInteractions(tooltipContent);
} else {
tooltipContent.html(`<div class="text-red-400">获取详情失败</div>`);
}
loadingSpinner.addClass('hidden');
}).catch(error => {
tooltipContent.html(`<div class="text-red-400">请求出错</div>`);
loadingSpinner.addClass('hidden');
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');
// 定位提示框
positionTooltip(tooltip, element);
// 重置历史记录
tooltipHistory = [];
// 显示加载中
tooltipContent.empty();
loadingSpinner.removeClass('hidden');
tooltip.removeClass('hidden');
// 使用共享方法显示内容
showChessTooltipContent(chessId, element, tooltipContent);
}
/**
* 显示羁绊悬停提示
* @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');
// 定位提示框
positionTooltip(tooltip, element);
// 重置历史记录
tooltipHistory = [];
// 显示加载中
tooltipContent.empty();
loadingSpinner.removeClass('hidden');
tooltip.removeClass('hidden');
// 使用共享方法显示内容
showSynergyTooltipContent(synergyId, element, tooltipContent);
}
/**
* 定位提示框
* @param {jQuery} tooltip 提示元素
* @param {Element} element 触发元素
*/
function positionTooltip(tooltip, element) {
const rect = element.getBoundingClientRect();
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); // 居中显示
}
}
// 计算垂直位置,考虑提示框不超出屏幕
let top = rect.top;
const tooltipHeight = Math.min(500, window.innerHeight * 0.8); // 预估高度
if (top + tooltipHeight > window.innerHeight) {
// 尝试显示在元素上方
top = Math.max(10, rect.top - tooltipHeight);
}
// 设置位置
tooltip.css({
left: `${left}px`,
top: `${top}px`,
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);
};
}
/**
* 获取棋子或羁绊的详细信息
* @param {string} type 类型'chess''synergy'
* @param {string} id 棋子或羁绊ID
* @returns {Promise} 详情数据
*/
function fetchItemDetails(type, id) {
// 使用缓存避免重复请求
if (!window.tftDetailsCache) {
window.tftDetailsCache = {};
}
const cacheKey = `${type}_${id}`;
if (window.tftDetailsCache[cacheKey]) {
return Promise.resolve(window.tftDetailsCache[cacheKey]);
}
return new Promise((resolve, reject) => {
$.ajax({
url: `/api/details?type=${type}&id=${id}`,
method: 'GET',
dataType: 'json',
success: (response) => {
// 缓存结果
if (response.status === 'success') {
window.tftDetailsCache[cacheKey] = response;
}
resolve(response);
},
error: reject
});
});
}
// 存储当前悬停元素ID
let currentHoveredId = null;
/**
* 初始化悬停提示
*/
function initTooltips() {
// 创建提示框
createTooltip();
const tooltipElement = $('#tft-tooltip');
// 棋子悬停事件 - 使用防抖
const debouncedShowChessTooltip = debounce((chessId, element) => {
if (currentHoveredId === chessId) {
showChessTooltip(chessId, element);
}
}, 50);
$(document).on('mouseenter', '.chess-item', function(e) {
// 避免在点击移除按钮时显示提示
if ($(e.target).closest('.remove-chess').length) {
return;
}
const chessId = $(this).data('chess-id');
if (chessId) {
currentHoveredId = chessId;
debouncedShowChessTooltip(chessId, this);
// 添加活跃状态
$(this).addClass('tooltip-active');
}
});
// 羁绊悬停事件 - 使用防抖
const debouncedShowSynergyTooltip = debounce((synergyId, element) => {
if (currentHoveredId === synergyId) {
showSynergyTooltip(synergyId, element);
}
}, 50);
$(document).on('mouseenter', '.synergy-item', function(e) {
// 避免在点击移除按钮时显示提示
if ($(e.target).closest('.remove-synergy').length) {
return;
}
const synergyId = $(this).data('synergy-id');
if (synergyId) {
currentHoveredId = synergyId;
debouncedShowSynergyTooltip(synergyId, this);
// 添加活跃状态
$(this).addClass('tooltip-active');
}
});
// 离开时隐藏提示
$(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;
});
}