Warhummer/templates/weight_management.html
2025-06-25 09:35:26 +08:00

478 lines
18 KiB
HTML

<!-- 创建一个新的角色权重管理页面 weight_management.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>角色权重管理 - 战锤40K行星总督</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/admin.css') }}">
<style>
.weight-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-top: 20px;
}
.weight-card {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
padding: 15px;
display: flex;
flex-direction: column;
gap: 10px;
}
.weight-card-header {
display: flex;
align-items: center;
gap: 10px;
}
.weight-card-avatar {
font-size: 2em;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
border-radius: 25px;
}
.weight-card-name {
flex: 1;
}
.weight-card-name h3 {
margin: 0;
font-size: 1.2em;
}
.weight-card-name p {
margin: 4px 0 0;
font-size: 0.9em;
color: #666;
}
.weight-control {
display: flex;
align-items: center;
gap: 8px;
margin-top: 10px;
}
.weight-control input {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.weight-control button {
padding: 8px 12px;
background-color: #343a40;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.weight-control button:hover {
background-color: #23272b;
}
.type-filter {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.type-filter button {
padding: 8px 16px;
background-color: #f0f0f0;
border: none;
border-radius: 4px;
cursor: pointer;
}
.type-filter button.active {
background-color: #343a40;
color: white;
}
.type-badge {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.8em;
margin-top: 4px;
}
.type-resident {
background-color: #28a745;
color: white;
}
.type-special {
background-color: #17a2b8;
color: white;
}
.type-status {
background-color: #6c757d;
color: white;
}
.save-all-btn {
margin-top: 20px;
padding: 10px 16px;
background-color: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.save-all-btn:hover {
background-color: #218838;
}
</style>
</head>
<body class="admin-page">
<div class="admin-sidebar">
<div class="admin-logo">
<h2>战锤40K</h2>
<p>管理员控制台</p>
</div>
<nav class="admin-nav">
<a href="{{ url_for('admin_index') }}">
<span class="icon">🏠</span>
<span class="text">主页</span>
</a>
<a href="{{ url_for('admin_characters') }}">
<span class="icon">👤</span>
<span class="text">管理人物</span>
</a>
<a href="{{ url_for('admin_dashboard') }}">
<span class="icon">📊</span>
<span class="text">数据面板</span>
</a>
<a href="{{ url_for('admin_weight_management') }}" class="active">
<span class="icon">⚖️</span>
<span class="text">权重管理</span>
</a>
<a href="{{ url_for('admin_logout') }}" class="logout">
<span class="icon">🚪</span>
<span class="text">登出</span>
</a>
</nav>
</div>
<div class="admin-content">
<header class="admin-header">
<h1>角色权重管理</h1>
<div class="header-actions">
<button id="save-all-weights-btn" class="save-all-btn">
<span class="icon">💾</span> 保存所有权重
</button>
</div>
</header>
<div class="admin-toolbar">
<div class="search-box">
<input type="text" id="character-search" placeholder="搜索角色...">
</div>
<div class="type-filter">
<button data-type="all" class="active">所有类型</button>
<button data-type="resident">常驻角色</button>
<button data-type="special">特殊角色</button>
<button data-type="status">状态效果</button>
</div>
</div>
<div class="weight-grid" id="weights-container">
<!-- 角色权重卡片将通过JavaScript加载 -->
<div class="loading-indicator">加载中...</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const weightsContainer = document.getElementById('weights-container');
const characterSearch = document.getElementById('character-search');
const saveAllBtn = document.getElementById('save-all-weights-btn');
const typeFilterButtons = document.querySelectorAll('.type-filter button');
// 状态变量
let characters = [];
let filteredCharacters = [];
let changedWeights = {};
let currentTypeFilter = 'all';
// 加载所有角色
function loadCharacters() {
weightsContainer.innerHTML = '<div class="loading-indicator">加载中...</div>';
fetch('/api/admin/characters')
.then(response => response.json())
.then(data => {
characters = data.characters || [];
filteredCharacters = [...characters];
renderWeightCards();
})
.catch(error => {
console.error('加载角色失败:', error);
weightsContainer.innerHTML = '<div class="error-message">加载失败,请刷新页面重试。</div>';
});
}
// 渲染权重卡片
function renderWeightCards() {
if (filteredCharacters.length === 0) {
weightsContainer.innerHTML = '<div class="empty-message">没有找到符合条件的角色</div>';
return;
}
weightsContainer.innerHTML = '';
filteredCharacters.forEach(character => {
const card = document.createElement('div');
card.className = 'weight-card';
card.dataset.id = character.id;
// 设置角色类型标签
let typeLabel = '未知';
let typeClass = '';
switch(character.type) {
case 'resident':
typeLabel = '常驻角色';
typeClass = 'type-resident';
break;
case 'special':
typeLabel = '特殊角色';
typeClass = 'type-special';
break;
case 'status':
typeLabel = '状态效果';
typeClass = 'type-status';
break;
}
card.innerHTML = `
<div class="weight-card-header">
<div class="weight-card-avatar">${character.avatar || '👤'}</div>
<div class="weight-card-name">
<h3>${character.name || '无名角色'}</h3>
<p>${character.title || '无头衔'}</p>
<span class="type-badge ${typeClass}">${typeLabel}</span>
</div>
</div>
<div class="weight-control">
<input type="number" class="weight-input" min="1" max="10" value="${character.weight || 100}">
<button class="save-weight-btn" data-id="${character.id}">保存</button>
</div>
`;
weightsContainer.appendChild(card);
// 添加权重输入框变化事件
const weightInput = card.querySelector('.weight-input');
weightInput.addEventListener('change', () => {
const newWeight = parseInt(weightInput.value);
if (!isNaN(newWeight) && newWeight >= 1 && newWeight <= 10) {
changedWeights[character.id] = newWeight;
}
});
// 添加保存按钮点击事件
const saveBtn = card.querySelector('.save-weight-btn');
saveBtn.addEventListener('click', () => {
const input = card.querySelector('.weight-input');
const newWeight = parseInt(input.value);
if (!isNaN(newWeight) && newWeight >= 1 && newWeight <= 10) {
saveCharacterWeight(character.id, newWeight);
}
});
});
}
// 保存单个角色的权重
function saveCharacterWeight(characterId, weight) {
const character = characters.find(c => c.id === characterId);
if (!character) return;
// 创建角色数据
const characterData = {
id: character.id,
name: character.name,
title: character.title,
avatar: character.avatar,
avatarPath: character.avatarPath,
type: character.type,
weight: weight,
events: character.events || []
};
// 发送请求
fetch('/api/admin/character', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(characterData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新本地数据
character.weight = weight;
// 视觉反馈
const card = document.querySelector(`.weight-card[data-id="${characterId}"]`);
if (card) {
const saveBtn = card.querySelector('.save-weight-btn');
const originalText = saveBtn.textContent;
saveBtn.textContent = '已保存';
saveBtn.style.backgroundColor = '#28a745';
setTimeout(() => {
saveBtn.textContent = originalText;
saveBtn.style.backgroundColor = '';
}, 1500);
}
// 从更改列表中删除
delete changedWeights[characterId];
} else {
alert('保存失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('保存角色权重失败:', error);
alert('保存失败,请稍后重试');
});
}
// 保存所有更改的权重
function saveAllChangedWeights() {
const characterIds = Object.keys(changedWeights);
if (characterIds.length === 0) {
alert('没有需要保存的更改');
return;
}
// 显示保存中状态
saveAllBtn.textContent = '保存中...';
saveAllBtn.disabled = true;
// 创建保存请求的Promise数组
const savePromises = characterIds.map(id => {
const character = characters.find(c => c.id === id);
if (!character) return Promise.resolve();
// 创建角色数据
const characterData = {
id: character.id,
name: character.name,
title: character.title,
avatar: character.avatar,
avatarPath: character.avatarPath,
type: character.type,
weight: changedWeights[id],
events: character.events || []
};
// 发送请求
return fetch('/api/admin/character', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(characterData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新本地数据
character.weight = changedWeights[id];
return true;
} else {
console.error('保存失败:', data.error);
return false;
}
})
.catch(error => {
console.error('保存请求失败:', error);
return false;
});
});
// 等待所有保存请求完成
Promise.all(savePromises)
.then(results => {
const successCount = results.filter(Boolean).length;
// 恢复按钮状态
saveAllBtn.innerHTML = '<span class="icon">💾</span> 保存所有权重';
saveAllBtn.disabled = false;
if (successCount === characterIds.length) {
// 全部保存成功
alert(`成功保存了 ${successCount} 个角色的权重设置`);
changedWeights = {}; // 清空更改列表
// 重新加载角色列表
loadCharacters();
} else {
// 部分保存失败
alert(`成功保存了 ${successCount} 个角色的权重设置,${characterIds.length - successCount} 个保存失败`);
}
});
}
// 过滤角色
function filterCharacters() {
const searchTerm = characterSearch.value.toLowerCase();
filteredCharacters = characters.filter(character => {
const nameMatch = (character.name || '').toLowerCase().includes(searchTerm) ||
((character.title || '').toLowerCase().includes(searchTerm));
const typeMatch = currentTypeFilter === 'all' || character.type === currentTypeFilter;
return nameMatch && typeMatch;
});
renderWeightCards();
}
// 设置事件监听
characterSearch.addEventListener('input', filterCharacters);
saveAllBtn.addEventListener('click', saveAllChangedWeights);
// 类型过滤按钮
typeFilterButtons.forEach(button => {
button.addEventListener('click', () => {
// 更新活动按钮
typeFilterButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
// 设置当前过滤器类型
currentTypeFilter = button.dataset.type;
// 重新过滤
filterCharacters();
});
});
// 初始加载
loadCharacters();
});
</script>
</body>
</html>