mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-06 05:16:26 +08:00
347 lines
12 KiB
HTML
347 lines
12 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>积分管理 - RainOpsMini</title>
|
|
<link rel="stylesheet" href="/lib/css/bootstrap.min.css">
|
|
<link rel="stylesheet" href="/lib/css/font-awesome.min.css">
|
|
<style>
|
|
:root {
|
|
--primary-color: #4361ee;
|
|
--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: #3f37c9; }
|
|
|
|
.btn-danger {
|
|
background-color: white;
|
|
color: var(--danger-color);
|
|
border: 1px solid var(--danger-color);
|
|
}
|
|
.btn-danger:hover { background-color: #fff0f0; }
|
|
|
|
.container {
|
|
background: var(--card-bg);
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
padding: 20px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.search-bar {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.form-control {
|
|
padding: 8px 12px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
flex: 1;
|
|
max-width: 300px;
|
|
}
|
|
|
|
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; }
|
|
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 15px;
|
|
gap: 5px;
|
|
}
|
|
|
|
.page-btn {
|
|
padding: 5px 10px;
|
|
border: 1px solid #ddd;
|
|
background: white;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.page-btn.active {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
/* 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: 400px; 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; }
|
|
.modal-footer { margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px; }
|
|
|
|
.form-group { margin-bottom: 15px; }
|
|
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; }
|
|
.form-group input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<div class="title">
|
|
<i class="fas fa-coins" style="color: var(--primary-color)"></i> 积分管理
|
|
</div>
|
|
<button class="btn btn-primary" id="btn-add" onclick="showAddModal()">
|
|
<i class="fas fa-plus"></i> 添加/修改积分
|
|
</button>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<div class="search-bar">
|
|
<input type="text" id="searchInput" class="form-control" placeholder="搜索 SteamID..." onkeyup="if(event.key === 'Enter') loadData(1)">
|
|
<button class="btn btn-primary" onclick="loadData(1)">搜索</button>
|
|
</div>
|
|
|
|
<table id="dataTable">
|
|
<thead>
|
|
<tr>
|
|
<th>SteamID</th>
|
|
<th>积分余额</th>
|
|
<th>最后更新时间</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- Data loaded here -->
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="pagination" id="pagination"></div>
|
|
</div>
|
|
|
|
<!-- Edit/Add Modal -->
|
|
<div class="modal" id="editModal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<span id="modalTitle">修改积分</span>
|
|
<span style="cursor:pointer" onclick="closeModal()">×</span>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>SteamID</label>
|
|
<input type="text" id="editSteamId" placeholder="7656119..." />
|
|
</div>
|
|
<div class="form-group">
|
|
<label>积分数值</label>
|
|
<input type="number" id="editPoints" value="0" />
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-danger" onclick="closeModal()">取消</button>
|
|
<button class="btn btn-primary" onclick="saveData()">保存</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 = 'points';
|
|
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 "points" required.</div>';
|
|
throw new Error("Access Denied");
|
|
}
|
|
})();
|
|
|
|
let currentPage = 1;
|
|
const pageSize = 15;
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
loadData(1);
|
|
|
|
// Apply button permissions
|
|
if (!checkPermission('points.edit')) {
|
|
const btnAdd = document.getElementById('btn-add');
|
|
if (btnAdd) btnAdd.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
async function loadData(page) {
|
|
currentPage = page;
|
|
const search = document.getElementById('searchInput').value;
|
|
|
|
try {
|
|
const res = await fetch(`/api/points?page=${page}&pageSize=${pageSize}&search=${encodeURIComponent(search)}`);
|
|
if (!res.ok) throw new Error("加载失败");
|
|
const data = await res.json();
|
|
|
|
renderTable(data.list);
|
|
renderPagination(data.total, page);
|
|
} catch (err) {
|
|
alert(err.message);
|
|
}
|
|
}
|
|
|
|
function renderTable(list) {
|
|
const tbody = document.querySelector('#dataTable tbody');
|
|
tbody.innerHTML = '';
|
|
|
|
if (!list || list.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center;color:#888;">暂无数据</td></tr>';
|
|
return;
|
|
}
|
|
|
|
list.forEach(item => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${item.steamId}</td>
|
|
<td><strong>${item.points}</strong></td>
|
|
<td>${new Date(item.lastUpdated).toLocaleString()}</td>
|
|
<td>
|
|
${checkPermission('points.edit') ?
|
|
`<button class="btn btn-primary" style="padding:4px 8px;font-size:0.8rem;" onclick="showEditModal('${item.steamId}', ${item.points})">
|
|
<i class="fas fa-edit"></i> 修改
|
|
</button>` : ''}
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
function renderPagination(total, page) {
|
|
const container = document.getElementById('pagination');
|
|
container.innerHTML = '';
|
|
const totalPages = Math.ceil(total / pageSize);
|
|
|
|
if (totalPages <= 1) return;
|
|
|
|
// Previous
|
|
const prev = document.createElement('button');
|
|
prev.className = 'page-btn';
|
|
prev.innerText = '<';
|
|
prev.disabled = page === 1;
|
|
prev.onclick = () => loadData(page - 1);
|
|
container.appendChild(prev);
|
|
|
|
// Pages (simple version)
|
|
let start = Math.max(1, page - 2);
|
|
let end = Math.min(totalPages, page + 2);
|
|
|
|
for (let i = start; i <= end; i++) {
|
|
const btn = document.createElement('button');
|
|
btn.className = `page-btn ${i === page ? 'active' : ''}`;
|
|
btn.innerText = i;
|
|
btn.onclick = () => loadData(i);
|
|
container.appendChild(btn);
|
|
}
|
|
|
|
// Next
|
|
const next = document.createElement('button');
|
|
next.className = 'page-btn';
|
|
next.innerText = '>';
|
|
next.disabled = page === totalPages;
|
|
next.onclick = () => loadData(page + 1);
|
|
container.appendChild(next);
|
|
}
|
|
|
|
function showAddModal() {
|
|
document.getElementById('modalTitle').innerText = "添加/修改积分";
|
|
document.getElementById('editSteamId').value = "";
|
|
document.getElementById('editSteamId').disabled = false;
|
|
document.getElementById('editPoints').value = 0;
|
|
document.getElementById('editModal').classList.add('active');
|
|
}
|
|
|
|
function showEditModal(steamId, points) {
|
|
document.getElementById('modalTitle').innerText = "修改积分";
|
|
document.getElementById('editSteamId').value = steamId;
|
|
document.getElementById('editSteamId').disabled = true;
|
|
document.getElementById('editPoints').value = points;
|
|
document.getElementById('editModal').classList.add('active');
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('editModal').classList.remove('active');
|
|
}
|
|
|
|
async function saveData() {
|
|
const steamId = document.getElementById('editSteamId').value.trim();
|
|
const points = parseInt(document.getElementById('editPoints').value);
|
|
|
|
if (!steamId) {
|
|
alert("请输入 SteamID");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch('/api/points/update', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ steamId, points })
|
|
});
|
|
|
|
if (res.ok) {
|
|
closeModal();
|
|
loadData(currentPage);
|
|
alert("保存成功");
|
|
} else {
|
|
const err = await res.json();
|
|
alert("保存失败: " + (err.error || "未知错误"));
|
|
}
|
|
} catch (err) {
|
|
alert("请求失败: " + err.message);
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|