541 lines
26 KiB
HTML
541 lines
26 KiB
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') }}">
|
||
</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') }}" class="active">
|
||
<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_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="new-character-btn" class="btn btn-primary">
|
||
<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="filter-options">
|
||
<select id="character-type-filter">
|
||
<option value="all">所有类型</option>
|
||
<option value="resident">常驻角色</option>
|
||
<option value="special">特殊角色</option>
|
||
<option value="status">状态效果</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="characters-grid" id="characters-container">
|
||
<!-- 角色列表将通过JavaScript加载 -->
|
||
<div class="loading-indicator">加载中...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 模态框容器 - 将所有模态框放在body的直接子元素位置 -->
|
||
<div id="modals-container">
|
||
<!-- 角色编辑对话框 -->
|
||
<div class="modal" id="character-modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 id="modal-title">编辑人物</h2>
|
||
<span class="close-modal">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="character-form">
|
||
<input type="hidden" id="character-id">
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="character-name">名称</label>
|
||
<input type="text" id="character-name" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="character-title">称号</label>
|
||
<input type="text" id="character-title" required>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="character-avatar">头像表情</label>
|
||
<input type="text" id="character-avatar" required placeholder="使用Emoji或符号">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="character-avatar-path">头像图片路径</label>
|
||
<input type="text" id="character-avatar-path" placeholder="例如: images/characters/name.png">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="character-type">人物类型</label>
|
||
<select id="character-type" required>
|
||
<option value="resident">常驻角色</option>
|
||
<option value="special">特殊角色</option>
|
||
<option value="status">状态效果</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="character-weight">出现权重 (1-1000)</label>
|
||
<input type="number" id="character-weight" min="1" max="1000" value="100" required>
|
||
<small class="help-text">数值越大,角色出现概率越高。默认值为100。</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">保存</button>
|
||
<button type="button" class="btn btn-secondary close-btn">取消</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 事件编辑对话框 -->
|
||
<div class="modal" id="event-modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 id="event-modal-title">编辑事件</h2>
|
||
<span class="close-modal">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="event-form">
|
||
<input type="hidden" id="event-character-id">
|
||
<input type="hidden" id="event-id">
|
||
|
||
<div class="form-group">
|
||
<label for="event-text">事件描述</label>
|
||
<textarea id="event-text" required rows="3"></textarea>
|
||
</div>
|
||
|
||
<div class="event-options">
|
||
<div class="event-option">
|
||
<h3>选项A (蓝色/左滑)</h3>
|
||
<div class="form-group">
|
||
<label for="option-a-text">文本</label>
|
||
<input type="text" id="option-a-text" required>
|
||
</div>
|
||
|
||
<div class="effects-grid">
|
||
<div class="effect-item">
|
||
<label for="option-a-loyalty">忠诚度</label>
|
||
<input type="number" id="option-a-loyalty" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-a-chaos">混沌</label>
|
||
<input type="number" id="option-a-chaos" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-a-population">人口</label>
|
||
<input type="number" id="option-a-population" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-a-military">军事</label>
|
||
<input type="number" id="option-a-military" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-a-resources">资源</label>
|
||
<input type="number" id="option-a-resources" min="-25" max="25" value="0">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="option-a-add-status">添加状态</label>
|
||
<input type="text" id="option-a-add-status" placeholder="状态ID (可选)">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="option-a-remove-status">移除状态</label>
|
||
<input type="text" id="option-a-remove-status" placeholder="状态ID (可选)">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="event-option">
|
||
<h3>选项B (红色/右滑)</h3>
|
||
<div class="form-group">
|
||
<label for="option-b-text">文本</label>
|
||
<input type="text" id="option-b-text" required>
|
||
</div>
|
||
|
||
<div class="effects-grid">
|
||
<div class="effect-item">
|
||
<label for="option-b-loyalty">忠诚度</label>
|
||
<input type="number" id="option-b-loyalty" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-b-chaos">混沌</label>
|
||
<input type="number" id="option-b-chaos" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-b-population">人口</label>
|
||
<input type="number" id="option-b-population" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-b-military">军事</label>
|
||
<input type="number" id="option-b-military" min="-25" max="25" value="0">
|
||
</div>
|
||
<div class="effect-item">
|
||
<label for="option-b-resources">资源</label>
|
||
<input type="number" id="option-b-resources" min="-25" max="25" value="0">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="option-b-add-status">添加状态</label>
|
||
<input type="text" id="option-b-add-status" placeholder="状态ID (可选)">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="option-b-remove-status">移除状态</label>
|
||
<input type="text" id="option-b-remove-status" placeholder="状态ID (可选)">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">保存事件</button>
|
||
<button type="button" class="btn btn-secondary close-btn">取消</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色详情对话框 -->
|
||
<div class="modal" id="character-detail-modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 id="detail-title">角色详情</h2>
|
||
<span class="close-modal">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="character-info-section">
|
||
<div class="character-header">
|
||
<div class="character-avatar" id="detail-avatar">👤</div>
|
||
<div class="character-header-info">
|
||
<h3 id="detail-name">角色名称</h3>
|
||
<p id="detail-title-text">角色称号</p>
|
||
<p id="detail-type">角色类型</p>
|
||
<p id="detail-weight" class="character-weight">出现权重: <span>5</span></p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="character-actions">
|
||
<button id="edit-character-btn" class="btn btn-secondary">
|
||
<span class="icon">✏️</span> 编辑角色
|
||
</button>
|
||
<button id="delete-character-btn" class="btn btn-danger">
|
||
<span class="icon">🗑️</span> 删除角色
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="character-events-section">
|
||
<div class="section-header">
|
||
<h3>事件列表</h3>
|
||
<button id="add-event-btn" class="btn btn-primary">
|
||
<span class="icon">+</span> 添加事件
|
||
</button>
|
||
</div>
|
||
|
||
<div id="events-container" class="events-list">
|
||
<!-- 事件列表将通过JavaScript加载 -->
|
||
<div class="empty-events">该角色暂无事件</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 确认对话框 -->
|
||
<div class="modal" id="confirm-modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 id="confirm-title">确认操作</h2>
|
||
<span class="close-modal">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p id="confirm-message">您确定要执行此操作吗?</p>
|
||
<div class="form-actions">
|
||
<button id="confirm-ok" class="btn btn-danger">确认</button>
|
||
<button id="confirm-cancel" class="btn btn-secondary close-btn">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色管理页面脚本 -->
|
||
<script src="{{ url_for('static', filename='js/admin/characters.js') }}"></script>
|
||
<!-- 紧急修复代码 - 添加在 </body> 标签之前 -->
|
||
<style>
|
||
/* 强制修复模态框样式 */
|
||
.modal {
|
||
position: fixed !important;
|
||
top: 0 !important;
|
||
left: 0 !important;
|
||
right: 0 !important;
|
||
bottom: 0 !important;
|
||
width: 100vw !important;
|
||
height: 100vh !important;
|
||
background-color: rgba(0, 0, 0, 0.5) !important;
|
||
z-index: 999999 !important;
|
||
display: none;
|
||
padding: 20px !important;
|
||
box-sizing: border-box !important;
|
||
overflow: auto !important;
|
||
}
|
||
|
||
/* 确保每个模态框有不同的z-index,确认框最高 */
|
||
#character-modal { z-index: 999997 !important; }
|
||
#event-modal { z-index: 999998 !important; }
|
||
#character-detail-modal { z-index: 999996 !important; }
|
||
#confirm-modal { z-index: 999999 !important; }
|
||
|
||
/* 确保内容显示在最上层 */
|
||
.modal-content {
|
||
position: relative !important;
|
||
background-color: white !important;
|
||
margin: 10vh auto !important;
|
||
max-width: 800px !important;
|
||
width: 90% !important;
|
||
border-radius: 8px !important;
|
||
overflow: hidden !important;
|
||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5) !important;
|
||
max-height: 80vh !important;
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
}
|
||
|
||
.modal-body {
|
||
overflow-y: auto !important;
|
||
max-height: 60vh !important;
|
||
padding: 20px !important;
|
||
}
|
||
|
||
/* 确保确认模态框居中 */
|
||
#confirm-modal .modal-content {
|
||
max-width: 500px !important;
|
||
}
|
||
|
||
/* 特殊处理事件模态框 */
|
||
#event-modal .modal-content {
|
||
max-width: 1000px !important;
|
||
max-height: 90vh !important;
|
||
}
|
||
|
||
/* 移动设备适配 */
|
||
@media (max-width: 768px) {
|
||
.modal-content {
|
||
width: 95% !important;
|
||
margin: 5vh auto !important;
|
||
}
|
||
|
||
.modal-body {
|
||
max-height: 70vh !important;
|
||
}
|
||
|
||
.event-options {
|
||
flex-direction: column !important;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
// 紧急修复脚本
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 强制修复模态框问题
|
||
|
||
// 当点击"编辑角色"按钮时
|
||
document.getElementById('edit-character-btn').addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
// 先关闭详情模态框
|
||
document.getElementById('character-detail-modal').style.display = 'none';
|
||
|
||
// 再异步打开编辑模态框
|
||
setTimeout(function() {
|
||
if (currentCharacter) {
|
||
// 填充表单
|
||
document.getElementById('character-id').value = currentCharacter.id;
|
||
document.getElementById('character-name').value = currentCharacter.name || '';
|
||
document.getElementById('character-title').value = currentCharacter.title || '';
|
||
document.getElementById('character-avatar').value = currentCharacter.avatar || '';
|
||
document.getElementById('character-avatar-path').value = currentCharacter.avatarPath || '';
|
||
document.getElementById('character-type').value = currentCharacter.type || 'resident';
|
||
|
||
// 打开模态框
|
||
document.getElementById('character-modal').style.display = 'block';
|
||
}
|
||
}, 100);
|
||
|
||
return false;
|
||
});
|
||
|
||
// 当点击"添加事件"按钮时
|
||
document.getElementById('add-event-btn').addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
// 先关闭详情模态框
|
||
document.getElementById('character-detail-modal').style.display = 'none';
|
||
|
||
// 异步打开事件模态框
|
||
setTimeout(function() {
|
||
if (currentCharacter) {
|
||
// 重置表单
|
||
document.getElementById('event-form').reset();
|
||
document.getElementById('event-character-id').value = currentCharacter.id;
|
||
document.getElementById('event-id').value = generateUUID(); // 假设这个函数存在
|
||
|
||
// 打开模态框
|
||
document.getElementById('event-modal').style.display = 'block';
|
||
}
|
||
}, 100);
|
||
|
||
return false;
|
||
});
|
||
|
||
// 处理所有关闭按钮
|
||
document.querySelectorAll('.close-modal, .close-btn').forEach(function(btn) {
|
||
btn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
// 找到最近的模态框
|
||
var modal = this.closest('.modal');
|
||
if (modal) {
|
||
modal.style.display = 'none';
|
||
}
|
||
|
||
return false;
|
||
});
|
||
});
|
||
|
||
// 点击模态框背景关闭
|
||
document.querySelectorAll('.modal').forEach(function(modal) {
|
||
modal.addEventListener('click', function(e) {
|
||
if (e.target === this) {
|
||
this.style.display = 'none';
|
||
}
|
||
});
|
||
});
|
||
|
||
// 阻止内容区域点击传播
|
||
document.querySelectorAll('.modal-content').forEach(function(content) {
|
||
content.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
});
|
||
});
|
||
|
||
// 处理编辑事件按钮
|
||
document.addEventListener('click', function(e) {
|
||
if (e.target.classList.contains('edit-event-btn') || e.target.closest('.edit-event-btn')) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
// 找到事件项
|
||
var eventItem = e.target.closest('.event-item');
|
||
if (!eventItem) return;
|
||
|
||
// 关闭详情模态框
|
||
document.getElementById('character-detail-modal').style.display = 'none';
|
||
|
||
// 获取事件ID
|
||
var eventId = eventItem.querySelector('h4').textContent.split(':')[1].trim();
|
||
|
||
// 找到当前角色中的对应事件
|
||
if (currentCharacter && currentCharacter.events) {
|
||
var event = currentCharacter.events.find(function(e) {
|
||
return e.id == eventId;
|
||
});
|
||
|
||
if (event) {
|
||
// 异步打开编辑事件模态框
|
||
setTimeout(function() {
|
||
// 填充表单
|
||
document.getElementById('event-character-id').value = currentCharacter.id;
|
||
document.getElementById('event-id').value = event.id;
|
||
document.getElementById('event-text').value = event.text || '';
|
||
|
||
// 填充选项A
|
||
if (event.optionA) {
|
||
document.getElementById('option-a-text').value = event.optionA.text || '';
|
||
|
||
if (event.optionA.effects) {
|
||
document.getElementById('option-a-loyalty').value = event.optionA.effects.loyalty || 0;
|
||
document.getElementById('option-a-chaos').value = event.optionA.effects.chaos || 0;
|
||
document.getElementById('option-a-population').value = event.optionA.effects.population || 0;
|
||
document.getElementById('option-a-military').value = event.optionA.effects.military || 0;
|
||
document.getElementById('option-a-resources').value = event.optionA.effects.resources || 0;
|
||
}
|
||
|
||
document.getElementById('option-a-add-status').value = event.optionA.add_status || '';
|
||
document.getElementById('option-a-remove-status').value = event.optionA.remove_status || '';
|
||
}
|
||
|
||
// 填充选项B
|
||
if (event.optionB) {
|
||
document.getElementById('option-b-text').value = event.optionB.text || '';
|
||
|
||
if (event.optionB.effects) {
|
||
document.getElementById('option-b-loyalty').value = event.optionB.effects.loyalty || 0;
|
||
document.getElementById('option-b-chaos').value = event.optionB.effects.chaos || 0;
|
||
document.getElementById('option-b-population').value = event.optionB.effects.population || 0;
|
||
document.getElementById('option-b-military').value = event.optionB.effects.military || 0;
|
||
document.getElementById('option-b-resources').value = event.optionB.effects.resources || 0;
|
||
}
|
||
|
||
document.getElementById('option-b-add-status').value = event.optionB.add_status || '';
|
||
document.getElementById('option-b-remove-status').value = event.optionB.remove_status || '';
|
||
}
|
||
|
||
// 打开模态框
|
||
document.getElementById('event-modal').style.display = 'block';
|
||
}, 100);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |