Warhummer/static/js/admin/characters.js
2025-06-25 09:35:26 +08:00

682 lines
26 KiB
JavaScript

// 角色管理页面脚本 - 包含角色权重系统
document.addEventListener('DOMContentLoaded', () => {
// DOM元素
const charactersContainer = document.getElementById('characters-container');
const characterSearch = document.getElementById('character-search');
const characterTypeFilter = document.getElementById('character-type-filter');
const newCharacterBtn = document.getElementById('new-character-btn');
// 所有模态框
const characterModal = document.getElementById('character-modal');
const eventModal = document.getElementById('event-modal');
const characterDetailModal = document.getElementById('character-detail-modal');
const confirmModal = document.getElementById('confirm-modal');
// 角色表单元素
const characterForm = document.getElementById('character-form');
const characterIdInput = document.getElementById('character-id');
const characterNameInput = document.getElementById('character-name');
const characterTitleInput = document.getElementById('character-title');
const characterAvatarInput = document.getElementById('character-avatar');
const characterAvatarPathInput = document.getElementById('character-avatar-path');
const characterTypeInput = document.getElementById('character-type');
const characterWeightInput = document.getElementById('character-weight'); // 权重输入字段
// 事件表单元素
const eventForm = document.getElementById('event-form');
const eventCharacterIdInput = document.getElementById('event-character-id');
const eventIdInput = document.getElementById('event-id');
const eventTextInput = document.getElementById('event-text');
const optionATextInput = document.getElementById('option-a-text');
const optionBTextInput = document.getElementById('option-b-text');
// 详情页元素
const detailTitle = document.getElementById('detail-title');
const detailAvatar = document.getElementById('detail-avatar');
const detailName = document.getElementById('detail-name');
const detailTitleText = document.getElementById('detail-title-text');
const detailType = document.getElementById('detail-type');
const editCharacterBtn = document.getElementById('edit-character-btn');
const deleteCharacterBtn = document.getElementById('delete-character-btn');
const addEventBtn = document.getElementById('add-event-btn');
const eventsContainer = document.getElementById('events-container');
// 确认对话框元素
const confirmTitle = document.getElementById('confirm-title');
const confirmMessage = document.getElementById('confirm-message');
const confirmOkBtn = document.getElementById('confirm-ok');
const confirmCancelBtn = document.getElementById('confirm-cancel');
// 状态变量
let characters = [];
let currentCharacter = null;
let filteredCharacters = [];
let confirmCallback = null;
// 初始化
loadCharacters();
setupEventListeners();
// 设置事件监听器
function setupEventListeners() {
// 搜索和过滤
characterSearch.addEventListener('input', filterCharacters);
characterTypeFilter.addEventListener('change', filterCharacters);
// 新建角色按钮
newCharacterBtn.addEventListener('click', () => {
openNewCharacterModal();
});
// 表单提交
characterForm.addEventListener('submit', saveCharacter);
eventForm.addEventListener('submit', saveEvent);
// 编辑角色按钮
editCharacterBtn.addEventListener('click', () => {
if (currentCharacter) {
// 先关闭详情模态框
closeModal(characterDetailModal);
// 再打开编辑模态框
setTimeout(() => {
openEditCharacterModal(currentCharacter);
}, 300);
}
});
// 删除角色按钮
deleteCharacterBtn.addEventListener('click', () => {
if (currentCharacter) {
openConfirmModal(
'删除角色',
`确定要删除角色 "${currentCharacter.name}" 吗?此操作不可恢复,且会删除该角色的所有事件。`,
() => deleteCharacter(currentCharacter.id)
);
}
});
// 添加事件按钮
addEventBtn.addEventListener('click', () => {
if (currentCharacter) {
// 先关闭详情模态框
closeModal(characterDetailModal);
// 再打开事件模态框
setTimeout(() => {
openNewEventModal(currentCharacter.id);
}, 300);
}
});
// 确认对话框按钮
confirmOkBtn.addEventListener('click', () => {
if (confirmCallback) {
confirmCallback();
}
closeModal(confirmModal);
});
confirmCancelBtn.addEventListener('click', () => {
closeModal(confirmModal);
});
// 关闭按钮
document.querySelectorAll('.close-modal, .close-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const modal = e.target.closest('.modal');
if (modal) {
closeModal(modal);
}
});
});
// 点击模态框背景关闭
document.querySelectorAll('.modal').forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeModal(modal);
}
});
});
// 防止冒泡
document.querySelectorAll('.modal-content').forEach(content => {
content.addEventListener('click', (e) => {
e.stopPropagation();
});
});
}
// 加载所有角色
function loadCharacters() {
charactersContainer.innerHTML = '<div class="loading-indicator">加载中...</div>';
fetch('/api/admin/characters')
.then(response => response.json())
.then(data => {
characters = data.characters || [];
filteredCharacters = [...characters];
renderCharacters();
})
.catch(error => {
console.error('加载角色失败:', error);
charactersContainer.innerHTML = '<div class="error-message">加载失败,请刷新页面重试。</div>';
});
}
// 过滤角色
function filterCharacters() {
const searchTerm = characterSearch.value.toLowerCase();
const typeFilter = characterTypeFilter.value;
filteredCharacters = characters.filter(character => {
const nameMatch = (character.name || '').toLowerCase().includes(searchTerm) ||
((character.title || '').toLowerCase().includes(searchTerm));
const typeMatch = typeFilter === 'all' || character.type === typeFilter;
return nameMatch && typeMatch;
});
renderCharacters();
}
// 渲染角色列表
function renderCharacters() {
if (filteredCharacters.length === 0) {
charactersContainer.innerHTML = '<div class="empty-message">没有找到符合条件的角色</div>';
return;
}
charactersContainer.innerHTML = '';
filteredCharacters.forEach(character => {
const card = document.createElement('div');
card.className = 'character-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;
}
// 添加权重标签
const weightLabel = `<div class="character-weight">权重: ${character.weight || 100}</div>`;
card.innerHTML = `
<div class="card-avatar">${character.avatar || '👤'}</div>
<h3>${character.name || '无名角色'}</h3>
<p>${character.title || '无头衔'}</p>
<div class="character-type ${typeClass}">${typeLabel}</div>
${weightLabel}
`;
card.addEventListener('click', () => openCharacterDetail(character));
charactersContainer.appendChild(card);
});
}
// 打开新角色模态框
function openNewCharacterModal() {
// 重置表单
characterForm.reset();
characterIdInput.value = generateUUID();
// 设置默认权重值
characterWeightInput.value = 5;
// 设置标题
document.getElementById('modal-title').textContent = '新建人物';
// 打开模态框
openModal(characterModal);
}
// 打开编辑角色模态框
function openEditCharacterModal(character) {
// 设置标题
document.getElementById('modal-title').textContent = '编辑人物';
// 填充表单
characterIdInput.value = character.id;
characterNameInput.value = character.name || '';
characterTitleInput.value = character.title || '';
characterAvatarInput.value = character.avatar || '';
characterAvatarPathInput.value = character.avatarPath || '';
characterTypeInput.value = character.type || 'resident';
characterWeightInput.value = character.weight || 100; // 设置权重值
// 打开模态框
openModal(characterModal);
}
// 打开新事件模态框
function openNewEventModal(characterId) {
// 重置表单
eventForm.reset();
eventCharacterIdInput.value = characterId;
eventIdInput.value = generateUUID();
// 设置所有数值为0
document.querySelectorAll('.effect-item input[type="number"]').forEach(input => {
input.value = 0;
});
// 设置标题
document.getElementById('event-modal-title').textContent = '新建事件';
// 打开模态框
openModal(eventModal);
}
// 打开编辑事件模态框
function openEditEventModal(characterId, event) {
// 设置标题
document.getElementById('event-modal-title').textContent = '编辑事件';
// 填充表单基本信息
eventCharacterIdInput.value = characterId;
eventIdInput.value = event.id;
eventTextInput.value = event.text || '';
// 填充选项A
if (event.optionA) {
optionATextInput.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) {
optionBTextInput.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 || '';
}
// 打开模态框
openModal(eventModal);
}
// 打开角色详情
function openCharacterDetail(character) {
// 保存当前角色
currentCharacter = character;
// 填充详情
detailTitle.textContent = '角色详情: ' + character.name;
detailAvatar.textContent = character.avatar || '👤';
detailName.textContent = character.name || '无名角色';
detailTitleText.textContent = character.title || '无头衔';
// 显示权重
const weightElement = document.getElementById('detail-weight').querySelector('span');
if (weightElement) {
weightElement.textContent = character.weight || 100;
}
// 设置角色类型
let typeLabel = '未知类型';
switch(character.type) {
case 'resident':
typeLabel = '常驻角色';
break;
case 'special':
typeLabel = '特殊角色';
break;
case 'status':
typeLabel = '状态效果';
break;
}
detailType.textContent = typeLabel;
// 渲染事件列表
renderEvents(character);
// 打开模态框
openModal(characterDetailModal);
}
// 打开确认对话框
function openConfirmModal(title, message, callback) {
// 设置内容
confirmTitle.textContent = title;
confirmMessage.textContent = message;
confirmCallback = callback;
// 打开模态框
openModal(confirmModal);
}
// 打开模态框通用函数
function openModal(modal) {
// 禁止背景滚动
document.body.style.overflow = 'hidden';
// 确保模态框在DOM中
if (!document.body.contains(modal)) {
document.body.appendChild(modal);
}
// 显示模态框
modal.style.display = 'block';
}
// 关闭模态框通用函数
function closeModal(modal) {
modal.style.display = 'none';
// 恢复背景滚动
document.body.style.overflow = '';
}
// 渲染角色的事件列表
function renderEvents(character) {
if (!character.events || character.events.length === 0) {
eventsContainer.innerHTML = '<div class="empty-events">该角色暂无事件</div>';
return;
}
eventsContainer.innerHTML = '';
character.events.forEach(event => {
const eventItem = document.createElement('div');
eventItem.className = 'event-item';
eventItem.innerHTML = `
<div class="event-text">
<h4>事件ID: ${event.id}</h4>
<p>${event.text || '无描述'}</p>
</div>
<div class="event-actions">
<button class="edit-event-btn" title="编辑事件">✏️</button>
<button class="delete-event-btn" title="删除事件">🗑️</button>
</div>
`;
// 编辑事件按钮
eventItem.querySelector('.edit-event-btn').addEventListener('click', (e) => {
e.stopPropagation();
closeModal(characterDetailModal);
setTimeout(() => {
openEditEventModal(character.id, event);
}, 300);
});
// 删除事件按钮
eventItem.querySelector('.delete-event-btn').addEventListener('click', (e) => {
e.stopPropagation();
openConfirmModal(
'删除事件',
`确定要删除此事件吗?此操作不可恢复。`,
() => deleteEvent(character.id, event.id)
);
});
eventsContainer.appendChild(eventItem);
});
}
// 保存角色
function saveCharacter(e) {
e.preventDefault();
// 获取表单数据
const characterData = {
id: characterIdInput.value,
name: characterNameInput.value,
title: characterTitleInput.value,
avatar: characterAvatarInput.value,
avatarPath: characterAvatarPathInput.value,
type: characterTypeInput.value,
weight: parseInt(characterWeightInput.value) || 100, // 添加权重字段
events: []
};
// 如果是编辑现有角色,保留原有事件
const existingCharacter = characters.find(c => c.id === characterData.id);
if (existingCharacter && existingCharacter.events) {
characterData.events = existingCharacter.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) {
// 更新列表
loadCharacters();
// 关闭模态框
closeModal(characterModal);
// 如果是编辑现有角色,更新详情页
if (currentCharacter && currentCharacter.id === characterData.id) {
currentCharacter = characterData;
setTimeout(() => {
openCharacterDetail(currentCharacter);
}, 300);
}
} else {
alert('保存失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('保存角色失败:', error);
alert('保存失败,请稍后重试');
});
}
// 保存事件
function saveEvent(e) {
e.preventDefault();
// 收集表单数据
const characterId = eventCharacterIdInput.value;
const eventData = {
id: eventIdInput.value,
text: eventTextInput.value,
optionA: {
text: optionATextInput.value,
effects: {
loyalty: parseInt(document.getElementById('option-a-loyalty').value) || 0,
chaos: parseInt(document.getElementById('option-a-chaos').value) || 0,
population: parseInt(document.getElementById('option-a-population').value) || 0,
military: parseInt(document.getElementById('option-a-military').value) || 0,
resources: parseInt(document.getElementById('option-a-resources').value) || 0
}
},
optionB: {
text: optionBTextInput.value,
effects: {
loyalty: parseInt(document.getElementById('option-b-loyalty').value) || 0,
chaos: parseInt(document.getElementById('option-b-chaos').value) || 0,
population: parseInt(document.getElementById('option-b-population').value) || 0,
military: parseInt(document.getElementById('option-b-military').value) || 0,
resources: parseInt(document.getElementById('option-b-resources').value) || 0
}
}
};
// 添加状态效果(如果有)
const optionAAddStatus = document.getElementById('option-a-add-status').value;
if (optionAAddStatus) {
eventData.optionA.add_status = optionAAddStatus;
}
const optionARemoveStatus = document.getElementById('option-a-remove-status').value;
if (optionARemoveStatus) {
eventData.optionA.remove_status = optionARemoveStatus;
}
const optionBAddStatus = document.getElementById('option-b-add-status').value;
if (optionBAddStatus) {
eventData.optionB.add_status = optionBAddStatus;
}
const optionBRemoveStatus = document.getElementById('option-b-remove-status').value;
if (optionBRemoveStatus) {
eventData.optionB.remove_status = optionBRemoveStatus;
}
// 判断是新增还是更新
const isNewEvent = !characters.some(c =>
c.id === characterId &&
c.events &&
c.events.some(e => e.id === eventData.id)
);
const url = isNewEvent
? `/api/admin/character/${characterId}/event`
: `/api/admin/character/${characterId}/event/${eventData.id}`;
const method = isNewEvent ? 'POST' : 'PUT';
// 发送请求
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(eventData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 重新加载数据
loadCharacters();
// 关闭模态框
closeModal(eventModal);
// 如果当前有打开的角色详情,更新详情
if (currentCharacter && currentCharacter.id === characterId) {
fetch(`/api/admin/character/${characterId}`)
.then(response => response.json())
.then(data => {
if (data.character) {
currentCharacter = data.character;
setTimeout(() => {
openCharacterDetail(currentCharacter);
}, 300);
}
});
}
} else {
alert('保存失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('保存事件失败:', error);
alert('保存失败,请稍后重试');
});
}
// 删除角色
function deleteCharacter(characterId) {
fetch(`/api/admin/character/${characterId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新角色列表
loadCharacters();
// 关闭所有模态框
closeModal(characterDetailModal);
closeModal(confirmModal);
// 清空当前角色
currentCharacter = null;
} else {
alert('删除失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('删除角色失败:', error);
alert('删除失败,请稍后重试');
});
}
// 删除事件
function deleteEvent(characterId, eventId) {
fetch(`/api/admin/character/${characterId}/event/${eventId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 如果当前有打开的角色详情,更新详情
if (currentCharacter && currentCharacter.id === characterId) {
fetch(`/api/admin/character/${characterId}`)
.then(response => response.json())
.then(data => {
if (data.character) {
// 更新当前角色数据
currentCharacter = data.character;
// 重新渲染事件列表
renderEvents(currentCharacter);
}
});
}
// 更新角色列表
loadCharacters();
// 关闭确认框
closeModal(confirmModal);
} else {
alert('删除失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('删除事件失败:', error);
alert('删除失败,请稍后重试');
});
}
// 生成UUID
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
});