无编辑摘要 |
无编辑摘要 |
||
| 第1行: | 第1行: | ||
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */ | /* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */ | ||
// | // ============================================= | ||
// 角色属性查看器 - 动态创建版本 | |||
// ============================================= | |||
// 角色数据管理器 | |||
var CharacterDataManager = { | |||
cache: {}, | |||
getCharacterData: function(characterId) { | |||
var self = this; | |||
// 检查缓存 | |||
if (self.cache[characterId]) { | |||
return Promise.resolve(self.cache[characterId]); | |||
} | |||
return new Promise(function(resolve, reject) { | |||
// 构建数据页面路径 | |||
var formattedName = characterId.charAt(0).toUpperCase() + characterId.slice(1); | |||
var dataSource = 'Data:Character/' + formattedName + '.json'; | |||
var api = new mw.Api(); | |||
api.get({ | |||
action: 'parse', | |||
page: dataSource, | |||
prop: 'text', | |||
format: 'json' | |||
}).done(function(data) { | |||
try { | |||
var pageContent = data.parse.text['*']; | |||
var jsonMatch = pageContent.match(/\{[\s\S]*\}/); | |||
if (!jsonMatch) { | |||
reject('在页面中未找到JSON数据: ' + dataSource); | |||
return; | |||
} | |||
var characterData = JSON.parse(jsonMatch[0]); | |||
// 缓存数据 | |||
self.cache[characterId] = characterData; | |||
resolve(characterData); | |||
} catch (e) { | |||
reject('解析JSON数据失败: ' + e.message); | |||
} | |||
}).fail(function(error) { | |||
reject('API请求失败: ' + error); | |||
}); | |||
}); | |||
}, | |||
clearCache: function() { | |||
this.cache = {}; | |||
} | |||
}; | |||
// 主初始化函数 | |||
$(document).ready(function() { | $(document).ready(function() { | ||
// 添加全局样式 | |||
addCharacterStatsStyles(); | |||
// 初始化所有角色属性容器 | |||
$('.character-stats-container').each(function() { | $('.character-stats-container').each(function() { | ||
createCharacterStatsWidget($(this)); | createCharacterStatsWidget($(this)); | ||
}); | |||
// 当有新内容加载时也初始化 | |||
mw.hook('wikipage.content').add(function($content) { | |||
$content.find('.character-stats-container').each(function() { | |||
if (!$(this).find('.character-stats-widget').length) { | |||
createCharacterStatsWidget($(this)); | |||
} | |||
}); | |||
}); | }); | ||
}); | }); | ||
// 添加样式到页面 | |||
function addCharacterStatsStyles() { | |||
if ($('#character-stats-styles').length) { | |||
return; // 样式已存在 | |||
} | |||
var styles = ` | |||
<style id="character-stats-styles"> | |||
.character-stats-widget { | |||
max-width: 800px; | |||
margin: 0 auto; | |||
background-color: #ffffff; | |||
border-radius: 8px; | |||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |||
padding: 30px; | |||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |||
} | |||
.character-header { | |||
text-align: center; | |||
margin-bottom: 20px; | |||
} | |||
.character-name { | |||
color: #4a6fa5; | |||
margin-bottom: 10px; | |||
font-size: 28px; | |||
} | |||
.character-description { | |||
color: #666; | |||
font-style: italic; | |||
font-size: 16px; | |||
} | |||
.loading-message, .error-message { | |||
text-align: center; | |||
padding: 20px; | |||
color: #666; | |||
font-size: 16px; | |||
} | |||
.error-message { | |||
color: #d32f2f; | |||
background-color: #ffebee; | |||
border-radius: 4px; | |||
border: 1px solid #f5c6cb; | |||
} | |||
.level-selector { | |||
margin-bottom: 30px; | |||
} | |||
.level-display { | |||
text-align: center; | |||
font-size: 24px; | |||
font-weight: bold; | |||
margin-bottom: 15px; | |||
color: #4a6fa5; | |||
} | |||
.slider-container { | |||
position: relative; | |||
margin-bottom: 10px; | |||
} | |||
.level-slider { | |||
width: 100%; | |||
height: 10px; | |||
-webkit-appearance: none; | |||
appearance: none; | |||
background: linear-gradient(to right, #e0e0e0, #4a6fa5); | |||
outline: none; | |||
border-radius: 5px; | |||
cursor: pointer; | |||
} | |||
.level-slider::-webkit-slider-thumb { | |||
-webkit-appearance: none; | |||
appearance: none; | |||
width: 24px; | |||
height: 24px; | |||
border-radius: 50%; | |||
background: #4a6fa5; | |||
cursor: pointer; | |||
border: 3px solid white; | |||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |||
transition: all 0.2s; | |||
} | |||
.level-slider::-webkit-slider-thumb:hover { | |||
background: #3a5a80; | |||
transform: scale(1.1); | |||
} | |||
.level-slider::-moz-range-thumb { | |||
width: 24px; | |||
height: 24px; | |||
border-radius: 50%; | |||
background: #4a6fa5; | |||
cursor: pointer; | |||
border: 3px solid white; | |||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |||
} | |||
.quick-buttons { | |||
display: flex; | |||
justify-content: space-between; | |||
flex-wrap: wrap; | |||
gap: 5px; | |||
margin-top: 10px; | |||
} | |||
.level-btn { | |||
flex: 1; | |||
min-width: 60px; | |||
padding: 10px 5px; | |||
background-color: #e9ecef; | |||
border: none; | |||
border-radius: 6px; | |||
cursor: pointer; | |||
font-weight: bold; | |||
transition: all 0.2s; | |||
font-size: 14px; | |||
} | |||
.level-btn:hover { | |||
background-color: #d0d8e2; | |||
transform: translateY(-2px); | |||
} | |||
.level-btn.active { | |||
background-color: #4a6fa5; | |||
color: white; | |||
box-shadow: 0 2px 4px rgba(74, 111, 165, 0.3); | |||
} | |||
.stats-container { | |||
display: grid; | |||
grid-template-columns: 1fr 1fr; | |||
gap: 20px; | |||
margin-top: 20px; | |||
} | |||
.stat-card { | |||
background-color: white; | |||
border-radius: 8px; | |||
padding: 20px; | |||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | |||
border-left: 4px solid #4a6fa5; | |||
transition: transform 0.2s; | |||
} | |||
.stat-card:hover { | |||
transform: translateY(-2px); | |||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | |||
} | |||
.stat-card.attack { | |||
border-left-color: #ff6b6b; | |||
} | |||
.stat-card.health { | |||
border-left-color: #4caf50; | |||
} | |||
.stat-title { | |||
font-size: 18px; | |||
margin-bottom: 10px; | |||
display: flex; | |||
align-items: center; | |||
gap: 8px; | |||
font-weight: 600; | |||
} | |||
.stat-value { | |||
font-size: 32px; | |||
font-weight: bold; | |||
margin-bottom: 15px; | |||
color: #333; | |||
} | |||
.attack .stat-value { | |||
color: #ff6b6b; | |||
} | |||
.health .stat-value { | |||
color: #4caf50; | |||
} | |||
.stat-progress { | |||
height: 12px; | |||
background-color: #e9ecef; | |||
border-radius: 6px; | |||
overflow: hidden; | |||
margin-bottom: 10px; | |||
} | |||
.stat-bar { | |||
height: 100%; | |||
border-radius: 6px; | |||
width: 0%; | |||
transition: width 0.5s ease; | |||
} | |||
.attack .stat-bar { | |||
background: linear-gradient(90deg, #ff6b6b, #ff8e8e); | |||
} | |||
.health .stat-bar { | |||
background: linear-gradient(90deg, #4caf50, #66bb6a); | |||
} | |||
.stat-growth { | |||
margin-top: 10px; | |||
font-size: 14px; | |||
color: #666; | |||
font-style: italic; | |||
} | |||
@media (max-width: 768px) { | |||
.character-stats-widget { | |||
padding: 20px; | |||
margin: 10px; | |||
} | |||
.stats-container { | |||
grid-template-columns: 1fr; | |||
gap: 15px; | |||
} | |||
.quick-buttons { | |||
gap: 3px; | |||
} | |||
.level-btn { | |||
min-width: 50px; | |||
padding: 8px 3px; | |||
font-size: 12px; | |||
} | |||
.character-name { | |||
font-size: 24px; | |||
} | |||
.stat-value { | |||
font-size: 28px; | |||
} | |||
} | |||
@media (max-width: 480px) { | |||
.character-stats-widget { | |||
padding: 15px; | |||
} | |||
.level-btn { | |||
min-width: 40px; | |||
padding: 6px 2px; | |||
font-size: 11px; | |||
} | |||
.quick-buttons { | |||
gap: 2px; | |||
} | |||
} | |||
</style> | |||
`; | |||
$('head').append(styles); | |||
} | |||
// 创建角色属性查看器 | |||
function createCharacterStatsWidget($container) { | function createCharacterStatsWidget($container) { | ||
var characterId = $container.data('character'); | var characterId = $container.data('character'); | ||
// 创建 Widget | if (!characterId) { | ||
$container.html('<div class="error-message">错误:未指定角色ID</div>'); | |||
return; | |||
} | |||
// 创建 Widget HTML 结构 | |||
var widgetHTML = ` | var widgetHTML = ` | ||
<div class="character-stats-widget"> | <div class="character-stats-widget"> | ||
| 第28行: | 第375行: | ||
<div class="quick-buttons"> | <div class="quick-buttons"> | ||
<button class="level-btn" data-level="1"> | <button class="level-btn" data-level="1">1级</button> | ||
<button class="level-btn" data-level="10"> | <button class="level-btn" data-level="10">10级</button> | ||
<button class="level-btn" data-level="20"> | <button class="level-btn" data-level="20">20级</button> | ||
<button class="level-btn" data-level="30"> | <button class="level-btn" data-level="30">30级</button> | ||
<button class="level-btn" data-level="40"> | <button class="level-btn" data-level="40">40级</button> | ||
<button class="level-btn" data-level="50"> | <button class="level-btn" data-level="50">50级</button> | ||
<button class="level-btn" data-level="60"> | <button class="level-btn" data-level="60">60级</button> | ||
<button class="level-btn" data-level="70"> | <button class="level-btn" data-level="70">70级</button> | ||
<button class="level-btn" data-level="80"> | <button class="level-btn" data-level="80">80级</button> | ||
<button class="level-btn" data-level="90"> | <button class="level-btn" data-level="90">90级</button> | ||
</div> | </div> | ||
</div> | </div> | ||
| 第44行: | 第391行: | ||
<div class="stat-card attack"> | <div class="stat-card attack"> | ||
<div class="stat-title"> | <div class="stat-title"> | ||
<span>攻击力</span> | <span>⚔️ 攻击力</span> | ||
</div> | </div> | ||
<div class="stat-value attack-value">-</div> | <div class="stat-value attack-value">-</div> | ||
| 第55行: | 第402行: | ||
<div class="stat-card health"> | <div class="stat-card health"> | ||
<div class="stat-title"> | <div class="stat-title"> | ||
<span>生命值</span> | <span>❤️ 生命值</span> | ||
</div> | </div> | ||
<div class="stat-value health-value">-</div> | <div class="stat-value health-value">-</div> | ||
| 第66行: | 第413行: | ||
<div class="loading-message">正在加载角色数据...</div> | <div class="loading-message">正在加载角色数据...</div> | ||
<div class="error-message" style="display: none;"> | <div class="error-message" style="display: none;"></div> | ||
</div> | </div> | ||
`; | `; | ||
| 第73行: | 第420行: | ||
$container.html(widgetHTML); | $container.html(widgetHTML); | ||
// | // 显示加载状态 | ||
var $widget = $container.find('.character-stats-widget'); | |||
$widget.find('.level-selector, .stats-container').hide(); | |||
$widget.find('.loading-message').show(); | |||
// 加载角色数据并初始化 | |||
CharacterDataManager.getCharacterData(characterId) | |||
.then(function(characterData) { | |||
initCharacterStats($widget, characterData); | |||
$widget.find('.loading-message').hide(); | |||
$widget.find('.level-selector, .stats-container').show(); | |||
}) | |||
.catch(function(error) { | |||
console.error('加载角色数据失败:', error); | |||
$widget.find('.loading-message').hide(); | |||
$widget.find('.error-message').text('加载角色数据失败: ' + error).show(); | |||
}); | |||
} | |||
// 初始化角色属性显示 | |||
function initCharacterStats($widget, characterData) { | |||
// 防止重复初始化 | |||
if ($widget.data('initialized')) { | |||
return; | |||
} | |||
$widget.data('initialized', true); | |||
// 更新角色信息 | |||
$widget.find('.character-name').text(characterData.name); | |||
$widget.find('.character-description').text(characterData.description); | |||
// 生成完整的等级数据 | |||
var fullLevelData = generateFullLevelData(characterData); | |||
// 获取UI元素 | |||
var $levelSlider = $widget.find('.level-slider'); | |||
var $currentLevel = $widget.find('.current-level'); | |||
var $attackValue = $widget.find('.attack-value'); | |||
var $healthValue = $widget.find('.health-value'); | |||
var $attackBar = $widget.find('.attack-bar'); | |||
var $healthBar = $widget.find('.health-bar'); | |||
var $attackGrowth = $widget.find('.attack-growth'); | |||
var $healthGrowth = $widget.find('.health-growth'); | |||
var $quickButtons = $widget.find('.level-btn'); | |||
// 更新显示函数 | |||
function updateDisplay(level) { | |||
var stats = fullLevelData[level]; | |||
$currentLevel.text(level); | |||
$attackValue.text(formatNumber(stats.attack)); | |||
$healthValue.text(formatNumber(stats.health)); | |||
// 显示成长值 | |||
if (characterData.stats) { | |||
$attackGrowth.text('+' + (characterData.stats.attack_growth || '?')); | |||
$healthGrowth.text('+' + (characterData.stats.health_growth || '?')); | |||
} | |||
// 更新进度条宽度 (基于最大值的百分比) | |||
var maxAttack = fullLevelData[90].attack; | |||
var maxHealth = fullLevelData[90].health; | |||
$attackBar.css('width', (stats.attack / maxAttack) * 100 + '%'); | |||
$healthBar.css('width', (stats.health / maxHealth) * 100 + '%'); | |||
// 更新快速按钮激活状态 | |||
$quickButtons.removeClass('active'); | |||
$quickButtons.filter('[data-level="' + level + '"]').addClass('active'); | |||
} | |||
// 事件绑定 | |||
$levelSlider.on('input', function() { | |||
updateDisplay(parseInt(this.value)); | |||
}); | |||
$quickButtons.on('click', function() { | |||
var level = parseInt($(this).data('level')); | |||
$levelSlider.val(level); | |||
updateDisplay(level); | |||
}); | |||
// 添加键盘支持 | |||
$levelSlider.on('keydown', function(e) { | |||
var currentValue = parseInt(this.value); | |||
switch(e.key) { | |||
case 'ArrowLeft': | |||
case 'ArrowDown': | |||
e.preventDefault(); | |||
this.value = Math.max(1, currentValue - 1); | |||
updateDisplay(this.value); | |||
break; | |||
case 'ArrowRight': | |||
case 'ArrowUp': | |||
e.preventDefault(); | |||
this.value = Math.min(90, currentValue + 1); | |||
updateDisplay(this.value); | |||
break; | |||
case 'Home': | |||
e.preventDefault(); | |||
this.value = 1; | |||
updateDisplay(this.value); | |||
break; | |||
case 'End': | |||
e.preventDefault(); | |||
this.value = 90; | |||
updateDisplay(this.value); | |||
break; | |||
} | |||
}); | |||
// 初始显示 | |||
updateDisplay(1); | |||
} | |||
// 生成完整等级数据 | |||
function generateFullLevelData(characterData) { | |||
var fullData = {}; | |||
var baseAttack = (characterData.stats && characterData.stats.base_attack) || 100; | |||
. | var baseHealth = (characterData.stats && characterData.stats.base_health) || 1000; | ||
var attackGrowth = (characterData.stats && characterData.stats.attack_growth) || 25; | |||
var healthGrowth = (characterData.stats && characterData.stats.health_growth) || 300; | |||
// 生成1-90级的完整数据 | |||
for (var level = 1; level <= 90; level++) { | |||
// 如果有预定义的数据,使用预定义数据,否则计算 | |||
if (characterData.levels && characterData.levels[level]) { | |||
fullData[level] = characterData.levels[level]; | |||
} else { | |||
fullData[level] = { | |||
attack: baseAttack + attackGrowth * (level - 1), | |||
health: baseHealth + healthGrowth * (level - 1) | |||
}; | |||
. | } | ||
. | |||
. | |||
} | } | ||
// | return fullData; | ||
// | } | ||
// 数字格式化函数 | |||
function formatNumber(num) { | |||
if (num >= 1000000) { | |||
return (num / 1000000).toFixed(1) + 'M'; | |||
} else if (num >= 1000) { | |||
return (num / 1000).toFixed(1) + 'K'; | |||
} | |||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | |||
} | } | ||
2025年11月30日 (日) 14:34的版本
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */
// =============================================
// 角色属性查看器 - 动态创建版本
// =============================================
// 角色数据管理器
var CharacterDataManager = {
cache: {},
getCharacterData: function(characterId) {
var self = this;
// 检查缓存
if (self.cache[characterId]) {
return Promise.resolve(self.cache[characterId]);
}
return new Promise(function(resolve, reject) {
// 构建数据页面路径
var formattedName = characterId.charAt(0).toUpperCase() + characterId.slice(1);
var dataSource = 'Data:Character/' + formattedName + '.json';
var api = new mw.Api();
api.get({
action: 'parse',
page: dataSource,
prop: 'text',
format: 'json'
}).done(function(data) {
try {
var pageContent = data.parse.text['*'];
var jsonMatch = pageContent.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
reject('在页面中未找到JSON数据: ' + dataSource);
return;
}
var characterData = JSON.parse(jsonMatch[0]);
// 缓存数据
self.cache[characterId] = characterData;
resolve(characterData);
} catch (e) {
reject('解析JSON数据失败: ' + e.message);
}
}).fail(function(error) {
reject('API请求失败: ' + error);
});
});
},
clearCache: function() {
this.cache = {};
}
};
// 主初始化函数
$(document).ready(function() {
// 添加全局样式
addCharacterStatsStyles();
// 初始化所有角色属性容器
$('.character-stats-container').each(function() {
createCharacterStatsWidget($(this));
});
// 当有新内容加载时也初始化
mw.hook('wikipage.content').add(function($content) {
$content.find('.character-stats-container').each(function() {
if (!$(this).find('.character-stats-widget').length) {
createCharacterStatsWidget($(this));
}
});
});
});
// 添加样式到页面
function addCharacterStatsStyles() {
if ($('#character-stats-styles').length) {
return; // 样式已存在
}
var styles = `
<style id="character-stats-styles">
.character-stats-widget {
max-width: 800px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 30px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.character-header {
text-align: center;
margin-bottom: 20px;
}
.character-name {
color: #4a6fa5;
margin-bottom: 10px;
font-size: 28px;
}
.character-description {
color: #666;
font-style: italic;
font-size: 16px;
}
.loading-message, .error-message {
text-align: center;
padding: 20px;
color: #666;
font-size: 16px;
}
.error-message {
color: #d32f2f;
background-color: #ffebee;
border-radius: 4px;
border: 1px solid #f5c6cb;
}
.level-selector {
margin-bottom: 30px;
}
.level-display {
text-align: center;
font-size: 24px;
font-weight: bold;
margin-bottom: 15px;
color: #4a6fa5;
}
.slider-container {
position: relative;
margin-bottom: 10px;
}
.level-slider {
width: 100%;
height: 10px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(to right, #e0e0e0, #4a6fa5);
outline: none;
border-radius: 5px;
cursor: pointer;
}
.level-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: #4a6fa5;
cursor: pointer;
border: 3px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: all 0.2s;
}
.level-slider::-webkit-slider-thumb:hover {
background: #3a5a80;
transform: scale(1.1);
}
.level-slider::-moz-range-thumb {
width: 24px;
height: 24px;
border-radius: 50%;
background: #4a6fa5;
cursor: pointer;
border: 3px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.quick-buttons {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 5px;
margin-top: 10px;
}
.level-btn {
flex: 1;
min-width: 60px;
padding: 10px 5px;
background-color: #e9ecef;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.2s;
font-size: 14px;
}
.level-btn:hover {
background-color: #d0d8e2;
transform: translateY(-2px);
}
.level-btn.active {
background-color: #4a6fa5;
color: white;
box-shadow: 0 2px 4px rgba(74, 111, 165, 0.3);
}
.stats-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.stat-card {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
border-left: 4px solid #4a6fa5;
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.stat-card.attack {
border-left-color: #ff6b6b;
}
.stat-card.health {
border-left-color: #4caf50;
}
.stat-title {
font-size: 18px;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
.stat-value {
font-size: 32px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.attack .stat-value {
color: #ff6b6b;
}
.health .stat-value {
color: #4caf50;
}
.stat-progress {
height: 12px;
background-color: #e9ecef;
border-radius: 6px;
overflow: hidden;
margin-bottom: 10px;
}
.stat-bar {
height: 100%;
border-radius: 6px;
width: 0%;
transition: width 0.5s ease;
}
.attack .stat-bar {
background: linear-gradient(90deg, #ff6b6b, #ff8e8e);
}
.health .stat-bar {
background: linear-gradient(90deg, #4caf50, #66bb6a);
}
.stat-growth {
margin-top: 10px;
font-size: 14px;
color: #666;
font-style: italic;
}
@media (max-width: 768px) {
.character-stats-widget {
padding: 20px;
margin: 10px;
}
.stats-container {
grid-template-columns: 1fr;
gap: 15px;
}
.quick-buttons {
gap: 3px;
}
.level-btn {
min-width: 50px;
padding: 8px 3px;
font-size: 12px;
}
.character-name {
font-size: 24px;
}
.stat-value {
font-size: 28px;
}
}
@media (max-width: 480px) {
.character-stats-widget {
padding: 15px;
}
.level-btn {
min-width: 40px;
padding: 6px 2px;
font-size: 11px;
}
.quick-buttons {
gap: 2px;
}
}
</style>
`;
$('head').append(styles);
}
// 创建角色属性查看器
function createCharacterStatsWidget($container) {
var characterId = $container.data('character');
if (!characterId) {
$container.html('<div class="error-message">错误:未指定角色ID</div>');
return;
}
// 创建 Widget HTML 结构
var widgetHTML = `
<div class="character-stats-widget">
<div class="character-header">
<h2 class="character-name">加载中...</h2>
<p class="character-description"></p>
</div>
<div class="level-selector">
<div class="level-display">当前等级: <span class="current-level">1</span></div>
<div class="slider-container">
<input type="range" min="1" max="90" value="1" class="level-slider">
</div>
<div class="quick-buttons">
<button class="level-btn" data-level="1">1级</button>
<button class="level-btn" data-level="10">10级</button>
<button class="level-btn" data-level="20">20级</button>
<button class="level-btn" data-level="30">30级</button>
<button class="level-btn" data-level="40">40级</button>
<button class="level-btn" data-level="50">50级</button>
<button class="level-btn" data-level="60">60级</button>
<button class="level-btn" data-level="70">70级</button>
<button class="level-btn" data-level="80">80级</button>
<button class="level-btn" data-level="90">90级</button>
</div>
</div>
<div class="stats-container">
<div class="stat-card attack">
<div class="stat-title">
<span>⚔️ 攻击力</span>
</div>
<div class="stat-value attack-value">-</div>
<div class="stat-progress">
<div class="stat-bar attack-bar"></div>
</div>
<div class="stat-growth">每级成长: <span class="attack-growth">-</span></div>
</div>
<div class="stat-card health">
<div class="stat-title">
<span>❤️ 生命值</span>
</div>
<div class="stat-value health-value">-</div>
<div class="stat-progress">
<div class="stat-bar health-bar"></div>
</div>
<div class="stat-growth">每级成长: <span class="health-growth">-</span></div>
</div>
</div>
<div class="loading-message">正在加载角色数据...</div>
<div class="error-message" style="display: none;"></div>
</div>
`;
// 插入 HTML
$container.html(widgetHTML);
// 显示加载状态
var $widget = $container.find('.character-stats-widget');
$widget.find('.level-selector, .stats-container').hide();
$widget.find('.loading-message').show();
// 加载角色数据并初始化
CharacterDataManager.getCharacterData(characterId)
.then(function(characterData) {
initCharacterStats($widget, characterData);
$widget.find('.loading-message').hide();
$widget.find('.level-selector, .stats-container').show();
})
.catch(function(error) {
console.error('加载角色数据失败:', error);
$widget.find('.loading-message').hide();
$widget.find('.error-message').text('加载角色数据失败: ' + error).show();
});
}
// 初始化角色属性显示
function initCharacterStats($widget, characterData) {
// 防止重复初始化
if ($widget.data('initialized')) {
return;
}
$widget.data('initialized', true);
// 更新角色信息
$widget.find('.character-name').text(characterData.name);
$widget.find('.character-description').text(characterData.description);
// 生成完整的等级数据
var fullLevelData = generateFullLevelData(characterData);
// 获取UI元素
var $levelSlider = $widget.find('.level-slider');
var $currentLevel = $widget.find('.current-level');
var $attackValue = $widget.find('.attack-value');
var $healthValue = $widget.find('.health-value');
var $attackBar = $widget.find('.attack-bar');
var $healthBar = $widget.find('.health-bar');
var $attackGrowth = $widget.find('.attack-growth');
var $healthGrowth = $widget.find('.health-growth');
var $quickButtons = $widget.find('.level-btn');
// 更新显示函数
function updateDisplay(level) {
var stats = fullLevelData[level];
$currentLevel.text(level);
$attackValue.text(formatNumber(stats.attack));
$healthValue.text(formatNumber(stats.health));
// 显示成长值
if (characterData.stats) {
$attackGrowth.text('+' + (characterData.stats.attack_growth || '?'));
$healthGrowth.text('+' + (characterData.stats.health_growth || '?'));
}
// 更新进度条宽度 (基于最大值的百分比)
var maxAttack = fullLevelData[90].attack;
var maxHealth = fullLevelData[90].health;
$attackBar.css('width', (stats.attack / maxAttack) * 100 + '%');
$healthBar.css('width', (stats.health / maxHealth) * 100 + '%');
// 更新快速按钮激活状态
$quickButtons.removeClass('active');
$quickButtons.filter('[data-level="' + level + '"]').addClass('active');
}
// 事件绑定
$levelSlider.on('input', function() {
updateDisplay(parseInt(this.value));
});
$quickButtons.on('click', function() {
var level = parseInt($(this).data('level'));
$levelSlider.val(level);
updateDisplay(level);
});
// 添加键盘支持
$levelSlider.on('keydown', function(e) {
var currentValue = parseInt(this.value);
switch(e.key) {
case 'ArrowLeft':
case 'ArrowDown':
e.preventDefault();
this.value = Math.max(1, currentValue - 1);
updateDisplay(this.value);
break;
case 'ArrowRight':
case 'ArrowUp':
e.preventDefault();
this.value = Math.min(90, currentValue + 1);
updateDisplay(this.value);
break;
case 'Home':
e.preventDefault();
this.value = 1;
updateDisplay(this.value);
break;
case 'End':
e.preventDefault();
this.value = 90;
updateDisplay(this.value);
break;
}
});
// 初始显示
updateDisplay(1);
}
// 生成完整等级数据
function generateFullLevelData(characterData) {
var fullData = {};
var baseAttack = (characterData.stats && characterData.stats.base_attack) || 100;
var baseHealth = (characterData.stats && characterData.stats.base_health) || 1000;
var attackGrowth = (characterData.stats && characterData.stats.attack_growth) || 25;
var healthGrowth = (characterData.stats && characterData.stats.health_growth) || 300;
// 生成1-90级的完整数据
for (var level = 1; level <= 90; level++) {
// 如果有预定义的数据,使用预定义数据,否则计算
if (characterData.levels && characterData.levels[level]) {
fullData[level] = characterData.levels[level];
} else {
fullData[level] = {
attack: baseAttack + attackGrowth * (level - 1),
health: baseHealth + healthGrowth * (level - 1)
};
}
}
return fullData;
}
// 数字格式化函数
function formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}