2/19
This commit is contained in:
parent
32be9227d1
commit
301a100f16
@ -135,24 +135,37 @@ ob_start();
|
||||
<input type="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">权限设置</label>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="form-label mb-0">权限设置</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="manage_users" id="p_users" checked>
|
||||
<label class="form-check-label" for="p_users">管理用户</label>
|
||||
<input class="form-check-input" type="checkbox" id="selectAllAdd">
|
||||
<label class="form-check-label small" for="selectAllAdd">全选</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="view_orders" id="p_orders" checked>
|
||||
<label class="form-check-label" for="p_orders">查看订单</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="audit_finance" id="p_finance" checked>
|
||||
<label class="form-check-label" for="p_finance">财务审核</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="manage_kyc" id="p_kyc">
|
||||
<label class="form-check-label" for="p_kyc">实名审核</label>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 p-3 bg-light rounded border" id="add_permissions_container">
|
||||
<?php
|
||||
$available_perms = [
|
||||
'manage_users' => '用户管理',
|
||||
'audit_finance' => '财务管理',
|
||||
'manage_kyc' => '实名认证',
|
||||
'view_orders' => '订单查看',
|
||||
'manage_spot' => '币币交易',
|
||||
'manage_contract' => '合约交易',
|
||||
'manage_mining' => '矿机管理',
|
||||
'manage_exchange' => '兑换管理',
|
||||
'manage_binary' => '秒合约',
|
||||
'manage_ai' => 'AI控制',
|
||||
'manage_chat' => '客服系统',
|
||||
'manage_settings' => '系统设置',
|
||||
'view_stats' => '数据统计'
|
||||
];
|
||||
foreach ($available_perms as $val => $label):
|
||||
?>
|
||||
<div class="form-check" style="min-width: 100px;">
|
||||
<input class="form-check-input add-p-checkbox" type="checkbox" name="permissions[]" value="<?= $val ?>" id="p_<?= $val ?>">
|
||||
<label class="form-check-label" for="p_<?= $val ?>"><?= $label ?></label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -184,24 +197,20 @@ ob_start();
|
||||
<input type="text" name="password" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">权限设置</label>
|
||||
<div class="d-flex flex-wrap gap-2" id="edit_permissions_container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="form-label mb-0">权限设置</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input p-checkbox" type="checkbox" name="permissions[]" value="manage_users" id="ep_users">
|
||||
<label class="form-check-label" for="ep_users">管理用户</label>
|
||||
<input class="form-check-input" type="checkbox" id="selectAllEdit">
|
||||
<label class="form-check-label small" for="selectAllEdit">全选</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input p-checkbox" type="checkbox" name="permissions[]" value="view_orders" id="ep_orders">
|
||||
<label class="form-check-label" for="ep_orders">查看订单</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input p-checkbox" type="checkbox" name="permissions[]" value="audit_finance" id="ep_finance">
|
||||
<label class="form-check-label" for="ep_finance">财务审核</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input p-checkbox" type="checkbox" name="permissions[]" value="manage_kyc" id="ep_kyc">
|
||||
<label class="form-check-label" for="ep_kyc">实名审核</label>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 p-3 bg-light rounded border" id="edit_permissions_container">
|
||||
<?php foreach ($available_perms as $val => $label): ?>
|
||||
<div class="form-check" style="min-width: 100px;">
|
||||
<input class="form-check-input edit-p-checkbox" type="checkbox" name="permissions[]" value="<?= $val ?>" id="ep_<?= $val ?>">
|
||||
<label class="form-check-label" for="ep_<?= $val ?>"><?= $label ?></label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -220,20 +229,34 @@ function editAgent(agent) {
|
||||
document.getElementById('edit_username').value = agent.username;
|
||||
|
||||
// Clear checkboxes
|
||||
document.querySelectorAll('.p-checkbox').forEach(cb => cb.checked = false);
|
||||
document.querySelectorAll('.edit-p-checkbox').forEach(cb => cb.checked = false);
|
||||
document.getElementById('selectAllEdit').checked = false;
|
||||
|
||||
// Set checkboxes
|
||||
if (agent.permissions) {
|
||||
const perms = JSON.parse(agent.permissions);
|
||||
perms.forEach(p => {
|
||||
const cb = document.querySelector(`.p-checkbox[value="${p}"]`);
|
||||
const cb = document.querySelector(`.edit-p-checkbox[value="${p}"]`);
|
||||
if (cb) cb.checked = true;
|
||||
});
|
||||
|
||||
// Check if all are checked
|
||||
const allChecked = Array.from(document.querySelectorAll('.edit-p-checkbox')).every(cb => cb.checked);
|
||||
document.getElementById('selectAllEdit').checked = allChecked;
|
||||
}
|
||||
|
||||
var modal = new bootstrap.Modal(document.getElementById('editAgentModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// Select All logic
|
||||
document.getElementById('selectAllAdd').addEventListener('change', function() {
|
||||
document.querySelectorAll('.add-p-checkbox').forEach(cb => cb.checked = this.checked);
|
||||
});
|
||||
|
||||
document.getElementById('selectAllEdit').addEventListener('change', function() {
|
||||
document.querySelectorAll('.edit-p-checkbox').forEach(cb => cb.checked = this.checked);
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
|
||||
@ -15,7 +15,6 @@ function getLocalSetting($key, $default = null) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$settings = [
|
||||
'email_verification_enabled' => $_POST['email_verification_enabled'] ?? '0',
|
||||
'site_logo' => $_POST['site_logo'] ?? '',
|
||||
'usdt_trc20_address' => $_POST['usdt_trc20_address'] ?? '',
|
||||
'usdt_erc20_address' => $_POST['usdt_erc20_address'] ?? '',
|
||||
'usdt_bep20_address' => $_POST['usdt_bep20_address'] ?? '',
|
||||
@ -42,12 +41,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$stmt->execute([$logo_path, $logo_path]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle favicon upload if provided
|
||||
if (isset($_FILES['favicon_file']) && $_FILES['favicon_file']['error'] === 0) {
|
||||
$ext = pathinfo($_FILES['favicon_file']['name'], PATHINFO_EXTENSION);
|
||||
$filename = 'favicon_' . time() . '.' . $ext;
|
||||
$target = __DIR__ . '/../assets/images/' . $filename;
|
||||
if (!is_dir(__DIR__ . '/../assets/images/')) {
|
||||
mkdir(__DIR__ . '/../assets/images/', 0777, true);
|
||||
}
|
||||
if (move_uploaded_file($_FILES['favicon_file']['tmp_name'], $target)) {
|
||||
$favicon_path = '/assets/images/' . $filename;
|
||||
$stmt = db()->prepare("INSERT INTO system_settings (setting_key, setting_value) VALUES ('site_favicon', ?) ON DUPLICATE KEY UPDATE setting_value = ?");
|
||||
$stmt->execute([$favicon_path, $favicon_path]);
|
||||
}
|
||||
}
|
||||
|
||||
$success = true;
|
||||
}
|
||||
|
||||
$email_verify = getLocalSetting('email_verification_enabled', '0');
|
||||
$site_logo = getLocalSetting('site_logo', '');
|
||||
$site_favicon = getLocalSetting('site_favicon', '');
|
||||
$trc20_addr = getLocalSetting('usdt_trc20_address', '');
|
||||
$erc20_addr = getLocalSetting('usdt_erc20_address', '');
|
||||
$bep20_addr = getLocalSetting('usdt_bep20_address', '');
|
||||
@ -82,7 +97,7 @@ ob_start();
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-bold">前端 LOGO / 网站图标</label>
|
||||
<label class="form-label fw-bold">网站 LOGO</label>
|
||||
<div class="d-flex align-items-center gap-3 mb-2">
|
||||
<?php if ($site_logo): ?>
|
||||
<div class="bg-dark p-2 rounded">
|
||||
@ -91,7 +106,20 @@ ob_start();
|
||||
<?php endif; ?>
|
||||
<input type="file" name="logo_file" class="form-control">
|
||||
</div>
|
||||
<div class="form-text">上传后将同步更新网站 LOGO 和 Favicon 浏览器图标。建议使用透明 PNG。</div>
|
||||
<div class="form-text">上传后将同步更新网站所有页面的 LOGO。建议使用透明 PNG。</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-bold">网站图标 (Favicon)</label>
|
||||
<div class="d-flex align-items-center gap-3 mb-2">
|
||||
<?php if ($site_favicon): ?>
|
||||
<div class="bg-white p-2 rounded border">
|
||||
<img src="<?= $site_favicon ?>" height="32" width="32" class="d-block">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="favicon_file" class="form-control">
|
||||
</div>
|
||||
<div class="form-text">上传后将同步更新浏览器标签页图标。建议使用 32x32 或 64x64 的正方形图片。</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/layout.php';
|
||||
|
||||
// Check if admin
|
||||
if (!isset($_SESSION['admin_id'])) {
|
||||
@ -8,78 +9,164 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// In a real app, check for admin role here
|
||||
ob_start();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Byro Admin | 客服系统</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">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f0f2f5; font-family: 'Noto Sans SC', sans-serif; height: 100vh; margin: 0; }
|
||||
.sidebar { width: 350px; background: #fff; border-right: 1px solid #ddd; display: flex; flex-direction: column; }
|
||||
.main-chat { flex: 1; display: flex; flex-direction: column; background: #fff; }
|
||||
.user-list { flex: 1; overflow-y: auto; }
|
||||
.user-card { padding: 15px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: all 0.2s; }
|
||||
.user-card:hover { background: #f8f9fa; }
|
||||
.user-card.active { background: #e7f3ff; border-left: 5px solid #007bff; }
|
||||
.chat-header { padding: 15px 25px; border-bottom: 1px solid #ddd; background: #fff; z-index: 10; }
|
||||
.messages-area { flex: 1; padding: 25px; background: #f9f9f9; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; }
|
||||
.msg { max-width: 75%; padding: 10px 15px; border-radius: 18px; font-size: 14px; line-height: 1.5; }
|
||||
.msg-admin { align-self: flex-end; background: #007bff; color: #fff; border-bottom-right-radius: 2px; }
|
||||
.msg-user { align-self: flex-start; background: #fff; color: #333; border: 1px solid #eee; border-bottom-left-radius: 2px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
||||
.chat-input-area { padding: 20px; border-top: 1px solid #ddd; background: #fff; }
|
||||
.status-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 5px; }
|
||||
.status-online { background: #28a745; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="d-flex">
|
||||
<style>
|
||||
.chat-container {
|
||||
display: flex;
|
||||
height: calc(100vh - 120px);
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.05);
|
||||
}
|
||||
.user-sidebar {
|
||||
width: 300px;
|
||||
border-right: 1px solid #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.main-chat {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
}
|
||||
.user-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.user-card {
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid #f8f9fa;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.user-card:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
.user-card.active {
|
||||
background: #e7f3ff;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
.chat-header {
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #fff;
|
||||
}
|
||||
.messages-area {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background: #fdfdfd;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.msg {
|
||||
max-width: 80%;
|
||||
padding: 8px 14px;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
position: relative;
|
||||
}
|
||||
.msg-admin {
|
||||
align-self: flex-end;
|
||||
background: #007bff;
|
||||
color: #fff;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
.msg-user {
|
||||
align-self: flex-start;
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.chat-input-area {
|
||||
padding: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.remark-area {
|
||||
width: 250px;
|
||||
border-left: 1px solid #eee;
|
||||
padding: 15px;
|
||||
background: #fcfcfc;
|
||||
}
|
||||
.status-online {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #28a745;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
#remark-text {
|
||||
height: 150px;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sidebar">
|
||||
<div class="p-3 bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="/admin/index.php" class="text-white"><i class="bi bi-arrow-left"></i></a>
|
||||
<h5 class="m-0 fw-bold">客服中心</h5>
|
||||
<div class="chat-container">
|
||||
<!-- User List Sidebar -->
|
||||
<div class="user-sidebar">
|
||||
<div class="p-3 border-bottom bg-light">
|
||||
<input type="text" id="user-search" class="form-control form-control-sm" placeholder="搜索用户/IP...">
|
||||
</div>
|
||||
<div class="user-list" id="user-list">
|
||||
<!-- User cards filled by JS -->
|
||||
</div>
|
||||
<span class="badge bg-light text-primary">Live</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<input type="text" class="form-control form-control-sm" placeholder="搜索用户 UID / IP">
|
||||
</div>
|
||||
<div class="user-list" id="user-list">
|
||||
<!-- User cards -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-chat">
|
||||
<div class="chat-header" id="chat-header" style="display: none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="m-0 fw-bold" id="header-name">用户名称</h5>
|
||||
<div class="small text-muted" id="header-meta">UID: --- | IP: ---</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="status-dot status-online"></span>
|
||||
<span class="small text-success fw-bold">正在对话</span>
|
||||
<!-- Main Chat Window -->
|
||||
<div class="main-chat">
|
||||
<div class="chat-header" id="chat-header" style="display: none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="m-0 fw-bold" id="header-name">用户名称</h6>
|
||||
<div class="small" id="header-meta"><span class="text-muted">IP:</span> <span class="text-primary fw-bold" id="info-ip-header">---</span></div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="status-online"></span>
|
||||
<span class="small text-success">在线</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="messages-area" id="messages-area">
|
||||
<div class="m-auto text-center text-muted">
|
||||
<i class="bi bi-chat-left-dots fs-1 d-block mb-3"></i>
|
||||
<p>从左侧选择一个会话开始实时对话</p>
|
||||
<div class="messages-area" id="messages-area">
|
||||
<div class="m-auto text-center text-muted">
|
||||
<i class="bi bi-chat-left-dots fs-1 d-block mb-2"></i>
|
||||
<p>请从左侧选择一个会话</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-input-area" id="input-area" style="display: none;">
|
||||
<form id="chat-form" class="d-flex gap-2 align-items-center">
|
||||
<input type="file" id="image-input" style="display: none;" accept="image/*">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm rounded-circle" id="plus-btn" style="width: 32px; height: 32px; flex-shrink: 0;">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
<input type="text" id="msg-input" class="form-control" placeholder="输入回复内容..." autocomplete="off">
|
||||
<button type="submit" class="btn btn-primary btn-sm px-3">发送</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-input-area" id="input-area" style="display: none;">
|
||||
<form id="chat-form" class="d-flex gap-2">
|
||||
<input type="text" id="msg-input" class="form-control" placeholder="输入消息回复用户..." autocomplete="off">
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">发送回复</button>
|
||||
</form>
|
||||
<!-- Remark Area -->
|
||||
<div class="remark-area" id="remark-area" style="display: none;">
|
||||
<h6 class="fw-bold mb-3 mt-1"><i class="bi bi-pencil-square me-1"></i> 用户备注</h6>
|
||||
<div class="mb-3">
|
||||
<label class="small text-muted mb-1">当前用户备注信息</label>
|
||||
<textarea id="remark-text" class="form-control" placeholder="在此输入对该用户的备注..."></textarea>
|
||||
</div>
|
||||
<button id="save-remark-btn" class="btn btn-dark btn-sm w-100 fw-bold">保存备注</button>
|
||||
|
||||
<hr>
|
||||
<div class="user-info-box small">
|
||||
<div class="mb-2"><strong>UID:</strong> <span id="info-uid">---</span></div>
|
||||
<div class="mb-2"><strong>当前IP:</strong> <span id="info-ip">---</span></div>
|
||||
<div class="mb-2"><strong>最近活跃:</strong> <span id="info-time">---</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -92,20 +179,31 @@ async function refreshUsers() {
|
||||
const r = await fetch('/api/chat.php?action=admin_get_all');
|
||||
const users = await r.json();
|
||||
const list = document.getElementById('user-list');
|
||||
const search = document.getElementById('user-search').value.toLowerCase();
|
||||
|
||||
let html = '';
|
||||
users.forEach(u => {
|
||||
const isActive = (selectedIp === u.ip_address && selectedUser == u.user_id);
|
||||
const username = u.username || '匿名用户';
|
||||
const uid = u.uid || '---';
|
||||
const ip = u.ip_address;
|
||||
const remark = u.remark || '';
|
||||
|
||||
if (search && !username.toLowerCase().includes(search) && !ip.includes(search) && !uid.toString().includes(search)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isActive = (selectedIp === ip && selectedUser == u.user_id);
|
||||
html += `
|
||||
<div class="user-card ${isActive ? 'active' : ''}" onclick="openChat('${u.user_id}', '${u.ip_address}', '${u.username || '匿名用户'}', '${u.uid || '---'}')">
|
||||
<div class="user-card ${isActive ? 'active' : ''}" onclick="openChat('${u.user_id}', '${ip}', '${username}', '${uid}', '${remark.replace(/'/g, "\\'")}')">
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span class="fw-bold">${u.username || '匿名用户'}</span>
|
||||
<span class="small text-muted">${new Date(u.created_at).toLocaleTimeString()}</span>
|
||||
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span>
|
||||
<span class="text-muted" style="font-size: 10px;">${new Date(u.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
|
||||
</div>
|
||||
<div class="small text-truncate text-muted mb-1">${u.message}</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="badge bg-light text-dark border" style="font-size: 10px;">UID: ${u.uid || '---'}</span>
|
||||
<span class="text-primary" style="font-size: 10px;">${u.ip_address}</span>
|
||||
${remark ? `<div class="small text-danger text-truncate mb-1" style="font-size: 11px;">[备注: ${remark}]</div>` : ''}
|
||||
<div class="small text-truncate text-muted mb-1" style="font-size: 12px;">${u.message}</div>
|
||||
<div class="d-flex justify-content-between align-items-center" style="font-size: 10px;">
|
||||
<span class="text-secondary">UID: ${uid}</span>
|
||||
<span class="text-primary fw-bold">${ip}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -113,15 +211,22 @@ async function refreshUsers() {
|
||||
list.innerHTML = html;
|
||||
}
|
||||
|
||||
function openChat(userId, ip, name, uid) {
|
||||
function openChat(userId, ip, name, uid, remark) {
|
||||
selectedUser = userId;
|
||||
selectedIp = ip;
|
||||
document.getElementById('header-name').innerText = name;
|
||||
document.getElementById('header-meta').innerText = `UID: ${uid} | IP: ${ip}`;
|
||||
document.getElementById('info-ip-header').innerText = ip;
|
||||
document.getElementById('chat-header').style.display = 'block';
|
||||
document.getElementById('input-area').style.display = 'block';
|
||||
document.getElementById('remark-area').style.display = 'block';
|
||||
|
||||
document.getElementById('remark-text').value = remark;
|
||||
document.getElementById('info-uid').innerText = uid;
|
||||
document.getElementById('info-ip').innerText = ip;
|
||||
|
||||
lastMsgId = 0;
|
||||
fetchMessages();
|
||||
refreshUsers(); // Refresh list to update active state
|
||||
}
|
||||
|
||||
async function fetchMessages() {
|
||||
@ -130,7 +235,6 @@ async function fetchMessages() {
|
||||
const msgs = await r.json();
|
||||
const area = document.getElementById('messages-area');
|
||||
|
||||
// Simple filter for the specific conversation
|
||||
const filtered = msgs.filter(m => m.ip_address === selectedIp && (m.user_id == selectedUser || m.user_id == 0));
|
||||
|
||||
if (filtered.length > lastMsgId) {
|
||||
@ -143,9 +247,39 @@ async function fetchMessages() {
|
||||
});
|
||||
area.scrollTop = area.scrollHeight;
|
||||
lastMsgId = filtered.length;
|
||||
|
||||
if (filtered.length > 0) {
|
||||
document.getElementById('info-time').innerText = new Date(filtered[filtered.length-1].created_at).toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('plus-btn').addEventListener('click', () => {
|
||||
document.getElementById('image-input').click();
|
||||
});
|
||||
|
||||
document.getElementById('image-input').addEventListener('change', async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('user_id', selectedUser);
|
||||
formData.append('ip_address', selectedIp);
|
||||
|
||||
const r = await fetch('/api/chat.php?action=upload_image', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const res = await r.json();
|
||||
if (res.success) {
|
||||
fetchMessages();
|
||||
} else {
|
||||
alert('上传失败: ' + res.error);
|
||||
}
|
||||
e.target.value = ''; // Reset
|
||||
});
|
||||
|
||||
document.getElementById('chat-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const input = document.getElementById('msg-input');
|
||||
@ -162,16 +296,29 @@ document.getElementById('chat-form').addEventListener('submit', async (e) => {
|
||||
fetchMessages();
|
||||
});
|
||||
|
||||
// Clear notifications when on this page
|
||||
function clearNotifications() {
|
||||
fetch('/api/admin_notifications.php?action=clear&type=messages');
|
||||
}
|
||||
setInterval(clearNotifications, 5000);
|
||||
clearNotifications();
|
||||
document.getElementById('save-remark-btn').addEventListener('click', async () => {
|
||||
const remark = document.getElementById('remark-text').value;
|
||||
const fd = new URLSearchParams();
|
||||
fd.append('user_id', selectedUser);
|
||||
fd.append('ip_address', selectedIp);
|
||||
fd.append('remark', remark);
|
||||
|
||||
setInterval(refreshUsers, 3000);
|
||||
setInterval(fetchMessages, 1000);
|
||||
const r = await fetch('/api/chat.php?action=save_remark', { method: 'POST', body: fd });
|
||||
const res = await r.json();
|
||||
if (res.success) {
|
||||
alert('备注已保存');
|
||||
refreshUsers();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('user-search').addEventListener('input', refreshUsers);
|
||||
|
||||
setInterval(refreshUsers, 5000);
|
||||
setInterval(fetchMessages, 2000);
|
||||
refreshUsers();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderAdminPage($content, '在线客服');
|
||||
?>
|
||||
|
||||
@ -33,12 +33,19 @@ function renderAdminPage($content, $title = '后台管理') {
|
||||
global $admin, $lang;
|
||||
$current_page = basename($_SERVER['PHP_SELF']);
|
||||
|
||||
$site_logo = '';
|
||||
$site_logo = '/assets/images/logo.png';
|
||||
$site_favicon = '';
|
||||
$site_name = 'Byro';
|
||||
try {
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_logo'");
|
||||
$stmt->execute();
|
||||
$site_logo = $stmt->fetchColumn() ?: '';
|
||||
$val = $stmt->fetchColumn();
|
||||
if ($val) $site_logo = $val;
|
||||
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_favicon'");
|
||||
$stmt->execute();
|
||||
$val = $stmt->fetchColumn();
|
||||
$site_favicon = $val ?: $site_logo;
|
||||
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_name'");
|
||||
$stmt->execute();
|
||||
@ -53,8 +60,8 @@ function renderAdminPage($content, $title = '后台管理') {
|
||||
<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">
|
||||
<?php if ($site_logo): ?>
|
||||
<link rel="icon" href="<?= $site_logo ?>">
|
||||
<?php if ($site_favicon): ?>
|
||||
<link rel="icon" href="<?= $site_favicon ?>">
|
||||
<?php endif; ?>
|
||||
<style>
|
||||
:root {
|
||||
|
||||
@ -10,6 +10,26 @@ if (isset($_SESSION['admin_id'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$site_logo = '/assets/images/logo.png';
|
||||
$site_favicon = '';
|
||||
$site_name = 'Byro Admin';
|
||||
try {
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_logo'");
|
||||
$stmt->execute();
|
||||
$val = $stmt->fetchColumn();
|
||||
if ($val) $site_logo = $val;
|
||||
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_favicon'");
|
||||
$stmt->execute();
|
||||
$val = $stmt->fetchColumn();
|
||||
$site_favicon = $val ?: $site_logo;
|
||||
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = 'site_name'");
|
||||
$stmt->execute();
|
||||
$name = $stmt->fetchColumn();
|
||||
if ($name) $site_name = $name . ' Admin';
|
||||
} catch (Exception $e) {}
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
@ -40,9 +60,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理员登录 - Byro Admin</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">
|
||||
<?php if ($site_favicon): ?>
|
||||
<link rel="icon" href="<?= $site_favicon ?>">
|
||||
<?php endif; ?>
|
||||
<style>
|
||||
body {
|
||||
background: #f4f7f6;
|
||||
@ -78,7 +101,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="logo">
|
||||
<i class="bi bi-shield-lock-fill me-2"></i> 后台管理系统
|
||||
<?php if ($site_logo): ?>
|
||||
<img src="<?= $site_logo ?>" height="40" class="mb-2 d-block mx-auto">
|
||||
<?php else: ?>
|
||||
<i class="bi bi-shield-lock-fill me-2"></i>
|
||||
<?php endif; ?>
|
||||
<div>后台管理系统</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
|
||||
@ -194,7 +194,8 @@ ob_start();
|
||||
<?php
|
||||
$sql = "SELECT u.*,
|
||||
(SELECT available FROM user_balances WHERE user_id = u.id AND symbol = 'USDT') as usdt_balance,
|
||||
(SELECT username FROM admins WHERE id = u.agent_id) as agent_name
|
||||
(SELECT username FROM admins WHERE id = u.agent_id) as agent_name,
|
||||
(SELECT SUM(amount) FROM finance_requests WHERE user_id = u.id AND type='recharge' AND status='approved' AND symbol='USDT') as calculated_recharge
|
||||
FROM users u";
|
||||
$params = [];
|
||||
if ($admin['is_agent']) {
|
||||
@ -210,7 +211,8 @@ ob_start();
|
||||
<tr>
|
||||
<td><code><?= $u['uid'] ?></code></td>
|
||||
<td>
|
||||
<div class="fw-bold"><?= htmlspecialchars($u['username']) ?> <span class="badge bg-warning text-dark" style="font-size: 10px;">VIP <?= $u['vip_level'] ?></span></div>
|
||||
<?php $dynamicVip = getAutoVipLevel($u['calculated_recharge'] ?? 0); ?>
|
||||
<div class="fw-bold"><?= htmlspecialchars($u['username']) ?> <span class="badge bg-warning text-dark" style="font-size: 10px;">VIP <?= $dynamicVip ?></span></div>
|
||||
<div class="text-muted" style="font-size: 11px;"><?= $u['created_at'] ?></div>
|
||||
</td>
|
||||
<?php if (!$admin['is_agent']): ?>
|
||||
@ -382,18 +384,16 @@ ob_start();
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">VIP等级</label>
|
||||
<select name="vip_level" id="edit_vip_level" class="form-control">
|
||||
<label class="form-label">VIP等级 (系统根据充值自动计算)</label>
|
||||
<select name="vip_level" id="edit_vip_level" class="form-control" disabled>
|
||||
<option value="0">VIP 0</option>
|
||||
<option value="1">VIP 1</option>
|
||||
<option value="2">VIP 2</option>
|
||||
<option value="3">VIP 3</option>
|
||||
<option value="4">VIP 4</option>
|
||||
<option value="5">VIP 5</option>
|
||||
<option value="6">VIP 6</option>
|
||||
<option value="7">VIP 7</option>
|
||||
<option value="8">VIP 8</option>
|
||||
</select>
|
||||
<div class="form-text">当前累计充值: <span id="edit_calculated_recharge" class="fw-bold text-primary">0.00</span> USDT</div>
|
||||
</div>
|
||||
<?php if (!$admin['is_agent']): ?>
|
||||
<div class="col-md-6">
|
||||
@ -442,7 +442,19 @@ function editUser(user) {
|
||||
document.getElementById('edit_credit_score').value = user.credit_score;
|
||||
document.getElementById('edit_status').value = user.status;
|
||||
document.getElementById('edit_win_loss').value = user.win_loss_control;
|
||||
document.getElementById('edit_vip_level').value = user.vip_level || 0;
|
||||
|
||||
// Use calculated VIP level for display
|
||||
const recharge = parseFloat(user.calculated_recharge || 0);
|
||||
document.getElementById('edit_calculated_recharge').innerText = recharge.toFixed(2);
|
||||
|
||||
let vip = 0;
|
||||
if (recharge >= 250000) vip = 5;
|
||||
else if (recharge >= 180000) vip = 4;
|
||||
else if (recharge >= 100000) vip = 3;
|
||||
else if (recharge >= 50000) vip = 2;
|
||||
else if (recharge >= 10000) vip = 1;
|
||||
|
||||
document.getElementById('edit_vip_level').value = vip;
|
||||
document.getElementById('edit_remark').value = user.remark || '';
|
||||
if (document.getElementById('edit_agent_id_select')) {
|
||||
document.getElementById('edit_agent_id_select').value = user.agent_id || '';
|
||||
|
||||
88
api/chat.php
88
api/chat.php
@ -28,13 +28,22 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
|
||||
$targetPath = $targetDir . $filename;
|
||||
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
|
||||
$imageUrl = '/assets/images/chat/' . $filename;
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
$message = '<img src="' . $imageUrl . '" class="img-fluid rounded cursor-pointer" onclick="window.open(\'' . $imageUrl . '\')">';
|
||||
$message = '<img src="' . $imageUrl . '" class="img-fluid rounded cursor-pointer" style="max-width: 200px; max-height: 200px;" onclick="window.open(\'' . $imageUrl . '\')">';
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, 'user', $message, $ip]);
|
||||
if (isset($_SESSION['admin_id'])) {
|
||||
$user_id = $_POST['user_id'] ?? 0;
|
||||
$ip = $_POST['ip_address'] ?? '';
|
||||
$sender = 'admin';
|
||||
$admin_id = $_SESSION['admin_id'];
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $admin_id, $sender, $message, $ip]);
|
||||
} else {
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$ip = getRealIP();
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, 'user', $message, $ip]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'url' => $imageUrl]);
|
||||
} else {
|
||||
@ -46,7 +55,7 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
|
||||
if ($action === 'get_messages') {
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$target_user_id = $_GET['user_id'] ?? $user_id;
|
||||
$target_ip = $_GET['ip'] ?? $_SERVER['REMOTE_ADDR'];
|
||||
$target_ip = $_GET['ip'] ?? getRealIP();
|
||||
|
||||
// If admin is requesting, we use the provided user_id and ip
|
||||
if (isset($_SESSION['admin_id'])) {
|
||||
@ -55,7 +64,7 @@ if ($action === 'get_messages') {
|
||||
} else {
|
||||
// User requesting their own messages
|
||||
$stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (user_id = 0 AND ip_address = ?) ORDER BY created_at ASC");
|
||||
$stmt->execute([$user_id, $_SERVER['REMOTE_ADDR']]);
|
||||
$stmt->execute([$user_id, getRealIP()]);
|
||||
}
|
||||
|
||||
$messages = $stmt->fetchAll();
|
||||
@ -68,7 +77,7 @@ if ($action === 'send_message') {
|
||||
if (!$message) exit(json_encode(['success' => false]));
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
$ip = getRealIP();
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, 'user', $message, $ip]);
|
||||
@ -94,9 +103,68 @@ if ($action === 'admin_send') {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'ping') {
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$ip = getRealIP();
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address) VALUES (?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP");
|
||||
$stmt->execute([$user_id, $ip]);
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'admin_get_all') {
|
||||
// Get distinct users/IPs who have messaged
|
||||
$stmt = db()->query("SELECT m.*, u.username, u.uid FROM messages m LEFT JOIN users u ON m.user_id = u.id WHERE m.id IN (SELECT MAX(id) FROM messages GROUP BY user_id, ip_address) ORDER BY created_at DESC");
|
||||
// Get distinct users/IPs who have messaged or pinged
|
||||
// Combine messages and visitors to show even those who haven't messaged yet
|
||||
$stmt = db()->query("
|
||||
SELECT
|
||||
combined.user_id,
|
||||
combined.ip_address,
|
||||
COALESCE(m.message, '用户已进入聊天室') as message,
|
||||
COALESCE(m.created_at, combined.last_activity) as created_at,
|
||||
u.username,
|
||||
u.uid,
|
||||
r.remark
|
||||
FROM (
|
||||
SELECT user_id, ip_address, MAX(created_at) as last_activity FROM messages GROUP BY user_id, ip_address
|
||||
UNION
|
||||
SELECT user_id, ip_address, last_ping as last_activity FROM chat_visitors
|
||||
) combined
|
||||
LEFT JOIN (
|
||||
SELECT user_id, ip_address, message, created_at FROM messages WHERE id IN (SELECT MAX(id) FROM messages GROUP BY user_id, ip_address)
|
||||
) m ON (combined.user_id = m.user_id AND combined.ip_address = m.ip_address)
|
||||
LEFT JOIN users u ON combined.user_id = u.id
|
||||
LEFT JOIN chat_remarks r ON (combined.user_id = r.user_id AND combined.ip_address = r.ip_address)
|
||||
WHERE combined.last_activity > DATE_SUB(NOW(), INTERVAL 24 HOUR)
|
||||
ORDER BY created_at DESC
|
||||
");
|
||||
echo json_encode($stmt->fetchAll());
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'save_remark') {
|
||||
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
|
||||
|
||||
$user_id = $_POST['user_id'] ?? 0;
|
||||
$ip = $_POST['ip_address'] ?? '';
|
||||
$remark = $_POST['remark'] ?? '';
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO chat_remarks (user_id, ip_address, remark) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE remark = ?");
|
||||
$stmt->execute([$user_id, $ip, $remark, $remark]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'get_remark') {
|
||||
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
|
||||
|
||||
$user_id = $_GET['user_id'] ?? 0;
|
||||
$ip = $_GET['ip'] ?? '';
|
||||
|
||||
$stmt = db()->prepare("SELECT remark FROM chat_remarks WHERE user_id = ? AND ip_address = ?");
|
||||
$stmt->execute([$user_id, $ip]);
|
||||
$remark = $stmt->fetchColumn() ?: '';
|
||||
|
||||
echo json_encode(['success' => true, 'remark' => $remark]);
|
||||
exit;
|
||||
}
|
||||
|
||||
BIN
assets/images/carousel/slide1.jpg
Normal file
BIN
assets/images/carousel/slide1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
BIN
assets/images/carousel/slide2.jpg
Normal file
BIN
assets/images/carousel/slide2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
assets/images/carousel/slide3.jpg
Normal file
BIN
assets/images/carousel/slide3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/images/favicon_1771466523.png
Normal file
BIN
assets/images/favicon_1771466523.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 571 KiB |
BIN
assets/pasted-20260219-030240-21fe9e33.png
Normal file
BIN
assets/pasted-20260219-030240-21fe9e33.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
@ -8,7 +8,7 @@ if (!function_exists('getSetting')) {
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?");
|
||||
$stmt->execute([$key]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ? $row['setting_value'] : $default;
|
||||
return ($row && !empty($row['setting_value'])) ? $row['setting_value'] : $default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,3 +15,28 @@ function db() {
|
||||
}
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
function getRealIP() {
|
||||
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
|
||||
return $_SERVER['HTTP_CF_CONNECTING_IP'];
|
||||
}
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
return explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
|
||||
}
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
function getUserTotalRecharge($userId) {
|
||||
$stmt = db()->prepare("SELECT SUM(amount) FROM finance_requests WHERE user_id = ? AND type='recharge' AND status='approved' AND symbol='USDT'");
|
||||
$stmt->execute([$userId]);
|
||||
return (float)$stmt->fetchColumn() ?: 0;
|
||||
}
|
||||
|
||||
function getAutoVipLevel($totalRecharge) {
|
||||
if ($totalRecharge >= 250000) return 5;
|
||||
if ($totalRecharge >= 180000) return 4;
|
||||
if ($totalRecharge >= 100000) return 3;
|
||||
if ($totalRecharge >= 50000) return 2;
|
||||
if ($totalRecharge >= 10000) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
{"USD":1,"AED":3.67,"AFN":63.59,"ALL":81.5,"AMD":377.17,"ANG":1.79,"AOA":919.5,"ARS":1452.25,"AUD":1.41,"AWG":1.79,"AZN":1.7,"BAM":1.65,"BBD":2,"BDT":122.33,"BGN":1.6,"BHD":0.376,"BIF":2969.83,"BMD":1,"BND":1.26,"BOB":6.94,"BRL":5.22,"BSD":1,"BTN":90.69,"BWP":13.48,"BYN":2.85,"BZD":2,"CAD":1.36,"CDF":2291.41,"CHF":0.77,"CLF":0.0218,"CLP":863.28,"CNH":6.9,"CNY":6.91,"COP":3672.12,"CRC":482.41,"CUP":24,"CVE":93.14,"CZK":20.5,"DJF":177.72,"DKK":6.31,"DOP":62.2,"DZD":129.91,"EGP":47.01,"ERN":15,"ETB":155.2,"EUR":0.845,"FJD":2.19,"FKP":0.738,"FOK":6.31,"GBP":0.738,"GEL":2.68,"GGP":0.738,"GHS":11.03,"GIP":0.738,"GMD":74.13,"GNF":8753.13,"GTQ":7.68,"GYD":209.29,"HKD":7.82,"HNL":26.5,"HRK":6.36,"HTG":130.99,"HUF":319.14,"IDR":16842.78,"ILS":3.1,"IMP":0.738,"INR":90.69,"IQD":1309.66,"IRR":1280515.41,"ISK":122.56,"JEP":0.738,"JMD":156.27,"JOD":0.709,"JPY":153.28,"KES":129.03,"KGS":87.44,"KHR":4015.28,"KID":1.41,"KMF":415.55,"KRW":1443.65,"KWD":0.306,"KYD":0.833,"KZT":490.62,"LAK":21622.5,"LBP":89500,"LKR":309.18,"LRD":186.33,"LSL":16.04,"LYD":6.3,"MAD":9.14,"MDL":16.98,"MGA":4361.67,"MKD":52.04,"MMK":2103.54,"MNT":3531.98,"MOP":8.05,"MRU":39.84,"MUR":45.91,"MVR":15.46,"MWK":1743.52,"MXN":17.17,"MYR":3.9,"MZN":63.79,"NAD":16.04,"NGN":1354.91,"NIO":36.86,"NOK":9.53,"NPR":145.11,"NZD":1.66,"OMR":0.384,"PAB":1,"PEN":3.35,"PGK":4.3,"PHP":57.89,"PKR":279.89,"PLN":3.56,"PYG":6529.25,"QAR":3.64,"RON":4.31,"RSD":99.11,"RUB":76.99,"RWF":1461.96,"SAR":3.75,"SBD":7.93,"SCR":14.48,"SDG":449.19,"SEK":8.98,"SGD":1.26,"SHP":0.738,"SLE":24.62,"SLL":24619.11,"SOS":570.39,"SRD":37.7,"SSP":4590.66,"STN":20.69,"SYP":112.45,"SZL":16.04,"THB":31.24,"TJS":9.39,"TMT":3.5,"TND":2.86,"TOP":2.36,"TRY":43.72,"TTD":6.8,"TVD":1.41,"TWD":31.38,"TZS":2581.08,"UAH":43.3,"UGX":3516.59,"UYU":38.86,"UZS":12158.3,"VES":396.37,"VND":25898.69,"VUV":118.27,"WST":2.67,"XAF":554.06,"XCD":2.7,"XCG":1.79,"XDR":0.726,"XOF":554.06,"XPF":100.8,"YER":238.68,"ZAR":16.04,"ZMW":18.49,"ZWG":25.59,"ZWL":25.59}
|
||||
{"USD":1,"AED":3.67,"AFN":62.82,"ALL":81.38,"AMD":377.18,"ANG":1.79,"AOA":921.13,"ARS":1452.25,"AUD":1.42,"AWG":1.79,"AZN":1.7,"BAM":1.66,"BBD":2,"BDT":122.19,"BGN":1.6,"BHD":0.376,"BIF":2967.31,"BMD":1,"BND":1.27,"BOB":6.92,"BRL":5.22,"BSD":1,"BTN":90.77,"BWP":13.29,"BYN":2.84,"BZD":2,"CAD":1.37,"CDF":2281.91,"CHF":0.772,"CLF":0.0219,"CLP":866.65,"CNH":6.9,"CNY":6.91,"COP":3648.29,"CRC":481.3,"CUP":24,"CVE":93.37,"CZK":20.52,"DJF":177.72,"DKK":6.31,"DOP":61.55,"DZD":129.72,"EGP":46.95,"ERN":15,"ETB":154.44,"EUR":0.847,"FJD":2.2,"FKP":0.739,"FOK":6.31,"GBP":0.739,"GEL":2.68,"GGP":0.739,"GHS":11.02,"GIP":0.739,"GMD":74.1,"GNF":8762.47,"GTQ":7.67,"GYD":209.26,"HKD":7.82,"HNL":26.44,"HRK":6.38,"HTG":131.36,"HUF":320.31,"IDR":16895.02,"ILS":3.11,"IMP":0.739,"INR":90.77,"IQD":1310.4,"IRR":1281616.32,"ISK":122.37,"JEP":0.739,"JMD":155.71,"JOD":0.709,"JPY":154.42,"KES":129,"KGS":87.48,"KHR":4017.09,"KID":1.42,"KMF":416.61,"KRW":1447.31,"KWD":0.306,"KYD":0.833,"KZT":489.38,"LAK":21513.29,"LBP":89500,"LKR":309.13,"LRD":185.55,"LSL":16.06,"LYD":6.3,"MAD":9.14,"MDL":17.01,"MGA":4331.69,"MKD":52.14,"MMK":2100.51,"MNT":3550.21,"MOP":8.05,"MRU":39.97,"MUR":45.96,"MVR":15.45,"MWK":1739.99,"MXN":17.18,"MYR":3.9,"MZN":63.72,"NAD":16.06,"NGN":1338.38,"NIO":36.78,"NOK":9.52,"NPR":145.24,"NZD":1.67,"OMR":0.384,"PAB":1,"PEN":3.35,"PGK":4.33,"PHP":57.94,"PKR":279.71,"PLN":3.57,"PYG":6522.64,"QAR":3.64,"RON":4.3,"RSD":99.15,"RUB":76.67,"RWF":1460.55,"SAR":3.75,"SBD":8.02,"SCR":14.01,"SDG":543.58,"SEK":9.02,"SGD":1.27,"SHP":0.739,"SLE":24.46,"SLL":24455.37,"SOS":570.65,"SRD":37.71,"SSP":4568.21,"STN":20.75,"SYP":113.49,"SZL":16.06,"THB":31.26,"TJS":9.4,"TMT":3.5,"TND":2.86,"TOP":2.36,"TRY":43.77,"TTD":6.75,"TVD":1.42,"TWD":31.47,"TZS":2574.76,"UAH":43.3,"UGX":3532.39,"UYU":38.82,"UZS":12156.76,"VES":398.75,"VND":25919.9,"VUV":118.59,"WST":2.68,"XAF":555.48,"XCD":2.7,"XCG":1.79,"XDR":0.726,"XOF":555.48,"XPF":101.05,"YER":238.28,"ZAR":16.06,"ZMW":18.61,"ZWG":25.6,"ZWL":25.6}
|
||||
@ -244,6 +244,9 @@ csFileInput.addEventListener('change', async () => {
|
||||
csToggle.addEventListener('click', () => {
|
||||
csBox.classList.toggle('d-none');
|
||||
scrollToBottom();
|
||||
if (!csBox.classList.contains('d-none')) {
|
||||
fetch('/api/chat.php?action=ping');
|
||||
}
|
||||
});
|
||||
|
||||
csClose.addEventListener('click', () => csBox.classList.add('d-none'));
|
||||
|
||||
@ -14,7 +14,7 @@ if (!function_exists('getSetting')) {
|
||||
$stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?");
|
||||
$stmt->execute([$key]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ? $row['setting_value'] : $default;
|
||||
return ($row && !empty($row['setting_value'])) ? $row['setting_value'] : $default;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@ -25,16 +25,18 @@ if (!function_exists('getSetting')) {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<?php
|
||||
$site_logo = getSetting('site_logo', '/assets/images/logo.png');
|
||||
$site_favicon = getSetting('site_favicon', $site_logo);
|
||||
$site_name = getSetting('site_name', 'BYRO');
|
||||
?>
|
||||
<title><?= $site_name ?> | <?= __('site_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.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="/assets/css/style.css?v=<?= time() ?>">
|
||||
<link rel="icon" href="<?= $site_logo ?>">
|
||||
<link rel="icon" href="<?= $site_favicon ?>">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #0062ff;
|
||||
|
||||
@ -66,10 +66,10 @@ $translations = [
|
||||
'approx' => '约等于',
|
||||
'cookie_policy_title' => '隐私政策',
|
||||
'cookie_policy_content' => '<h3>1. 什么是隐私数据?</h3><p>隐私数据是您访问网站时存储在您的计算机或移动设备上的信息。它们广泛用于使网站运行或更高效地运行。</p>',
|
||||
'recharge_msg_fiat' => "用户ID:%uid% 申请充值金额:%amount% %currency%=%res%泰达币",
|
||||
'recharge_msg_crypto' => "用户ID:%uid% 申请充值金额:%amount% 泰达币 (%network%)",
|
||||
'withdraw_msg_fiat' => "用户ID:%uid% 申请提现金额:%amount% 泰达币=%res% %currency%",
|
||||
'withdraw_msg_crypto' => "用户ID:%uid% 申请提现金额:%amount% 泰达币 (%network%)",
|
||||
'recharge_msg_fiat' => "用户ID:%uid% 申请充值金额:%amount% %currency%=%res% USDT",
|
||||
'recharge_msg_crypto' => "用户ID:%uid% 申请充值金额:%amount% USDT (%network%)",
|
||||
'withdraw_msg_fiat' => "用户ID:%uid% 申请提现金额:%amount% USDT=%res% %currency%",
|
||||
'withdraw_msg_crypto' => "用户ID:%uid% 申请提现金额:%amount% USDT (%network%)",
|
||||
'select_network' => '选择网络',
|
||||
'address_copied' => '地址已复制',
|
||||
'withdrawal_password_error' => '提现密码错误',
|
||||
@ -78,7 +78,7 @@ $translations = [
|
||||
'withdraw_amount_label' => '提现金额',
|
||||
'i_have_paid' => '我已付款',
|
||||
'enter_amount' => '请输入金额',
|
||||
'crypto_recharge_warning' => '请仅向此地址发送泰达币。发送其他资产可能会导致永久丢失。',
|
||||
'crypto_recharge_warning' => '请仅向此地址发送 USDT。发送其他资产可能会导致永久丢失。',
|
||||
'recharge_steps' => '充值步骤',
|
||||
'security_tips' => '安全提示',
|
||||
'est_receive_fiat' => '预计收到法币',
|
||||
@ -91,7 +91,7 @@ $translations = [
|
||||
'recharge_amount' => '充值金额',
|
||||
'withdraw_amount' => '提现金额',
|
||||
'withdraw_address' => '提现地址',
|
||||
'min_withdraw_hint' => '最小提现金额 10 泰达币',
|
||||
'min_withdraw_hint' => '最小提现金额 10 USDT',
|
||||
'secure' => '安全',
|
||||
'fast' => '极速',
|
||||
'support_247' => '全天候支持',
|
||||
@ -107,7 +107,7 @@ $translations = [
|
||||
'select_currency' => '选择币种',
|
||||
'fiat_recharge' => '法币充值',
|
||||
'crypto_recharge' => '加密货币充值',
|
||||
'est_usdt' => '预计泰达币',
|
||||
'est_usdt' => '预计 USDT',
|
||||
'enter_address' => '请输入地址',
|
||||
'enter_password' => '请输入密码',
|
||||
'request_sent' => '请求已发送,请检查聊天。',
|
||||
@ -185,23 +185,23 @@ $translations = [
|
||||
'chinese' => '中文',
|
||||
'english' => '英文',
|
||||
'qr_code' => '二维码',
|
||||
'bitcoin' => '比特币',
|
||||
'ethereum' => '以太坊',
|
||||
'tether' => '泰达币',
|
||||
'binance_coin' => '币安币',
|
||||
'solana' => '索拉纳',
|
||||
'ripple' => '瑞波币',
|
||||
'cardano' => '卡尔达诺',
|
||||
'dogecoin' => '狗狗币',
|
||||
'polkadot' => '波卡',
|
||||
'polygon' => '多边形',
|
||||
'avalanche' => '雪崩',
|
||||
'chainlink' => '链连',
|
||||
'shiba_inu' => '柴犬币',
|
||||
'tron' => '波场',
|
||||
'bitcoin_cash' => '比特币现金',
|
||||
'litecoin' => '莱特币',
|
||||
'uniswap' => '独角兽',
|
||||
'bitcoin' => 'BTC',
|
||||
'ethereum' => 'ETH',
|
||||
'tether' => 'USDT',
|
||||
'binance_coin' => 'BNB',
|
||||
'solana' => 'SOL',
|
||||
'ripple' => 'XRP',
|
||||
'cardano' => 'ADA',
|
||||
'dogecoin' => 'DOGE',
|
||||
'polkadot' => 'DOT',
|
||||
'polygon' => 'MATIC',
|
||||
'avalanche' => 'AVAX',
|
||||
'chainlink' => 'LINK',
|
||||
'shiba_inu' => 'SHIB',
|
||||
'tron' => 'TRX',
|
||||
'bitcoin_cash' => 'BCH',
|
||||
'litecoin' => 'LTC',
|
||||
'uniswap' => 'UNI',
|
||||
'site_title' => '全球领先的数字资产交易平台',
|
||||
'unverified' => '未认证',
|
||||
'pending' => '审核中',
|
||||
@ -447,22 +447,22 @@ $translations = [
|
||||
'level' => '等级',
|
||||
'recharge_to_upgrade' => '还需充值 %amount% USDT 升级到 VIP %level%',
|
||||
'highest_level' => '已达到最高等级',
|
||||
'USDT' => '泰达币',
|
||||
'BTC' => '比特币',
|
||||
'ETH' => '以太坊',
|
||||
'BNB' => '币安币',
|
||||
'SOL' => '索拉纳',
|
||||
'XRP' => '瑞波币',
|
||||
'ADA' => '卡尔达诺',
|
||||
'DOGE' => '狗狗币',
|
||||
'DOT' => '波卡',
|
||||
'MATIC' => '多边形',
|
||||
'LINK' => '链连',
|
||||
'SHIB' => '柴犬币',
|
||||
'TRX' => '波场',
|
||||
'BCH' => '比特币现金',
|
||||
'LTC' => '莱特币',
|
||||
'UNI' => '独角兽',
|
||||
'USDT' => 'USDT',
|
||||
'BTC' => 'BTC',
|
||||
'ETH' => 'ETH',
|
||||
'BNB' => 'BNB',
|
||||
'SOL' => 'SOL',
|
||||
'XRP' => 'XRP',
|
||||
'ADA' => 'ADA',
|
||||
'DOGE' => 'DOGE',
|
||||
'DOT' => 'DOT',
|
||||
'MATIC' => 'MATIC',
|
||||
'LINK' => 'LINK',
|
||||
'SHIB' => 'SHIB',
|
||||
'TRX' => 'TRX',
|
||||
'BCH' => 'BCH',
|
||||
'LTC' => 'LTC',
|
||||
'UNI' => 'UNI',
|
||||
'security_level' => '安全等级',
|
||||
'login_password' => '登录密码',
|
||||
'old_password' => '原密码',
|
||||
@ -697,23 +697,23 @@ $translations = [
|
||||
'email' => 'Email',
|
||||
'email_placeholder' => 'Enter email address',
|
||||
'registration_verify' => 'Registration Verify',
|
||||
'bitcoin' => 'Bitcoin',
|
||||
'ethereum' => 'Ethereum',
|
||||
'tether' => 'Tether',
|
||||
'bitcoin' => 'BTC',
|
||||
'ethereum' => 'ETH',
|
||||
'tether' => 'USDT',
|
||||
'binance_coin' => 'BNB',
|
||||
'solana' => 'Solana',
|
||||
'ripple' => 'Ripple',
|
||||
'cardano' => 'Cardano',
|
||||
'dogecoin' => 'Dogecoin',
|
||||
'polkadot' => 'Polkadot',
|
||||
'polygon' => 'Polygon',
|
||||
'avalanche' => 'Avalanche',
|
||||
'chainlink' => 'Chainlink',
|
||||
'shiba_inu' => 'Shiba Inu',
|
||||
'tron' => 'TRON',
|
||||
'bitcoin_cash' => 'Bitcoin Cash',
|
||||
'litecoin' => 'Litecoin',
|
||||
'uniswap' => 'Uniswap',
|
||||
'solana' => 'SOL',
|
||||
'ripple' => 'XRP',
|
||||
'cardano' => 'ADA',
|
||||
'dogecoin' => 'DOGE',
|
||||
'polkadot' => 'DOT',
|
||||
'polygon' => 'MATIC',
|
||||
'avalanche' => 'AVAX',
|
||||
'chainlink' => 'LINK',
|
||||
'shiba_inu' => 'SHIB',
|
||||
'tron' => 'TRX',
|
||||
'bitcoin_cash' => 'BCH',
|
||||
'litecoin' => 'LTC',
|
||||
'uniswap' => 'UNI',
|
||||
'site_title' => 'Leading Digital Asset Platform',
|
||||
'unverified' => 'Unverified',
|
||||
'pending' => 'Pending',
|
||||
|
||||
@ -15,7 +15,7 @@ require_once __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
<div class="carousel-inner">
|
||||
<div class="carousel-item active">
|
||||
<div class="carousel-content p-5 text-white d-flex align-items-center" style="height: 550px; background: url('assets/images/pexels/slide1.jpg') center/cover;">
|
||||
<div class="carousel-content p-5 text-white d-flex align-items-center" style="height: 550px; background: url('assets/images/carousel/slide1.jpg') center/cover;">
|
||||
<div class="carousel-overlay" style="background: linear-gradient(90deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.3) 100%); position: absolute; top:0; left:0; right:0; bottom:0;"></div>
|
||||
<div class="container" style="position: relative; z-index: 10;">
|
||||
<div class="row">
|
||||
@ -32,7 +32,7 @@ require_once __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="carousel-content p-5 text-white d-flex align-items-center" style="height: 550px; background: url('assets/images/pexels/slide2.jpg') center/cover;">
|
||||
<div class="carousel-content p-5 text-white d-flex align-items-center" style="height: 550px; background: url('assets/images/carousel/slide2.jpg') center/cover;">
|
||||
<div class="carousel-overlay" style="background: linear-gradient(90deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.3) 100%); position: absolute; top:0; left:0; right:0; bottom:0;"></div>
|
||||
<div class="container" style="position: relative; z-index: 10;">
|
||||
<div class="row">
|
||||
@ -49,7 +49,7 @@ require_once __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="carousel-content p-5 text-white d-flex align-items-center" style="height: 550px; background: url('assets/images/pexels/slide3.jpg') center/cover;">
|
||||
<div class="carousel-content p-5 text-white d-flex align-items-center" style="height: 550px; background: url('assets/images/carousel/slide3.jpg') center/cover;">
|
||||
<div class="carousel-overlay" style="background: linear-gradient(90deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.3) 100%); position: absolute; top:0; left:0; right:0; bottom:0;"></div>
|
||||
<div class="container" style="position: relative; z-index: 10;">
|
||||
<div class="row">
|
||||
|
||||
4
news.php
4
news.php
@ -30,9 +30,9 @@ require_once __DIR__ . '/includes/header.php';
|
||||
<hr class="border-secondary">
|
||||
<h5 class="fw-bold mb-3"><?= __('popular_topics') ?></h5>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="badge border border-secondary p-2">#Bitcoin</span>
|
||||
<span class="badge border border-secondary p-2">#BTC</span>
|
||||
<span class="badge border border-secondary p-2">#Web3</span>
|
||||
<span class="badge border border-secondary p-2">#Ethereum</span>
|
||||
<span class="badge border border-secondary p-2">#ETH</span>
|
||||
<span class="badge border border-secondary p-2">#DeFi</span>
|
||||
<span class="badge border border-secondary p-2">#NFTs</span>
|
||||
</div>
|
||||
|
||||
32
profile.php
32
profile.php
@ -42,6 +42,7 @@ foreach ($userBalances as $ub) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get coins prices (simple static list)
|
||||
function getCoinPrice($symbol) {
|
||||
$prices = [
|
||||
'BTC' => 65432.10,
|
||||
@ -60,26 +61,9 @@ function getCoinPrice($symbol) {
|
||||
return $prices[strtoupper($symbol)] ?? 1.00;
|
||||
}
|
||||
|
||||
// Use the VIP level from the database column as the source of truth
|
||||
$vipLevel = $userData['vip_level'] ?? 0;
|
||||
|
||||
function getAutoVipLevel($totalRecharge) {
|
||||
if ($totalRecharge >= 10000000) return 7;
|
||||
if ($totalRecharge >= 5000000) return 6;
|
||||
if ($totalRecharge >= 1000000) return 5;
|
||||
if ($totalRecharge >= 500000) return 4;
|
||||
if ($totalRecharge >= 100000) return 3;
|
||||
if ($totalRecharge >= 50000) return 2;
|
||||
if ($totalRecharge >= 10001) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the database VIP level is 0, we can optionally fall back to calculation,
|
||||
// but since the admin can now set it to any level, we should respect the database.
|
||||
// However, to keep it "automatic" for new users, we can do:
|
||||
if ($vipLevel == 0) {
|
||||
$vipLevel = getAutoVipLevel($userData['total_recharge'] ?? 0);
|
||||
}
|
||||
// Calculate total recharge and dynamic VIP level
|
||||
$totalRecharge = getUserTotalRecharge($user['id']);
|
||||
$vipLevel = getAutoVipLevel($totalRecharge);
|
||||
|
||||
// Fetch transactions
|
||||
$stmt = db()->prepare("SELECT * FROM transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT 50");
|
||||
@ -133,10 +117,10 @@ $kycStatusColor = [
|
||||
<span class="text-white-50 small"><i class="bi bi-gem me-2 text-warning"></i><?= __('vip_level') ?></span>
|
||||
<span class="badge bg-warning text-dark fw-bold"><?= __('level') ?> <?= $vipLevel ?></span>
|
||||
</div>
|
||||
<?php if ($vipLevel < 7):
|
||||
<?php if ($vipLevel < 5):
|
||||
$nextLevel = $vipLevel + 1;
|
||||
$thresholds = [1 => 10001, 2 => 50000, 3 => 100000, 4 => 500000, 5 => 1000000, 6 => 5000000, 7 => 10000000];
|
||||
$needed = $thresholds[$nextLevel] - ($userData['total_recharge'] ?? 0);
|
||||
$thresholds = [1 => 10000, 2 => 50000, 3 => 100000, 4 => 180000, 5 => 250000];
|
||||
$needed = $thresholds[$nextLevel] - $totalRecharge;
|
||||
if ($needed > 0):
|
||||
?>
|
||||
<div class="list-group-item py-2 bg-black bg-opacity-20 border-0">
|
||||
@ -144,7 +128,7 @@ $kycStatusColor = [
|
||||
<?= str_replace(['%amount%', '%level%'], [number_format($needed, 2), $nextLevel], __('recharge_to_upgrade')) ?>
|
||||
</div>
|
||||
<div class="progress rounded-pill shadow-sm" style="height: 4px; background: rgba(255,255,255,0.1);">
|
||||
<div class="progress-bar bg-warning progress-bar-striped progress-bar-animated" style="width: <?= min(100, (($userData['total_recharge'] ?? 0) / $thresholds[$nextLevel]) * 100) ?>%"></div>
|
||||
<div class="progress-bar bg-warning progress-bar-striped progress-bar-animated" style="width: <?= min(100, ($totalRecharge / $thresholds[$nextLevel]) * 100) ?>%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
38
recharge.php
38
recharge.php
@ -122,7 +122,7 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
||||
<img src="<?= getCoinIcon('USDT') ?>" width="32" height="32" alt="USDT">
|
||||
<div>
|
||||
<div class="fw-bold text-white"><?= $lang === 'zh' ? __('USDT') : 'USDT' ?></div>
|
||||
<div class="text-white-50 small"><?= $lang === 'zh' ? __('tether') : 'Tether' ?></div>
|
||||
<div class="text-white-50 small"><?= __('tether') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -194,6 +194,18 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
||||
let currentNetwork = 'TRC20';
|
||||
let currentAddress = '<?= $trc20_addr ?>';
|
||||
|
||||
function notify(icon, title, text = '') {
|
||||
return Swal.fire({
|
||||
icon: icon,
|
||||
title: title,
|
||||
text: text,
|
||||
background: '#1e2329',
|
||||
color: '#fff',
|
||||
confirmButtonColor: '#0062ff',
|
||||
confirmButtonText: '<?= __("confirm") ?>'
|
||||
});
|
||||
}
|
||||
|
||||
function updateRate() {
|
||||
const select = document.getElementById('fiatCurrency');
|
||||
const symbol = select.value;
|
||||
@ -241,7 +253,7 @@ function confirmFiatOrder() {
|
||||
const rate = parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
|
||||
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
alert('<?= __("enter_amount") ?>');
|
||||
notify('warning', '<?= __("enter_amount") ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -271,7 +283,7 @@ function confirmFiatOrder() {
|
||||
.replace('%res%', preciseRes);
|
||||
sendToCS(message);
|
||||
} else {
|
||||
alert(data.error || '<?= __('request_failed') ?>');
|
||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -281,7 +293,7 @@ function confirmCryptoOrder() {
|
||||
const amount = parseFloat(amountInput.value);
|
||||
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
alert('<?= __("enter_amount") ?>');
|
||||
notify('warning', '<?= __("enter_amount") ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -304,7 +316,7 @@ function confirmCryptoOrder() {
|
||||
.replace('%amount%', amount);
|
||||
sendToCS(message);
|
||||
} else {
|
||||
alert(data.error || '<?= __('request_failed') ?>');
|
||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -320,9 +332,9 @@ function sendToCS(message) {
|
||||
if (csInput) {
|
||||
csInput.value = message;
|
||||
document.getElementById('cs-form').dispatchEvent(new Event('submit'));
|
||||
alert('<?= __("request_sent") ?>');
|
||||
notify('success', '<?= __("request_sent") ?>');
|
||||
} else {
|
||||
alert('<?= __("cs_connect_fail") ?>');
|
||||
notify('error', '<?= __("cs_connect_fail") ?>');
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,7 +342,17 @@ function copyAddress() {
|
||||
const addr = document.getElementById('cryptoAddress');
|
||||
addr.select();
|
||||
document.execCommand('copy');
|
||||
alert('<?= __("copy_success") ?>');
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '<?= __("copy_success") ?>',
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 3000,
|
||||
timerProgressBar: true,
|
||||
background: '#1e2329',
|
||||
color: '#fff'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
36
withdraw.php
36
withdraw.php
@ -55,7 +55,7 @@ $available = $bal['available'] ?? 0;
|
||||
<img src="<?= getCoinIcon('USDT') ?>" width="32" height="32" alt="USDT">
|
||||
<div>
|
||||
<div class="fw-bold text-white"><?= $lang === 'zh' ? __('USDT') : 'USDT' ?></div>
|
||||
<div class="text-white-50 small"><?= $lang === 'zh' ? __('tether') : 'Tether' ?></div>
|
||||
<div class="text-white-50 small"><?= __('tether') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -186,6 +186,18 @@ $available = $bal['available'] ?? 0;
|
||||
<script>
|
||||
let currentWithdrawNetwork = 'TRC20';
|
||||
|
||||
function notify(icon, title, text = '') {
|
||||
return Swal.fire({
|
||||
icon: icon,
|
||||
title: title,
|
||||
text: text,
|
||||
background: '#1e2329',
|
||||
color: '#fff',
|
||||
confirmButtonColor: '#0062ff',
|
||||
confirmButtonText: '<?= __("confirm") ?>'
|
||||
});
|
||||
}
|
||||
|
||||
function setMax(inputId) {
|
||||
document.getElementById(inputId).value = <?= $available ?>;
|
||||
if (inputId === 'withdrawAmount') calculateCryptoWithdraw();
|
||||
@ -236,10 +248,10 @@ function confirmCryptoWithdraw() {
|
||||
const amount = parseFloat(document.getElementById('withdrawAmount').value);
|
||||
const password = document.getElementById('withdrawPassword').value;
|
||||
|
||||
if (!addr) { alert('<?= __("enter_address") ?>'); return; }
|
||||
if (!amount || amount < 10) { alert('<?= __("min_withdraw_hint") ?>'); return; }
|
||||
if (amount > <?= $available ?>) { alert('<?= __("insufficient_balance") ?>'); return; }
|
||||
if (!password) { alert('<?= __("enter_password") ?>'); return; }
|
||||
if (!addr) { notify('warning', '<?= __("enter_address") ?>'); return; }
|
||||
if (!amount || amount < 10) { notify('warning', '<?= __("min_withdraw_hint") ?>'); return; }
|
||||
if (amount > <?= $available ?>) { notify('error', '<?= __("insufficient_balance") ?>'); return; }
|
||||
if (!password) { notify('warning', '<?= __("enter_password") ?>'); return; }
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'withdraw');
|
||||
@ -261,7 +273,7 @@ function confirmCryptoWithdraw() {
|
||||
.replace('%amount%', amount.toFixed(2));
|
||||
sendWithdrawToCS(message);
|
||||
} else {
|
||||
alert(data.error || '<?= __('request_failed') ?>');
|
||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -274,9 +286,9 @@ function confirmFiatWithdraw() {
|
||||
const rate = parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
|
||||
const password = document.getElementById('fiatWithdrawPassword').value;
|
||||
|
||||
if (isNaN(amount) || amount < 10) { alert('<?= __("min_withdraw_hint") ?>'); return; }
|
||||
if (amount > <?= $available ?>) { alert('<?= __("insufficient_balance") ?>'); return; }
|
||||
if (!password) { alert('<?= __("enter_password") ?>'); return; }
|
||||
if (isNaN(amount) || amount < 10) { notify('warning', '<?= __("min_withdraw_hint") ?>'); return; }
|
||||
if (amount > <?= $available ?>) { notify('error', '<?= __("insufficient_balance") ?>'); return; }
|
||||
if (!password) { notify('warning', '<?= __("enter_password") ?>'); return; }
|
||||
|
||||
const estFiat = amount * rate;
|
||||
|
||||
@ -305,7 +317,7 @@ function confirmFiatWithdraw() {
|
||||
.replace('%res%', preciseRes);
|
||||
sendWithdrawToCS(message);
|
||||
} else {
|
||||
alert(data.error || '<?= __('request_failed') ?>');
|
||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -321,9 +333,9 @@ function sendWithdrawToCS(message) {
|
||||
if (csInput) {
|
||||
csInput.value = message;
|
||||
document.getElementById('cs-form').dispatchEvent(new Event('submit'));
|
||||
alert('<?= __("request_sent") ?>');
|
||||
notify('success', '<?= __("request_sent") ?>');
|
||||
} else {
|
||||
alert('<?= __("cs_connect_fail") ?>');
|
||||
notify('error', '<?= __("cs_connect_fail") ?>');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user