468 lines
22 KiB
PHP
468 lines
22 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../db/config.php';
|
|
require_once __DIR__ . '/../includes/lang.php';
|
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
|
|
|
// Force simplified Chinese for admin
|
|
$lang = 'zh';
|
|
// $_SESSION['lang'] = 'zh'; // Do not persist to session to avoid affecting front-end default language
|
|
|
|
// Admin check
|
|
$admin = null;
|
|
if (isset($_SESSION['admin_id'])) {
|
|
$stmt = db()->prepare("SELECT * FROM admins WHERE id = ?");
|
|
$stmt->execute([$_SESSION['admin_id']]);
|
|
$admin = $stmt->fetch();
|
|
}
|
|
|
|
if (!$admin) {
|
|
header('Location: /admin/login.php');
|
|
exit;
|
|
}
|
|
|
|
// Helper to check permissions
|
|
if (!function_exists('hasPermission')) {
|
|
function hasPermission($p) {
|
|
global $admin;
|
|
if (!$admin['is_agent']) return true; // Super admin has all permissions
|
|
$perms = json_decode($admin['permissions'] ?? '[]', true);
|
|
return in_array($p, $perms);
|
|
}
|
|
}
|
|
|
|
function renderAdminPage($content, $title = '后台管理') {
|
|
global $admin, $lang;
|
|
$current_page = basename($_SERVER['PHP_SELF']);
|
|
|
|
$site_logo = getSetting('site_logo', '/assets/images/logo.png');
|
|
$site_favicon = getSetting('site_favicon', $site_logo);
|
|
$site_name = getSetting('site_name', 'Byro');
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="<?= $lang ?>">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title><?= $title ?> - <?= $site_name ?></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.11.1/font/bootstrap-icons.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<?php if ($site_favicon): ?>
|
|
<link rel="icon" href="<?= $site_favicon ?>">
|
|
<?php endif; ?>
|
|
<style>
|
|
:root {
|
|
--sidebar-width: 240px;
|
|
--header-height: 60px;
|
|
--bg-color: #f8f9fa;
|
|
--text-color: #333;
|
|
--border-color: #dee2e6;
|
|
}
|
|
body {
|
|
background-color: var(--bg-color);
|
|
color: var(--text-color);
|
|
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
}
|
|
.admin-sidebar {
|
|
width: var(--sidebar-width);
|
|
height: 100vh;
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
background: #fff;
|
|
border-right: 1px solid var(--border-color);
|
|
z-index: 1000;
|
|
overflow-y: auto;
|
|
}
|
|
.admin-header {
|
|
height: var(--header-height);
|
|
background: #fff;
|
|
border-bottom: 1px solid var(--border-color);
|
|
position: fixed;
|
|
left: var(--sidebar-width);
|
|
right: 0;
|
|
top: 0;
|
|
z-index: 999;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 24px;
|
|
justify-content: space-between;
|
|
}
|
|
.admin-main {
|
|
margin-left: var(--sidebar-width);
|
|
margin-top: var(--header-height);
|
|
padding: 24px;
|
|
}
|
|
.sidebar-logo {
|
|
height: var(--header-height);
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 20px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
font-weight: bold;
|
|
font-size: 20px;
|
|
color: #0062ff;
|
|
background: #fff;
|
|
}
|
|
.admin-nav {
|
|
padding-bottom: 40px;
|
|
}
|
|
.nav-link {
|
|
padding: 12px 20px;
|
|
color: #555;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
text-decoration: none;
|
|
border-bottom: 1px solid #f1f1f1;
|
|
}
|
|
.nav-link:hover, .nav-link.active {
|
|
background: #f0f7ff;
|
|
color: #0062ff;
|
|
}
|
|
.nav-link i {
|
|
font-size: 18px;
|
|
}
|
|
.card {
|
|
border: none;
|
|
border-radius: 8px;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
}
|
|
.table-container {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="admin-sidebar">
|
|
<div class="sidebar-logo">
|
|
<?php if ($site_logo): ?>
|
|
<img src="<?= $site_logo ?>?v=<?= time() ?>" height="30" class="me-2">
|
|
<?php else: ?>
|
|
<i class="bi bi-shield-lock-fill me-2"></i>
|
|
<?php endif; ?>
|
|
<?= $admin['is_agent'] ? __('agent_panel') : __('admin_panel') ?>
|
|
</div>
|
|
<div class="admin-nav">
|
|
<!-- 1. 平台首页 -->
|
|
<a href="/admin/index.php" class="nav-link <?= $current_page == 'index.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-speedometer2"></i> <?= __('platform_home') ?>
|
|
</a>
|
|
|
|
<!-- 2. 用户管理 -->
|
|
<?php if (hasPermission('manage_users')): ?>
|
|
<a href="/admin/users.php" class="nav-link <?= $current_page == 'users.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-people"></i> <?= __('users') ?>
|
|
<span class="badge bg-success rounded-pill ms-auto d-none" id="users-badge">0</span>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<!-- 3. 代理管理 -->
|
|
<?php if (!$admin['is_agent']): ?>
|
|
<a href="/admin/agents.php" class="nav-link <?= $current_page == 'agents.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-person-badge"></i> <?= __('agents') ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<!-- 4. 实名认证 -->
|
|
<?php if (hasPermission('manage_kyc')): ?>
|
|
<a href="/admin/kyc.php" class="nav-link <?= $current_page == 'kyc.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-person-vcard"></i> <?= __('real_name') ?>
|
|
<span class="badge bg-danger rounded-pill ms-auto d-none" id="kyc-badge">0</span>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<!-- 5. 充提管理 -->
|
|
<?php if (hasPermission('audit_finance')): ?>
|
|
<a href="/admin/finance.php" class="nav-link <?= $current_page == 'finance.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-wallet2"></i> <?= __('finance_management') ?>
|
|
<span class="badge bg-danger rounded-pill ms-auto d-none" id="finance-badge">0</span>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<!-- 6. 财务明细 -->
|
|
<?php if (hasPermission('view_orders')): ?>
|
|
<a href="/admin/transactions.php" class="nav-link <?= $current_page == 'transactions.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-list-ul"></i> <?= __('finance_details') ?>
|
|
</a>
|
|
|
|
<!-- 7. 秒合约管理 -->
|
|
<a href="/admin/binary.php" class="nav-link <?= $current_page == 'binary.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-clock"></i> <?= __('sec_contract_management') ?>
|
|
<span class="badge bg-info rounded-pill ms-auto d-none" id="binary-badge">0</span>
|
|
</a>
|
|
|
|
<!-- 8. 现货管理 -->
|
|
<a href="/admin/spot.php" class="nav-link <?= $current_page == 'spot.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-currency-exchange"></i> <?= __('spot_trading') ?>
|
|
<span class="badge bg-info rounded-pill ms-auto d-none" id="spot-badge">0</span>
|
|
</a>
|
|
|
|
<!-- 9. 合约管理 -->
|
|
<a href="/admin/contract.php" class="nav-link <?= $current_page == 'contract.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-layers"></i> <?= __('contract_trading') ?>
|
|
<span class="badge bg-info rounded-pill ms-auto d-none" id="contract-badge">0</span>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<!-- 10. 兑换管理 -->
|
|
<?php if (!$admin['is_agent']): ?>
|
|
<a href="/admin/exchange.php" class="nav-link <?= $current_page == 'exchange.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-arrow-left-right"></i> <?= __('exchange_management') ?>
|
|
</a>
|
|
|
|
<!-- 11. 挖矿管理 -->
|
|
<a href="/admin/mining.php" class="nav-link <?= $current_page == 'mining.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-cpu"></i> <?= __('mining_management') ?>
|
|
</a>
|
|
|
|
<!-- 12. AI控盘 -->
|
|
<a href="/admin/ai_control.php" class="nav-link <?= $current_page == 'ai_control.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-robot"></i> <?= __('ai_control') ?>
|
|
</a>
|
|
|
|
<!-- 13. 在线客服 -->
|
|
<a href="/admin/customer_service.php" class="nav-link <?= $current_page == 'customer_service.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-headset"></i> <?= __('online_support') ?>
|
|
<span class="badge bg-warning rounded-pill ms-auto d-none" id="messages-badge">0</span>
|
|
</a>
|
|
|
|
<!-- 14. 后台设置 -->
|
|
<a href="/admin/backend_settings.php" class="nav-link <?= $current_page == 'backend_settings.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-gear"></i> <?= __('backend_settings') ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<!-- 15. 个人设置 -->
|
|
<a href="/admin/profile.php" class="nav-link <?= $current_page == 'profile.php' ? 'active' : '' ?>">
|
|
<i class="bi bi-person-gear"></i> <?= __('personal_settings') ?>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="admin-header">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<button onclick="location.reload()" class="btn btn-sm btn-light border">
|
|
<i class="bi bi-arrow-clockwise"></i> 刷新
|
|
</button>
|
|
<h5 class="mb-0"><?= $title ?></h5>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-3">
|
|
<div class="text-muted small">欢迎您, <?= htmlspecialchars($admin['username']) ?></div>
|
|
<div class="position-relative me-2">
|
|
<i class="bi bi-bell fs-5"></i>
|
|
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger d-none" id="total-badge">
|
|
0
|
|
</span>
|
|
</div>
|
|
<a href="/" class="btn btn-sm btn-outline-primary">返回首页</a>
|
|
<a href="/auth/logout.php" class="btn btn-sm btn-outline-danger">登出</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="admin-main">
|
|
<?= $content ?>
|
|
</div>
|
|
<script>
|
|
// Handle dismissible cards and badge clearing
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const dismissedCards = JSON.parse(localStorage.getItem('dismissed_admin_cards') || '[]');
|
|
const visitedPages = JSON.parse(localStorage.getItem('visited_admin_pages') || '[]');
|
|
const currentPage = window.location.pathname;
|
|
|
|
// Clear badges based on current page
|
|
if (currentPage.includes('finance.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=finance');
|
|
} else if (currentPage.includes('kyc.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=kyc');
|
|
} else if (currentPage.includes('binary.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=binary');
|
|
} else if (currentPage.includes('contract.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=contract');
|
|
} else if (currentPage.includes('spot.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=spot');
|
|
} else if (currentPage.includes('customer_service.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=messages');
|
|
} else if (currentPage.includes('users.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=users');
|
|
}
|
|
|
|
document.querySelectorAll('.card-dismissible').forEach(card => {
|
|
const cardId = card.getAttribute('data-card-id') || currentPage;
|
|
|
|
// Auto-hide if already dismissed OR if page was visited once and it's marked as auto-dismiss
|
|
if (dismissedCards.includes(cardId) || (visitedPages.includes(currentPage) && card.classList.contains('card-auto-dismiss'))) {
|
|
card.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
const closeBtn = document.createElement('button');
|
|
closeBtn.className = 'btn-close position-absolute top-0 end-0 m-2';
|
|
closeBtn.style.zIndex = '10';
|
|
closeBtn.onclick = function() {
|
|
card.style.display = 'none';
|
|
if (!dismissedCards.includes(cardId)) {
|
|
dismissedCards.push(cardId);
|
|
localStorage.setItem('dismissed_admin_cards', JSON.stringify(dismissedCards));
|
|
}
|
|
};
|
|
card.style.position = 'relative';
|
|
card.appendChild(closeBtn);
|
|
});
|
|
|
|
// Mark current page as visited
|
|
if (!visitedPages.includes(currentPage)) {
|
|
visitedPages.push(currentPage);
|
|
localStorage.setItem('visited_admin_pages', JSON.stringify(visitedPages));
|
|
}
|
|
});
|
|
|
|
let lastTotal = 0;
|
|
let lastSoundTotal = parseInt(sessionStorage.getItem('last_sound_total') || '-1');
|
|
|
|
function speak(text) {
|
|
if ('speechSynthesis' in window) {
|
|
const utterance = new SpeechSynthesisUtterance(text);
|
|
utterance.lang = 'zh-CN';
|
|
window.speechSynthesis.speak(utterance);
|
|
}
|
|
}
|
|
|
|
function checkNotifications() {
|
|
const currentPage = window.location.pathname;
|
|
fetch('/api/admin_notifications.php')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
const counts = data.counts;
|
|
|
|
// Auto-clear current page types
|
|
if (currentPage.includes('finance.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=finance');
|
|
counts.recharge = 0;
|
|
counts.withdrawal = 0;
|
|
} else if (currentPage.includes('customer_service.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=messages');
|
|
counts.messages = 0;
|
|
} else if (currentPage.includes('users.php')) {
|
|
fetch('/api/admin_notifications.php?action=clear&type=users');
|
|
counts.users = 0;
|
|
}
|
|
// ... other pages can be added here
|
|
|
|
const total = counts.recharge + counts.withdrawal + counts.kyc + counts.binary + counts.contract + counts.spot + counts.messages + counts.users;
|
|
const soundTotal = counts.sound_total || 0;
|
|
|
|
// Finance badge
|
|
const financeBadge = document.getElementById('finance-badge');
|
|
if (financeBadge) {
|
|
const fCount = (counts.recharge || 0) + (counts.withdrawal || 0);
|
|
if (fCount > 0) {
|
|
financeBadge.innerText = fCount;
|
|
financeBadge.classList.remove('d-none');
|
|
} else {
|
|
financeBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// KYC badge
|
|
const kycBadge = document.getElementById('kyc-badge');
|
|
if (kycBadge) {
|
|
if (counts.kyc > 0) {
|
|
kycBadge.innerText = counts.kyc;
|
|
kycBadge.classList.remove('d-none');
|
|
} else {
|
|
kycBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// Binary badge
|
|
const binaryBadge = document.getElementById('binary-badge');
|
|
if (binaryBadge) {
|
|
if (counts.binary > 0) {
|
|
binaryBadge.innerText = counts.binary;
|
|
binaryBadge.classList.remove('d-none');
|
|
} else {
|
|
binaryBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// Contract badge
|
|
const contractBadge = document.getElementById('contract-badge');
|
|
if (contractBadge) {
|
|
if (counts.contract > 0) {
|
|
contractBadge.innerText = counts.contract;
|
|
contractBadge.classList.remove('d-none');
|
|
} else {
|
|
contractBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// Spot badge
|
|
const spotBadge = document.getElementById('spot-badge');
|
|
if (spotBadge) {
|
|
if (counts.spot > 0) {
|
|
spotBadge.innerText = counts.spot;
|
|
spotBadge.classList.remove('d-none');
|
|
} else {
|
|
spotBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// Users badge
|
|
const usersBadge = document.getElementById('users-badge');
|
|
if (usersBadge) {
|
|
if (counts.users > 0) {
|
|
usersBadge.innerText = counts.users;
|
|
usersBadge.classList.remove('d-none');
|
|
} else {
|
|
usersBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// Messages badge
|
|
const messagesBadge = document.getElementById('messages-badge');
|
|
if (messagesBadge) {
|
|
if (counts.messages > 0) {
|
|
messagesBadge.innerText = counts.messages;
|
|
messagesBadge.classList.remove('d-none');
|
|
} else {
|
|
messagesBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
// Total badge
|
|
const totalBadge = document.getElementById('total-badge');
|
|
if (totalBadge) {
|
|
if (total > 0) {
|
|
totalBadge.innerText = total;
|
|
totalBadge.classList.remove('d-none');
|
|
} else {
|
|
totalBadge.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
if (lastSoundTotal !== -1 && soundTotal > lastSoundTotal) {
|
|
speak("你有新的消息,请注意查收");
|
|
}
|
|
lastTotal = total;
|
|
lastSoundTotal = soundTotal;
|
|
sessionStorage.setItem('last_sound_total', lastSoundTotal);
|
|
}
|
|
})
|
|
.catch(e => console.error('Notification check failed:', e));
|
|
}
|
|
|
|
// Check every 10 seconds
|
|
setInterval(checkNotifications, 10000);
|
|
checkNotifications();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
}
|
|
?>
|