799 lines
37 KiB
HTML
799 lines
37 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 href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
|
<style>
|
|
.stat-card {
|
|
border-radius: 15px;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
transition: transform 0.3s;
|
|
}
|
|
.stat-card:hover {
|
|
transform: translateY(-5px);
|
|
}
|
|
.rarity-r { color: #6c757d; }
|
|
.rarity-sr { color: #0d6efd; }
|
|
.rarity-ssr { color: #ffc107; }
|
|
.rarity-sp { color: #dc3545; }
|
|
.achievement-card {
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
border-radius: 10px;
|
|
padding: 15px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.progress {
|
|
height: 25px;
|
|
}
|
|
.navbar-brand {
|
|
font-weight: bold;
|
|
}
|
|
.table th {
|
|
border-top: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
|
<div class="container">
|
|
<a class="navbar-brand" href="#">
|
|
<i class="bi bi-dice-5"></i> 阴阳师抽卡系统 - 管理后台
|
|
</a>
|
|
<div class="ms-auto">
|
|
<span class="navbar-text">
|
|
<i class="bi bi-shield-lock"></i> 管理员访问
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container mt-4">
|
|
<!-- 统计概览 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card stat-card bg-light">
|
|
<div class="card-body text-center">
|
|
<h5 class="card-title"><i class="bi bi-people"></i> 参与人数</h5>
|
|
<h2 class="text-primary" id="totalUsers">-</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card stat-card bg-light">
|
|
<div class="card-body text-center">
|
|
<h5 class="card-title"><i class="bi bi-dice-6"></i> 总抽卡次数</h5>
|
|
<h2 class="text-success" id="totalDraws">-</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card stat-card bg-light">
|
|
<div class="card-body text-center">
|
|
<h5 class="card-title"><i class="bi bi-trophy"></i> SSR/SP总数</h5>
|
|
<h2 class="text-warning" id="totalSSRSP">-</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card stat-card bg-light">
|
|
<div class="card-body text-center">
|
|
<h5 class="card-title"><i class="bi bi-percent"></i> SSR/SP概率</h5>
|
|
<h2 class="text-danger" id="ssrSpRate">-</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 稀有度分布 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5><i class="bi bi-pie-chart"></i> 稀有度分布</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h3 class="rarity-r">R</h3>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar bg-secondary" id="rRate" style="width: 0%"></div>
|
|
</div>
|
|
<h4 id="rCount">-</h4>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h3 class="rarity-sr">SR</h3>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar bg-primary" id="srRate" style="width: 0%"></div>
|
|
</div>
|
|
<h4 id="srCount">-</h4>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h3 class="rarity-ssr">SSR</h3>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar bg-warning" id="ssrRate" style="width: 0%"></div>
|
|
</div>
|
|
<h4 id="ssrCount">-</h4>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h3 class="rarity-sp">SP</h3>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar bg-danger" id="spRate" style="width: 0%"></div>
|
|
</div>
|
|
<h4 id="spCount">-</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 排行榜 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5><i class="bi bi-trophy"></i> SSR/SP排行榜</h5>
|
|
<button class="btn btn-sm btn-outline-primary" onclick="refreshRankList()">
|
|
<i class="bi bi-arrow-clockwise"></i> 刷新
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>排名</th>
|
|
<th>用户ID</th>
|
|
<th>总抽卡次数</th>
|
|
<th class="rarity-r">R</th>
|
|
<th class="rarity-sr">SR</th>
|
|
<th class="rarity-ssr">SSR</th>
|
|
<th class="rarity-sp">SP</th>
|
|
<th>SSR/SP总数</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="rankListBody">
|
|
<tr>
|
|
<td colspan="9" class="text-center">加载中...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 用户查询 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5><i class="bi bi-person-search"></i> 用户查询</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control" id="userIdInput" placeholder="输入用户ID">
|
|
<button class="btn btn-primary" onclick="queryUser()">
|
|
<i class="bi bi-search"></i> 查询
|
|
</button>
|
|
</div>
|
|
|
|
<div id="userStatsResult" style="display: none;">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>抽卡统计</h6>
|
|
<table class="table table-sm">
|
|
<tr>
|
|
<td>总抽卡次数</td>
|
|
<td id="userTotalDraws">-</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="rarity-r">R卡数量</td>
|
|
<td id="userRCount">-</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="rarity-sr">SR卡数量</td>
|
|
<td id="userSRCount">-</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="rarity-ssr">SSR卡数量</td>
|
|
<td id="userSSRCount">-</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="rarity-sp">SP卡数量</td>
|
|
<td id="userSPCount">-</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>最近抽卡记录</h6>
|
|
<div id="recentDraws">
|
|
加载中...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 成就查询 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5><i class="bi bi-award"></i> 成就查询</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control" id="achievementUserIdInput" placeholder="输入用户ID">
|
|
<button class="btn btn-primary" onclick="queryAchievements()">
|
|
<i class="bi bi-search"></i> 查询
|
|
</button>
|
|
</div>
|
|
|
|
<div id="achievementResult" style="display: none;">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>已解锁成就</h6>
|
|
<div id="unlockedAchievements">
|
|
加载中...
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>成就进度</h6>
|
|
<div class="achievement-card">
|
|
<div class="d-flex justify-content-between">
|
|
<span>连续抽卡天数</span>
|
|
<span id="consecutiveDays">-</span>
|
|
</div>
|
|
<div class="progress mt-2">
|
|
<div class="progress-bar" id="consecutiveDaysProgress" style="width: 0%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="achievement-card">
|
|
<div class="d-flex justify-content-between">
|
|
<span>无SSR/SP连击</span>
|
|
<span id="noSsrStreak">-</span>
|
|
</div>
|
|
<div class="progress mt-2">
|
|
<div class="progress-bar bg-danger" id="noSsrStreakProgress" style="width: 0%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 每日详细抽卡记录 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5><i class="bi bi-table"></i> 今日抽卡详细记录</h5>
|
|
<div>
|
|
<input type="date" class="form-control form-control-sm d-inline-block me-2" id="recordDateInput" style="width: auto;">
|
|
<button class="btn btn-sm btn-outline-primary" onclick="loadDailyRecords()">
|
|
<i class="bi bi-arrow-clockwise"></i> 刷新
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>抽卡时间</th>
|
|
<th>用户ID</th>
|
|
<th>式神名称</th>
|
|
<th>稀有度</th>
|
|
<th>成就解锁</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="dailyRecordsBody">
|
|
<tr>
|
|
<td colspan="5" class="text-center">加载中...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mt-3">
|
|
<div>
|
|
<span>总记录数: <strong id="totalRecordsCount">0</strong></span>
|
|
</div>
|
|
<div>
|
|
<nav aria-label="Page navigation">
|
|
<ul class="pagination pagination-sm" id="recordsPagination">
|
|
<!-- 分页控件将通过JS动态生成 -->
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="bg-light text-center py-3 mt-5">
|
|
<div class="container">
|
|
<p class="mb-0">阴阳师抽卡系统 - 管理后台 © 2024</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// API配置
|
|
const API_BASE = '/onmyoji_gacha/api';
|
|
let ADMIN_TOKEN = localStorage.getItem('adminToken');
|
|
|
|
// 令牌重置函数
|
|
function resetToken() {
|
|
localStorage.removeItem('adminToken');
|
|
const newToken = prompt('请输入管理员令牌:');
|
|
if (newToken) {
|
|
ADMIN_TOKEN = newToken;
|
|
localStorage.setItem('adminToken', ADMIN_TOKEN);
|
|
// 更新请求头
|
|
headers.Authorization = `Bearer ${ADMIN_TOKEN}`;
|
|
console.log('令牌已重置:', ADMIN_TOKEN);
|
|
// 重新加载数据
|
|
loadDailyStats();
|
|
loadRankList();
|
|
loadDailyRecords();
|
|
} else {
|
|
alert('需要管理员令牌才能访问');
|
|
}
|
|
}
|
|
|
|
// 如果没有保存的令牌,提示输入
|
|
if (!ADMIN_TOKEN) {
|
|
ADMIN_TOKEN = prompt('请输入管理员令牌:');
|
|
if (ADMIN_TOKEN) {
|
|
localStorage.setItem('adminToken', ADMIN_TOKEN);
|
|
} else {
|
|
alert('需要管理员令牌才能访问');
|
|
window.location.reload();
|
|
}
|
|
}
|
|
|
|
console.log('使用的管理员令牌:', ADMIN_TOKEN);
|
|
|
|
// API请求头
|
|
const headers = {
|
|
'Authorization': `Bearer ${ADMIN_TOKEN}`,
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
console.log('请求头:', headers);
|
|
|
|
// 添加令牌重置按钮到导航栏
|
|
window.addEventListener('DOMContentLoaded', function() {
|
|
const navbar = document.querySelector('.navbar .ms-auto');
|
|
const resetButton = document.createElement('button');
|
|
resetButton.className = 'btn btn-outline-light btn-sm ms-2';
|
|
resetButton.innerHTML = '<i class="bi bi-key"></i> 重置令牌';
|
|
resetButton.onclick = resetToken;
|
|
navbar.appendChild(resetButton);
|
|
});
|
|
|
|
// 页面加载时获取数据
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadDailyStats();
|
|
loadRankList();
|
|
// 设置今天的日期为默认值
|
|
const today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('recordDateInput').value = today;
|
|
loadDailyRecords();
|
|
});
|
|
|
|
// 加载今日统计
|
|
async function loadDailyStats() {
|
|
try {
|
|
console.log('正在请求每日统计...');
|
|
const response = await fetch(`${API_BASE}/stats/daily`, { headers });
|
|
console.log('响应状态:', response.status);
|
|
console.log('响应头:', response.headers);
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error('API 错误响应:', errorText);
|
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('每日统计数据:', data);
|
|
|
|
if (data.success) {
|
|
const stats = data.stats;
|
|
document.getElementById('totalUsers').textContent = stats.total_users;
|
|
document.getElementById('totalDraws').textContent = stats.total_draws;
|
|
|
|
const ssrSpTotal = stats.rarity_stats.SSR + stats.rarity_stats.SP;
|
|
document.getElementById('totalSSRSP').textContent = ssrSpTotal;
|
|
|
|
const ssrSpRate = ((ssrSpTotal / stats.total_draws) * 100).toFixed(2);
|
|
document.getElementById('ssrSpRate').textContent = ssrSpRate + '%';
|
|
|
|
// 更新稀有度分布
|
|
updateRarityDistribution(stats.rarity_stats, stats.total_draws);
|
|
} else {
|
|
console.error('API 返回失败:', data);
|
|
}
|
|
} catch (error) {
|
|
console.error('加载统计数据失败:', error);
|
|
// 显示错误信息给用户
|
|
document.getElementById('totalUsers').textContent = '错误';
|
|
document.getElementById('totalDraws').textContent = '错误';
|
|
document.getElementById('totalSSRSP').textContent = '错误';
|
|
document.getElementById('ssrSpRate').textContent = '错误';
|
|
}
|
|
}
|
|
|
|
// 更新稀有度分布
|
|
function updateRarityDistribution(rarityStats, totalDraws) {
|
|
const rarities = ['R', 'SR', 'SSR', 'SP'];
|
|
|
|
rarities.forEach(rarity => {
|
|
const count = rarityStats[rarity];
|
|
const rate = totalDraws > 0 ? (count / totalDraws * 100).toFixed(1) : 0;
|
|
|
|
document.getElementById(`${rarity.toLowerCase()}Count`).textContent = count;
|
|
document.getElementById(`${rarity.toLowerCase()}Rate`).style.width = rate + '%';
|
|
document.getElementById(`${rarity.toLowerCase()}Rate`).textContent = rate + '%';
|
|
});
|
|
}
|
|
|
|
// 加载排行榜
|
|
async function loadRankList() {
|
|
try {
|
|
console.log('正在请求排行榜数据...');
|
|
const response = await fetch(`${API_BASE}/stats/rank`, { headers });
|
|
console.log('排行榜响应状态:', response.status);
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error('排行榜 API 错误响应:', errorText);
|
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('排行榜数据:', data);
|
|
|
|
if (data.success) {
|
|
const tbody = document.getElementById('rankListBody');
|
|
tbody.innerHTML = '';
|
|
|
|
data.data.forEach((user, index) => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${index + 1}</td>
|
|
<td>${user.user_id}</td>
|
|
<td>${user.total_draws}</td>
|
|
<td class="rarity-r">${user.R_count}</td>
|
|
<td class="rarity-sr">${user.SR_count}</td>
|
|
<td class="rarity-ssr">${user.SSR_count}</td>
|
|
<td class="rarity-sp">${user.SP_count}</td>
|
|
<td><strong>${user.ssr_sp_total}</strong></td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewUserDetails('${user.user_id}')">
|
|
<i class="bi bi-eye"></i> 详情
|
|
</button>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
if (data.data.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="9" class="text-center">暂无数据</td></tr>';
|
|
}
|
|
} else {
|
|
console.error('排行榜 API 返回失败:', data);
|
|
document.getElementById('rankListBody').innerHTML = '<tr><td colspan="9" class="text-center">数据加载失败</td></tr>';
|
|
}
|
|
} catch (error) {
|
|
console.error('加载排行榜失败:', error);
|
|
document.getElementById('rankListBody').innerHTML = '<tr><td colspan="9" class="text-center">加载失败,请检查令牌</td></tr>';
|
|
}
|
|
}
|
|
|
|
// 刷新排行榜
|
|
function refreshRankList() {
|
|
loadRankList();
|
|
}
|
|
|
|
// 查询用户
|
|
async function queryUser() {
|
|
const userId = document.getElementById('userIdInput').value.trim();
|
|
if (!userId) {
|
|
alert('请输入用户ID');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/stats/user/${userId}`, { headers });
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
document.getElementById('userStatsResult').style.display = 'block';
|
|
|
|
document.getElementById('userTotalDraws').textContent = data.total_draws;
|
|
document.getElementById('userRCount').textContent = data.R_count;
|
|
document.getElementById('userSRCount').textContent = data.SR_count;
|
|
document.getElementById('userSSRCount').textContent = data.SSR_count;
|
|
document.getElementById('userSPCount').textContent = data.SP_count;
|
|
|
|
// 显示最近抽卡记录
|
|
const recentDrawsDiv = document.getElementById('recentDraws');
|
|
if (data.recent_draws && data.recent_draws.length > 0) {
|
|
recentDrawsDiv.innerHTML = data.recent_draws.map(draw =>
|
|
`<div class="mb-1">
|
|
<span class="badge bg-secondary">${draw.date}</span>
|
|
<span class="badge rarity-${draw.rarity.toLowerCase()}">${draw.rarity}</span>
|
|
${draw.name}
|
|
</div>`
|
|
).join('');
|
|
} else {
|
|
recentDrawsDiv.innerHTML = '<p class="text-muted">暂无抽卡记录</p>';
|
|
}
|
|
} else {
|
|
alert('未找到用户数据');
|
|
document.getElementById('userStatsResult').style.display = 'none';
|
|
}
|
|
} catch (error) {
|
|
console.error('查询用户失败:', error);
|
|
alert('查询失败');
|
|
}
|
|
}
|
|
|
|
// 查看用户详情
|
|
function viewUserDetails(userId) {
|
|
document.getElementById('userIdInput').value = userId;
|
|
queryUser();
|
|
document.getElementById('achievementUserIdInput').value = userId;
|
|
queryAchievements();
|
|
|
|
// 滚动到用户查询区域
|
|
document.querySelector('.card:has(#userIdInput)').scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
|
|
// 查询成就
|
|
async function queryAchievements() {
|
|
const userId = document.getElementById('achievementUserIdInput').value.trim();
|
|
if (!userId) {
|
|
alert('请输入用户ID');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/achievements/${userId}`, { headers });
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
document.getElementById('achievementResult').style.display = 'block';
|
|
|
|
// 显示已解锁成就
|
|
const unlockedDiv = document.getElementById('unlockedAchievements');
|
|
const achievements = Object.entries(data.achievements);
|
|
|
|
if (achievements.length > 0) {
|
|
unlockedDiv.innerHTML = achievements.map(([id, info]) =>
|
|
`<div class="achievement-card">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>${id}</span>
|
|
<span class="badge bg-success">已解锁</span>
|
|
</div>
|
|
<small class="text-muted">${info.unlocked_date}</small>
|
|
</div>`
|
|
).join('');
|
|
} else {
|
|
unlockedDiv.innerHTML = '<p class="text-muted">暂无已解锁成就</p>';
|
|
}
|
|
|
|
// 显示成就进度
|
|
const progress = data.progress;
|
|
document.getElementById('consecutiveDays').textContent = progress.consecutive_days + ' 天';
|
|
document.getElementById('noSsrStreak').textContent = progress.no_ssr_streak + ' 次';
|
|
|
|
// 更新进度条
|
|
const consecutiveProgress = Math.min((progress.consecutive_days / 150) * 100, 100);
|
|
document.getElementById('consecutiveDaysProgress').style.width = consecutiveProgress + '%';
|
|
|
|
const noSsrProgress = Math.min((progress.no_ssr_streak / 180) * 100, 100);
|
|
document.getElementById('noSsrStreakProgress').style.width = noSsrProgress + '%';
|
|
} else {
|
|
alert('未找到用户成就数据');
|
|
document.getElementById('achievementResult').style.display = 'none';
|
|
}
|
|
} catch (error) {
|
|
console.error('查询成就失败:', error);
|
|
alert('查询失败');
|
|
}
|
|
}
|
|
|
|
// 每页记录数
|
|
const RECORDS_PER_PAGE = 20;
|
|
let currentRecords = [];
|
|
let currentPage = 1;
|
|
|
|
// 加载每日详细抽卡记录
|
|
async function loadDailyRecords() {
|
|
const date = document.getElementById('recordDateInput').value;
|
|
if (!date) {
|
|
alert('请选择日期');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
console.log('正在请求每日详细抽卡记录...', date);
|
|
const response = await fetch(`${API_BASE}/records/daily?date=${date}`, { headers });
|
|
console.log('每日详细记录响应状态:', response.status);
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error('每日详细记录 API 错误响应:', errorText);
|
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('每日详细记录数据:', data);
|
|
|
|
if (data.success) {
|
|
currentRecords = data.records;
|
|
currentPage = 1;
|
|
document.getElementById('totalRecordsCount').textContent = data.total_count;
|
|
displayRecords();
|
|
} else {
|
|
console.error('每日详细记录 API 返回失败:', data);
|
|
document.getElementById('dailyRecordsBody').innerHTML = '<tr><td colspan="5" class="text-center">暂无数据</td></tr>';
|
|
document.getElementById('totalRecordsCount').textContent = '0';
|
|
document.getElementById('recordsPagination').innerHTML = '';
|
|
}
|
|
} catch (error) {
|
|
console.error('加载每日详细记录失败:', error);
|
|
document.getElementById('dailyRecordsBody').innerHTML = '<tr><td colspan="5" class="text-center">加载失败,请检查令牌</td></tr>';
|
|
document.getElementById('totalRecordsCount').textContent = '0';
|
|
document.getElementById('recordsPagination').innerHTML = '';
|
|
}
|
|
}
|
|
|
|
// 显示记录(支持分页)
|
|
function displayRecords() {
|
|
const tbody = document.getElementById('dailyRecordsBody');
|
|
tbody.innerHTML = '';
|
|
|
|
// 计算分页
|
|
const startIndex = (currentPage - 1) * RECORDS_PER_PAGE;
|
|
const endIndex = Math.min(startIndex + RECORDS_PER_PAGE, currentRecords.length);
|
|
const pageRecords = currentRecords.slice(startIndex, endIndex);
|
|
|
|
if (pageRecords.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" class="text-center">暂无数据</td></tr>';
|
|
document.getElementById('recordsPagination').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
// 显示当前页的记录
|
|
pageRecords.forEach(record => {
|
|
const row = document.createElement('tr');
|
|
|
|
// 格式化成就解锁信息
|
|
let achievementsHtml = '';
|
|
if (record.unlocked_achievements && record.unlocked_achievements.length > 0) {
|
|
achievementsHtml = record.unlocked_achievements.map(achievement =>
|
|
`<span class="badge bg-success me-1">${achievement}</span>`
|
|
).join('');
|
|
} else {
|
|
achievementsHtml = '<span class="text-muted">无</span>';
|
|
}
|
|
|
|
row.innerHTML = `
|
|
<td>${record.draw_time}</td>
|
|
<td>${record.user_id}</td>
|
|
<td>${record.shikigami_name}</td>
|
|
<td><span class="badge rarity-${record.rarity.toLowerCase()}">${record.rarity}</span></td>
|
|
<td>${achievementsHtml}</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
// 更新分页控件
|
|
updatePagination();
|
|
}
|
|
|
|
// 更新分页控件
|
|
function updatePagination() {
|
|
const totalPages = Math.ceil(currentRecords.length / RECORDS_PER_PAGE);
|
|
const pagination = document.getElementById('recordsPagination');
|
|
pagination.innerHTML = '';
|
|
|
|
if (totalPages <= 1) {
|
|
return;
|
|
}
|
|
|
|
// 上一页按钮
|
|
const prevLi = document.createElement('li');
|
|
prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
|
|
prevLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(${currentPage - 1})">上一页</a>`;
|
|
pagination.appendChild(prevLi);
|
|
|
|
// 页码按钮
|
|
const maxVisiblePages = 5;
|
|
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
|
|
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
|
|
|
|
if (endPage - startPage + 1 < maxVisiblePages) {
|
|
startPage = Math.max(1, endPage - maxVisiblePages + 1);
|
|
}
|
|
|
|
if (startPage > 1) {
|
|
const firstLi = document.createElement('li');
|
|
firstLi.className = 'page-item';
|
|
firstLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(1)">1</a>`;
|
|
pagination.appendChild(firstLi);
|
|
|
|
if (startPage > 2) {
|
|
const ellipsisLi = document.createElement('li');
|
|
ellipsisLi.className = 'page-item disabled';
|
|
ellipsisLi.innerHTML = `<a class="page-link" href="#">...</a>`;
|
|
pagination.appendChild(ellipsisLi);
|
|
}
|
|
}
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
const pageLi = document.createElement('li');
|
|
pageLi.className = `page-item ${i === currentPage ? 'active' : ''}`;
|
|
pageLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(${i})">${i}</a>`;
|
|
pagination.appendChild(pageLi);
|
|
}
|
|
|
|
if (endPage < totalPages) {
|
|
if (endPage < totalPages - 1) {
|
|
const ellipsisLi = document.createElement('li');
|
|
ellipsisLi.className = 'page-item disabled';
|
|
ellipsisLi.innerHTML = `<a class="page-link" href="#">...</a>`;
|
|
pagination.appendChild(ellipsisLi);
|
|
}
|
|
|
|
const lastLi = document.createElement('li');
|
|
lastLi.className = 'page-item';
|
|
lastLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(${totalPages})">${totalPages}</a>`;
|
|
pagination.appendChild(lastLi);
|
|
}
|
|
|
|
// 下一页按钮
|
|
const nextLi = document.createElement('li');
|
|
nextLi.className = `page-item ${currentPage === totalPages ? 'disabled' : ''}`;
|
|
nextLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(${currentPage + 1})">下一页</a>`;
|
|
pagination.appendChild(nextLi);
|
|
}
|
|
|
|
// 切换页面
|
|
function changePage(page) {
|
|
const totalPages = Math.ceil(currentRecords.length / RECORDS_PER_PAGE);
|
|
if (page < 1 || page > totalPages) {
|
|
return;
|
|
}
|
|
currentPage = page;
|
|
displayRecords();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |