Files
squad-rain-ops-mini/wwwroot/role-manager.html

607 lines
21 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>角色管理</title>
<link rel="stylesheet" href="/lib/css/bootstrap.min.css">
<link rel="stylesheet" href="/lib/css/font-awesome.min.css">
<style>
:root {
--primary-color: #0056b3;
--bg-color: #f0f2f5;
--card-bg: #ffffff;
--text-color: #2c3e50;
--border-color: #d1d5db;
--danger-color: #d93025;
--success-color: #107c10;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
min-height: 100vh;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.title {
font-size: 1.5rem;
font-weight: bold;
display: flex;
align-items: center;
gap: 10px;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
display: inline-flex;
align-items: center;
gap: 6px;
transition: background-color 0.2s;
text-decoration: none;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: #005a9e;
}
.btn-danger {
background-color: white;
color: var(--danger-color);
border: 1px solid var(--danger-color);
}
.btn-danger:hover {
background-color: #fff0f0;
}
.btn-sm {
padding: 4px 8px;
font-size: 0.85rem;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
}
.container {
background: var(--card-bg);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
padding: 20px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f9f9f9;
font-weight: 600;
color: #666;
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal.active {
display: flex;
}
.modal-content {
background-color: white;
padding: 25px;
border-radius: 8px;
width: 500px;
max-width: 90%;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.modal-header {
font-size: 1.2rem;
font-weight: bold;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
}
.close-btn {
cursor: pointer;
font-size: 1.5rem;
line-height: 1;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-control {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
textarea.form-control {
resize: vertical;
min-height: 80px;
}
.permissions-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 10px;
margin-top: 5px;
background: #f9f9f9;
padding: 10px;
border-radius: 4px;
}
.permissions-section {
margin-bottom: 15px;
border: 1px solid #eee;
border-radius: 6px;
padding: 10px;
}
.permissions-section h4 {
margin: 0 0 10px 0;
font-size: 1rem;
color: #555;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9rem;
cursor: pointer;
}
.actions-cell {
display: flex;
gap: 8px;
}
</style>
</head>
<body>
<div class="header">
<div class="title">
<a href="/user-manager.html" class="btn btn-secondary btn-sm" style="margin-right: 10px;"><i class="fas fa-arrow-left"></i> 返回子账户</a>
<i class="fas fa-user-tag"></i> 角色管理
</div>
<button class="btn btn-primary" onclick="openAddModal()"><i class="fas fa-plus"></i> 新增角色</button>
</div>
<div class="container">
<table id="roleTable">
<thead>
<tr>
<th>角色名称</th>
<th>描述</th>
<th>权限数量</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- Roles will be loaded here -->
</tbody>
</table>
</div>
<!-- Edit/Add Modal -->
<div id="roleModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span id="modalTitle">新增角色</span>
<span class="close-btn" onclick="closeModal('roleModal')">&times;</span>
</div>
<div class="form-group">
<label>角色名称</label>
<input type="text" id="roleName" class="form-control" placeholder="例如:管理员">
</div>
<div class="form-group">
<label>描述</label>
<textarea id="roleDescription" class="form-control" placeholder="角色职责描述..."></textarea>
</div>
<div class="form-group">
<label>权限配置</label>
<div class="permissions-section">
<h4 style="display:flex;justify-content:space-between;align-items:center;">
菜单访问权限
<label class="checkbox-label" style="font-size:0.9rem;font-weight:normal;">
<input type="checkbox" onchange="toggleSelectAll(this)"> 全选
</label>
</h4>
<div id="permissionContainer" style="max-height: 400px; overflow-y: auto;">
<!-- Permissions generated by JS -->
</div>
</div>
</div>
<div style="text-align: right; margin-top: 20px;">
<button class="btn btn-secondary" onclick="closeModal('roleModal')">取消</button>
<button class="btn btn-primary" onclick="saveRole()">保存</button>
</div>
</div>
</div>
<script>
// Permission Check
window.checkPermission = function(key) {
const isSuper = localStorage.getItem('isSuperAdmin') === 'true';
if (isSuper) return true;
try {
const perms = JSON.parse(localStorage.getItem('permissions') || '[]');
return perms.includes('all') || perms.includes(key);
} catch { return false; }
};
(function() {
const required = 'roles.view';
if (!checkPermission(required)) {
document.body.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100vh;color:red;font-size:1.5rem;">Access Denied: Permission "roles.view" required.</div>';
throw new Error("Access Denied");
}
})();
let currentRoleId = null;
let allRoles = [];
const PERMISSION_CONFIG = [
{
category: "核心功能",
items: [
{ label: "仪表盘", value: "dashboard", children: [
{ label: "聊天面板", value: "dashboard.chat" },
{ label: "战斗事件", value: "dashboard.battle" },
{ label: "在线玩家", value: "dashboard.online" },
{ label: "刷新列表", value: "dashboard.refresh" },
{ label: "随机阵营", value: "dashboard.shuffle" },
{ label: "清理挂机", value: "dashboard.clearafk" },
{ label: "地图管理", value: "dashboard.maps" },
{ label: "设置票数", value: "dashboard.settickets" }
]},
{ label: "RCON控制台", value: "rcon", children: [{ label: "执行命令", value: "rcon.execute" }] },
{ label: "数据分析", value: "analytics" },
{ label: "历史对局", value: "history" },
{ label: "日志查询", value: "logs" }
]
},
{
category: "玩家管理",
items: [
{ label: "玩家库", value: "players", children: [
{ label: "踢出玩家", value: "players.kick" },
{ label: "封禁玩家", value: "players.ban" },
{ label: "警告玩家", value: "players.warn" },
{ label: "更换阵营", value: "players.changeteam" },
{ label: "解散小队", value: "players.disbandsquad" },
{ label: "移出小队", value: "players.removesquad" },
{ label: "游戏时长", value: "players.gametime" }
]},
{ label: "积分管理", value: "points.view", children: [
{ label: "修改积分", value: "points.edit" }
]},
{ label: "聊天消息", value: "chat" }
]
},
{
category: "系统管理",
items: [
{ label: "权限管理", value: "admins.view", children: [
{ label: "新增管理员", value: "admins.add" },
{ label: "编辑管理员", value: "admins.edit" },
{ label: "删除管理员", value: "admins.delete" }
]},
{ label: "会员管理", value: "vips.view", children: [
{ label: "新增会员", value: "vips.add" },
{ label: "编辑会员", value: "vips.edit" },
{ label: "删除会员", value: "vips.delete" }
]},
{ label: "CDK管理", value: "cdks.view", children: [
{ label: "生成CDK", value: "cdks.add" },
{ label: "删除CDK", value: "cdks.delete" }
]},
{ label: "子账户管理", value: "users.view", children: [
{ label: "新增账户", value: "users.add" },
{ label: "编辑账户", value: "users.edit" },
{ label: "删除账户", value: "users.delete" }
]},
{ label: "角色管理", value: "roles.view", children: [
{ label: "新增角色", value: "roles.add" },
{ label: "编辑角色", value: "roles.edit" },
{ label: "删除角色", value: "roles.delete" }
]}
]
}
];
document.addEventListener('DOMContentLoaded', () => {
renderPermissions();
loadRoles();
// Button permissions
if (!checkPermission('roles.add')) {
const btn = document.querySelector('button[onclick="openAddModal()"]');
if (btn) btn.style.display = 'none';
}
});
function renderPermissions() {
const container = document.getElementById('permissionContainer');
container.innerHTML = '';
PERMISSION_CONFIG.forEach(group => {
const groupDiv = document.createElement('div');
groupDiv.style.marginBottom = '15px';
const title = document.createElement('div');
title.style.borderBottom = '1px solid #eee';
title.style.paddingBottom = '5px';
title.style.marginBottom = '10px';
title.style.fontWeight = 'bold';
title.style.color = '#555';
title.innerText = group.category;
groupDiv.appendChild(title);
const grid = document.createElement('div');
grid.className = 'permissions-grid';
group.items.forEach(item => {
const itemDiv = document.createElement('div');
const label = document.createElement('label');
label.className = 'checkbox-label';
label.style.fontWeight = 'bold';
label.innerHTML = `<input type="checkbox" value="${item.value}" onchange="toggleChildren(this)"> ${item.label}`;
itemDiv.appendChild(label);
if (item.children) {
const childrenDiv = document.createElement('div');
childrenDiv.style.marginLeft = '20px';
childrenDiv.style.marginTop = '5px';
childrenDiv.style.display = 'flex';
childrenDiv.style.flexDirection = 'column';
childrenDiv.style.gap = '5px';
item.children.forEach(child => {
const childLabel = document.createElement('label');
childLabel.className = 'checkbox-label';
childLabel.style.fontSize = '0.85rem';
childLabel.innerHTML = `<input type="checkbox" value="${child.value}"> ${child.label}`;
childrenDiv.appendChild(childLabel);
});
itemDiv.appendChild(childrenDiv);
}
grid.appendChild(itemDiv);
});
groupDiv.appendChild(grid);
container.appendChild(groupDiv);
});
}
function toggleChildren(source) {
const itemDiv = source.parentNode.parentNode;
const childrenDiv = itemDiv.querySelector('div');
if (childrenDiv) {
childrenDiv.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = source.checked);
}
}
function loadRoles() {
fetch('/api/roles')
.then(res => {
if (res.status === 401) {
alert('无权访问');
window.location.href = '/login.html';
return [];
}
return res.json();
})
.then(roles => {
allRoles = roles || [];
renderTable(allRoles);
})
.catch(err => console.error(err));
}
function renderTable(roles) {
const tbody = document.querySelector('#roleTable tbody');
tbody.innerHTML = '';
const canEdit = checkPermission('roles.edit');
const canDelete = checkPermission('roles.delete');
roles.forEach(role => {
const tr = document.createElement('tr');
let actions = '';
if (canEdit) {
actions += `<button class="btn btn-primary btn-sm" onclick="openEditModal('${role.id}')"><i class="fas fa-edit"></i> 编辑</button>`;
}
if (canDelete) {
actions += `<button class="btn btn-danger btn-sm" onclick="deleteRole('${role.id}')"><i class="fas fa-trash"></i> 删除</button>`;
}
tr.innerHTML = `
<td>${role.name}</td>
<td>${role.description || '-'}</td>
<td><span class="status-badge" style="background:#eef;color:#448;">${role.permissions ? role.permissions.length : 0} 项权限</span></td>
<td>${new Date(role.createdAt).toLocaleString()}</td>
<td class="actions-cell">
${actions}
</td>
`;
tbody.appendChild(tr);
});
}
function openAddModal() {
currentRoleId = null;
document.getElementById('modalTitle').innerText = '新增角色';
document.getElementById('roleName').value = '';
document.getElementById('roleDescription').value = '';
// Clear checkboxes
document.querySelectorAll('.permissions-grid input[type="checkbox"]').forEach(cb => cb.checked = false);
document.getElementById('roleModal').classList.add('active');
}
function openEditModal(id) {
const role = allRoles.find(r => r.id === id);
if (!role) return;
currentRoleId = id;
document.getElementById('modalTitle').innerText = '编辑角色';
document.getElementById('roleName').value = role.name;
document.getElementById('roleDescription').value = role.description;
// Set checkboxes
document.querySelectorAll('.permissions-grid input[type="checkbox"]').forEach(cb => {
cb.checked = role.permissions && role.permissions.includes(cb.value);
});
document.getElementById('roleModal').classList.add('active');
}
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('active');
}
function toggleSelectAll(source) {
const checkboxes = document.querySelectorAll('.permissions-grid input[type="checkbox"]');
checkboxes.forEach(cb => cb.checked = source.checked);
}
function saveRole() {
const name = document.getElementById('roleName').value;
const description = document.getElementById('roleDescription').value;
if (!name) {
alert('请输入角色名称');
return;
}
const permissions = [];
document.querySelectorAll('.permissions-grid input[type="checkbox"]:checked').forEach(cb => {
permissions.push(cb.value);
});
const roleData = {
name: name,
description: description,
permissions: permissions
};
const url = currentRoleId ? `/api/roles/${currentRoleId}` : '/api/roles';
const method = currentRoleId ? 'PUT' : 'POST';
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(roleData)
})
.then(async res => {
if (res.ok) {
closeModal('roleModal');
loadRoles();
} else {
const err = await res.json();
alert('保存失败: ' + (err.error || '未知错误'));
}
})
.catch(err => {
console.error(err);
alert('请求失败');
});
}
function deleteRole(id) {
if (!confirm('确定要删除该角色吗?')) return;
fetch(`/api/roles/${id}`, {
method: 'DELETE'
})
.then(async res => {
if (res.ok) {
loadRoles();
} else {
const err = await res.json();
alert('删除失败: ' + (err.error || '未知错误'));
}
})
.catch(err => {
console.error(err);
alert('请求失败');
});
}
// Close modal when clicking outside
window.onclick = function(event) {
const modal = document.getElementById('roleModal');
if (event.target === modal) {
closeModal('roleModal');
}
}
</script>
</body>
</html>