Files
squad-rain-ops-mini/wwwroot/points-manager.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()">&times;</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>