最新完美
This commit is contained in:
parent
868df18001
commit
23ce9c4446
@ -6,9 +6,7 @@ $pdo = db();
|
||||
// Handle deletion of chat
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['delete_user_id'])) {
|
||||
$del_id = $_GET['delete_user_id'];
|
||||
// Delete messages
|
||||
$pdo->prepare("DELETE FROM messages WHERE user_id = ?")->execute([$del_id]);
|
||||
// Optionally update orders to not show in chat
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'rejected' WHERE user_id = ? AND status IN ('matching', 'submitting')")->execute([$del_id]);
|
||||
header("Location: chat.php");
|
||||
exit;
|
||||
@ -70,7 +68,6 @@ $chat_users = $pdo->query("
|
||||
.back-btn { color: #848E9C; text-decoration: none; font-size: 0.9rem; padding: 15px; border-bottom: 1px solid #2B3139; display: block; }
|
||||
.back-btn:hover { color: white; background: #2B3139; }
|
||||
|
||||
/* Custom alert */
|
||||
#custom-alert { display: none; position: fixed; top: 20px; right: 20px; background: #f0b90b; color: black; padding: 20px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); z-index: 10000; animation: slideIn 0.5s; width: 300px; }
|
||||
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||||
</style>
|
||||
@ -82,7 +79,7 @@ $chat_users = $pdo->query("
|
||||
<strong style="font-size: 1.1rem;">新通知 / NEW NOTIFICATION</strong>
|
||||
</div>
|
||||
<div id="alert-msg">您有新的充值申请或用户消息。</div>
|
||||
<button onclick="location.reload()" style="margin-top: 15px; width: 100%; padding: 8px; background: black; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">立即查看</button>
|
||||
<button onclick="location.reload()" style="margin-top: 15px; width: 100%; padding: 8px; background: black; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">立即刷新列表</button>
|
||||
</div>
|
||||
|
||||
<div class="admin-layout">
|
||||
@ -93,7 +90,7 @@ $chat_users = $pdo->query("
|
||||
<a href="kyc.php" class="menu-item"><i class="fas fa-id-card"></i> KYC 审核</a>
|
||||
<a href="chat.php" class="menu-item active">
|
||||
<i class="fas fa-headset"></i> 客服管理
|
||||
<?php if($unread_msgs > 0 || $pending_orders > 0): ?><span class="badge"><?php echo ($unread_msgs + $pending_orders); ?></span><?php endif; ?>
|
||||
<?php if($unread_msgs > 0 || $pending_orders > 0): ?><span class="badge" id="total-badge"><?php echo ($unread_msgs + $pending_orders); ?></span><?php endif; ?>
|
||||
</a>
|
||||
<a href="spot_orders.php" class="menu-item"><i class="fas fa-exchange-alt"></i> 现货交易</a>
|
||||
<a href="futures_orders.php" class="menu-item"><i class="fas fa-file-contract"></i> 合约交易</a>
|
||||
@ -107,21 +104,23 @@ $chat_users = $pdo->query("
|
||||
<div class="user-list">
|
||||
<a href="index.php" class="back-btn"><i class="fas fa-arrow-left"></i> 返回</a>
|
||||
<div style="padding: 15px; color: #848E9C; font-size: 0.8rem; border-bottom: 1px solid #2B3139;">最近联系人 / 充值申请</div>
|
||||
<?php foreach($chat_users as $u): ?>
|
||||
<div class="user-item <?php echo $user_id == $u['id'] ? 'active' : ''; ?>" onclick="if(event.target.closest('.delete-btn')) return; location.href='chat.php?user_id=<?php echo $u['id']; ?>'">
|
||||
<div class="user-info-row">
|
||||
<div class="user-name">
|
||||
<?php echo htmlspecialchars($u['username']); ?>
|
||||
<?php if($u['recharge_status']): ?><span class="recharge-label">充值申请</span><?php endif; ?>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<?php if($u['unread_count'] > 0 || $u['recharge_status'] == 'matching'): ?><div class="dot"></div><?php endif; ?>
|
||||
<i class="fas fa-trash delete-btn" title="删除记录" onclick="if(confirm('确定要删除与该用户的聊天及充值申请吗?')) window.location.href='chat.php?action=delete&delete_user_id=<?php echo $u['id']; ?>'"></i>
|
||||
<div id="user-items-container">
|
||||
<?php foreach($chat_users as $u): ?>
|
||||
<div class="user-item <?php echo $user_id == $u['id'] ? 'active' : ''; ?>" onclick="if(event.target.closest('.delete-btn')) return; location.href='chat.php?user_id=<?php echo $u['id']; ?>'">
|
||||
<div class="user-info-row">
|
||||
<div class="user-name">
|
||||
<?php echo htmlspecialchars($u['username']); ?>
|
||||
<?php if($u['recharge_status']): ?><span class="recharge-label">充值申请</span><?php endif; ?>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<?php if($u['unread_count'] > 0 || $u['recharge_status'] == 'matching'): ?><div class="dot"></div><?php endif; ?>
|
||||
<i class="fas fa-trash delete-btn" title="删除记录" onclick="if(confirm('确定要删除与该用户的聊天及充值申请吗?')) window.location.href='chat.php?action=delete&delete_user_id=<?php echo $u['id']; ?>'"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="last-msg"><?php echo htmlspecialchars($u['last_msg']); ?></div>
|
||||
</div>
|
||||
<div class="last-msg"><?php echo htmlspecialchars($u['last_msg']); ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-main">
|
||||
@ -149,11 +148,20 @@ function checkNotifications() {
|
||||
document.getElementById('notif-sound').play().catch(e => {});
|
||||
document.getElementById('custom-alert').style.display = 'block';
|
||||
document.getElementById('alert-msg').innerText = "您有新的充值申请或用户消息 (当前未读: " + data.total + ")";
|
||||
|
||||
const badge = document.getElementById('total-badge');
|
||||
if (badge) badge.innerText = data.total;
|
||||
} else if (data.total < lastTotal) {
|
||||
const badge = document.getElementById('total-badge');
|
||||
if (badge) {
|
||||
if (data.total === 0) badge.remove();
|
||||
else badge.innerText = data.total;
|
||||
}
|
||||
}
|
||||
lastTotal = data.total;
|
||||
});
|
||||
}
|
||||
setInterval(checkNotifications, 5000);
|
||||
setInterval(checkNotifications, 2000);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@ -7,7 +7,7 @@ $pdo = db();
|
||||
$user_id = $_GET['user_id'] ?? null;
|
||||
if (!$user_id) die("需要用户 ID");
|
||||
|
||||
// 标记为已读
|
||||
// Initial mark as read
|
||||
$pdo->prepare("UPDATE messages SET is_read = 1 WHERE user_id = ? AND sender = 'user'")->execute([$user_id]);
|
||||
|
||||
// 处理消息发送
|
||||
@ -25,15 +25,14 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
|
||||
$name = $_POST['account_name'] ?? '';
|
||||
$number = $_POST['account_number'] ?? '';
|
||||
$remarks = $_POST['remarks'] ?? '';
|
||||
$info = "🏦 银行名称:$bank\n👤 收款姓名:$name\n💳 收款账号:$number\n📝 备注说明:$remarks";
|
||||
$info = "Bank:$bank\nAccount Name:$name\nBank Acc:$number\nRemarks:$remarks";
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'matched', bank_account_info = ? WHERE id = ?")->execute([$info, $oid]);
|
||||
// 发送消息给用户
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $info]);
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "✅ 匹配成功!收款账户已下发,请在转账后上传凭证。"]);
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "✅ 匹配成功!收款账户已下发,请在转账后上传凭证。clearly"]);
|
||||
echo "<script>parent.location.reload();</script>";
|
||||
exit;
|
||||
} elseif ($_POST['action'] == 'send_withdraw_format') {
|
||||
$format = "📋 请提供以下提现收款信息:\n\n1. 银行/钱包名称:\n2. 收款人姓名:\n3. 账号/地址:\n4. 支行/网络:";
|
||||
$format = "Bank:\nBank Acc:\nAccount Name:";
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'matched', bank_account_info = ? WHERE id = ?")->execute([$format, $oid]);
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $format]);
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "✅ 请按上述格式回复您的收款详情。"]);
|
||||
@ -47,11 +46,9 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
if ($order['order_type'] === 'deposit') {
|
||||
// 充值:增加余额
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['usdt_amount'], $user_id]);
|
||||
$msg = "🎉 您的充值已确认到账!";
|
||||
} else {
|
||||
// 提现:余额已在发起时扣除,这里仅标记完成
|
||||
$msg = "🎉 您的提现申请已处理完成,请查收!";
|
||||
}
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'completed' WHERE id = ?")->execute([$oid]);
|
||||
@ -71,7 +68,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
if ($order['order_type'] === 'withdrawal') {
|
||||
// 提现拒绝:退还余额
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['usdt_amount'], $user_id]);
|
||||
}
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'rejected' WHERE id = ?")->execute([$oid]);
|
||||
@ -101,7 +97,10 @@ $pending_orders = $orders->fetchAll();
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<style>
|
||||
body { margin: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #0B0E11; color: white; display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
|
||||
.chat-header { padding: 12px 20px; background: #1E2329; border-bottom: 1px solid #2B3139; display: flex; justify-content: space-between; align-items: center; font-size: 14px; }
|
||||
.chat-header { padding: 12px 20px; background: #1E2329; border-bottom: 1px solid #2B3139; display: flex; flex-direction: column; gap: 5px; font-size: 13px; }
|
||||
.header-top { display: flex; justify-content: space-between; align-items: center; }
|
||||
.header-bottom { display: flex; justify-content: space-between; font-size: 12px; color: #848E9C; }
|
||||
|
||||
#chat-box { flex: 1; padding: 20px; display: flex; flex-direction: column; gap: 15px; overflow-y: auto; scroll-behavior: smooth; background: #0b0e11; }
|
||||
|
||||
.msg { max-width: 80%; padding: 12px 16px; border-radius: 16px; font-size: 13.5px; line-height: 1.6; position: relative; }
|
||||
@ -123,13 +122,25 @@ $pending_orders = $orders->fetchAll();
|
||||
.status-matching { background: rgba(240, 185, 11, 0.2); color: #f0b90b; }
|
||||
.status-matched { background: rgba(0, 192, 135, 0.2); color: #00c087; }
|
||||
.status-paid { background: rgba(14, 203, 129, 0.2); color: #0ecb81; }
|
||||
|
||||
.sending { opacity: 0.7; }
|
||||
.sending::after { content: '...'; position: absolute; right: 5px; bottom: 5px; font-size: 10px; }
|
||||
|
||||
.icon-btn { background: #2b3139; border: 1px solid #3b424d; width: 40px; height: 40px; border-radius: 8px; color: #f0b90b; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
|
||||
.icon-btn:hover { background: #3b424d; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="chat-header">
|
||||
<span><i class="fas fa-user-circle"></i> <b><?php echo htmlspecialchars($userData['username'] ?? ''); ?></b> (UID: <?php echo $userData['uid'] ?? ''; ?>)</span>
|
||||
<span style="color: #00c087; font-weight: bold;">余额: <?php echo number_format($userData['balance'] ?? 0, 2); ?> USDT</span>
|
||||
<div class="header-top">
|
||||
<span><i class="fas fa-user-circle"></i> <b><?php echo htmlspecialchars($userData['username'] ?? ''); ?></b> (UID: <?php echo $userData['uid'] ?? ''; ?>)</span>
|
||||
<span style="color: #00c087; font-weight: bold;">余额: <?php echo number_format($userData['balance'] ?? 0, 2); ?> USDT</span>
|
||||
</div>
|
||||
<div class="header-bottom">
|
||||
<span><i class="fas fa-network-wired"></i> 实时 IP: <?php echo htmlspecialchars($userData['last_ip'] ?? '未知'); ?></span>
|
||||
<span><i class="fas fa-clock"></i> 当前时间: <?php echo date('Y-m-d H:i:s'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; display: flex; flex-direction: column; overflow: hidden;">
|
||||
@ -137,22 +148,11 @@ $pending_orders = $orders->fetchAll();
|
||||
<div class="order-panel">
|
||||
<div style="font-size: 11px; color: #848E9C; margin-bottom: 10px; font-weight: bold;">待处理申请 (ACTIVE REQUESTS)</div>
|
||||
<?php foreach($pending_orders as $o):
|
||||
// Determine the display text for the order type
|
||||
$orderTypeDisplay = '';
|
||||
if ($o['order_type'] === 'deposit') {
|
||||
$orderTypeDisplay = '充值';
|
||||
} elseif ($o['order_type'] === 'withdrawal') {
|
||||
$orderTypeDisplay = '提现';
|
||||
}
|
||||
// Determine the display text for the status
|
||||
$orderTypeDisplay = $o['order_type'] === 'deposit' ? '充值' : '提现';
|
||||
$statusDisplay = '';
|
||||
if ($o['status'] === 'matching') {
|
||||
$statusDisplay = '待受理';
|
||||
} elseif ($o['status'] === 'matched') {
|
||||
$statusDisplay = '已下发/待确认';
|
||||
} elseif ($o['status'] === 'paid') {
|
||||
$statusDisplay = '待审核';
|
||||
}
|
||||
if ($o['status'] === 'matching') $statusDisplay = '待受理';
|
||||
elseif ($o['status'] === 'matched') $statusDisplay = '已下发';
|
||||
elseif ($o['status'] === 'paid') $statusDisplay = '待审核';
|
||||
?>
|
||||
<div class="order-card">
|
||||
<div style="display:flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
||||
@ -206,19 +206,28 @@ $pending_orders = $orders->fetchAll();
|
||||
</div>
|
||||
|
||||
<form class="input-area" id="msg-form">
|
||||
<button type="button" class="icon-btn" id="upload-btn" onclick="document.getElementById('image-input').click()">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
<input type="file" id="image-input" accept="image/*" style="display: none;" onchange="uploadImage(this)">
|
||||
|
||||
<input type="text" id="msg-input" placeholder="输入消息..." autocomplete="off">
|
||||
<button type="submit"><i class="fas fa-paper-plane"></i></button>
|
||||
</form>
|
||||
|
||||
<audio id="notif-sound" src="https://assets.mixkit.co/active_storage/sfx/2354/2354-preview.mp3" preload="auto"></audio>
|
||||
<audio id="notif-sound" src="https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3" preload="auto"></audio>
|
||||
|
||||
<script>
|
||||
const chatBox = document.getElementById('chat-box');
|
||||
const msgInput = document.getElementById('msg-input');
|
||||
const uploadBtn = document.getElementById('upload-btn');
|
||||
const notifSound = document.getElementById('notif-sound');
|
||||
let lastMsgId = 0;
|
||||
let lastMessagesHtml = '';
|
||||
let isSending = false;
|
||||
|
||||
async function loadMessages() {
|
||||
if (isSending) return;
|
||||
try {
|
||||
const resp = await fetch('../api/get_messages.php?user_id=<?php echo $user_id; ?>');
|
||||
const res = await resp.json();
|
||||
@ -230,7 +239,6 @@ $pending_orders = $orders->fetchAll();
|
||||
if (m.sender === 'user' && lastMsgId > 0) hasNewUserMsg = true;
|
||||
lastMsgId = m.id;
|
||||
}
|
||||
const isUser = m.sender === 'user';
|
||||
html += `
|
||||
<div class="msg ${m.sender}">
|
||||
${m.type === 'image' ? `<img src="../${m.message}" style="max-width:100%; border-radius:8px; cursor:pointer;" onclick="window.open(this.src)">` : m.message.replace(/\n/g, '<br>')}
|
||||
@ -243,9 +251,15 @@ $pending_orders = $orders->fetchAll();
|
||||
try { notifSound.play(); } catch(e) {}
|
||||
}
|
||||
|
||||
const isAtBottom = chatBox.scrollHeight - chatBox.scrollTop <= chatBox.clientHeight + 100;
|
||||
chatBox.innerHTML = html;
|
||||
if (isAtBottom) chatBox.scrollTop = chatBox.scrollHeight;
|
||||
if (html !== lastMessagesHtml) {
|
||||
const isAtBottom = chatBox.scrollHeight - chatBox.scrollTop <= chatBox.clientHeight + 100;
|
||||
chatBox.innerHTML = html;
|
||||
lastMessagesHtml = html;
|
||||
if (isAtBottom) chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
// Mark as read
|
||||
fetch('../api/get_messages.php?action=mark_read&user_id=<?php echo $user_id; ?>&reader=admin');
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
@ -253,16 +267,64 @@ $pending_orders = $orders->fetchAll();
|
||||
document.getElementById('msg-form').onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const msg = msgInput.value.trim();
|
||||
if (!msg) return;
|
||||
if (!msg || isSending) return;
|
||||
|
||||
msgInput.value = '';
|
||||
isSending = true;
|
||||
|
||||
// Optimistic UI
|
||||
const tempMsg = document.createElement('div');
|
||||
tempMsg.className = 'msg admin sending';
|
||||
tempMsg.innerHTML = `${msg.replace(/\n/g, '<br>')}<span class="msg-time">${new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>`;
|
||||
chatBox.appendChild(tempMsg);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('message', msg);
|
||||
await fetch(window.location.href, { method: 'POST', body: formData });
|
||||
loadMessages();
|
||||
|
||||
try {
|
||||
await fetch(window.location.href, { method: 'POST', body: formData });
|
||||
isSending = false;
|
||||
await loadMessages();
|
||||
} catch (e) {
|
||||
isSending = false;
|
||||
tempMsg.style.background = '#f6465d';
|
||||
tempMsg.innerHTML += ' (发送失败)';
|
||||
}
|
||||
};
|
||||
|
||||
function uploadImage(input) {
|
||||
if (!input.files || !input.files[0]) return;
|
||||
const formData = new FormData();
|
||||
formData.append('image', input.files[0]);
|
||||
formData.append('is_admin', '1'); // Optional, to distinguish sender if needed
|
||||
|
||||
uploadBtn.disabled = true;
|
||||
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||
|
||||
// We need a separate API or same API for admin image upload
|
||||
// api/upload_chat_image.php already handles sending message as 'user'
|
||||
// I should probably create a separate one or modify it.
|
||||
// Let's modify api/upload_chat_image.php to support admin sender.
|
||||
|
||||
fetch('../api/upload_chat_image.php?sender=admin&user_id=<?php echo $user_id; ?>', { method: 'POST', body: formData })
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
loadMessages();
|
||||
} else {
|
||||
alert(res.error || '上传失败');
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
uploadBtn.disabled = false;
|
||||
uploadBtn.innerHTML = '<i class="fas fa-plus"></i>';
|
||||
input.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
loadMessages();
|
||||
setInterval(loadMessages, 3000);
|
||||
setInterval(loadMessages, 1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -4,9 +4,9 @@ require_once '../db/config.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
if (!isset($_SESSION['user_id']) && !isset($_GET['admin_key'])) {
|
||||
// Basic protection, though admin usually has session
|
||||
// For this project, admin session is also set in $_SESSION['user_id'] or checked by auth.php
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
@ -14,47 +14,34 @@ $pdo = db();
|
||||
// Action for admin notification count
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'count_unread') {
|
||||
$unread_msgs = $pdo->query("SELECT COUNT(*) FROM messages WHERE sender = 'user' AND is_read = 0")->fetchColumn();
|
||||
$pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN ('matching', 'submitting')")->fetchColumn();
|
||||
$pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN ('matching', 'paid')")->fetchColumn();
|
||||
echo json_encode(['total' => (int)($unread_msgs + $pending_orders)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Mark messages as read
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'mark_read' && isset($_GET['user_id'])) {
|
||||
$u_id = $_GET['user_id'];
|
||||
$sender_type = isset($_GET['reader']) && $_GET['reader'] === 'admin' ? 'user' : 'admin';
|
||||
$pdo->prepare("UPDATE messages SET is_read = 1 WHERE user_id = ? AND sender = ?")->execute([$u_id, $sender_type]);
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Support both regular user and admin polling for specific user
|
||||
$user_id = isset($_GET['user_id']) ? $_GET['user_id'] : $_SESSION['user_id'];
|
||||
$user_id = isset($_GET['user_id']) ? $_GET['user_id'] : ($_SESSION['user_id'] ?? null);
|
||||
|
||||
// If fetch_all is provided, return all messages for this user
|
||||
if (isset($_GET['fetch_all'])) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM messages WHERE user_id = ? ORDER BY id ASC");
|
||||
$stmt->execute([$user_id]);
|
||||
$msgs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
echo json_encode(['success' => true, 'data' => $msgs]);
|
||||
if (!$user_id) {
|
||||
echo json_encode(['success' => false, 'error' => 'No user_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If last_id is provided, return new messages since then
|
||||
if (isset($_GET['last_id'])) {
|
||||
$last_id = (int)$_GET['last_id'];
|
||||
$stmt = $pdo->prepare("SELECT * FROM messages WHERE user_id = ? AND id > ? ORDER BY id ASC");
|
||||
$stmt->execute([$user_id, $last_id]);
|
||||
$msgs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
echo json_encode(['success' => true, 'data' => $msgs]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Default action: return count and last_id, and if requested, full data
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*), MAX(id) FROM messages WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$res = $stmt->fetch();
|
||||
$count = $res[0];
|
||||
$last_id = $res[1];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM messages WHERE user_id = ? ORDER BY id DESC LIMIT 20");
|
||||
// Default action: return last 50 messages in ASC order
|
||||
$stmt = $pdo->prepare("SELECT * FROM (SELECT * FROM messages WHERE user_id = ? ORDER BY id DESC LIMIT 50) AS sub ORDER BY id ASC");
|
||||
$stmt->execute([$user_id]);
|
||||
$msgs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'count' => (int)$count,
|
||||
'last_id' => (int)$last_id,
|
||||
'data' => $msgs
|
||||
]);
|
||||
]);
|
||||
|
||||
@ -4,17 +4,22 @@ session_start();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
// Check authorization - either user session or admin session
|
||||
// For simplicity in this environment, we assume if it's called from admin it might have different session or we check params
|
||||
// But usually admin also has session_start() and auth.php
|
||||
|
||||
$pdo = db();
|
||||
|
||||
$sender = isset($_GET['sender']) && $_GET['sender'] === 'admin' ? 'admin' : 'user';
|
||||
$user_id = isset($_GET['user_id']) ? $_GET['user_id'] : ($_SESSION['user_id'] ?? null);
|
||||
|
||||
if (!$user_id) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized or missing User ID']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$pdo = db();
|
||||
|
||||
// Handle Confirm Payment action
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'confirm_payment') {
|
||||
// Check for active order that is 'matched'
|
||||
// Handle Confirm Payment action (User only)
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'confirm_payment' && $sender === 'user') {
|
||||
$stmt = $pdo->prepare("SELECT id FROM fiat_orders WHERE user_id = ? AND status = 'matched' ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$user_id]);
|
||||
$order = $stmt->fetch();
|
||||
@ -24,11 +29,8 @@ if (isset($_GET['action']) && $_GET['action'] === 'confirm_payment') {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update status to submitting
|
||||
$stmt = $pdo->prepare("UPDATE fiat_orders SET status = 'submitting' WHERE id = ?");
|
||||
$stmt->execute([$order['id']]);
|
||||
|
||||
// Send a system message to chat
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'user', '我已完成支付,请查收凭证。')")->execute([$user_id]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
@ -49,7 +51,7 @@ if (!in_array($ext, $allowed)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$filename = 'chat_' . $user_id . '_' . time() . '_' . mt_rand(1000, 9999) . '.' . $ext;
|
||||
$filename = 'chat_' . ($sender === 'admin' ? 'admin_' : '') . $user_id . '_' . time() . '_' . mt_rand(1000, 9999) . '.' . $ext;
|
||||
$dir = '../assets/images/chat/';
|
||||
if (!is_dir($dir)) mkdir($dir, 0775, true);
|
||||
|
||||
@ -58,17 +60,18 @@ $target = $dir . $filename;
|
||||
if (move_uploaded_file($file['tmp_name'], $target)) {
|
||||
$path = 'assets/images/chat/' . $filename;
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, sender, type, message) VALUES (?, 'user', 'image', ?)");
|
||||
$stmt->execute([$user_id, $path]);
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, sender, type, message) VALUES (?, ?, 'image', ?)");
|
||||
$stmt->execute([$user_id, $sender, $path]);
|
||||
|
||||
// If there is an active order, update its proof_image
|
||||
$stmt = $pdo->prepare("SELECT id FROM fiat_orders WHERE user_id = ? AND status IN ('matched', 'matching', 'submitting') ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$user_id]);
|
||||
$order = $stmt->fetch();
|
||||
|
||||
if ($order) {
|
||||
$stmt = $pdo->prepare("UPDATE fiat_orders SET proof_image = ? WHERE id = ?");
|
||||
$stmt->execute([$path, $order['id']]);
|
||||
// If it's a user uploading, also update active order proof
|
||||
if ($sender === 'user') {
|
||||
$stmt = $pdo->prepare("SELECT id FROM fiat_orders WHERE user_id = ? AND status IN ('matched', 'matching', 'submitting') ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$user_id]);
|
||||
$order = $stmt->fetch();
|
||||
if ($order) {
|
||||
$stmt = $pdo->prepare("UPDATE fiat_orders SET proof_image = ? WHERE id = ?");
|
||||
$stmt->execute([$path, $order['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'path' => $path]);
|
||||
|
||||
@ -19,6 +19,7 @@ body {
|
||||
color: var(--text-color);
|
||||
line-height: 1.5;
|
||||
padding-bottom: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
@ -60,6 +61,29 @@ body {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Utility Classes */
|
||||
.d-none { display: none !important; }
|
||||
.d-flex { display: flex !important; }
|
||||
.d-block { display: block !important; }
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.d-sm-none { display: none !important; }
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.d-md-none { display: none !important; }
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.d-md-block { display: block !important; }
|
||||
.d-md-flex { display: flex !important; }
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.d-lg-block { display: block !important; }
|
||||
.d-lg-flex { display: flex !important; }
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.d-xl-flex { display: flex !important; }
|
||||
}
|
||||
|
||||
/* Colorful Icons */
|
||||
.fa-home { color: #5d5dff; }
|
||||
.fa-chart-line { color: #00e676; }
|
||||
@ -181,12 +205,6 @@ body {
|
||||
}
|
||||
.sidebar-overlay.open { display: block; }
|
||||
|
||||
/* Market Trends Table */
|
||||
.market-table-container {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* Responsive Grid Helper */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
@ -197,30 +215,43 @@ body {
|
||||
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; }
|
||||
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 30px; }
|
||||
|
||||
.responsive-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 380px;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* Mobile Optimizations */
|
||||
@media (max-width: 992px) {
|
||||
.navbar { padding: 0 1rem; }
|
||||
.nav-links { display: none; }
|
||||
.hero-section { flex-direction: column; text-align: center; padding: 40px 5%; }
|
||||
.grid-3, .grid-2 { grid-template-columns: 1fr; }
|
||||
.grid-3, .grid-2, .responsive-grid { grid-template-columns: 1fr; }
|
||||
.mobile-bottom-nav { display: flex; }
|
||||
body { padding-bottom: 70px; }
|
||||
|
||||
.section-title { font-size: 1.8rem; }
|
||||
|
||||
/* Market Table Mobile */
|
||||
.market-table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.market-table th:nth-child(4),
|
||||
.market-table td:nth-child(4) { display: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.logo-text { font-size: 1.4rem !important; }
|
||||
.logo-svg { width: 28px !important; height: 28px !important; }
|
||||
.container { padding: 0 15px; }
|
||||
.logo-text { font-size: 1.2rem !important; }
|
||||
.logo-svg { width: 24px !important; height: 24px !important; }
|
||||
|
||||
.btn-login-hide { display: none; }
|
||||
|
||||
.market-table th, .market-table td { padding: 12px 15px !important; }
|
||||
.market-table td div { font-size: 0.9rem !important; }
|
||||
.market-table th, .market-table td { padding: 10px 12px !important; }
|
||||
.market-table td div { font-size: 0.85rem !important; }
|
||||
|
||||
.deposit-card, .withdraw-card { padding: 25px 20px !important; }
|
||||
}
|
||||
|
||||
/* User Profile Dropdown Adjustments */
|
||||
|
||||
BIN
assets/pasted-20260214-025356-727c5b4a.png
Normal file
BIN
assets/pasted-20260214-025356-727c5b4a.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
81
chat.php
81
chat.php
@ -9,7 +9,7 @@ $user_id = $_SESSION['user_id'];
|
||||
$pdo = db();
|
||||
|
||||
// Fetch user info
|
||||
$stmt = $pdo->prepare("SELECT uid, username FROM users WHERE id = ?");
|
||||
$stmt = $pdo->prepare("SELECT uid, username, last_ip FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
@ -25,8 +25,8 @@ if (isset($_GET['action']) && $_GET['action'] === 'complete_order' && $active_or
|
||||
$stmt = $pdo->prepare("UPDATE fiat_orders SET status = 'paid' WHERE id = ?");
|
||||
$stmt->execute([$active_order['id']]);
|
||||
|
||||
$type_text = ($active_order['order_type'] === 'deposit') ? "充值" : "提现";
|
||||
$msg = "✅ 用户已点击确认,$type_text 申请等待审核中。";
|
||||
$type_text = ($active_order['order_type'] === 'deposit') ? __('nav_deposit') : __('nav_withdraw');
|
||||
$msg = "✅ " . __('confirm_transfer') . ", $type_text " . __('paid_waiting_tip');
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$user_id, $msg]);
|
||||
|
||||
@ -37,7 +37,10 @@ if (isset($_GET['action']) && $_GET['action'] === 'complete_order' && $active_or
|
||||
// Fetch greeting message
|
||||
$stmt = $pdo->prepare("SELECT value FROM settings WHERE name = 'chat_greeting'");
|
||||
$stmt->execute();
|
||||
$greeting = $stmt->fetchColumn() ?: '您好!欢迎咨询 NovaEx 官方客服,请问有什么可以帮您?如果是充值咨询,请提供您的充值金额和币种。';
|
||||
$greeting = $stmt->fetchColumn();
|
||||
if (!$greeting) {
|
||||
$greeting = ($lang == 'zh') ? '您好!欢迎咨询 NovaEx 官方客服,请问有什么可以帮您?如果是充值咨询,请提供您的充值金额和币种。' : 'Hello! Welcome to NovaEx official customer service. How can we help you today? For deposit inquiries, please provide your amount and currency.';
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
$msg = trim($_POST['message']);
|
||||
@ -59,9 +62,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
.msg-container { display: flex; flex-direction: column; transition: all 0.3s ease; }
|
||||
.msg-content { max-width: 75%; padding: 12px 18px; border-radius: 18px; font-size: 14px; line-height: 1.6; position: relative; }
|
||||
|
||||
.locked-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); backdrop-filter: blur(5px); z-index: 9999; display: flex; align-items: center; justify-content: center; }
|
||||
.locked-modal { background: #1e2329; width: 90%; max-width: 500px; border-radius: 24px; padding: 40px; text-align: center; border: 1px solid var(--primary-color); }
|
||||
|
||||
.account-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 450px; background: #1e2329; border-radius: 24px; border: 1px solid var(--primary-color); z-index: 10001; padding: 30px; box-shadow: 0 20px 50px rgba(0,0,0,0.8); display: none; }
|
||||
.modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 10000; display: none; }
|
||||
|
||||
@ -78,13 +78,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
<div style="width: 60px; height: 60px; background: rgba(240,185,11,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--primary-color); margin: 0 auto 15px; font-size: 24px;">
|
||||
<i class="fas fa-university"></i>
|
||||
</div>
|
||||
<h3 style="margin: 0; color: white;" id="modal-title">账户已匹配</h3>
|
||||
<p style="color: var(--text-muted); font-size: 14px; margin-top: 5px;" id="modal-tip">请按照下方账户信息完成操作。</p>
|
||||
<h3 style="margin: 0; color: white;" id="modal-title"><?php echo __('matched_status'); ?></h3>
|
||||
<p style="color: var(--text-muted); font-size: 14px; margin-top: 5px;" id="modal-tip"><?php echo __('matched_info_tip'); ?></p>
|
||||
</div>
|
||||
<div id="acc-info" style="background: #161a1e; padding: 20px; border-radius: 16px; color: white; font-size: 14px; line-height: 2; border: 1px solid #2b3139; margin-bottom: 25px;">
|
||||
<!-- Filled by JS -->
|
||||
</div>
|
||||
<button onclick="closeAccModal()" class="btn-primary" style="width: 100%; padding: 15px; border-radius: 12px; font-weight: 800;">确认</button>
|
||||
<button onclick="closeAccModal()" class="btn-primary" style="width: 100%; padding: 15px; border-radius: 12px; font-weight: 800;"><?php echo __('confirm'); ?></button>
|
||||
</div>
|
||||
|
||||
<div id="chat-container" class="container" style="max-width: 850px; margin: 30px auto; padding: 0; height: calc(100vh - 200px); min-height: 500px;">
|
||||
@ -93,25 +93,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<i class="fas fa-lock"></i>
|
||||
<div>
|
||||
<div style="font-size: 14px; font-weight: 800;"><?php echo ($active_order['order_type'] === 'deposit') ? '充值进行中' : '提现申请中'; ?></div>
|
||||
<div style="font-size: 14px; font-weight: 800;"><?php echo ($active_order['order_type'] === 'deposit') ? __('nav_deposit') : __('nav_withdraw'); ?></div>
|
||||
<div style="font-size: 12px; opacity: 0.8;"><?php
|
||||
if ($active_order['status'] === 'matching') echo '等待客服响应...';
|
||||
elseif ($active_order['status'] === 'matched') echo ($active_order['order_type'] === 'deposit') ? '请按要求转账并上传凭证' : '请提供您的收款信息';
|
||||
elseif ($active_order['status'] === 'paid') echo '正在审核凭证,请稍候...';
|
||||
if ($active_order['status'] === 'matching') echo __('matching_status') . '...';
|
||||
elseif ($active_order['status'] === 'matched') echo ($active_order['order_type'] === 'deposit') ? __('matched_info_tip') : __('withdraw_info_tip');
|
||||
elseif ($active_order['status'] === 'paid') echo __('paid_waiting_tip');
|
||||
?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<?php if ($active_order['status'] === 'matched' && $active_order['order_type'] === 'deposit'): ?>
|
||||
<?php if ($active_order['status'] === 'matched'): ?>
|
||||
<button onclick="showCurrentAccInfo()" class="btn-primary" style="padding: 8px 15px; font-size: 12px; border-radius: 8px; background: #2b3139; color: white; border: 1px solid #3b424d;">
|
||||
查看账户
|
||||
<?php echo ($active_order['order_type'] === 'deposit') ? __('view_account') : __('view_withdraw_format'); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($active_order['status'] === 'matched'): ?>
|
||||
<button onclick="window.location.href='?action=complete_order'" class="btn-primary" style="padding: 8px 15px; font-size: 12px; border-radius: 8px;">
|
||||
<?php echo ($active_order['order_type'] === 'deposit') ? '完成转账' : '确认发送'; ?>
|
||||
<?php echo ($active_order['order_type'] === 'deposit') ? __('complete_transfer_btn') : __('confirm'); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@ -126,21 +126,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
<i class="fas fa-headset fa-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="margin: 0; font-size: 18px; color: white;">官方客服中心</h3>
|
||||
<h3 style="margin: 0; font-size: 18px; color: white;"><?php echo __('support_online'); ?></h3>
|
||||
<div style="display: flex; align-items: center; gap: 5px; font-size: 12px; color: #00c087;">
|
||||
<span style="width: 8px; height: 8px; background: #00c087; border-radius: 50%; display: inline-block;"></span> 24/7 全天候在线
|
||||
<span style="width: 8px; height: 8px; background: #00c087; border-radius: 50%; display: inline-block;"></span> 24/7 <?php echo ($lang == 'zh' ? '全天候在线' : 'Always Online'); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; font-size: 12px; color: #848e9c;">
|
||||
<div>UID: <?php echo $user['uid']; ?></div>
|
||||
<a href="index.php" style="color: var(--primary-color); text-decoration: none; display: <?php echo $is_locked ? 'none' : 'block'; ?>; margin-top: 5px;"><i class="fas fa-times"></i> 退出</a>
|
||||
<div>UID: <?php echo htmlspecialchars($user['uid']); ?></div>
|
||||
<div style="font-size: 10px; opacity: 0.7;">IP: <?php echo htmlspecialchars($user['last_ip']); ?></div>
|
||||
<a href="index.php" style="color: var(--primary-color); text-decoration: none; display: <?php echo $is_locked ? 'none' : 'block'; ?>; margin-top: 5px;"><i class="fas fa-times"></i> <?php echo __('cancel'); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat Body -->
|
||||
<div id="chat-box">
|
||||
<div style="text-align: center; color: #848e9c; padding: 20px;">正在连接客服...</div>
|
||||
<div style="text-align: center; color: #848e9c; padding: 20px;">Connecting...</div>
|
||||
</div>
|
||||
|
||||
<!-- Input Area -->
|
||||
@ -151,7 +152,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
</button>
|
||||
<input type="file" id="image-input" accept="image/*" style="display: none;" onchange="uploadImage(this)">
|
||||
|
||||
<input type="text" id="chat-input" placeholder="请输入消息..."
|
||||
<input type="text" id="chat-input" placeholder="<?php echo __('type_message'); ?>"
|
||||
style="flex: 1; background: #161a1e; border: 1px solid #2b3139; border-radius: 12px; padding: 14px 20px; color: white; outline: none; font-size: 14px;" autocomplete="off">
|
||||
|
||||
<button type="submit" style="background: #f0b90b; border: none; color: black; width: 50px; height: 50px; border-radius: 12px; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0;">
|
||||
@ -164,15 +165,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
|
||||
<?php if ($is_locked): ?>
|
||||
<script>
|
||||
// Prevent leaving the page
|
||||
window.onbeforeunload = function() {
|
||||
return "您的充值/提现流程尚未完成,请勿离开此页面。";
|
||||
return "<?php echo ($lang == 'zh' ? '您的业务流程尚未完成,请勿离开此页面。' : 'Your transaction is not complete. Please stay on this page.'); ?>";
|
||||
};
|
||||
// Hide navigation elements
|
||||
document.querySelector('nav')?.style.display = 'none';
|
||||
document.querySelector('footer')?.style.display = 'none';
|
||||
|
||||
// Prevent back navigation
|
||||
history.pushState(null, null, location.href);
|
||||
window.onpopstate = function() {
|
||||
history.pushState(null, null, location.href);
|
||||
@ -201,7 +199,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
<div class="msg-content" style="background: #2b3139; color: white; border-bottom-left-radius: 4px; border: 1px solid #3b424d;">
|
||||
${greeting.replace(/\n/g, '<br>')}
|
||||
</div>
|
||||
<span style="font-size: 10px; color: #5e6673; margin-top: 6px;">系统助手</span>
|
||||
<span style="font-size: 10px; color: #5e6673; margin-top: 6px;">Support</span>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
@ -233,20 +231,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
if (res.success && res.order) {
|
||||
currentBankAccountInfo = res.order.bank_account_info;
|
||||
if (res.order.status !== lastStatus) {
|
||||
if (res.order.status === 'matched') {
|
||||
// Only show modal for DEPOSIT
|
||||
if (res.order.order_type === 'deposit') {
|
||||
showAccModal('账户已匹配', '请按照下方账户信息完成转账,并务必上传截图凭证。', res.order.bank_account_info);
|
||||
}
|
||||
const title = res.order.order_type === 'deposit' ? '<?php echo __('matched_status'); ?>' : '<?php echo __('withdraw_format_title'); ?>';
|
||||
const tip = res.order.order_type === 'deposit' ? '<?php echo __('matched_info_tip'); ?>' : '<?php echo __('withdraw_info_tip'); ?>';
|
||||
showAccModal(title, tip, res.order.bank_account_info);
|
||||
} else if (res.order.status === 'completed' || res.order.status === 'rejected') {
|
||||
alert(res.order.status === 'completed' ? "业务处理成功!" : "业务被拒绝,请联系客服了解详情。");
|
||||
alert(res.order.status === 'completed' ? "Success!" : "Request rejected.");
|
||||
window.onbeforeunload = null;
|
||||
location.href = 'profile.php';
|
||||
}
|
||||
lastStatus = res.order.status;
|
||||
}
|
||||
} else if (res.success && !res.order && lastStatus !== '') {
|
||||
// Order disappeared or completed
|
||||
window.onbeforeunload = null;
|
||||
location.href = 'profile.php';
|
||||
}
|
||||
@ -263,7 +258,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
}
|
||||
|
||||
function showCurrentAccInfo() {
|
||||
showAccModal('收款账户信息', '请核对以下转账信息。', currentBankAccountInfo);
|
||||
const orderType = '<?php echo $active_order['order_type'] ?? ''; ?>';
|
||||
const title = orderType === 'deposit' ? '<?php echo __('view_account'); ?>' : '<?php echo __('view_withdraw_format'); ?>';
|
||||
const tip = orderType === 'deposit' ? '<?php echo __('matched_info_tip'); ?>' : '<?php echo __('withdraw_info_tip'); ?>';
|
||||
showAccModal(title, tip, currentBankAccountInfo);
|
||||
}
|
||||
|
||||
function closeAccModal() {
|
||||
@ -298,21 +296,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
if (res.success) {
|
||||
loadMessages();
|
||||
} else {
|
||||
alert(res.error || '上传失败');
|
||||
alert(res.error || 'Upload failed');
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
uploadBtn.disabled = false;
|
||||
uploadBtn.innerHTML = '<i class="fas fa-plus"></i>';
|
||||
input.value = ''; // Reset input
|
||||
input.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// Show modal on load if matched and deposit
|
||||
window.onload = () => {
|
||||
const orderType = '<?php echo $active_order['order_type'] ?? ''; ?>';
|
||||
if (lastStatus === 'matched' && orderType === 'deposit' && currentBankAccountInfo) {
|
||||
showAccModal('账户已匹配', '请按照下方账户信息完成转账,并务必上传截图凭证。', currentBankAccountInfo);
|
||||
if (lastStatus === 'matched' && currentBankAccountInfo) {
|
||||
const title = orderType === 'deposit' ? '<?php echo __('matched_status'); ?>' : '<?php echo __('withdraw_format_title'); ?>';
|
||||
const tip = orderType === 'deposit' ? '<?php echo __('matched_info_tip'); ?>' : '<?php echo __('withdraw_info_tip'); ?>';
|
||||
showAccModal(title, tip, currentBankAccountInfo);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
166
chat_iframe.php
166
chat_iframe.php
@ -1,9 +1,10 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/i18n.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
die("Please login first.");
|
||||
die(__('please_login'));
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
@ -18,40 +19,75 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch greeting message
|
||||
$stmt = $db->prepare("SELECT value FROM settings WHERE name = 'chat_greeting'");
|
||||
$stmt->execute();
|
||||
$greeting = $stmt->fetchColumn();
|
||||
if (!$greeting) {
|
||||
$greeting = ($lang == 'zh') ? '您好!欢迎咨询 NovaEx 官方客服,请问有什么可以帮您?' : 'Hello! Welcome to NovaEx official customer service. How can we help you today?';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="<?php echo $lang; ?>">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<style>
|
||||
body { margin: 0; padding: 0; font-family: 'Inter', sans-serif; background: #161a1e; color: white; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
|
||||
:root {
|
||||
--primary-color: #f0b90b;
|
||||
--bg-color: #161a1e;
|
||||
--card-bg: #1e2329;
|
||||
--border-color: #2b3139;
|
||||
--text-color: #ffffff;
|
||||
--text-muted: #848e9c;
|
||||
}
|
||||
body { margin: 0; padding: 0; font-family: 'Inter', sans-serif; background: var(--bg-color); color: white; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
|
||||
#chat-box { flex: 1; overflow-y: auto; padding: 15px; display: flex; flex-direction: column; gap: 12px; scroll-behavior: smooth; }
|
||||
.msg { max-width: 80%; padding: 10px 14px; border-radius: 12px; font-size: 14px; line-height: 1.4; word-wrap: break-word; }
|
||||
.msg.user { align-self: flex-end; background: #f0b90b; color: black; border-bottom-right-radius: 2px; }
|
||||
.msg { max-width: 80%; padding: 10px 14px; border-radius: 12px; font-size: 14px; line-height: 1.4; word-wrap: break-word; position: relative; }
|
||||
.msg.user { align-self: flex-end; background: var(--primary-color); color: black; border-bottom-right-radius: 2px; }
|
||||
.msg.admin { align-self: flex-start; background: #2b3139; color: #EAECEF; border-bottom-left-radius: 2px; border: 1px solid #3b424d; }
|
||||
.msg-time { font-size: 10px; opacity: 0.5; margin-top: 4px; display: block; }
|
||||
.chat-input-area { padding: 12px; background: #1e2329; border-top: 1px solid #2b3139; display: flex; gap: 10px; align-items: center; }
|
||||
input[type="text"] { flex: 1; background: #0b0e11; border: 1px solid #2b3139; border-radius: 8px; padding: 10px 12px; color: white; outline: none; }
|
||||
.icon-btn { background: #2b3139; border: 1px solid #3b424d; width: 40px; height: 40px; border-radius: 8px; color: #f0b90b; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
|
||||
.icon-btn { background: #2b3139; border: 1px solid #3b424d; width: 40px; height: 40px; border-radius: 8px; color: var(--primary-color); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
|
||||
.icon-btn:hover { background: #3b424d; }
|
||||
.send-btn { background: #f0b90b; border: none; width: 40px; height: 40px; border-radius: 8px; color: black; cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
||||
.send-btn { background: var(--primary-color); border: none; width: 40px; height: 40px; border-radius: 8px; color: black; cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
||||
#chat-box::-webkit-scrollbar { width: 4px; }
|
||||
#chat-box::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
|
||||
img.chat-img { max-width: 100%; border-radius: 8px; margin-top: 5px; cursor: pointer; }
|
||||
|
||||
/* Modal inside iframe */
|
||||
.modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 85%; background: var(--card-bg); border-radius: 16px; border: 1px solid var(--primary-color); z-index: 1000; padding: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); display: none; }
|
||||
.backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 999; display: none; }
|
||||
.modal-btn { width: 100%; background: var(--primary-color); border: none; padding: 10px; border-radius: 8px; font-weight: bold; cursor: pointer; margin-top: 15px; }
|
||||
|
||||
.sending { opacity: 0.7; }
|
||||
.sending::after { content: '...'; position: absolute; right: 5px; bottom: 5px; font-size: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="acc-backdrop" class="backdrop"></div>
|
||||
<div id="acc-modal" class="modal">
|
||||
<div style="text-align: center; margin-bottom: 15px;">
|
||||
<i class="fas fa-university" style="color: var(--primary-color); font-size: 24px;"></i>
|
||||
<h4 style="margin: 10px 0 5px;" id="modal-title"><?php echo __('matched_status'); ?></h4>
|
||||
<p style="color: var(--text-muted); font-size: 12px;" id="modal-tip"><?php echo __('matched_info_tip'); ?></p>
|
||||
</div>
|
||||
<div id="acc-info" style="background: #161a1e; padding: 15px; border-radius: 8px; font-size: 13px; line-height: 1.6; border: 1px solid #2b3139;"></div>
|
||||
<button onclick="closeModal()" class="modal-btn"><?php echo __('confirm'); ?></button>
|
||||
</div>
|
||||
|
||||
<div id="chat-box"></div>
|
||||
|
||||
<form id="chat-form" class="chat-input-area">
|
||||
<button type="button" class="icon-btn" id="upload-btn" onclick="document.getElementById('image-input').click()">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
<input type="file" id="image-input" accept="image/*" style="display: none;" onchange="uploadImage(this)>
|
||||
<input type="file" id="image-input" accept="image/*" style="display: none;" onchange="uploadImage(this)">
|
||||
|
||||
<input type="text" id="msg-input" placeholder="Type a message..." autocomplete="off">
|
||||
<input type="text" id="msg-input" placeholder="<?php echo __('type_message'); ?>" autocomplete="off">
|
||||
<button type="submit" class="send-btn"><i class="fas fa-paper-plane"></i></button>
|
||||
</form>
|
||||
|
||||
@ -59,41 +95,110 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
const chatBox = document.getElementById('chat-box');
|
||||
const msgInput = document.getElementById('msg-input');
|
||||
const uploadBtn = document.getElementById('upload-btn');
|
||||
|
||||
const greeting = `<?php echo addslashes($greeting); ?>`;
|
||||
let lastStatus = '';
|
||||
let lastOrderId = 0;
|
||||
let lastMessagesHtml = '';
|
||||
let isSending = false;
|
||||
|
||||
async function loadMessages() {
|
||||
if (isSending) return; // Prevent refresh while sending to avoid flickering
|
||||
try {
|
||||
const resp = await fetch('api/get_messages.php');
|
||||
const res = await resp.json();
|
||||
if (res.success) {
|
||||
let html = '';
|
||||
res.data.forEach(m => {
|
||||
const content = m.type === 'image'
|
||||
? `<img src="${m.message}" class="chat-img" onclick="window.open(this.src)">`
|
||||
: m.message.replace(/\n/g, '<br>');
|
||||
|
||||
html += `
|
||||
<div class="msg ${m.sender}">
|
||||
${content}
|
||||
<span class="msg-time">${new Date(m.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
|
||||
if (res.data.length === 0) {
|
||||
chatBox.innerHTML = `
|
||||
<div class="msg admin">
|
||||
${greeting.replace(/\n/g, '<br>')}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
const isAtBottom = chatBox.scrollHeight - chatBox.scrollTop <= chatBox.clientHeight + 100;
|
||||
chatBox.innerHTML = html;
|
||||
if (isAtBottom) chatBox.scrollTop = chatBox.scrollHeight;
|
||||
} else {
|
||||
let html = '';
|
||||
res.data.forEach(m => {
|
||||
const content = m.type === 'image'
|
||||
? `<img src="${m.message}" class="chat-img" onclick="window.open(this.src)">`
|
||||
: m.message.replace(/\n/g, '<br>');
|
||||
|
||||
html += `
|
||||
<div class="msg ${m.sender}">
|
||||
${content}
|
||||
<span class="msg-time">${new Date(m.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
if (html !== lastMessagesHtml) {
|
||||
const isAtBottom = chatBox.scrollHeight - chatBox.scrollTop <= chatBox.clientHeight + 100;
|
||||
chatBox.innerHTML = html;
|
||||
lastMessagesHtml = html;
|
||||
if (isAtBottom) chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
// Mark messages as read
|
||||
fetch('api/get_messages.php?action=mark_read&user_id=<?php echo $user_id; ?>&reader=user');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function checkOrderStatus() {
|
||||
try {
|
||||
const resp = await fetch('api/check_order_status.php');
|
||||
const res = await resp.json();
|
||||
if (res.success && res.order) {
|
||||
if (res.order.status === 'matched' && (res.order.status !== lastStatus || res.order.id !== lastOrderId)) {
|
||||
const title = res.order.order_type === 'deposit' ? '<?php echo __('matched_status'); ?>' : '<?php echo __('withdraw_format_title'); ?>';
|
||||
const tip = res.order.order_type === 'deposit' ? '<?php echo __('matched_info_tip'); ?>' : '<?php echo __('withdraw_info_tip'); ?>';
|
||||
showModal(title, tip, res.order.bank_account_info);
|
||||
lastStatus = res.order.status;
|
||||
lastOrderId = res.order.id;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function showModal(title, tip, info) {
|
||||
if (!info) return;
|
||||
document.getElementById('modal-title').innerText = title;
|
||||
document.getElementById('modal-tip').innerText = tip;
|
||||
document.getElementById('acc-info').innerHTML = info.replace(/\n/g, '<br>');
|
||||
document.getElementById('acc-backdrop').style.display = 'block';
|
||||
document.getElementById('acc-modal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('acc-backdrop').style.display = 'none';
|
||||
document.getElementById('acc-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('chat-form').onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const msg = msgInput.value.trim();
|
||||
if (!msg) return;
|
||||
if (!msg || isSending) return;
|
||||
|
||||
msgInput.value = '';
|
||||
isSending = true;
|
||||
|
||||
// Optimistic UI: Add message locally
|
||||
const tempMsg = document.createElement('div');
|
||||
tempMsg.className = 'msg user sending';
|
||||
tempMsg.innerHTML = `${msg.replace(/\n/g, '<br>')}<span class="msg-time">${new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>`;
|
||||
chatBox.appendChild(tempMsg);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('message', msg);
|
||||
await fetch('chat_iframe.php', { method: 'POST', body: formData });
|
||||
loadMessages();
|
||||
|
||||
try {
|
||||
await fetch('chat_iframe.php', { method: 'POST', body: formData });
|
||||
isSending = false;
|
||||
await loadMessages();
|
||||
} catch (e) {
|
||||
isSending = false;
|
||||
tempMsg.style.background = '#ff4d4f';
|
||||
tempMsg.innerHTML += ' (Failed)';
|
||||
}
|
||||
};
|
||||
|
||||
function uploadImage(input) {
|
||||
@ -120,8 +225,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['message'])) {
|
||||
});
|
||||
}
|
||||
|
||||
// Initial calls
|
||||
loadMessages();
|
||||
setInterval(loadMessages, 3000);
|
||||
checkOrderStatus();
|
||||
|
||||
// Polling
|
||||
setInterval(loadMessages, 1000);
|
||||
setInterval(checkOrderStatus, 2000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -5,6 +5,9 @@ define('DB_NAME', 'app_38350');
|
||||
define('DB_USER', 'app_38350');
|
||||
define('DB_PASS', 'c79d2d31-7d44-4d51-ac22-5bfd0886fcc2');
|
||||
|
||||
// Set timezone to China Standard Time
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
|
||||
function db() {
|
||||
static $pdo;
|
||||
if (!$pdo) {
|
||||
|
||||
@ -231,10 +231,10 @@ CREATE TABLE `users` (
|
||||
-- Dump completed on 2026-02-12 14:31:49
|
||||
|
||||
-- Dumping data
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
INSERT INTO `admins` VALUES
|
||||
(1,'admin','$2y$10$dltSsFIkB4FbJPgOPUFLGuDehX/bAFM96fxywvSL9jaEsgA/1r4pG','admin',NULL,NULL,'2026-02-12 08:33:20');
|
||||
INSERT INTO `settings` VALUES
|
||||
INSERT INTO `admins` (id, username, password, role, created_at) VALUES
|
||||
(1,'admin','$2y$10$RJoR9U.GZwbNe14JqaWY7e2NQcJVNQYGr6rDFyzqVne4D1Ym5/ORS','admin',NOW());
|
||||
|
||||
INSERT INTO `settings` (name, value) VALUES
|
||||
('chat_greeting','您好!欢迎咨询 NovaEx 官方客服,请问有什么可以帮您?如果是充值咨询,请提供您的充值金额和币种。'),
|
||||
('price_control','0'),
|
||||
('win_rate','70');
|
||||
('win_rate','70');
|
||||
102
deposit.php
102
deposit.php
@ -1,15 +1,19 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; }
|
||||
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/currency_helper.php';
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
// 检查是否有正在进行的订单,如果有则跳转到聊天
|
||||
$stmt = $db->prepare("SELECT id FROM fiat_orders WHERE user_id = ? AND status IN ('matching', 'matched', 'paid') ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
if ($stmt->fetch()) { header("Location: chat.php"); exit; }
|
||||
|
||||
include 'header.php';
|
||||
|
||||
$fiat_rates = get_fiat_rates();
|
||||
$fiat_currencies_info = [
|
||||
@ -45,12 +49,18 @@ $fiat_currencies_info = [
|
||||
.input-group-custom input { background: none; border: none; color: white; font-size: 1.2rem; font-weight: 700; width: 100%; outline: none; }
|
||||
|
||||
.safe-banner { background: rgba(14, 203, 129, 0.1); border: 1px solid rgba(14, 203, 129, 0.2); padding: 20px; border-radius: 16px; color: var(--success-color); display: flex; gap: 15px; align-items: center; margin-bottom: 30px; }
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.method-card { padding: 20px 15px; }
|
||||
.method-card .icon-box { width: 40px; height: 40px; font-size: 16px; }
|
||||
.method-card div:nth-child(2) { font-size: 0.95rem !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="deposit-container">
|
||||
<div class="container" style="max-width: 1100px;">
|
||||
<div style="margin-bottom: 30px;">
|
||||
<a href="profile.php" class="back-btn"><i class="fas fa-arrow-left"></i> 个人中心</a>
|
||||
<a href="profile.php" class="back-btn" style="color: var(--text-muted); text-decoration: none; font-size: 14px;"><i class="fas fa-arrow-left"></i> 个人中心</a>
|
||||
<h1 style="font-size: 2.2rem; font-weight: 800; margin-top: 10px;">充值</h1>
|
||||
<p style="color: var(--text-muted);">通过多种通道安全地为您的账户充值</p>
|
||||
</div>
|
||||
@ -63,7 +73,7 @@ $fiat_currencies_info = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 380px; gap: 30px;">
|
||||
<div class="responsive-grid">
|
||||
<div>
|
||||
<div class="deposit-card" style="padding: 40px;">
|
||||
<h3 style="margin-bottom: 25px; font-weight: 800;">1. 选择充值方式</h3>
|
||||
@ -71,21 +81,21 @@ $fiat_currencies_info = [
|
||||
<div id="method-fiat" class="method-card active" onclick="switchDepositMethod('fiat')">
|
||||
<div class="icon-box" style="background: rgba(79,172,254,0.1); color: #4facfe;"><i class="fas fa-university"></i></div>
|
||||
<div style="font-weight: 800; font-size: 1.1rem;">法币充值</div>
|
||||
<div style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">银行转账 / 全球 OTC</div>
|
||||
<div style="color: var(--text-muted); font-size: 11px; margin-top: 4px;">银行转账 / OTC</div>
|
||||
</div>
|
||||
<div id="method-usdt" class="method-card" onclick="switchDepositMethod('usdt')">
|
||||
<div class="icon-box" style="background: rgba(14,203,129,0.1); color: var(--success-color);"><i class="fas fa-coins"></i></div>
|
||||
<div style="font-weight: 800; font-size: 1.1rem;">USDT 充值</div>
|
||||
<div style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">USDT (TRC20, ERC20, BEP20)</div>
|
||||
<div style="color: var(--text-muted); font-size: 11px; margin-top: 4px;">TRC20, ERC20...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="fiat-form-section">
|
||||
<h3 style="margin-bottom: 25px; font-weight: 800;">2. 订单详情</h3>
|
||||
<form action="matching.php" method="POST">
|
||||
<form id="fiat-deposit-form" action="matching.php" method="POST">
|
||||
<input type="hidden" name="order_type" value="deposit">
|
||||
<input type="hidden" name="type" value="fiat">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 25px;">
|
||||
<div style="display: flex; flex-direction: column; gap: 20px; margin-bottom: 25px;">
|
||||
<div>
|
||||
<label style="display: block; color: var(--text-muted); font-size: 13px; margin-bottom: 8px;">充值币种</label>
|
||||
<select name="currency" id="fiat-select" onchange="updateExchangeRate()" style="width: 100%; padding: 16px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 16px; outline: none; font-weight: 600;">
|
||||
@ -106,17 +116,17 @@ $fiat_currencies_info = [
|
||||
</div>
|
||||
|
||||
<div style="background: rgba(255,255,255,0.03); padding: 25px; border-radius: 20px; margin-bottom: 30px; border: 1px dashed var(--border-color);">
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 10px; font-size: 14px;">
|
||||
<span style="color: var(--text-muted);">实时汇率</span>
|
||||
<span style="font-weight: 700;">1 USDT ≈ <span id="rate-text">--</span> <span class="current-fiat-code">USD</span></span>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; border-top: 1px solid rgba(255,255,255,0.05); pt: 15px; margin-top: 15px; padding-top: 15px;">
|
||||
<span style="font-weight: 800; font-size: 1.1rem;">预计收到</span>
|
||||
<span style="font-weight: 800; font-size: 1.5rem; color: var(--primary-color);"><span id="receive-text">0.00</span> USDT</span>
|
||||
<span style="font-weight: 800; font-size: 1.4rem; color: var(--primary-color);"><span id="receive-text">0.00</span> USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 20px; border-radius: 16px; font-size: 1.1rem; font-weight: 800;">
|
||||
<button type="submit" class="btn-primary submit-btn" style="width: 100%; padding: 20px; border-radius: 16px; font-size: 1.1rem; font-weight: 800;">
|
||||
发起充值请求
|
||||
</button>
|
||||
</form>
|
||||
@ -124,13 +134,13 @@ $fiat_currencies_info = [
|
||||
|
||||
<div id="usdt-form-section" style="display: none;">
|
||||
<h3 style="margin-bottom: 25px; font-weight: 800;">2. 订单详情</h3>
|
||||
<form action="matching.php" method="POST">
|
||||
<form id="usdt-deposit-form" action="matching.php" method="POST">
|
||||
<input type="hidden" name="order_type" value="deposit">
|
||||
<input type="hidden" name="type" value="usdt">
|
||||
<input type="hidden" name="currency" value="USDT">
|
||||
|
||||
<label style="display: block; color: var(--text-muted); font-size: 13px; margin-bottom: 12px;">选择网络</label>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 25px;">
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 25px;">
|
||||
<label class="network-opt"><input type="radio" name="network" value="TRC20" checked style="display:none;"><div class="network-box">TRC20</div></label>
|
||||
<label class="network-opt"><input type="radio" name="network" value="ERC20" style="display:none;"><div class="network-box">ERC20</div></label>
|
||||
<label class="network-opt"><input type="radio" name="network" value="BEP20" style="display:none;"><div class="network-box">BEP20</div></label>
|
||||
@ -144,7 +154,7 @@ $fiat_currencies_info = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 20px; border-radius: 16px; font-size: 1.1rem; font-weight: 800; background: var(--success-color);">
|
||||
<button type="submit" class="btn-primary submit-btn" style="width: 100%; padding: 20px; border-radius: 16px; font-size: 1.1rem; font-weight: 800; background: var(--success-color);">
|
||||
发起充值请求
|
||||
</button>
|
||||
</form>
|
||||
@ -152,7 +162,7 @@ $fiat_currencies_info = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-sidebar">
|
||||
<div class="sidebar-area">
|
||||
<div class="deposit-card" style="padding: 30px;">
|
||||
<h4 style="font-weight: 800; margin-bottom: 25px;"><i class="fas fa-info-circle" style="color: var(--primary-color);"></i> 充值步骤说明</h4>
|
||||
<div class="instruction-item">
|
||||
@ -166,7 +176,7 @@ $fiat_currencies_info = [
|
||||
<div class="instruction-number">2</div>
|
||||
<div style="font-size: 14px; color: var(--text-muted);">
|
||||
<strong style="color: white; display: block; margin-bottom: 4px;">联系在线客服</strong>
|
||||
您将自动跳转至客服聊天界面,客服将为您匹配唯一的收款账户。
|
||||
您将自动弹出客服聊天界面,客服将为您匹配唯一的收款账户。
|
||||
</div>
|
||||
</div>
|
||||
<div class="instruction-item">
|
||||
@ -200,7 +210,7 @@ $fiat_currencies_info = [
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.network-box { padding: 15px; background: #161a1e; border: 1px solid var(--border-color); border-radius: 12px; text-align: center; font-weight: 700; cursor: pointer; transition: 0.3s; }
|
||||
.network-box { padding: 15px; background: #161a1e; border: 1px solid var(--border-color); border-radius: 12px; text-align: center; font-weight: 700; cursor: pointer; transition: 0.3s; font-size: 13px; }
|
||||
.network-opt input:checked + .network-box { border-color: var(--success-color); background: rgba(14,203,129,0.05); color: var(--success-color); }
|
||||
</style>
|
||||
|
||||
@ -227,6 +237,54 @@ $fiat_currencies_info = [
|
||||
}
|
||||
|
||||
updateExchangeRate();
|
||||
|
||||
// AJAX Form Submission
|
||||
const forms = ['fiat-deposit-form', 'usdt-deposit-form'];
|
||||
forms.forEach(id => {
|
||||
const form = document.getElementById(id);
|
||||
if (form) {
|
||||
form.onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const btn = form.querySelector('.submit-btn');
|
||||
const originalText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 处理中...';
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
const resp = await fetch('matching.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
const res = await resp.json();
|
||||
if (res.success) {
|
||||
// Trigger chat popup
|
||||
if (typeof openCSChat === 'function') {
|
||||
openCSChat();
|
||||
// Optional: Disable forms to prevent double submission
|
||||
form.style.opacity = '0.5';
|
||||
form.style.pointerEvents = 'none';
|
||||
btn.innerHTML = '<i class="fas fa-check"></i> 请求已发送';
|
||||
} else {
|
||||
window.location.href = 'chat.php';
|
||||
}
|
||||
} else {
|
||||
alert(res.error || '提交失败,请稍后重试');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert('网络错误,请稍后重试');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
127
futures.php
127
futures.php
@ -86,9 +86,62 @@ if ($user_id) {
|
||||
#ob-panel-content { flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2b3139 transparent; }
|
||||
#ob-panel-content::-webkit-scrollbar { width: 4px; }
|
||||
#ob-panel-content::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
|
||||
|
||||
/* Mobile Specific Styles */
|
||||
@media (max-width: 991px) {
|
||||
.trading-container { flex-direction: column; min-height: auto; }
|
||||
.center-col { min-width: 100%; border-right: none; }
|
||||
.chart-box { height: 300px; }
|
||||
.order-box { padding: 15px; }
|
||||
#records-list-container { height: auto; max-height: 400px; }
|
||||
.trading-page-wrapper { padding-bottom: 70px; }
|
||||
|
||||
.mobile-trade-nav {
|
||||
display: flex; background: #161a1e; border-bottom: 1px solid #2b3139; padding: 10px 15px; gap: 10px;
|
||||
}
|
||||
.mobile-trade-nav a {
|
||||
flex: 1; text-align: center; padding: 8px 0; background: #2b3139; border-radius: 8px; font-size: 13px; color: #848e9c; text-decoration: none; font-weight: 600; border: 1px solid transparent;
|
||||
}
|
||||
.mobile-trade-nav a.active {
|
||||
background: rgba(0, 82, 255, 0.1); color: var(--primary-color); border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.mobile-symbol-selector {
|
||||
display: flex; align-items: center; justify-content: space-between; padding: 12px 15px; background: #161a1e; border-bottom: 1px solid #2b3139;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.mobile-trade-nav, .mobile-symbol-selector { display: none; }
|
||||
}
|
||||
|
||||
/* Mobile Drawer */
|
||||
#mobile-pairs-drawer {
|
||||
position: fixed; top: 0; left: -100%; width: 100%; height: 100%; background: #0b0e11; z-index: 3000; transition: 0.3s; padding: 20px; display: flex; flex-direction: column;
|
||||
}
|
||||
#mobile-pairs-drawer.open { left: 0; }
|
||||
</style>
|
||||
|
||||
<div class="trading-page-wrapper">
|
||||
<!-- Mobile Navigation Tabs -->
|
||||
<div class="mobile-trade-nav">
|
||||
<a href="options.php"><?php echo __('nav_options'); ?></a>
|
||||
<a href="spot.php"><?php echo __('nav_spot'); ?></a>
|
||||
<a href="futures.php" class="active"><?php echo __('nav_futures'); ?></a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Symbol Selector -->
|
||||
<div class="mobile-symbol-selector" onclick="toggleMobilePairs()">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<img id="curr-icon-mobile" src="" class="coin-icon" style="width: 28px; height: 28px;">
|
||||
<span id="curr-pair-mobile" style="font-weight: 800; font-size: 18px; color: white;">--/--</span>
|
||||
<i class="fas fa-caret-down" style="color: #848e9c;"></i>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div id="curr-price-mobile" style="font-size: 18px; font-weight: 800; color: #0ecb81;">--</div>
|
||||
<div id="curr-change-mobile" style="font-size: 12px; font-weight: 600;">--</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trading-container">
|
||||
<!-- Left Column -->
|
||||
<div class="left-col d-none d-lg-flex">
|
||||
@ -103,7 +156,7 @@ if ($user_id) {
|
||||
|
||||
<!-- Center Column -->
|
||||
<div class="center-col">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header d-none d-lg-flex">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<img id="curr-icon" src="" class="coin-icon">
|
||||
<span id="curr-pair" style="font-weight: 800; font-size: 18px; color:white;">--/--</span>
|
||||
@ -159,19 +212,43 @@ if ($user_id) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Pairs Drawer -->
|
||||
<div id="mobile-pairs-drawer">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h3 style="margin: 0; font-weight: 800;"><?php echo __('market'); ?></h3>
|
||||
<i class="fas fa-times" onclick="toggleMobilePairs()" style="font-size: 20px; color: #848e9c;"></i>
|
||||
</div>
|
||||
<div class="search-box" style="padding: 0; margin-bottom: 20px;">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" id="pair-search-mobile" placeholder="<?php echo __('search_contract'); ?>">
|
||||
</div>
|
||||
<div id="pairs-list-mobile" style="flex: 1; overflow-y: auto;"></div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
||||
<script>
|
||||
const userAssets = <?php echo json_encode($user_assets); ?>;
|
||||
|
||||
function toggleMobilePairs() {
|
||||
document.getElementById('mobile-pairs-drawer').classList.toggle('open');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let currentPair = 'BTCUSDT', currentPrice = 0, ws, leverage = 20;
|
||||
let currentPair = 'BTCUSDT', currentPrice = 0, ws, leverage = 20, chartWidget;
|
||||
const marketData = {}, pairs = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT', 'DOTUSDT', 'MATICUSDT', 'LTCUSDT', 'TRXUSDT', 'LINKUSDT', 'UNIUSDT', 'ATOMUSDT', 'ETCUSDT', 'BCHUSDT', 'FILUSDT', 'ICPUSDT', 'NEARUSDT', 'AAVEUSDT', 'ALGOUSDT'];
|
||||
const dom = {
|
||||
currIcon: document.getElementById('curr-icon'),
|
||||
currPair: document.getElementById('curr-pair'),
|
||||
currPrice: document.getElementById('curr-price'),
|
||||
currChange: document.getElementById('curr-change'),
|
||||
currIconMobile: document.getElementById('curr-icon-mobile'),
|
||||
currPairMobile: document.getElementById('curr-pair-mobile'),
|
||||
currPriceMobile: document.getElementById('curr-price-mobile'),
|
||||
currChangeMobile: document.getElementById('curr-change-mobile'),
|
||||
tvContainer: document.getElementById('tv_chart_container'),
|
||||
pairsList: document.getElementById('pairs-list'),
|
||||
pairsListMobile: document.getElementById('pairs-list-mobile'),
|
||||
pairSearchMobile: document.getElementById('pair-search-mobile'),
|
||||
asksList: document.getElementById('asks-list'),
|
||||
bidsList: document.getElementById('bids-list'),
|
||||
midPrice: document.getElementById('mid-price'),
|
||||
@ -208,23 +285,37 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
marketData[data.s] = data;
|
||||
if (data.s === currentPair) {
|
||||
currentPrice = parseFloat(data.c);
|
||||
dom.currPrice.innerText = currentPrice.toLocaleString('en-US', {minimumFractionDigits:2});
|
||||
dom.currChange.innerText = (data.P >= 0 ? '+' : '') + parseFloat(data.P).toFixed(2) + '%';
|
||||
dom.currPrice.style.color = dom.currChange.style.color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
dom.midPrice.innerText = currentPrice.toFixed(2);
|
||||
const color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
const priceStr = currentPrice.toLocaleString('en-US', {minimumFractionDigits:2});
|
||||
const changeStr = (data.P >= 0 ? '+' : '') + parseFloat(data.P).toFixed(2) + '%';
|
||||
|
||||
if(dom.currPrice) dom.currPrice.innerText = priceStr;
|
||||
if(dom.currChange) dom.currChange.innerText = changeStr;
|
||||
if(dom.currPrice) dom.currPrice.style.color = dom.currChange.style.color = color;
|
||||
|
||||
dom.currPriceMobile.innerText = priceStr;
|
||||
dom.currPriceMobile.style.color = color;
|
||||
dom.currChangeMobile.innerText = changeStr;
|
||||
dom.currChangeMobile.style.color = color;
|
||||
|
||||
if(dom.midPrice) dom.midPrice.innerText = currentPrice.toFixed(2);
|
||||
}
|
||||
if (dom.pairsList.offsetParent) renderPairs();
|
||||
if (dom.pairsList.offsetParent || dom.pairsListMobile.offsetParent) renderPairs();
|
||||
} else if (stream.endsWith('@depth20@100ms')) {
|
||||
dom.asksList.innerHTML = data.asks.slice(0, 20).reverse().map(a => `<div class="ob-row" onclick="document.getElementById('futures-amount').value=${parseFloat(a[1]).toFixed(4)};"><span style="color:#f6465d">${parseFloat(a[0]).toFixed(2)}</span><span>${parseFloat(a[1]).toFixed(4)}</span></div>`).join('');
|
||||
dom.bidsList.innerHTML = data.bids.slice(0, 20).map(b => `<div class="ob-row" onclick="document.getElementById('futures-amount').value=${parseFloat(b[1]).toFixed(4)};"><span style="color:#0ecb81">${parseFloat(b[0]).toFixed(2)}</span><span>${parseFloat(b[1]).toFixed(4)}</span></div>`).join('');
|
||||
if(dom.asksList) {
|
||||
dom.asksList.innerHTML = data.asks.slice(0, 20).reverse().map(a => `<div class="ob-row" onclick="document.getElementById('futures-amount').value=${parseFloat(a[1]).toFixed(4)};"><span style="color:#f6465d">${parseFloat(a[0]).toFixed(2)}</span><span>${parseFloat(a[1]).toFixed(4)}</span></div>`).join('');
|
||||
dom.bidsList.innerHTML = data.bids.slice(0, 20).map(b => `<div class="ob-row" onclick="document.getElementById('futures-amount').value=${parseFloat(b[1]).toFixed(4)};"><span style="color:#0ecb81">${parseFloat(b[0]).toFixed(2)}</span><span>${parseFloat(b[1]).toFixed(4)}</span></div>`).join('');
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = () => setTimeout(connectWS, 5000);
|
||||
}
|
||||
|
||||
function renderPairs() {
|
||||
const q = document.getElementById('pair-search').value.toUpperCase();
|
||||
dom.pairsList.innerHTML = pairs.filter(p => p.includes(q)).map(p => {
|
||||
const query = document.getElementById('pair-search').value.toUpperCase();
|
||||
const queryMobile = dom.pairSearchMobile.value.toUpperCase();
|
||||
|
||||
const generateHtml = (q) => pairs.filter(p => p.includes(q)).map(p => {
|
||||
const d = marketData[p] || {};
|
||||
return `<div class="pair-item ${p === currentPair ? 'active' : ''}" onclick="switchPair('${p}')">
|
||||
<img src="https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png" class="coin-icon" onerror="this.style.opacity=0">
|
||||
@ -232,17 +323,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<div style="text-align:right"><div style="color:white; font-weight:600">${d.c ? parseFloat(d.c).toFixed(2) : '--'}</div><div style="color:${d.P>=0?'#0ecb81':'#f6465d'}; font-size:11px">${d.P?(d.P>=0?'+':'')+parseFloat(d.P).toFixed(2)+'%':'--'}</div></div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
dom.pairsList.innerHTML = generateHtml(query);
|
||||
dom.pairsListMobile.innerHTML = generateHtml(queryMobile);
|
||||
}
|
||||
|
||||
window.switchPair = (p) => {
|
||||
currentPair = p;
|
||||
dom.currPair.innerText = p.replace('USDT', '/USDT') + ' Perp';
|
||||
dom.currIcon.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
if(dom.currPair) dom.currPair.innerText = p.replace('USDT', '/USDT') + ' Perp';
|
||||
dom.currPairMobile.innerText = p.replace('USDT', '/USDT') + ' Perp';
|
||||
if(dom.currIcon) dom.currIcon.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
dom.currIconMobile.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
initChart(p);
|
||||
updateBalances();
|
||||
connectWS();
|
||||
if(document.getElementById('mobile-pairs-drawer').classList.contains('open')) toggleMobilePairs();
|
||||
};
|
||||
|
||||
dom.pairSearchMobile.addEventListener('input', renderPairs);
|
||||
|
||||
async function placeOrder(side) {
|
||||
const amount = parseFloat(dom.amountInput.value);
|
||||
if (!amount || amount <= 0) { alert("Amount > 0"); return; }
|
||||
@ -298,4 +397,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
setInterval(fetchOrders, 5000);
|
||||
});
|
||||
</script>
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
71
header.php
71
header.php
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
date_default_timezone_set('Asia/Dubai');
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
@ -146,16 +145,16 @@ $site_logo = $settings['site_logo'] ?? null;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="floating-service" onclick="toggleCSChat()" title="Customer Service">
|
||||
<div class="floating-service" id="floatingChatBtn" onclick="toggleCSChat()" title="<?php echo __('support_online'); ?>">
|
||||
<i class="fas fa-headset"></i>
|
||||
</div>
|
||||
|
||||
<div id="cs-chat-window">
|
||||
<div class="chat-header">
|
||||
<span><?php echo __('site_name'); ?> Support</span>
|
||||
<span><?php echo __('support_online'); ?></span>
|
||||
<i class="fas fa-times" onclick="toggleCSChat()" style="cursor: pointer;"></i>
|
||||
</div>
|
||||
<iframe src="chat_iframe.php"></iframe>
|
||||
<iframe id="cs-chat-iframe" src="chat_iframe.php"></iframe>
|
||||
</div>
|
||||
|
||||
<nav class="navbar">
|
||||
@ -204,11 +203,12 @@ $site_logo = $settings['site_logo'] ?? null;
|
||||
</a>
|
||||
<div class="dropdown-content">
|
||||
<div style="padding: 16px; border-bottom: 1px solid var(--border-color); background: rgba(0,82,255,0.05);">
|
||||
<div style="font-weight: 800; color: white; font-size: 14px;"><?php echo $_SESSION['username'] ?? 'User'; ?></div>
|
||||
<div style="font-weight: 800; color: white; font-size: 14px;"><?php echo htmlspecialchars($_SESSION['username'] ?? 'User'); ?></div>
|
||||
<div style="color: var(--text-muted); font-size: 11px; margin-top: 4px;">UID: <?php echo $_SESSION['uid'] ?? '------'; ?></div>
|
||||
</div>
|
||||
<a href="profile.php"><i class="fas fa-wallet" style="color: #03a9f4;"></i> <?php echo __('nav_assets'); ?></a>
|
||||
<a href="deposit.php"><i class="fas fa-plus-circle" style="color: #00f2fe;"></i> <?php echo __('nav_deposit'); ?></a>
|
||||
<a href="withdraw.php"><i class="fas fa-minus-circle" style="color: #f6465d;"></i> <?php echo __('nav_withdraw') ?? '提现'; ?></a>
|
||||
<a href="security.php"><i class="fas fa-shield-alt" style="color: #ffd600;"></i> <?php echo __('nav_security'); ?></a>
|
||||
<a href="logout.php" style="color: var(--danger-color);"><i class="fas fa-sign-out-alt"></i> <?php echo __('nav_logout'); ?></a>
|
||||
</div>
|
||||
@ -256,4 +256,63 @@ $site_logo = $settings['site_logo'] ?? null;
|
||||
const chat = document.getElementById('cs-chat-window');
|
||||
chat.style.display = chat.style.display === 'flex' ? 'none' : 'flex';
|
||||
}
|
||||
</script>
|
||||
|
||||
function openCSChat() {
|
||||
const chat = document.getElementById('cs-chat-window');
|
||||
if (chat.style.display !== 'flex') {
|
||||
chat.style.display = 'flex';
|
||||
}
|
||||
// Refresh iframe to show latest info
|
||||
const iframe = document.getElementById('cs-chat-iframe');
|
||||
if (iframe) iframe.src = iframe.src;
|
||||
}
|
||||
|
||||
// Auto-popup logic
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
let lastMatchedOrderId = localStorage.getItem('lastMatchedOrderId') || 0;
|
||||
let lastAdminMsgId = localStorage.getItem('lastAdminMsgId') || 0;
|
||||
|
||||
async function checkAutoPopup() {
|
||||
// Only check if not on chat.php
|
||||
if (window.location.pathname.includes('chat.php')) return;
|
||||
|
||||
try {
|
||||
// Check for order status changes
|
||||
const orderResp = await fetch('api/check_order_status.php');
|
||||
const orderRes = await orderResp.json();
|
||||
if (orderRes.success && orderRes.order) {
|
||||
// If status is matched (admin sent account info/format)
|
||||
if (orderRes.order.status === 'matched' && orderRes.order.id >= lastMatchedOrderId) {
|
||||
// Check if bank_account_info exists and we haven't popped for THIS specific update
|
||||
const lastPopInfo = localStorage.getItem('lastPopInfo_' + orderRes.order.id);
|
||||
if (orderRes.order.bank_account_info && orderRes.order.bank_account_info !== lastPopInfo) {
|
||||
localStorage.setItem('lastPopInfo_' + orderRes.order.id, orderRes.order.bank_account_info);
|
||||
lastMatchedOrderId = orderRes.order.id;
|
||||
localStorage.setItem('lastMatchedOrderId', lastMatchedOrderId);
|
||||
openCSChat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for new admin messages
|
||||
const msgResp = await fetch('api/get_messages.php');
|
||||
const msgRes = await msgResp.json();
|
||||
if (msgRes.success && msgRes.data && msgRes.data.length > 0) {
|
||||
const latestMsg = msgRes.data[0];
|
||||
if (latestMsg.sender === 'admin' && latestMsg.id > lastAdminMsgId) {
|
||||
lastAdminMsgId = latestMsg.id;
|
||||
localStorage.setItem('lastAdminMsgId', lastAdminMsgId);
|
||||
openCSChat();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Popup check failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(checkAutoPopup, 2000); // Faster check
|
||||
checkAutoPopup();
|
||||
<?php endif; ?>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -211,7 +211,7 @@ $translations = [
|
||||
'days' => 'Days',
|
||||
'coming_soon' => 'Coming Soon',
|
||||
'next_project' => 'Next Project',
|
||||
// New keys for error messages
|
||||
|
||||
'error_placing_order' => 'Error placing order',
|
||||
'network_error' => 'Network Error',
|
||||
'could_not_connect_server' => 'Could not connect to server',
|
||||
@ -222,6 +222,41 @@ $translations = [
|
||||
'websocket_connected' => 'Connected to market data.',
|
||||
'websocket_error' => 'Market data connection error!',
|
||||
'websocket_disconnected' => 'Market data disconnected. Reconnecting...',
|
||||
|
||||
'create_account' => 'Create Account',
|
||||
'join_novaex_tip' => 'Join NovaEx - The Leading Crypto Exchange',
|
||||
'email_or_phone' => 'Email or Phone',
|
||||
'enter_email_phone' => 'Enter your email or phone',
|
||||
'login_password' => 'Login Password',
|
||||
'set_password' => 'Set your login password',
|
||||
'confirm_password_label' => 'Confirm Password',
|
||||
'confirm_your_password' => 'Confirm your password',
|
||||
'agree_terms' => 'I have read and agree to the Privacy Policy and Terms of Service.',
|
||||
'already_have_account' => 'Already have an account?',
|
||||
'fill_all_fields' => 'Please fill all fields.',
|
||||
'passwords_not_match' => 'Passwords do not match.',
|
||||
'username_taken' => 'Username already taken.',
|
||||
'registration_failed' => 'Registration failed.',
|
||||
'login_to_novaex' => 'Login to NovaEx',
|
||||
'welcome_back' => 'Welcome back! Please enter your details.',
|
||||
'dont_have_account' => 'Don\'t have an account?',
|
||||
'invalid_credentials' => 'Invalid username or password.',
|
||||
'type_message' => 'Type a message...',
|
||||
'please_login' => 'Please login first.',
|
||||
'support_online' => 'Support Online',
|
||||
'view_account' => 'View Account',
|
||||
'payment_proof' => 'Payment Proof',
|
||||
'upload_proof' => 'Upload Proof',
|
||||
'confirm_transfer' => 'Confirm Transfer',
|
||||
'matched_info_tip' => 'Account info received, please transfer and upload proof.',
|
||||
'paid_waiting_tip' => 'Proof uploaded, waiting for review.',
|
||||
'complete_transfer_btn' => 'Complete Transfer',
|
||||
'withdraw_info_tip' => 'Please reply with your bank details as requested.',
|
||||
'matched_status' => 'Matched',
|
||||
'paid_status' => 'Paid',
|
||||
'matching_status' => 'Matching',
|
||||
'withdraw_format_title' => 'Withdraw Info',
|
||||
'view_withdraw_format' => 'View Format',
|
||||
],
|
||||
'zh' => [
|
||||
'site_name' => 'NovaEx',
|
||||
@ -280,7 +315,7 @@ $translations = [
|
||||
'global_partners' => '全球合作伙伴',
|
||||
'partners_subtitle' => '深受全球领先组织和金融机构的信任。',
|
||||
|
||||
'footer_desc' => 'NovaEx 是全球领先的数字资产 trading 平台,为全球用户提供安全稳定的交易服务。',
|
||||
'footer_desc' => 'NovaEx 是全球领先的数字资产交易所,为全球用户提供安全稳定的交易服务。',
|
||||
'about' => '关于',
|
||||
'about_us' => '关于我们',
|
||||
'careers' => '职业介绍',
|
||||
@ -430,7 +465,7 @@ $translations = [
|
||||
'days' => '天',
|
||||
'coming_soon' => '即将到来',
|
||||
'next_project' => '下一个项目',
|
||||
// New keys for error messages
|
||||
|
||||
'error_placing_order' => '下单失败',
|
||||
'network_error' => '网络错误',
|
||||
'could_not_connect_server' => '无法连接到服务器',
|
||||
@ -441,6 +476,41 @@ $translations = [
|
||||
'websocket_connected' => '市场数据已连接。',
|
||||
'websocket_error' => '市场数据连接错误!',
|
||||
'websocket_disconnected' => '市场数据已断开。正在重新连接...',
|
||||
|
||||
'create_account' => '创建账户',
|
||||
'join_novaex_tip' => '加入 NovaEx - 全球领先的数字资产交易所',
|
||||
'email_or_phone' => '邮箱或手机号',
|
||||
'enter_email_phone' => '输入您的邮箱或手机号',
|
||||
'login_password' => '登录密码',
|
||||
'set_password' => '设置您的登录密码',
|
||||
'confirm_password_label' => '确认密码',
|
||||
'confirm_your_password' => '请再次输入密码',
|
||||
'agree_terms' => '我已阅读并同意隐私政策和服务条款。',
|
||||
'already_have_account' => '已有账户?',
|
||||
'fill_all_fields' => '请填写所有字段。',
|
||||
'passwords_not_match' => '两次输入的密码不一致。',
|
||||
'username_taken' => '用户名已被占用。',
|
||||
'registration_failed' => '注册失败。',
|
||||
'login_to_novaex' => '登录 NovaEx',
|
||||
'welcome_back' => '欢迎回来!请输入您的详细信息。',
|
||||
'dont_have_account' => '还没有账户?',
|
||||
'invalid_credentials' => '用户名或密码无效。',
|
||||
'type_message' => '输入消息...',
|
||||
'please_login' => '请先登录。',
|
||||
'support_online' => '在线客服',
|
||||
'view_account' => '查看账户',
|
||||
'payment_proof' => '支付凭证',
|
||||
'upload_proof' => '上传凭证',
|
||||
'confirm_transfer' => '确认转账',
|
||||
'matched_info_tip' => '收款账户已下发,请转账后上传凭证。',
|
||||
'paid_waiting_tip' => '凭证已上传,等待审核。',
|
||||
'complete_transfer_btn' => '完成转账',
|
||||
'withdraw_info_tip' => '请按要求回复您的银行收款详情。',
|
||||
'matched_status' => '已匹配',
|
||||
'paid_status' => '已支付',
|
||||
'matching_status' => '匹配中',
|
||||
'withdraw_format_title' => '提现信息',
|
||||
'view_withdraw_format' => '查看格式',
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
26
login.php
26
login.php
@ -15,10 +15,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
// Capture and update IP
|
||||
// Capture and update IP correctly
|
||||
$user_ip = $_SERVER['REMOTE_ADDR'];
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||
$user_ip = trim($ips[0]);
|
||||
}
|
||||
$pdo->prepare("UPDATE users SET last_ip = ? WHERE id = ?")->execute([$user_ip, $user['id']]);
|
||||
|
||||
@ -28,7 +29,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
$error = "Invalid username or password.";
|
||||
$error = __('invalid_credentials');
|
||||
}
|
||||
}
|
||||
?>
|
||||
@ -37,8 +38,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
<main style="background: #0b0e11; min-height: calc(100vh - 64px); display: flex; align-items: center; justify-content: center; padding: 40px 20px;">
|
||||
<div style="width: 100%; max-width: 480px; background: var(--card-bg); padding: 50px; border-radius: 32px; border: 1px solid var(--border-color); box-shadow: 0 20px 40px rgba(0,0,0,0.4);">
|
||||
<h2 style="font-size: 2.2rem; font-weight: 800; margin-bottom: 10px; text-align: center; color: white;">Welcome Back</h2>
|
||||
<p style="text-align: center; color: var(--text-muted); margin-bottom: 40px;">Log in to your account to continue trading</p>
|
||||
<h2 style="font-size: 2.2rem; font-weight: 800; margin-bottom: 10px; text-align: center; color: white;"><?php echo __('login_to_novaex'); ?></h2>
|
||||
<p style="text-align: center; color: var(--text-muted); margin-bottom: 40px;"><?php echo __('welcome_back'); ?></p>
|
||||
|
||||
<?php if($error): ?>
|
||||
<div style="background: rgba(246,70,93,0.1); color: var(--danger-color); padding: 15px; border-radius: 12px; margin-bottom: 25px; border: 1px solid var(--danger-color); text-align: center; font-size: 14px;">
|
||||
@ -48,26 +49,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
<form method="POST">
|
||||
<div style="margin-bottom: 25px;">
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;">Account</label>
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;"><?php echo __('email_or_phone'); ?></label>
|
||||
<div style="position: relative;">
|
||||
<i class="fas fa-user" style="position: absolute; left: 15px; top: 15px; color: #555;"></i>
|
||||
<input type="text" name="username" required placeholder="Enter your email or phone" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
<input type="text" name="username" required placeholder="<?php echo __('enter_email_phone'); ?>" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 30px;">
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;">Password</label>
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;"><?php echo __('login_password'); ?></label>
|
||||
<div style="position: relative;">
|
||||
<i class="fas fa-lock" style="position: absolute; left: 15px; top: 15px; color: #555;"></i>
|
||||
<input type="password" name="password" required placeholder="Enter your password" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
<input type="password" name="password" required placeholder="<?php echo __('set_password'); ?>" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 30px;">
|
||||
<a href="#" style="color: var(--primary-color); text-decoration: none; font-size: 0.85rem;">Forgot Password?</a>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 18px; font-weight: 800; font-size: 1.1rem; border-radius: 16px; box-shadow: 0 10px 20px rgba(0,82,255,0.2);"><?php echo __('nav_login'); ?></button>
|
||||
</form>
|
||||
<div style="text-align: center; margin-top: 30px; border-top: 1px solid var(--border-color); padding-top: 30px;">
|
||||
<span style="color: var(--text-muted);">Don't have an account?</span> <a href="register.php" style="color: var(--primary-color); text-decoration: none; font-weight: bold;"><?php echo __('nav_register'); ?></a>
|
||||
<span style="color: var(--text-muted);"><?php echo __('dont_have_account'); ?></span> <a href="register.php" style="color: var(--primary-color); text-decoration: none; font-weight: bold;"><?php echo __('nav_register'); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
48
matching.php
48
matching.php
@ -3,6 +3,15 @@ require_once 'db/config.php';
|
||||
require_once 'includes/currency_helper.php';
|
||||
session_start();
|
||||
|
||||
function json_die($error) {
|
||||
if (isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && $_SERVER["HTTP_X_REQUESTED_WITH"] === "XMLHttpRequest") {
|
||||
echo json_encode(["success" => false, "error" => $error]);
|
||||
} else {
|
||||
die($error);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
@ -18,6 +27,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['amount'])) {
|
||||
$currency = $_POST['currency'] ?? 'USDT';
|
||||
$network = $_POST['network'] ?? '';
|
||||
|
||||
if ($amount <= 0) {
|
||||
json_die("Invalid amount");
|
||||
}
|
||||
|
||||
$fiat_rates = get_fiat_rates();
|
||||
$rate = $fiat_rates[$currency] ?? 1.0;
|
||||
|
||||
@ -33,7 +46,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['amount'])) {
|
||||
$balance = (float)$stmt->fetchColumn();
|
||||
|
||||
if ($balance < $usdt_amount) {
|
||||
die("余额不足");
|
||||
json_die("余额不足");
|
||||
}
|
||||
|
||||
// For withdrawal, we might need a trading password check if present
|
||||
if (isset($_POST['trading_password'])) {
|
||||
$stmt = $pdo->prepare("SELECT trading_password FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$saved_pass = $stmt->fetchColumn();
|
||||
if ($saved_pass && $saved_pass !== $_POST['trading_password']) {
|
||||
json_die("交易密码错误");
|
||||
}
|
||||
}
|
||||
|
||||
// Deduct balance immediately for withdrawal
|
||||
@ -58,17 +81,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['amount'])) {
|
||||
// Notification message for admin/chat
|
||||
$type_text = ($order_type === 'deposit') ? "充值" : "提现";
|
||||
$method_info = ($type === 'usdt') ? "USDT ($network)" : "法币 ($currency)";
|
||||
$msg = "📢 用户发起 $type_text 请求\n金额: $amount $currency\n订单号: #$order_id\n方式: $method_info";
|
||||
|
||||
// More active messages as if user is typing
|
||||
if ($order_type === 'deposit') {
|
||||
$msg = "你好,我已发起一笔 $amount $currency 的充值申请。请为我匹配收款账户。";
|
||||
} else {
|
||||
$msg = "你好,我已发起一笔 $amount $currency 的提现申请。请发送收款信息格式给我。";
|
||||
}
|
||||
|
||||
// System record (visible to admin as a request tag)
|
||||
$sys_msg = "📢 订单详情\n类型: $type_text\n金额: $amount $currency\n方式: $method_info\n单号: #$order_id";
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$user_id, $msg]);
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$user_id, $sys_msg]);
|
||||
|
||||
$pdo->commit();
|
||||
header("Location: chat.php");
|
||||
if (isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && $_SERVER["HTTP_X_REQUESTED_WITH"] === "XMLHttpRequest") {
|
||||
echo json_encode(["success" => true, "order_id" => $order_id]);
|
||||
exit;
|
||||
} else {
|
||||
header("Location: chat.php");
|
||||
}
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
die("Error: " . $e->getMessage());
|
||||
json_die("Error: " . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
header("Location: index.php");
|
||||
|
||||
127
options.php
127
options.php
@ -110,6 +110,34 @@ if ($user_id) {
|
||||
#order-book-list::-webkit-scrollbar { width: 4px; }
|
||||
#order-book-list::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
|
||||
|
||||
/* Mobile Specific Styles */
|
||||
@media (max-width: 991px) {
|
||||
.trading-container { flex-direction: column; min-height: auto; }
|
||||
.center-col { min-width: 100%; border-right: none; }
|
||||
.chart-box { height: 300px; }
|
||||
.order-panel { padding: 15px; }
|
||||
.duration-grid { grid-template-columns: repeat(3, 1fr); }
|
||||
.table-responsive { height: auto; max-height: 400px; }
|
||||
.trading-page-wrapper { padding-bottom: 70px; } /* Space for bottom nav */
|
||||
|
||||
.mobile-trade-nav {
|
||||
display: flex; background: #161a1e; border-bottom: 1px solid #2b3139; padding: 10px 15px; gap: 10px;
|
||||
}
|
||||
.mobile-trade-nav a {
|
||||
flex: 1; text-align: center; padding: 8px 0; background: #2b3139; border-radius: 8px; font-size: 13px; color: #848e9c; text-decoration: none; font-weight: 600; border: 1px solid transparent;
|
||||
}
|
||||
.mobile-trade-nav a.active {
|
||||
background: rgba(0, 82, 255, 0.1); color: var(--primary-color); border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.mobile-symbol-selector {
|
||||
display: flex; align-items: center; justify-content: space-between; padding: 12px 15px; background: #161a1e; border-bottom: 1px solid #2b3139;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.mobile-trade-nav, .mobile-symbol-selector { display: none; }
|
||||
}
|
||||
|
||||
/* Countdown Modal Styles */
|
||||
.countdown-modal {
|
||||
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||
@ -150,11 +178,37 @@ if ($user_id) {
|
||||
.result-title { font-size: 24px; font-weight: 800; margin-bottom: 10px; }
|
||||
.result-amount { font-size: 32px; font-weight: 800; margin-bottom: 30px; }
|
||||
.result-btn { background: var(--primary-color); color: white; padding: 12px 40px; border-radius: 10px; font-weight: 700; cursor: pointer; border: none; }
|
||||
|
||||
/* Mobile Drawer */
|
||||
#mobile-pairs-drawer {
|
||||
position: fixed; top: 0; left: -100%; width: 100%; height: 100%; background: #0b0e11; z-index: 3000; transition: 0.3s; padding: 20px; display: flex; flex-direction: column;
|
||||
}
|
||||
#mobile-pairs-drawer.open { left: 0; }
|
||||
</style>
|
||||
|
||||
<div class="trading-page-wrapper">
|
||||
<!-- Mobile Navigation Tabs -->
|
||||
<div class="mobile-trade-nav">
|
||||
<a href="options.php" class="active"><?php echo __('nav_options'); ?></a>
|
||||
<a href="spot.php"><?php echo __('nav_spot'); ?></a>
|
||||
<a href="futures.php"><?php echo __('nav_futures'); ?></a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Symbol Selector -->
|
||||
<div class="mobile-symbol-selector" onclick="toggleMobilePairs()">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<img id="curr-icon-mobile" src="" class="coin-icon" style="width: 28px; height: 28px;">
|
||||
<span id="curr-pair-mobile" style="font-weight: 800; font-size: 18px; color: white;">--/--</span>
|
||||
<i class="fas fa-caret-down" style="color: #848e9c;"></i>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div id="curr-price-mobile" style="font-size: 18px; font-weight: 800; color: #0ecb81;">--</div>
|
||||
<div id="curr-change-mobile" style="font-size: 12px; font-weight: 600;">--</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trading-container">
|
||||
<!-- Left: Pair Selection -->
|
||||
<!-- Left: Pair Selection (Desktop) -->
|
||||
<div class="left-col d-none d-lg-flex">
|
||||
<div class="category-tabs">
|
||||
<div class="category-tab active" onclick="location.href='options.php'"><?php echo __('nav_options'); ?></div>
|
||||
@ -170,7 +224,7 @@ if ($user_id) {
|
||||
|
||||
<!-- Center: Chart & Order -->
|
||||
<div class="center-col">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header d-none d-lg-flex">
|
||||
<div class="d-flex align-items-center" style="gap:12px;">
|
||||
<img id="curr-icon" src="" class="coin-icon">
|
||||
<span id="curr-pair" style="font-weight: 800; font-size: 18px; color:white;">--/--</span>
|
||||
@ -243,7 +297,7 @@ if ($user_id) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Order Book -->
|
||||
<!-- Right: Order Book (Desktop) -->
|
||||
<div class="right-col d-none d-xl-flex">
|
||||
<div class="col-header"><?php echo __('order_book'); ?></div>
|
||||
<div class="ob-header">
|
||||
@ -259,6 +313,19 @@ if ($user_id) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Pairs Drawer -->
|
||||
<div id="mobile-pairs-drawer">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h3 style="margin: 0; font-weight: 800;"><?php echo __('market'); ?></h3>
|
||||
<i class="fas fa-times" onclick="toggleMobilePairs()" style="font-size: 20px; color: #848e9c;"></i>
|
||||
</div>
|
||||
<div class="search-box" style="padding: 0; margin-bottom: 20px;">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" id="pair-search-mobile" placeholder="<?php echo __('search_currency'); ?>">
|
||||
</div>
|
||||
<div id="pairs-list-mobile" style="flex: 1; overflow-y: auto;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Countdown Modal -->
|
||||
<div id="countdown-modal" class="countdown-modal">
|
||||
<div class="modal-content-card">
|
||||
@ -317,6 +384,10 @@ if ($user_id) {
|
||||
<script>
|
||||
const userAssets = <?php echo json_encode($user_assets); ?>;
|
||||
|
||||
function toggleMobilePairs() {
|
||||
document.getElementById('mobile-pairs-drawer').classList.toggle('open');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let currentPair = 'BTCUSDT', currentPrice = 0, ws, chartWidget;
|
||||
let selectedDuration = 60, selectedRate = 8, minAmount = 100;
|
||||
@ -331,10 +402,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
currPair: document.getElementById('curr-pair'),
|
||||
currPrice: document.getElementById('curr-price'),
|
||||
currChange: document.getElementById('curr-change'),
|
||||
currIconMobile: document.getElementById('curr-icon-mobile'),
|
||||
currPairMobile: document.getElementById('curr-pair-mobile'),
|
||||
currPriceMobile: document.getElementById('curr-price-mobile'),
|
||||
currChangeMobile: document.getElementById('curr-change-mobile'),
|
||||
hHigh: document.getElementById('h-high'),
|
||||
hLow: document.getElementById('h-low'),
|
||||
pairsList: document.getElementById('pairs-list'),
|
||||
pairsListMobile: document.getElementById('pairs-list-mobile'),
|
||||
pairSearch: document.getElementById('pair-search'),
|
||||
pairSearchMobile: document.getElementById('pair-search-mobile'),
|
||||
asksList: document.getElementById('asks-list'),
|
||||
bidsList: document.getElementById('bids-list'),
|
||||
midPrice: document.getElementById('price-mid'),
|
||||
@ -397,17 +474,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
if (data.s === currentPair) {
|
||||
currentPrice = parseFloat(data.c);
|
||||
updatePriceUI(data);
|
||||
dom.midPrice.innerText = currentPrice.toFixed(2);
|
||||
dom.midPrice.style.color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
if(dom.midPrice) {
|
||||
dom.midPrice.innerText = currentPrice.toFixed(2);
|
||||
dom.midPrice.style.color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
}
|
||||
|
||||
if (dom.modal.style.display === 'flex') {
|
||||
dom.modalCurrPrice.innerText = currentPrice.toFixed(4);
|
||||
dom.modalCurrPrice.style.color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
}
|
||||
}
|
||||
if (dom.pairsList.offsetParent) renderPairs();
|
||||
if (dom.pairsList.offsetParent || dom.pairsListMobile.offsetParent) renderPairs();
|
||||
} else if (stream.endsWith('@depth20@100ms')) {
|
||||
renderOrderBook(data.bids, data.asks);
|
||||
if(dom.asksList) renderOrderBook(data.bids, data.asks);
|
||||
}
|
||||
};
|
||||
ws.onclose = () => setTimeout(connectWebSocket, 5000);
|
||||
@ -422,18 +501,27 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
function updatePriceUI(d) {
|
||||
const color = d.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
const priceStr = parseFloat(d.c).toLocaleString('en-US', {minimumFractionDigits: 2});
|
||||
dom.currPrice.innerText = priceStr;
|
||||
dom.currPrice.style.color = color;
|
||||
dom.currChange.innerText = `${d.P >= 0 ? '+' : ''}${parseFloat(d.P).toFixed(2)}%`;
|
||||
dom.currChange.style.color = color;
|
||||
dom.hHigh.innerText = parseFloat(d.h).toFixed(2);
|
||||
dom.hLow.innerText = parseFloat(d.l).toFixed(2);
|
||||
|
||||
if(dom.currPrice) dom.currPrice.innerText = priceStr;
|
||||
if(dom.currPrice) dom.currPrice.style.color = color;
|
||||
if(dom.currChange) dom.currChange.innerText = `${d.P >= 0 ? '+' : ''}${parseFloat(d.P).toFixed(2)}%`;
|
||||
if(dom.currChange) dom.currChange.style.color = color;
|
||||
|
||||
dom.currPriceMobile.innerText = priceStr;
|
||||
dom.currPriceMobile.style.color = color;
|
||||
dom.currChangeMobile.innerText = `${d.P >= 0 ? '+' : ''}${parseFloat(d.P).toFixed(2)}%`;
|
||||
dom.currChangeMobile.style.color = color;
|
||||
|
||||
if(dom.hHigh) dom.hHigh.innerText = parseFloat(d.h).toFixed(2);
|
||||
if(dom.hLow) dom.hLow.innerText = parseFloat(d.l).toFixed(2);
|
||||
document.title = `${priceStr} | ${currentPair}`;
|
||||
}
|
||||
|
||||
function renderPairs() {
|
||||
const query = dom.pairSearch.value.toUpperCase();
|
||||
dom.pairsList.innerHTML = pairs.filter(p => p.includes(query)).map(p => {
|
||||
const queryMobile = dom.pairSearchMobile.value.toUpperCase();
|
||||
|
||||
const generateHtml = (q) => pairs.filter(p => p.includes(q)).map(p => {
|
||||
const d = marketData[p] || {};
|
||||
const price = d.c ? parseFloat(d.c).toFixed(2) : '--';
|
||||
const change = d.P ? `${parseFloat(d.P) >= 0 ? '+' : ''}${parseFloat(d.P).toFixed(2)}%` : '--';
|
||||
@ -444,15 +532,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<div style="text-align:right"><div style="font-weight:600; color:white;">${price}</div><div style="font-size:11px; color:${color}">${change}</div></div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
dom.pairsList.innerHTML = generateHtml(query);
|
||||
dom.pairsListMobile.innerHTML = generateHtml(queryMobile);
|
||||
}
|
||||
|
||||
function switchPair(pair) {
|
||||
currentPair = pair;
|
||||
dom.currPair.innerText = `${pair.replace('USDT', '/USDT')}`;
|
||||
dom.currPairMobile.innerText = `${pair.replace('USDT', '/USDT')}`;
|
||||
dom.currIcon.src = `https://assets.coincap.io/assets/icons/${pair.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
dom.currIconMobile.src = `https://assets.coincap.io/assets/icons/${pair.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
initChartWidget(pair);
|
||||
renderPairs();
|
||||
connectWebSocket();
|
||||
if(document.getElementById('mobile-pairs-drawer').classList.contains('open')) toggleMobilePairs();
|
||||
}
|
||||
|
||||
function updatePotentialProfit() {
|
||||
@ -584,7 +678,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
dom.pairsList.addEventListener('click', e => { const item = e.target.closest('.pair-item'); if (item) switchPair(item.dataset.pair); });
|
||||
dom.pairsListMobile.addEventListener('click', e => { const item = e.target.closest('.pair-item'); if (item) switchPair(item.dataset.pair); });
|
||||
dom.pairSearch.addEventListener('input', renderPairs);
|
||||
dom.pairSearchMobile.addEventListener('input', renderPairs);
|
||||
|
||||
document.querySelectorAll('.time-btn').forEach(btn => btn.addEventListener('click', () => {
|
||||
document.querySelector('.time-btn.active').classList.remove('active');
|
||||
btn.classList.add('active');
|
||||
@ -612,4 +709,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
81
profile.php
81
profile.php
@ -111,10 +111,11 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
transition: 0.3s;
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-deposit { background: var(--primary-color); color: #000; }
|
||||
.btn-deposit { background: var(--primary-color); color: white; }
|
||||
.btn-withdraw { background: #2b3139; color: white; border: 1px solid #3b424d; }
|
||||
.btn-deposit:hover { background: #f8d33a; transform: translateY(-3px); box-shadow: 0 10px 20px rgba(240, 185, 11, 0.2); }
|
||||
.btn-deposit:hover { background: #0042cc; transform: translateY(-3px); box-shadow: 0 10px 20px rgba(0, 82, 255, 0.2); }
|
||||
.btn-withdraw:hover { background: #3b424d; transform: translateY(-3px); }
|
||||
|
||||
.profile-tabs {
|
||||
@ -149,7 +150,7 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
border: 1px solid #2b3139;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.record-card:hover { border-color: rgba(240, 185, 11, 0.3); transform: translateY(-5px); }
|
||||
.record-card:hover { border-color: rgba(0, 82, 255, 0.3); transform: translateY(-5px); }
|
||||
|
||||
.status-badge {
|
||||
padding: 5px 12px;
|
||||
@ -158,8 +159,23 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) { .record-grid { grid-template-columns: 1fr; } }
|
||||
@media (max-width: 992px) { .profile-grid { grid-template-columns: 1fr; } }
|
||||
@media (max-width: 1200px) {
|
||||
.record-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 992px) {
|
||||
.profile-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.balance-card { padding: 30px 20px; }
|
||||
.balance-val { font-size: 2.5rem; }
|
||||
.profile-tabs { gap: 20px; padding: 0 20px; }
|
||||
.profile-tab-btn { font-size: 1rem; }
|
||||
.tab-content { padding: 25px 15px !important; }
|
||||
.record-card { padding: 15px; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.assets-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="profile-container">
|
||||
@ -247,7 +263,7 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
|
||||
<!-- 资产页签 -->
|
||||
<div id="assets-tab" class="tab-content" style="padding: 40px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<div class="assets-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<?php
|
||||
$coins = [
|
||||
['symbol' => 'USDT', 'name' => 'Tether', 'balance' => $user['balance'] ?? 0],
|
||||
@ -279,18 +295,18 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
foreach ($coins as $coin):
|
||||
?>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; padding: 22px; background: rgba(255,255,255,0.02); border-radius: 20px; border: 1px solid rgba(255,255,255,0.05); transition: 0.3s;">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<img src="https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/<?php echo strtolower($coin['symbol']); ?>.png" width="40" height="40" onerror="this.src='https://cdn-icons-png.flaticon.com/512/2585/2585274.png'">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<img src="https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/<?php echo strtolower($coin['symbol']); ?>.png" width="36" height="36" onerror="this.src='https://cdn-icons-png.flaticon.com/512/2585/2585274.png'">
|
||||
<div>
|
||||
<div style="font-weight: 800; font-size: 1.2rem; color: white;"><?php echo $coin['symbol']; ?></div>
|
||||
<div style="font-size: 0.8rem; color: var(--text-muted);"><?php echo $coin['name']; ?></div>
|
||||
<div style="font-weight: 800; font-size: 1.1rem; color: white;"><?php echo $coin['symbol']; ?></div>
|
||||
<div style="font-size: 0.75rem; color: var(--text-muted);"><?php echo $coin['name']; ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div style="font-weight: 800; font-family: 'Roboto Mono', monospace; font-size: 1.2rem; color: white;">
|
||||
<div style="font-weight: 800; font-family: 'Roboto Mono', monospace; font-size: 1.1rem; color: white;">
|
||||
<?php echo number_format($coin['balance'], $coin['symbol'] === 'USDT' ? 2 : 6); ?>
|
||||
</div>
|
||||
<div style="font-size: 0.8rem; color: var(--text-muted); text-transform: uppercase;">可用余额</div>
|
||||
<div style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase;">余额</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
@ -345,7 +361,7 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
allRecords.sort((a, b) => new Date(b.created_at || b.open_time) - new Date(a.created_at || a.open_time));
|
||||
|
||||
if (allRecords.length === 0) {
|
||||
container.innerHTML = '<div style="grid-column: span 2; text-align: center; padding: 100px; color: var(--text-muted);"><i class="fas fa-file-invoice" style="font-size: 4rem; opacity: 0.2; margin-bottom: 25px;"></i><br>暂无任何交易记录</div>';
|
||||
container.innerHTML = '<div style="grid-column: span 1; text-align: center; padding: 100px; color: var(--text-muted);"><i class="fas fa-file-invoice" style="font-size: 4rem; opacity: 0.2; margin-bottom: 25px;"></i><br>暂无任何交易记录</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -363,28 +379,28 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 18px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span style="font-weight: 800; color: white; font-size: 1.2rem;">${symbol}</span>
|
||||
<span style="background: rgba(240,185,11,0.1); color: var(--primary-color); padding: 3px 10px; border-radius: 6px; font-size: 10px; font-weight: 800;">${r.trade_label}</span>
|
||||
<span style="font-weight: 800; color: white; font-size: 1.1rem;">${symbol}</span>
|
||||
<span style="background: rgba(0,82,255,0.1); color: var(--primary-color); padding: 3px 8px; border-radius: 6px; font-size: 9px; font-weight: 800;">${r.trade_label}</span>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-top: 6px;">${time}</div>
|
||||
<div style="font-size: 11px; color: var(--text-muted); margin-top: 6px;">${time}</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div style="font-size: 10px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 2px;">盈利 (USDT)</div>
|
||||
<div style="font-weight: 800; color: ${profitColor}; font-size: 1.3rem;">${isProfit ? '+' : ''}${profit.toFixed(2)}</div>
|
||||
<div style="font-size: 9px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 2px;">盈利 (USDT)</div>
|
||||
<div style="font-weight: 800; color: ${profitColor}; font-size: 1.2rem;">${isProfit ? '+' : ''}${profit.toFixed(2)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; background: rgba(0,0,0,0.2); padding: 15px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.03);">
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; background: rgba(0,0,0,0.2); padding: 12px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.03);">
|
||||
<div>
|
||||
<div style="font-size: 10px; color: var(--text-muted); margin-bottom: 4px;">类型</div>
|
||||
<div style="font-weight: 800; color: ${r.side === 'buy' || r.direction === 'up' ? 'var(--success-color)' : 'var(--danger-color)'}; font-size: 13px;">${(r.side === 'buy' || r.direction === 'up') ? '看涨/做多' : '看跌/做空'}</div>
|
||||
<div style="font-size: 9px; color: var(--text-muted); margin-bottom: 4px;">类型</div>
|
||||
<div style="font-weight: 800; color: ${r.side === 'buy' || r.direction === 'up' ? 'var(--success-color)' : 'var(--danger-color)'}; font-size: 12px;">${(r.side === 'buy' || r.direction === 'up') ? '看涨' : '看跌'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 10px; color: var(--text-muted); margin-bottom: 4px;">成交金额</div>
|
||||
<div style="font-weight: 800; color: white; font-size: 13px;">${parseFloat(r.amount || r.invest).toFixed(2)}</div>
|
||||
<div style="font-size: 9px; color: var(--text-muted); margin-bottom: 4px;">金额</div>
|
||||
<div style="font-weight: 800; color: white; font-size: 12px;">${parseFloat(r.amount || r.invest).toFixed(2)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 10px; color: var(--text-muted); margin-bottom: 4px;">成交价格</div>
|
||||
<div style="font-weight: 800; color: white; font-size: 13px;">${parseFloat(r.price || r.open_price).toLocaleString()}</div>
|
||||
<div style="font-size: 9px; color: var(--text-muted); margin-bottom: 4px;">价格</div>
|
||||
<div style="font-weight: 800; color: white; font-size: 12px;">${parseFloat(r.price || r.open_price).toLocaleString()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -401,16 +417,15 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
<div class="record-card">
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<span style="font-weight: 800; color: white; font-size: 1.2rem;">${typeLabel}</span>
|
||||
<span style="font-size: 11px; font-weight: 800; color: ${statusColor}; background: ${statusColor}1A; padding: 4px 10px; border-radius: 6px;">${statusLabel}</span>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<span style="font-weight: 800; color: white; font-size: 1.1rem;">${typeLabel}</span>
|
||||
<span style="font-size: 10px; font-weight: 800; color: ${statusColor}; background: ${statusColor}1A; padding: 4px 8px; border-radius: 6px;">${statusLabel}</span>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-top: 10px; font-family: 'Roboto Mono', monospace;">${r.created_at}</div>
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-top: 8px; font-style: italic; opacity: 0.6;">${r.description || ''}</div>
|
||||
<div style="font-size: 11px; color: var(--text-muted); margin-top: 10px; font-family: 'Roboto Mono', monospace;">${r.created_at}</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div style="font-size: 10px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 4px;">变动金额 (USDT)</div>
|
||||
<div style="font-weight: 800; color: ${amountColor}; font-size: 1.5rem;">${isPositive ? '+' : ''}${amount.toFixed(2)}</div>
|
||||
<div style="font-size: 9px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 4px;">变动 (USDT)</div>
|
||||
<div style="font-weight: 800; color: ${amountColor}; font-size: 1.3rem;">${isPositive ? '+' : ''}${amount.toFixed(2)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -419,7 +434,7 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
|
||||
});
|
||||
container.innerHTML = html;
|
||||
} catch (e) {
|
||||
container.innerHTML = '<div style="grid-column: span 2; text-align: center; padding: 60px; color: var(--danger-color); font-weight: 800;"><i class="fas fa-exclamation-triangle fa-2x"></i><br>数据加载失败,请重试</div>';
|
||||
container.innerHTML = '<div style="grid-column: span 1; text-align: center; padding: 60px; color: var(--danger-color); font-weight: 800;"><i class="fas fa-exclamation-triangle fa-2x"></i><br>数据加载失败</div>';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
37
register.php
37
register.php
@ -11,26 +11,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
$error = "Please fill all fields.";
|
||||
$error = __('fill_all_fields');
|
||||
} elseif ($password !== $confirm_password) {
|
||||
$error = "Passwords do not match.";
|
||||
$error = __('passwords_not_match');
|
||||
} else {
|
||||
$pdo = db();
|
||||
// Check if user exists
|
||||
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
if ($stmt->fetch()) {
|
||||
$error = "Username already taken.";
|
||||
$error = __('username_taken');
|
||||
} else {
|
||||
// Generate UID starting from 618120
|
||||
$stmt = $pdo->query("SELECT COUNT(*) FROM users");
|
||||
$count = $stmt->fetchColumn();
|
||||
$uid = 618120 + $count + mt_rand(1, 9);
|
||||
|
||||
// Capture IP
|
||||
// Capture IP correctly
|
||||
$user_ip = $_SERVER['REMOTE_ADDR'];
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||
$user_ip = trim($ips[0]);
|
||||
}
|
||||
|
||||
// Register and auto-login
|
||||
@ -43,7 +44,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
$error = "Registration failed.";
|
||||
$error = __('registration_failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,8 +55,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
<main style="background: #0b0e11; min-height: calc(100vh - 64px); display: flex; align-items: center; justify-content: center; padding: 40px 20px;">
|
||||
<div style="width: 100%; max-width: 480px; background: var(--card-bg); padding: 50px; border-radius: 32px; border: 1px solid var(--border-color); box-shadow: 0 20px 40px rgba(0,0,0,0.4);">
|
||||
<h2 style="font-size: 2.2rem; font-weight: 800; margin-bottom: 10px; text-align: center; color: white;">Create Account</h2>
|
||||
<p style="text-align: center; color: var(--text-muted); margin-bottom: 40px;">Join NovaEx - The Leading Crypto Exchange</p>
|
||||
<h2 style="font-size: 2.2rem; font-weight: 800; margin-bottom: 10px; text-align: center; color: white;"><?php echo __('create_account'); ?></h2>
|
||||
<p style="text-align: center; color: var(--text-muted); margin-bottom: 40px;"><?php echo __('join_novaex_tip'); ?></p>
|
||||
|
||||
<?php if($error): ?>
|
||||
<div style="background: rgba(246,70,93,0.1); color: var(--danger-color); padding: 15px; border-radius: 12px; margin-bottom: 25px; border: 1px solid var(--danger-color); text-align: center; font-size: 14px;">
|
||||
@ -65,36 +66,36 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
<form method="POST">
|
||||
<div style="margin-bottom: 25px;">
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;">Email or Phone</label>
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;"><?php echo __('email_or_phone'); ?></label>
|
||||
<div style="position: relative;">
|
||||
<i class="fas fa-envelope" style="position: absolute; left: 15px; top: 15px; color: #555;"></i>
|
||||
<input type="text" name="username" required placeholder="Enter your email or phone" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
<input type="text" name="username" required placeholder="<?php echo __('enter_email_phone'); ?>" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 25px;">
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;">Login Password</label>
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;"><?php echo __('login_password'); ?></label>
|
||||
<div style="position: relative;">
|
||||
<i class="fas fa-lock" style="position: absolute; left: 15px; top: 15px; color: #555;"></i>
|
||||
<input type="password" name="password" required placeholder="Set your login password" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
<input type="password" name="password" required placeholder="<?php echo __('set_password'); ?>" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 30px;">
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;">Confirm Password</label>
|
||||
<label style="display: block; margin-bottom: 10px; color: var(--text-muted); font-size: 14px;"><?php echo __('confirm_password_label'); ?></label>
|
||||
<div style="position: relative;">
|
||||
<i class="fas fa-check-double" style="position: absolute; left: 15px; top: 15px; color: #555;"></i>
|
||||
<input type="password" name="confirm_password" required placeholder="Confirm your password" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
<input type="password" name="confirm_password" required placeholder="<?php echo __('confirm_your_password'); ?>" style="width: 100%; padding: 15px 15px 15px 45px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 30px; display: flex; align-items: flex-start; gap: 12px;">
|
||||
<input type="checkbox" required style="margin-top: 4px; accent-color: var(--primary-color);">
|
||||
<span style="font-size: 0.85rem; color: var(--text-muted); line-height: 1.5;">I have read and agree to the <a href="privacy.php" style="color: var(--primary-color);">Privacy Policy</a> and <a href="terms.php" style="color: var(--primary-color);">Terms of Service</a>.</span>
|
||||
<span style="font-size: 0.85rem; color: var(--text-muted); line-height: 1.5;"><?php echo __('agree_terms'); ?></span>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 18px; font-weight: 800; font-size: 1.1rem; border-radius: 16px; box-shadow: 0 10px 20px rgba(0,82,255,0.2);"><?php echo __('nav_register'); ?></button>
|
||||
</form>
|
||||
<div style="text-align: center; margin-top: 30px; border-top: 1px solid var(--border-color); padding-top: 30px;">
|
||||
<span style="color: var(--text-muted);">Already have an account?</span> <a href="login.php" style="color: var(--primary-color); text-decoration: none; font-weight: bold;"><?php echo __('nav_login'); ?></a>
|
||||
<span style="color: var(--text-muted);"><?php echo __('already_have_account'); ?></span> <a href="login.php" style="color: var(--primary-color); text-decoration: none; font-weight: bold;"><?php echo __('nav_login'); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php include 'footer.php'; ?>
|
||||
131
spot.php
131
spot.php
@ -83,9 +83,62 @@ if ($user_id) {
|
||||
#ob-panel-content { flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2b3139 transparent; }
|
||||
#ob-panel-content::-webkit-scrollbar { width: 4px; }
|
||||
#ob-panel-content::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
|
||||
|
||||
/* Mobile Specific Styles */
|
||||
@media (max-width: 991px) {
|
||||
.trading-container { flex-direction: column; min-height: auto; }
|
||||
.center-col { min-width: 100%; border-right: none; }
|
||||
.chart-box { height: 300px; }
|
||||
.order-box { padding: 15px; }
|
||||
#records-list-container { height: auto; max-height: 400px; }
|
||||
.trading-page-wrapper { padding-bottom: 70px; }
|
||||
|
||||
.mobile-trade-nav {
|
||||
display: flex; background: #161a1e; border-bottom: 1px solid #2b3139; padding: 10px 15px; gap: 10px;
|
||||
}
|
||||
.mobile-trade-nav a {
|
||||
flex: 1; text-align: center; padding: 8px 0; background: #2b3139; border-radius: 8px; font-size: 13px; color: #848e9c; text-decoration: none; font-weight: 600; border: 1px solid transparent;
|
||||
}
|
||||
.mobile-trade-nav a.active {
|
||||
background: rgba(0, 82, 255, 0.1); color: var(--primary-color); border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.mobile-symbol-selector {
|
||||
display: flex; align-items: center; justify-content: space-between; padding: 12px 15px; background: #161a1e; border-bottom: 1px solid #2b3139;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.mobile-trade-nav, .mobile-symbol-selector { display: none; }
|
||||
}
|
||||
|
||||
/* Mobile Drawer */
|
||||
#mobile-pairs-drawer {
|
||||
position: fixed; top: 0; left: -100%; width: 100%; height: 100%; background: #0b0e11; z-index: 3000; transition: 0.3s; padding: 20px; display: flex; flex-direction: column;
|
||||
}
|
||||
#mobile-pairs-drawer.open { left: 0; }
|
||||
</style>
|
||||
|
||||
<div class="trading-page-wrapper">
|
||||
<!-- Mobile Navigation Tabs -->
|
||||
<div class="mobile-trade-nav">
|
||||
<a href="options.php"><?php echo __('nav_options'); ?></a>
|
||||
<a href="spot.php" class="active"><?php echo __('nav_spot'); ?></a>
|
||||
<a href="futures.php"><?php echo __('nav_futures'); ?></a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Symbol Selector -->
|
||||
<div class="mobile-symbol-selector" onclick="toggleMobilePairs()">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<img id="curr-icon-mobile" src="" class="coin-icon" style="width: 28px; height: 28px;">
|
||||
<span id="curr-pair-mobile" style="font-weight: 800; font-size: 18px; color: white;">--/--</span>
|
||||
<i class="fas fa-caret-down" style="color: #848e9c;"></i>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div id="curr-price-mobile" style="font-size: 18px; font-weight: 800; color: #0ecb81;">--</div>
|
||||
<div id="curr-change-mobile" style="font-size: 12px; font-weight: 600;">--</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trading-container">
|
||||
<!-- Left Column -->
|
||||
<div class="left-col d-none d-lg-flex">
|
||||
@ -100,7 +153,7 @@ if ($user_id) {
|
||||
|
||||
<!-- Center Column -->
|
||||
<div class="center-col">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header d-none d-lg-flex">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<img id="curr-icon" src="" class="coin-icon">
|
||||
<span id="curr-pair" style="font-weight: 800; font-size: 18px; color:white;">--/--</span>
|
||||
@ -163,9 +216,27 @@ if ($user_id) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Pairs Drawer -->
|
||||
<div id="mobile-pairs-drawer">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h3 style="margin: 0; font-weight: 800;"><?php echo __('market'); ?></h3>
|
||||
<i class="fas fa-times" onclick="toggleMobilePairs()" style="font-size: 20px; color: #848e9c;"></i>
|
||||
</div>
|
||||
<div class="search-box" style="padding: 0; margin-bottom: 20px;">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" id="pair-search-mobile" placeholder="<?php echo __('search_currency'); ?>">
|
||||
</div>
|
||||
<div id="pairs-list-mobile" style="flex: 1; overflow-y: auto;"></div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
||||
<script>
|
||||
const userAssets = <?php echo json_encode($user_assets); ?>;
|
||||
|
||||
function toggleMobilePairs() {
|
||||
document.getElementById('mobile-pairs-drawer').classList.toggle('open');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let currentPair = 'BTCUSDT', currentPrice = 0, ws, chartWidget;
|
||||
const marketData = {}, pairs = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT', 'DOTUSDT', 'MATICUSDT', 'LTCUSDT', 'TRXUSDT', 'LINKUSDT', 'UNIUSDT', 'ATOMUSDT', 'ETCUSDT', 'BCHUSDT', 'FILUSDT', 'ICPUSDT', 'NEARUSDT', 'AAVEUSDT', 'ALGOUSDT'];
|
||||
@ -174,8 +245,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
currPair: document.getElementById('curr-pair'),
|
||||
currPrice: document.getElementById('curr-price'),
|
||||
currChange: document.getElementById('curr-change'),
|
||||
currIconMobile: document.getElementById('curr-icon-mobile'),
|
||||
currPairMobile: document.getElementById('curr-pair-mobile'),
|
||||
currPriceMobile: document.getElementById('curr-price-mobile'),
|
||||
currChangeMobile: document.getElementById('curr-change-mobile'),
|
||||
tvContainer: document.getElementById('tv_chart_container'),
|
||||
pairsList: document.getElementById('pairs-list'),
|
||||
pairsListMobile: document.getElementById('pairs-list-mobile'),
|
||||
pairSearchMobile: document.getElementById('pair-search-mobile'),
|
||||
asksList: document.getElementById('asks-list'),
|
||||
bidsList: document.getElementById('bids-list'),
|
||||
midPrice: document.getElementById('mid-price'),
|
||||
@ -216,24 +293,38 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
marketData[data.s] = data;
|
||||
if (data.s === currentPair) {
|
||||
currentPrice = parseFloat(data.c);
|
||||
dom.currPrice.innerText = currentPrice.toLocaleString('en-US', {minimumFractionDigits:2});
|
||||
dom.currChange.innerText = (data.P >= 0 ? '+' : '') + parseFloat(data.P).toFixed(2) + '%';
|
||||
dom.currPrice.style.color = dom.currChange.style.color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
dom.midPrice.innerText = currentPrice.toFixed(2);
|
||||
const color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
||||
const priceStr = currentPrice.toLocaleString('en-US', {minimumFractionDigits:2});
|
||||
const changeStr = (data.P >= 0 ? '+' : '') + parseFloat(data.P).toFixed(2) + '%';
|
||||
|
||||
if(dom.currPrice) dom.currPrice.innerText = priceStr;
|
||||
if(dom.currChange) dom.currChange.innerText = changeStr;
|
||||
if(dom.currPrice) dom.currPrice.style.color = dom.currChange.style.color = color;
|
||||
|
||||
dom.currPriceMobile.innerText = priceStr;
|
||||
dom.currPriceMobile.style.color = color;
|
||||
dom.currChangeMobile.innerText = changeStr;
|
||||
dom.currChangeMobile.style.color = color;
|
||||
|
||||
if(dom.midPrice) dom.midPrice.innerText = currentPrice.toFixed(2);
|
||||
}
|
||||
if (dom.pairsList.offsetParent) renderPairs();
|
||||
if (dom.pairsList.offsetParent || dom.pairsListMobile.offsetParent) renderPairs();
|
||||
} else if (type.startsWith('depth')) {
|
||||
const row = (p, q, c) => `<div class="ob-row" onclick="document.getElementById('spot-amount').value=${parseFloat(q).toFixed(4)};"><span style="color:${c}">${parseFloat(p).toFixed(2)}</span><span>${parseFloat(q).toFixed(4)}</span></div>`;
|
||||
dom.asksList.innerHTML = data.asks.slice(0, 20).reverse().map(a => row(a[0], a[1], '#f6465d')).join('');
|
||||
dom.bidsList.innerHTML = data.bids.slice(0, 20).map(b => row(b[0], b[1], '#0ecb81')).join('');
|
||||
if(dom.asksList) {
|
||||
const row = (p, q, c) => `<div class="ob-row" onclick="document.getElementById('spot-amount').value=${parseFloat(q).toFixed(4)};"><span style="color:${c}">${parseFloat(p).toFixed(2)}</span><span>${parseFloat(q).toFixed(4)}</span></div>`;
|
||||
dom.asksList.innerHTML = data.asks.slice(0, 20).reverse().map(a => row(a[0], a[1], '#f6465d')).join('');
|
||||
dom.bidsList.innerHTML = data.bids.slice(0, 20).map(b => row(b[0], b[1], '#0ecb81')).join('');
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = () => setTimeout(connectWS, 5000);
|
||||
}
|
||||
|
||||
function renderPairs() {
|
||||
const q = document.getElementById('pair-search').value.toUpperCase();
|
||||
dom.pairsList.innerHTML = pairs.filter(p => p.includes(q)).map(p => {
|
||||
const query = document.getElementById('pair-search').value.toUpperCase();
|
||||
const queryMobile = dom.pairSearchMobile.value.toUpperCase();
|
||||
|
||||
const generateHtml = (q) => pairs.filter(p => p.includes(q)).map(p => {
|
||||
const d = marketData[p] || {};
|
||||
return `<div class="pair-item ${p === currentPair ? 'active' : ''}" onclick="switchPair('${p}')">
|
||||
<img src="https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png" class="coin-icon" onerror="this.style.opacity=0">
|
||||
@ -241,22 +332,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<div style="text-align:right"><div style="color:white; font-weight:600">${d.c ? parseFloat(d.c).toFixed(2) : '--'}</div><div style="color:${d.P>=0?'#0ecb81':'#f6465d'}; font-size:11px">${d.P?(d.P>=0?'+':'')+parseFloat(d.P).toFixed(2)+'%':'--'}</div></div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
dom.pairsList.innerHTML = generateHtml(query);
|
||||
dom.pairsListMobile.innerHTML = generateHtml(queryMobile);
|
||||
}
|
||||
|
||||
window.switchPair = (p) => {
|
||||
currentPair = p;
|
||||
dom.currPair.innerText = p.replace('USDT', '/USDT');
|
||||
dom.currIcon.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
if(dom.currPair) dom.currPair.innerText = p.replace('USDT', '/USDT');
|
||||
dom.currPairMobile.innerText = p.replace('USDT', '/USDT');
|
||||
if(dom.currIcon) dom.currIcon.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
dom.currIconMobile.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
||||
initChart(p);
|
||||
updateBalances();
|
||||
connectWS();
|
||||
if(document.getElementById('mobile-pairs-drawer').classList.contains('open')) toggleMobilePairs();
|
||||
};
|
||||
|
||||
dom.slider.oninput = () => {
|
||||
const percent = dom.slider.value / 100;
|
||||
const coin = currentPair.replace('USDT', '');
|
||||
// Simplified slider logic
|
||||
};
|
||||
dom.pairSearchMobile.addEventListener('input', renderPairs);
|
||||
|
||||
async function placeOrder(side) {
|
||||
const amount = parseFloat(dom.amountInput.value);
|
||||
@ -313,4 +406,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
setInterval(fetchOrders, 5000);
|
||||
});
|
||||
</script>
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
96
withdraw.php
96
withdraw.php
@ -1,23 +1,23 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/currency_helper.php';
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/currency_helper.php';
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
// Check for active orders
|
||||
$stmt = $db->prepare("SELECT id FROM fiat_orders WHERE user_id = ? AND status IN ('matching', 'matched', 'paid') ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
if ($stmt->fetch()) { header("Location: chat.php"); exit; }
|
||||
|
||||
include 'header.php';
|
||||
|
||||
$fiat_rates = get_fiat_rates();
|
||||
$fiat_currencies_info = [
|
||||
@ -52,12 +52,18 @@ $error = '';
|
||||
.input-group-custom input { background: none; border: none; color: white; font-size: 1.2rem; font-weight: 700; width: 100%; outline: none; }
|
||||
|
||||
.safety-alert { background: rgba(246, 70, 93, 0.1); border: 1px solid rgba(246, 70, 93, 0.2); padding: 20px; border-radius: 16px; color: var(--danger-color); display: flex; gap: 15px; align-items: center; margin-bottom: 30px; }
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.method-card { padding: 20px 15px; }
|
||||
.method-card .icon-box { width: 40px; height: 40px; font-size: 16px; }
|
||||
.method-card div:nth-child(2) { font-size: 0.95rem !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="withdraw-container">
|
||||
<div class="container" style="max-width: 1100px;">
|
||||
<div style="margin-bottom: 30px;">
|
||||
<a href="profile.php" class="back-btn"><i class="fas fa-arrow-left"></i> 个人中心</a>
|
||||
<a href="profile.php" class="back-btn" style="color: var(--text-muted); text-decoration: none; font-size: 14px;"><i class="fas fa-arrow-left"></i> 个人中心</a>
|
||||
<h1 style="font-size: 2.2rem; font-weight: 800; margin-top: 10px;">提现</h1>
|
||||
<p style="color: var(--text-muted);">安全地将您的资产提取至个人账户</p>
|
||||
</div>
|
||||
@ -69,7 +75,7 @@ $error = '';
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 380px; gap: 30px;">
|
||||
<div class="responsive-grid">
|
||||
<div>
|
||||
<div class="withdraw-card">
|
||||
<h3 style="margin-bottom: 25px; font-weight: 800;">1. 选择提现方式</h3>
|
||||
@ -77,12 +83,12 @@ $error = '';
|
||||
<div id="method-fiat" class="method-card" onclick="switchWithdrawMethod('fiat')">
|
||||
<div class="icon-box" style="background: rgba(79,172,254,0.1); color: #4facfe;"><i class="fas fa-university"></i></div>
|
||||
<div style="font-weight: 800; font-size: 1.1rem;">法币提现</div>
|
||||
<div style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">银行转账 / 全球 OTC</div>
|
||||
<div style="color: var(--text-muted); font-size: 11px; margin-top: 4px;">银行转账 / OTC</div>
|
||||
</div>
|
||||
<div id="method-usdt" class="method-card active" onclick="switchWithdrawMethod('usdt')">
|
||||
<div class="icon-box" style="background: rgba(14,203,129,0.1); color: var(--success-color);"><i class="fas fa-coins"></i></div>
|
||||
<div style="font-weight: 800; font-size: 1.1rem;">USDT 提现</div>
|
||||
<div style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">USDT 区块链转账</div>
|
||||
<div style="color: var(--text-muted); font-size: 11px; margin-top: 4px;">区块链转账</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -138,18 +144,18 @@ $error = '';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 20px; border-radius: 16px; font-size: 1.1rem; font-weight: 800; background: var(--danger-color);">
|
||||
<button type="submit" id="submit-btn" class="btn-primary" style="width: 100%; padding: 20px; border-radius: 16px; font-size: 1.1rem; font-weight: 800; background: var(--danger-color);">
|
||||
发起提现请求
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-sidebar">
|
||||
<div class="sidebar-area">
|
||||
<div class="withdraw-card" style="padding: 30px;">
|
||||
<h4 style="font-weight: 800; margin-bottom: 25px;"><i class="fas fa-shield-check" style="color: var(--success-color);"></i> 提现安全说明</h4>
|
||||
<div style="font-size: 13px; color: var(--text-muted); line-height: 1.8;">
|
||||
<p style="margin-bottom: 15px;">为了您的账户安全,提现申请将由人工审核,您将跳转至在线客服界面完成后续步骤。</p>
|
||||
<p style="margin-bottom: 15px;">为了您的账户安全,提现申请将由人工审核,您将自动弹出在线客服界面完成后续步骤。</p>
|
||||
<p style="margin-bottom: 15px;">发起申请后,请按照客服要求提供您的收款账户信息。</p>
|
||||
<div style="background: rgba(255, 255, 255, 0.03); padding: 15px; border-radius: 12px; border-left: 3px solid var(--primary-color);">
|
||||
请确保您的收款账户信息 100% 正确。资金一旦发出,将无法追回。
|
||||
@ -162,7 +168,7 @@ $error = '';
|
||||
<div style="font-size: 13px; color: var(--text-muted);">
|
||||
提现过程中遇到问题?联系我们的 24/7 在线客服。
|
||||
<div style="margin-top: 20px;">
|
||||
<a href="chat.php" class="btn" style="width: 100%; background: #2b3139; color: white; border-radius: 10px; padding: 10px;">联系客服</a>
|
||||
<a href="javascript:void(0)" onclick="openCSChat()" class="btn" style="width: 100%; background: #2b3139; color: white; border-radius: 10px; padding: 10px; text-decoration: none; display: block; text-align: center;">联系客服</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -193,6 +199,62 @@ $error = '';
|
||||
}
|
||||
|
||||
updateWithdrawRate();
|
||||
|
||||
// AJAX Form Submission
|
||||
const withdrawForm = document.getElementById('withdraw-form');
|
||||
if (withdrawForm) {
|
||||
withdrawForm.onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const btn = document.getElementById('submit-btn');
|
||||
const originalText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 处理中...';
|
||||
|
||||
try {
|
||||
const formData = new FormData(withdrawForm);
|
||||
const resp = await fetch('matching.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
// For withdrawals, matching.php might return plain text if balance is insufficient
|
||||
const text = await resp.text();
|
||||
let res;
|
||||
try {
|
||||
res = JSON.parse(text);
|
||||
} catch (e) {
|
||||
alert(text || '提交失败,请检查余额或密码');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.success) {
|
||||
// Trigger chat popup
|
||||
if (typeof openCSChat === 'function') {
|
||||
openCSChat();
|
||||
withdrawForm.style.opacity = '0.5';
|
||||
withdrawForm.style.pointerEvents = 'none';
|
||||
btn.innerHTML = '<i class="fas fa-check"></i> 请求已发送';
|
||||
} else {
|
||||
window.location.href = 'chat.php';
|
||||
}
|
||||
} else {
|
||||
alert(res.error || '提交失败,请稍后重试');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert('网络错误,请稍后重试');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php include 'footer.php'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user