交易所
This commit is contained in:
parent
7c4904baad
commit
79b07243ce
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
require_once '../includes/currency_helper.php';
|
||||
session_start();
|
||||
$pdo = db();
|
||||
|
||||
@ -20,18 +21,37 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['message'])) {
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
|
||||
$oid = $_POST['order_id'];
|
||||
if ($_POST['action'] == 'match') {
|
||||
$info = $_POST['account_info'];
|
||||
$bank = $_POST['bank_name'] ?? '';
|
||||
$name = $_POST['account_name'] ?? '';
|
||||
$number = $_POST['account_number'] ?? '';
|
||||
$remarks = $_POST['remarks'] ?? '';
|
||||
|
||||
$info = "🏦 银行名称:$bank\n👤 收款姓名:$name\n💳 收款账号:$number\n📝 备注说明:$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\n请转账并上传凭证。 οδηγός"]);
|
||||
|
||||
$notif = "✅ 匹配成功!收款账户已下发。请在页面强制弹窗中查看详细信息并进行转账。";
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $notif]);
|
||||
} elseif ($_POST['action'] == 'complete') {
|
||||
$stmt = $pdo->prepare("SELECT amount FROM fiat_orders WHERE id = ?");
|
||||
$stmt = $pdo->prepare("SELECT amount, currency, exchange_rate FROM fiat_orders WHERE id = ?");
|
||||
$stmt->execute([$oid]);
|
||||
$amt = $stmt->fetchColumn();
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$amt, $user_id]);
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'completed' WHERE id = ?")->execute([$oid]);
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "充值已到账,金额:$amt USDT"]);
|
||||
$order = $stmt->fetch();
|
||||
$amt = $order['amount'];
|
||||
$cur = $order['currency'];
|
||||
|
||||
$fiat_rates = get_fiat_rates();
|
||||
$real_time_rate = $fiat_rates[$cur] ?? $order['exchange_rate'];
|
||||
$usdt_amt = ($real_time_rate > 0) ? ($amt / $real_time_rate) : $amt;
|
||||
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$usdt_amt, $user_id]);
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'completed', usdt_amount = ?, exchange_rate = ? WHERE id = ?")
|
||||
->execute([$usdt_amt, $real_time_rate, $oid]);
|
||||
|
||||
$notif = "🎉 充值已确认到账!\n金额:" . number_format($amt, 2) . " $cur\n实时汇率:1 USDT = " . number_format($real_time_rate, 4) . " $cur\n入账:" . number_format($usdt_amt, 2) . " USDT";
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, $notif]);
|
||||
} elseif ($_POST['action'] == 'reject') {
|
||||
$pdo->prepare("UPDATE fiat_orders SET status = 'rejected' WHERE id = ?")->execute([$oid]);
|
||||
$pdo->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'admin', ?)")->execute([$user_id, "❌ 您的充值申请 #$oid 已被拒绝。"]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +66,8 @@ $msgs = $messages->fetchAll();
|
||||
$orders = $pdo->prepare("SELECT * FROM fiat_orders WHERE user_id = ? AND status IN ('matching', 'submitting', 'matched') ORDER BY id DESC");
|
||||
$orders->execute([$user_id]);
|
||||
$pending_orders = $orders->fetchAll();
|
||||
|
||||
$current_rates = get_fiat_rates();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
@ -53,100 +75,111 @@ $pending_orders = $orders->fetchAll();
|
||||
<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; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #0B0E11; color: white; display: flex; flex-direction: column; height: 100vh; }
|
||||
.chat-header { padding: 15px; background: #1E2329; border-bottom: 1px solid #2B3139; display: flex; justify-content: space-between; align-items: center; }
|
||||
.chat-box { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 15px; }
|
||||
.msg { max-width: 70%; padding: 10px 15px; border-radius: 8px; font-size: 14px; line-height: 1.5; }
|
||||
.msg.admin { align-self: flex-end; background: #377aff; color: white; }
|
||||
.msg.user { align-self: flex-start; background: #2B3139; color: #EAECEF; }
|
||||
.msg-time { font-size: 11px; color: #848E9C; margin-top: 5px; display: block; }
|
||||
|
||||
.recharge-panel { background: #1E2329; border-bottom: 1px solid #2B3139; padding: 15px; }
|
||||
.order-card { background: #0B0E11; border: 1px solid #2B3139; border-radius: 6px; padding: 12px; margin-bottom: 10px; }
|
||||
.input-area { padding: 15px; background: #1E2329; border-top: 1px solid #2B3139; display: flex; gap: 10px; }
|
||||
input[type="text"] { flex: 1; background: #0B0E11; border: 1px solid #2B3139; color: white; padding: 10px; border-radius: 4px; outline: none; }
|
||||
button { background: #377aff; border: none; color: white; padding: 10px 20px; border-radius: 4px; cursor: pointer; }
|
||||
|
||||
.status-badge { font-size: 11px; padding: 2px 6px; border-radius: 3px; }
|
||||
.matching { background: #ff3c00; }
|
||||
.submitting { background: #00c087; }
|
||||
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #0B0E11; color: white; display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
|
||||
.chat-header { padding: 15px 20px; background: #1E2329; border-bottom: 1px solid #2B3139; display: flex; justify-content: space-between; align-items: center; z-index: 10; }
|
||||
.main-content { flex: 1; display: flex; flex-direction: column; overflow-y: auto; }
|
||||
.chat-box { flex: 1; padding: 20px; display: flex; flex-direction: column; gap: 15px; }
|
||||
.msg { max-width: 85%; padding: 12px 16px; border-radius: 12px; font-size: 14px; line-height: 1.6; position: relative; }
|
||||
.msg.admin { align-self: flex-end; background: #377aff; color: white; border-bottom-right-radius: 2px; }
|
||||
.msg.user { align-self: flex-start; background: #2B3139; color: #EAECEF; border-bottom-left-radius: 2px; }
|
||||
.msg-time { font-size: 10px; color: #848E9C; margin-top: 5px; display: block; text-align: right; }
|
||||
.recharge-panel { background: #1E2329; border-bottom: 1px solid #2B3139; padding: 15px 20px; }
|
||||
.order-card { background: #161A1E; border: 1px solid #2B3139; border-radius: 16px; padding: 15px; margin-bottom: 12px; }
|
||||
.input-area { padding: 15px 20px; background: #1E2329; border-top: 1px solid #2B3139; display: flex; gap: 12px; }
|
||||
input[type="text"], input[type="number"], textarea { width: 100%; background: #0B0E11; border: 1px solid #2B3139; color: white; padding: 10px; border-radius: 8px; outline: none; margin-bottom: 8px; font-size: 13px; }
|
||||
button { background: #377aff; border: none; color: white; padding: 10px 20px; border-radius: 8px; cursor: pointer; font-weight: bold; }
|
||||
.status-badge { font-size: 10px; padding: 4px 10px; border-radius: 6px; font-weight: 800; }
|
||||
.matching { background: rgba(240, 185, 11, 0.1); color: #f0b90b; }
|
||||
.submitting { background: rgba(0, 192, 135, 0.1); color: #00c087; }
|
||||
.btn-complete { background: #00c087; }
|
||||
.btn-reject { background: #f6465d; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="chat-header">
|
||||
<div>
|
||||
<span style="font-weight: bold;"><?php echo htmlspecialchars($userData['username']); ?></span>
|
||||
<span style="color: #848E9C; font-size: 0.8rem; margin-left: 10px;">UID: <?php echo $userData['uid']; ?></span>
|
||||
<span style="color: #848E9C; font-size: 0.8rem; margin-left: 10px;">IP: <?php echo htmlspecialchars($userData['last_ip'] ?? '未知'); ?></span>
|
||||
<span style="font-weight: 800;"><?php echo htmlspecialchars($userData['username'] ?? 'User'); ?></span>
|
||||
<span style="color: #848E9C; font-size: 11px; margin-left: 8px;">UID: <?php echo $userData['uid'] ?? 'N/A'; ?></span>
|
||||
</div>
|
||||
<div style="font-size: 0.8rem; color: #00c087;">
|
||||
余额: <?php echo number_format($userData['balance'], 2); ?> USDT
|
||||
<div style="text-align: right;">
|
||||
<div style="font-size: 11px; color: #848E9C;">余额: <span style="color: #00c087;"><?php echo number_format($userData['balance'] ?? 0, 2); ?> USDT</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($pending_orders)): ?>
|
||||
<div class="recharge-panel">
|
||||
<h4 style="margin: 0 0 10px 0; color: #F0B90B; font-size: 0.9rem;"><i class="fas fa-wallet"></i> 待处理充值申请</h4>
|
||||
<div class="main-content">
|
||||
<?php if (!empty($pending_orders)): ?>
|
||||
<div class="recharge-panel">
|
||||
<div style="font-weight: bold; color: #F0B90B; margin-bottom: 10px; font-size: 12px;">待处理充值</div>
|
||||
<?php foreach($pending_orders as $o):
|
||||
// Corrected the newline escape sequence here
|
||||
$is_matching = ($o['status'] == 'matching');
|
||||
$live_rate = $current_rates[$o['currency']] ?? $o['exchange_rate'];
|
||||
$live_usdt = ($live_rate > 0) ? ($o['amount'] / $live_rate) : $o['amount'];
|
||||
?>
|
||||
<div class="order-card">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 0.9rem;">充值: <b><?php echo number_format($o['amount'], 2); ?> <?php echo $o['currency']; ?></b></span>
|
||||
<span class="status-badge <?php echo $o['status']; ?>"><?php echo $o['status'] == 'matching' ? '待匹配' : '待审核'; ?></span>
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
||||
<div>
|
||||
<div style="font-size: 1rem; font-weight: 900;"><?php echo number_format($o['amount'], 2); ?> <?php echo $o['currency']; ?></div>
|
||||
<div style="font-size: 11px; color: #848E9C;">≈ <?php echo number_format($live_usdt, 2); ?> USDT (汇率: <?php echo number_format($live_rate, 4); ?>)</div>
|
||||
</div>
|
||||
<span class="status-badge <?php echo $o['status']; ?>">
|
||||
<?php
|
||||
if($o['status'] == 'matching') echo '等待分配账户';
|
||||
elseif($o['status'] == 'matched') echo '已分配/待支付';
|
||||
elseif($o['status'] == 'submitting') echo '已提交凭证';
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<?php if($o['status'] == 'matching'): ?>
|
||||
<form method="POST" style="margin-top: 10px; display: flex; gap: 5px;">
|
||||
<?php if($is_matching): ?>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
|
||||
<input type="hidden" name="action" value="match">
|
||||
<input type="text" name="account_info" placeholder="在此输入收款卡号/姓名" required style="font-size: 0.8rem;">
|
||||
<button type="submit" style="font-size: 0.8rem; padding: 5px 10px;">分配账户</button>
|
||||
<input type="text" name="bank_name" placeholder="银行名称 (如: 中国工商银行)" required>
|
||||
<input type="text" name="account_name" placeholder="收款人姓名" required>
|
||||
<input type="text" name="account_number" placeholder="银行账号" required>
|
||||
<textarea name="remarks" placeholder="注意事项 (可选)" style="height: 50px;"></textarea>
|
||||
<button type="submit" style="width: 100%;">确认匹配账户</button>
|
||||
</form>
|
||||
<?php elseif($o['status'] == 'submitting' || $o['status'] == 'matched'): ?>
|
||||
<div style="margin-top: 10px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<?php if($o['proof_image']):
|
||||
// Corrected the path concatenation here
|
||||
?>
|
||||
<a href="../<?php echo $o['proof_image']; ?>" target="_blank" style="color: #00c087; font-size: 0.8rem;"><i class="fas fa-image"></i> 查看凭证</a>
|
||||
<?php else:
|
||||
// Corrected the string literal here
|
||||
?>
|
||||
<span style="color: #5e6673; font-size: 0.8rem;">未上传凭证</span>
|
||||
<?php else: ?>
|
||||
<div style="background: rgba(255,255,255,0.03); padding: 10px; border-radius: 8px; margin-bottom: 10px; font-size: 12px;">
|
||||
<?php echo nl2br(htmlspecialchars($o['bank_account_info'])); ?>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<?php if($o['proof_image']): ?>
|
||||
<a href="../<?php echo $o['proof_image']; ?>" target="_blank" style="color: #00c087; font-size: 12px; text-decoration: none; border: 1px solid #00c087; padding: 5px 10px; border-radius: 5px;">查看凭证</a>
|
||||
<?php endif; ?>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<div style="flex: 1; display: flex; gap: 5px; justify-content: flex-end;">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
|
||||
<input type="hidden" name="action" value="complete">
|
||||
<button type="submit" style="background: #00c087; font-size: 0.8rem; padding: 5px 10px;">同意入金</button>
|
||||
<button type="submit" class="btn-complete" style="padding: 5px 10px; font-size: 12px;">同意</button>
|
||||
</form>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
|
||||
<input type="hidden" name="action" value="reject">
|
||||
<button type="submit" style="background: #f6465d; font-size: 0.8rem; padding: 5px 10px;">拒绝</button>
|
||||
<button type="submit" class="btn-reject" style="padding: 5px 10px; font-size: 12px;">拒绝</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="chat-box" id="chat-box">
|
||||
<?php foreach($msgs as $m):
|
||||
// Corrected the nl2br usage here
|
||||
?>
|
||||
<div class="chat-box" id="chat-box">
|
||||
<?php foreach($msgs as $m): ?>
|
||||
<div class="msg <?php echo $m['sender']; ?>">
|
||||
<?php echo nl2br(htmlspecialchars($m['message'])); ?>
|
||||
<span class="msg-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="input-area" id="msg-form">
|
||||
<input type="text" id="msg-input" placeholder="输入回复内容..." autocomplete="off">
|
||||
<input type="text" id="msg-input" placeholder="输入消息..." autocomplete="off">
|
||||
<button type="submit"><i class="fas fa-paper-plane"></i></button>
|
||||
</form>
|
||||
|
||||
@ -162,23 +195,21 @@ $pending_orders = $orders->fetchAll();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('message', msg);
|
||||
|
||||
input.value = '';
|
||||
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.className = 'msg admin';
|
||||
// Corrected the newline replacement here
|
||||
msgDiv.innerHTML = msg.replace(/\n/g, '<br>') + '<span class="msg-time">刚刚</span>';
|
||||
msgDiv.innerHTML = msg.replace(/\n/g, '<br>') + `<span class="msg-time">${new Date().getHours()}:${new Date().getMinutes()}</span>`;
|
||||
chatBox.appendChild(msgDiv);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
await fetch(window.location.href, { method: 'POST', body: formData });
|
||||
};
|
||||
|
||||
// Auto refresh
|
||||
setInterval(async () => {
|
||||
const res = await fetch('../api/get_messages.php?user_id=<?php echo $user_id; ?>');
|
||||
const data = await res.json();
|
||||
if (data.new_messages) {
|
||||
if (data && data.count > <?php echo count($msgs); ?>) {
|
||||
location.reload();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
@ -3,6 +3,8 @@ require_once '../db/config.php';
|
||||
session_start();
|
||||
$pdo = db();
|
||||
|
||||
$faceValue = 10;
|
||||
|
||||
if (isset($_POST['action']) && isset($_POST['order_id'])) {
|
||||
$oid = $_POST['order_id'];
|
||||
$action = $_POST['action'];
|
||||
@ -11,22 +13,30 @@ if (isset($_POST['action']) && isset($_POST['order_id'])) {
|
||||
$orderStmt->execute([$oid]);
|
||||
$order = $orderStmt->fetch();
|
||||
|
||||
if ($order) {
|
||||
if ($order && $order['status'] == 'open') {
|
||||
$user_id = $order['user_id'];
|
||||
$margin = $order['total'] / $order['leverage'];
|
||||
|
||||
if ($action == 'approve') {
|
||||
// Logic for closing a futures position with profit
|
||||
// Simplified: adding the total + profit (if side is buy and price went up)
|
||||
// But usually the user wants to "approve" the TP execution.
|
||||
// Settle at TP price (or current price if tp not set, but here we assume admin uses the TP field as exit price)
|
||||
$exit_price = (float)($order['tp_price'] ?: $order['price']);
|
||||
$entry_price = (float)$order['price'];
|
||||
$nominal = (float)$order['amount'] * $faceValue;
|
||||
|
||||
$profit = 0;
|
||||
if ($order['side'] == 'buy') {
|
||||
$profit = ($order['tp_price'] - $order['price']) * $order['amount'] * $order['leverage'];
|
||||
$profit = ($exit_price / $entry_price - 1) * $nominal;
|
||||
} else {
|
||||
$profit = ($order['price'] - $order['tp_price']) * $order['amount'] * $order['leverage'];
|
||||
$profit = (1 - $exit_price / $entry_price) * $nominal;
|
||||
}
|
||||
|
||||
$new_balance = $order['balance'] + $order['total'] + $profit;
|
||||
$pdo->prepare("UPDATE users SET balance = ? WHERE id = ?")->execute([$new_balance, $order['user_id']]);
|
||||
$payout = $margin + $profit;
|
||||
if ($payout < 0) $payout = 0; // Negative equity handled as 0 payout (liquidation)
|
||||
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$payout, $user_id]);
|
||||
$pdo->prepare("UPDATE trading_orders SET status = 'closed', admin_status = 'approved', win_loss = 'win' WHERE id = ?")->execute([$oid]);
|
||||
} elseif ($action == 'reject') {
|
||||
// Reject usually means loss/liquidation. No payout.
|
||||
$pdo->prepare("UPDATE trading_orders SET status = 'cancelled', admin_status = 'rejected', win_loss = 'loss' WHERE id = ?")->execute([$oid]);
|
||||
} elseif ($action == 'set_win_loss') {
|
||||
$win_loss = $_POST['win_loss'];
|
||||
@ -91,8 +101,7 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN
|
||||
<th>方向</th>
|
||||
<th>杠杆</th>
|
||||
<th>开仓价</th>
|
||||
<th>止盈价</th>
|
||||
<th>止损价</th>
|
||||
<th>退出价(止盈)</th>
|
||||
<th>保证金</th>
|
||||
<th>盈亏控制</th>
|
||||
<th>状态</th>
|
||||
@ -109,8 +118,7 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN
|
||||
<td><?php echo $o['leverage']; ?>x</td>
|
||||
<td><?php echo number_format($o['price'], 4); ?></td>
|
||||
<td><?php echo number_format($o['tp_price'], 4); ?></td>
|
||||
<td><?php echo number_format($o['sl_price'], 4); ?></td>
|
||||
<td><?php echo number_format($o['total'], 2); ?></td>
|
||||
<td><?php echo number_format($o['total'] / $o['leverage'], 2); ?></td>
|
||||
<td>
|
||||
<form method="POST" style="display: flex; gap: 5px;">
|
||||
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
|
||||
@ -135,12 +143,12 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN
|
||||
<form method="POST">
|
||||
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<button type="submit" class="btn-sm btn-approve" title="按止盈价结算">同意结算</button>
|
||||
<button type="submit" class="btn-sm btn-approve" title="按退出价结算">同意结算</button>
|
||||
</form>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="order_id" value="<?php echo $o['id']; ?>">
|
||||
<input type="hidden" name="action" value="reject">
|
||||
<button type="submit" class="btn-sm btn-reject" title="按止损/爆仓处理">拒绝结算</button>
|
||||
<button type="submit" class="btn-sm btn-reject" title="按亏损处理">拒绝结算</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@ -7,21 +7,34 @@ if (isset($_POST['action']) && isset($_POST['order_id'])) {
|
||||
$oid = $_POST['order_id'];
|
||||
$action = $_POST['action'];
|
||||
|
||||
$orderStmt = $pdo->prepare("SELECT o.*, u.balance FROM trading_orders o JOIN users u ON o.user_id = u.id WHERE o.id = ?");
|
||||
$orderStmt = $pdo->prepare("SELECT * FROM trading_orders WHERE id = ?");
|
||||
$orderStmt->execute([$oid]);
|
||||
$order = $orderStmt->fetch();
|
||||
|
||||
if ($order) {
|
||||
if ($order && $order['status'] == 'open') {
|
||||
$user_id = $order['user_id'];
|
||||
$symbol = $order['symbol'];
|
||||
$coin_symbol = str_replace('USDT', '', $symbol);
|
||||
|
||||
if ($action == 'approve') {
|
||||
// If it's a sell order, we close it and update user balance
|
||||
if ($order['side'] == 'sell') {
|
||||
$new_balance = $order['balance'] + $order['total'];
|
||||
$pdo->prepare("UPDATE users SET balance = ? WHERE id = ?")->execute([$new_balance, $order['user_id']]);
|
||||
if ($order['side'] == 'buy') {
|
||||
// Buy approved: Add coins to user_assets
|
||||
$stmt = $pdo->prepare("INSERT INTO user_assets (user_id, symbol, amount) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE amount = amount + ?");
|
||||
$stmt->execute([$user_id, $coin_symbol, $order['amount'], $order['amount']]);
|
||||
} else {
|
||||
// Sell approved: Add USDT to users balance
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['total'], $user_id]);
|
||||
}
|
||||
$pdo->prepare("UPDATE trading_orders SET status = 'closed', admin_status = 'approved' WHERE id = ?")->execute([$oid]);
|
||||
} elseif ($action == 'reject') {
|
||||
// If rejected, we might need to refund the amount if it was a buy order,
|
||||
// but usually "rejecting" a profitable sell means it just stays open or gets cancelled.
|
||||
if ($order['side'] == 'buy') {
|
||||
// Buy rejected: Refund USDT to users balance
|
||||
$pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['total'], $user_id]);
|
||||
} else {
|
||||
// Sell rejected: Refund coins to user_assets
|
||||
$stmt = $pdo->prepare("INSERT INTO user_assets (user_id, symbol, amount) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE amount = amount + ?");
|
||||
$stmt->execute([$user_id, $coin_symbol, $order['amount'], $order['amount']]);
|
||||
}
|
||||
$pdo->prepare("UPDATE trading_orders SET status = 'cancelled', admin_status = 'rejected' WHERE id = ?")->execute([$oid]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ try {
|
||||
|
||||
if (!$order) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => 'Order not found or already processed']);
|
||||
echo json_encode(['success' => false, 'error' => '订单未找到或已处理']);
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -37,12 +37,22 @@ try {
|
||||
$stmt = $db->prepare("UPDATE trading_orders SET status = 'cancelled' WHERE id = ?");
|
||||
$stmt->execute([$order_id]);
|
||||
|
||||
// Refund balance (simplified: return the total/cost to balance)
|
||||
$cost = ($order['type'] === 'futures') ? ($order['total'] / $order['leverage']) : ($order['side'] === 'buy' ? $order['total'] : 0);
|
||||
|
||||
if ($cost > 0) {
|
||||
if ($order['type'] === 'spot') {
|
||||
if ($order['side'] === 'buy') {
|
||||
// Refund USDT
|
||||
$stmt = $db->prepare("UPDATE users SET balance = balance + ? WHERE id = ?");
|
||||
$stmt->execute([$cost, $user_id]);
|
||||
$stmt->execute([$order['total'], $user_id]);
|
||||
} else {
|
||||
// Refund coins
|
||||
$coin_symbol = str_replace('USDT', '', $order['symbol']);
|
||||
$stmt = $db->prepare("INSERT INTO user_assets (user_id, symbol, amount) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE amount = amount + ?");
|
||||
$stmt->execute([$user_id, $coin_symbol, $order['amount'], $order['amount']]);
|
||||
}
|
||||
} else {
|
||||
// Futures: Refund margin
|
||||
$margin = $order['total'] / $order['leverage'];
|
||||
$stmt = $db->prepare("UPDATE users SET balance = balance + ? WHERE id = ?");
|
||||
$stmt->execute([$margin, $user_id]);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
39
api/get_assets.php
Normal file
39
api/get_assets.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
|
||||
// Get USDT balance
|
||||
$stmt = $db->prepare("SELECT balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$usdt = $stmt->fetchColumn();
|
||||
|
||||
// Get other assets
|
||||
$stmt = $db->prepare("SELECT symbol, amount FROM user_assets WHERE user_id = ? AND amount > 0");
|
||||
$stmt->execute([$user_id]);
|
||||
$other_assets = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$assets = [['symbol' => 'USDT', 'amount' => (float)$usdt]];
|
||||
foreach ($other_assets as $asset) {
|
||||
$assets[] = [
|
||||
'symbol' => $asset['symbol'],
|
||||
'amount' => (float)$asset['amount']
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $assets]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@ -9,9 +9,10 @@ if (!isset($_SESSION['user_id'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
// Support both regular user and admin polling
|
||||
$user_id = isset($_GET['user_id']) ? $_GET['user_id'] : $_SESSION['user_id'];
|
||||
|
||||
// Admin Poll Logic
|
||||
// Admin Poll Logic (Global notifications)
|
||||
if (isset($_GET['admin_poll'])) {
|
||||
// Check for latest message ID
|
||||
$last_id = db()->query("SELECT MAX(id) FROM messages")->fetchColumn() ?: 0;
|
||||
@ -21,7 +22,7 @@ if (isset($_GET['admin_poll'])) {
|
||||
|
||||
// Get latest message content if it's a recharge notification
|
||||
$latest_msg = db()->query("SELECT message FROM messages WHERE sender = 'user' ORDER BY id DESC LIMIT 1")->fetchColumn();
|
||||
$is_recharge = (strpos($latest_msg, '[RECHARGE_') === 0);
|
||||
$is_recharge = ($latest_msg && strpos($latest_msg, '[RECHARGE_') === 0);
|
||||
|
||||
echo json_encode([
|
||||
'last_id' => (int)$last_id,
|
||||
@ -32,7 +33,7 @@ if (isset($_GET['admin_poll'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Regular User Poll Logic
|
||||
// Regular User or specific Chat Polling Logic
|
||||
$stmt = db()->prepare("SELECT COUNT(*) FROM messages WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$count = $stmt->fetchColumn();
|
||||
|
||||
@ -17,41 +17,73 @@ if (!$data) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$symbol = $data['symbol'];
|
||||
$symbol = $data['symbol']; // e.g., BTCUSDT
|
||||
$type = $data['type']; // spot or futures
|
||||
$side = $data['side']; // buy or sell
|
||||
$order_type = $data['order_type']; // limit or market
|
||||
$price = $data['price'];
|
||||
$amount = $data['amount'];
|
||||
$total = $data['total'];
|
||||
$leverage = $data['leverage'] ?? 1;
|
||||
$tp_price = $data['tp_price'] ?? null;
|
||||
$sl_price = $data['sl_price'] ?? null;
|
||||
$price = (float)$data['price'];
|
||||
$amount = (float)$data['amount'];
|
||||
$total = (float)$data['total'];
|
||||
$leverage = (int)($data['leverage'] ?? 1);
|
||||
$tp_price = isset($data['tp_price']) ? (float)$data['tp_price'] : null;
|
||||
$sl_price = isset($data['sl_price']) ? (float)$data['sl_price'] : null;
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$db->beginTransaction();
|
||||
|
||||
// Check balance if buying spot or opening long/short futures (simplified)
|
||||
if ($type === 'spot') {
|
||||
if ($side === 'buy') {
|
||||
// Check USDT balance
|
||||
$stmt = $db->prepare("SELECT balance FROM users WHERE id = ? FOR UPDATE");
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch();
|
||||
$balance = (float)$stmt->fetchColumn();
|
||||
|
||||
if ($side === 'buy' || $type === 'futures') {
|
||||
$cost = $type === 'futures' ? $total / $leverage : $total;
|
||||
if ($user['balance'] < $cost) {
|
||||
if ($balance < $total) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => 'Insufficient balance']);
|
||||
echo json_encode(['success' => false, 'error' => '余额不足 (USDT)']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Deduct balance
|
||||
// Deduct USDT
|
||||
$stmt = $db->prepare("UPDATE users SET balance = balance - ? WHERE id = ?");
|
||||
$stmt->execute([$cost, $user_id]);
|
||||
$stmt->execute([$total, $user_id]);
|
||||
} else {
|
||||
// Spot Sell: Check coin balance
|
||||
$coin_symbol = str_replace('USDT', '', $symbol);
|
||||
$stmt = $db->prepare("SELECT amount FROM user_assets WHERE user_id = ? AND symbol = ? FOR UPDATE");
|
||||
$stmt->execute([$user_id, $coin_symbol]);
|
||||
$asset_amount = (float)$stmt->fetchColumn();
|
||||
|
||||
if ($asset_amount < $amount) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => '资产余额不足 (' . $coin_symbol . ')']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Deduct coin
|
||||
$stmt = $db->prepare("UPDATE user_assets SET amount = amount - ? WHERE user_id = ? AND symbol = ?");
|
||||
$stmt->execute([$amount, $user_id, $coin_symbol]);
|
||||
}
|
||||
} else {
|
||||
// Futures: Deduct margin (USDT)
|
||||
$margin = $total / $leverage;
|
||||
$stmt = $db->prepare("SELECT balance FROM users WHERE id = ? FOR UPDATE");
|
||||
$stmt->execute([$user_id]);
|
||||
$balance = (float)$stmt->fetchColumn();
|
||||
|
||||
if ($balance < $margin) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => '余额不足 (USDT)']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("UPDATE users SET balance = balance - ? WHERE id = ?");
|
||||
$stmt->execute([$margin, $user_id]);
|
||||
}
|
||||
|
||||
// Insert order
|
||||
$stmt = $db->prepare("INSERT INTO trading_orders (user_id, symbol, type, side, order_type, price, amount, total, leverage, tp_price, sl_price) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt = $db->prepare("INSERT INTO trading_orders (user_id, symbol, type, side, order_type, price, amount, total, leverage, tp_price, sl_price, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'open')");
|
||||
$stmt->execute([$user_id, $symbol, $type, $side, $order_type, $price, $amount, $total, $leverage, $tp_price, $sl_price]);
|
||||
|
||||
$db->commit();
|
||||
|
||||
9
db/migrations/01_create_user_assets.sql
Normal file
9
db/migrations/01_create_user_assets.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS user_assets (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
symbol VARCHAR(20) NOT NULL,
|
||||
amount DECIMAL(30, 10) DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY user_symbol (user_id, symbol)
|
||||
);
|
||||
189
deposit.php
189
deposit.php
@ -7,25 +7,38 @@ if (!isset($_SESSION['user_id'])) {
|
||||
}
|
||||
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/currency_helper.php';
|
||||
|
||||
$db = db();
|
||||
|
||||
// Check for existing pending orders to force workflow
|
||||
$stmt = $db->prepare("SELECT id FROM fiat_orders WHERE user_id = ? AND status IN ('matching', 'matched', 'submitting') ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$pending_order = $stmt->fetch();
|
||||
if ($pending_order) {
|
||||
header("Location: matching.php?order_id=" . $pending_order['id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
$fiat_currencies = [
|
||||
'USD' => ['name' => 'US Dollar', 'rate' => 1.00],
|
||||
'EUR' => ['name' => 'Euro', 'rate' => 0.92],
|
||||
'GBP' => ['name' => 'British Pound', 'rate' => 0.79],
|
||||
'CNY' => ['name' => 'Chinese Yuan', 'rate' => 7.23],
|
||||
'HKD' => ['name' => 'Hong Kong Dollar', 'rate' => 7.82],
|
||||
'JPY' => ['name' => 'Japanese Yen', 'rate' => 151.45],
|
||||
'KRW' => ['name' => 'Korean Won', 'rate' => 1350.20],
|
||||
'SGD' => ['name' => 'Singapore Dollar', 'rate' => 1.35],
|
||||
'TWD' => ['name' => 'Taiwan Dollar', 'rate' => 32.10],
|
||||
'THB' => ['name' => 'Thai Baht', 'rate' => 36.50],
|
||||
'VND' => ['name' => 'Vietnamese Dong', 'rate' => 24800],
|
||||
'IDR' => ['name' => 'Indonesian Rupiah', 'rate' => 15850],
|
||||
'MYR' => ['name' => 'Malaysian Ringgit', 'rate' => 4.74],
|
||||
$fiat_rates = get_fiat_rates();
|
||||
$fiat_currencies_info = [
|
||||
'USD' => 'US Dollar',
|
||||
'EUR' => 'Euro',
|
||||
'GBP' => 'British Pound',
|
||||
'CNY' => 'Chinese Yuan',
|
||||
'HKD' => 'Hong Kong Dollar',
|
||||
'JPY' => 'Japanese Yen',
|
||||
'KRW' => 'Korean Won',
|
||||
'SGD' => 'Singapore Dollar',
|
||||
'TWD' => 'Taiwan Dollar',
|
||||
'THB' => 'Thai Baht',
|
||||
'VND' => 'Vietnamese Dong',
|
||||
'IDR' => 'Indonesian Rupiah',
|
||||
'MYR' => 'Malaysian Ringgit',
|
||||
];
|
||||
?>
|
||||
|
||||
@ -74,14 +87,14 @@ $fiat_currencies = [
|
||||
</div>
|
||||
|
||||
<div id="fiat-section" style="background: var(--card-bg); padding: 40px; border-radius: 24px; border: 1px solid var(--border-color);">
|
||||
<form action="matching.php" method="POST" id="fiat-form">
|
||||
<form action="matching.php" method="POST" id="fiat-form" onsubmit="return handleDeposit(event, 'fiat')">
|
||||
<input type="hidden" name="type" value="fiat">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-bottom: 30px;">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 12px; color: var(--text-muted); font-size: 14px;"><?php echo __('select_currency', '选择币种'); ?></label>
|
||||
<select name="currency" id="currency-select" style="width: 100%; padding: 15px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1rem; outline: none;">
|
||||
<?php foreach ($fiat_currencies as $code => $info): ?>
|
||||
<option value="<?php echo $code; ?>" data-rate="<?php echo $info['rate']; ?>"><?php echo $code; ?> - <?php echo $info['name']; ?></option>
|
||||
<?php foreach ($fiat_rates as $code => $rate): ?>
|
||||
<option value="<?php echo $code; ?>" data-rate="<?php echo $rate; ?>"><?php echo $code; ?> - <?php echo $fiat_currencies_info[$code] ?? $code; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
@ -93,8 +106,8 @@ $fiat_currencies = [
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px;">
|
||||
<div id="rate-display" style="padding: 20px; background: #161a1e; border-radius: 12px; border: 1px dashed var(--border-color); display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center;">
|
||||
<div style="color: var(--text-muted); font-size: 12px; margin-bottom: 5px;">Exchange Rate</div>
|
||||
<div style="font-weight: bold; font-size: 1.1rem;"><span id="rate-value">7.23</span> <span id="rate-currency">CNY</span> = 1 USDT</div>
|
||||
<div style="color: var(--text-muted); font-size: 12px; margin-bottom: 5px;">Real-time Exchange Rate</div>
|
||||
<div style="font-weight: bold; font-size: 1.1rem;"><span id="rate-value">...</span> <span id="rate-currency">...</span> = 1 USDT</div>
|
||||
</div>
|
||||
<div id="result-display" style="padding: 20px; background: rgba(79, 172, 254, 0.05); border-radius: 12px; border: 1px solid rgba(79, 172, 254, 0.2); display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center;">
|
||||
<div style="color: var(--primary-color); font-size: 12px; margin-bottom: 5px;">You will receive</div>
|
||||
@ -110,12 +123,12 @@ $fiat_currencies = [
|
||||
Please complete the payment within the time limit after matching. Once submitted, our customer service will verify your deposit.
|
||||
</p>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 18px; font-size: 1.1rem; border-radius: 12px;">Continue to Match Account</button>
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 18px; font-size: 1.1rem; border-radius: 12px;">Match Account / 匹配充值账户</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="usdt-section" style="background: var(--card-bg); padding: 40px; border-radius: 24px; border: 1px solid var(--border-color); display: none;">
|
||||
<form action="matching.php" method="POST">
|
||||
<form action="matching.php" method="POST" id="usdt-form" onsubmit="return handleDeposit(event, 'usdt')">
|
||||
<input type="hidden" name="type" value="usdt">
|
||||
<div style="margin-bottom: 30px;">
|
||||
<label style="display: block; margin-bottom: 15px; color: var(--text-muted); font-size: 14px;">Select Network</label>
|
||||
@ -141,19 +154,147 @@ $fiat_currencies = [
|
||||
<label style="display: block; margin-bottom: 12px; color: var(--text-muted); font-size: 14px;">Deposit Amount (USDT)</label>
|
||||
<input type="number" name="amount" placeholder="Min. 10" required style="width: 100%; padding: 15px; background: #161a1e; border: 1px solid var(--border-color); color: white; border-radius: 12px; font-size: 1.2rem; font-weight: bold; outline: none;">
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 18px; font-size: 1.1rem; border-radius: 12px; background: var(--success-color);">Get Address</button>
|
||||
<button type="submit" class="btn-primary" style="width: 100%; padding: 18px; font-size: 1.1rem; border-radius: 12px; background: var(--success-color);">Get Address / 获取充值地址</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Enhanced Deposit Matching Modal -->
|
||||
<div id="matching-modal" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); display: none; align-items: center; justify-content: center; z-index: 10000; backdrop-filter: blur(10px);">
|
||||
<div style="background: #161a1e; width: 500px; border-radius: 32px; border: 1px solid rgba(255,255,255,0.1); overflow: hidden; box-shadow: 0 30px 70px rgba(0,0,0,0.8); position: relative;">
|
||||
|
||||
<div style="height: 6px; width: 100%; background: #2b3139;">
|
||||
<div id="modal-progress" style="height: 100%; width: 0%; background: var(--primary-color); transition: width 0.5s;"></div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 30px; background: #1e2329; border-bottom: 1px solid #2b3139; display: flex; align-items: center; justify-content: space-between;">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<div style="width: 45px; height: 45px; background: var(--primary-color); border-radius: 12px; display: flex; align-items: center; justify-content: center; color: white; box-shadow: 0 10px 20px rgba(0,82,255,0.3);">
|
||||
<i class="fas fa-headset"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 800; font-size: 18px; letter-spacing: 0.5px;">在线客服 / ONLINE SUPPORT</div>
|
||||
<div id="matching-status-text" style="font-size: 11px; color: #00c087; font-weight: bold;"><i class="fas fa-circle-notch fa-spin"></i> 正在安全握手 / Establishing Connection...</div>
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="document.getElementById('matching-modal').style.display='none'" style="background: none; border: none; color: #848e9c; cursor: pointer; font-size: 20px;"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
|
||||
<div style="padding: 40px; text-align: center;">
|
||||
<div id="step-visual" style="margin-bottom: 30px; position: relative; height: 100px; display: flex; align-items: center; justify-content: center;">
|
||||
<div class="pulse-ring-large"></div>
|
||||
<div class="pulse-ring-small"></div>
|
||||
<i id="step-icon" class="fas fa-shield-alt" style="font-size: 50px; color: var(--primary-color); position: relative; z-index: 2; transition: 0.3s;"></i>
|
||||
</div>
|
||||
|
||||
<h3 id="step-title" style="margin: 0 0 10px 0; font-size: 1.6rem; font-weight: 800;">账户安全检测中...</h3>
|
||||
<p id="step-desc" style="color: var(--text-muted); font-size: 14px; line-height: 1.8; margin-bottom: 30px; height: 50px;">
|
||||
系统正在对您的交易环境进行全方位安全扫描,确保资金链路 100% 可信。
|
||||
</p>
|
||||
|
||||
<div style="background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.05); border-radius: 20px; padding: 25px; margin-bottom: 35px; text-align: left;">
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 15px;">
|
||||
<div style="width: 24px; height: 24px; background: rgba(240, 185, 11, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #f0b90b; font-size: 12px;">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<div style="font-weight: bold; font-size: 14px; color: #f0b90b;">入金安全协议声明</div>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #848e9c; line-height: 1.8;">
|
||||
• 本次充值将由官方承兑商提供实时结算支持。<br>
|
||||
• 严禁向任何非系统分配的账户进行转账。<br>
|
||||
• 确认订单后,系统将为您开启专属绿色通道。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="confirm-deposit-btn" disabled style="width: 100%; padding: 20px; background: #2b3139; border: none; color: #5e6673; border-radius: 16px; font-weight: 800; font-size: 1.1rem; cursor: not-allowed; transition: 0.3s; box-shadow: 0 15px 30px rgba(0,0,0,0.2);">
|
||||
请等待安全匹配 / PLEASE WAIT...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.network-label { padding: 20px; background: #161a1e; border: 1px solid var(--border-color); border-radius: 16px; cursor: pointer; text-align: center; transition: 0.2s; }
|
||||
.network-label.active { border-color: var(--success-color); background: rgba(14,203,129,0.05); color: var(--success-color); }
|
||||
|
||||
.pulse-ring-large { position: absolute; width: 120px; height: 120px; border: 2px solid var(--primary-color); border-radius: 50%; animation: pulse-anim 2s infinite; opacity: 0; }
|
||||
.pulse-ring-small { position: absolute; width: 80px; height: 80px; border: 1px solid var(--primary-color); border-radius: 50%; animation: pulse-anim 2s infinite 0.5s; opacity: 0; }
|
||||
|
||||
@keyframes pulse-anim {
|
||||
0% { transform: scale(0.6); opacity: 0; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { transform: scale(1.4); opacity: 0; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let currentFormId = '';
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: "安全链路检测中...",
|
||||
desc: "系统正在对您的交易环境进行全方位安全扫描,确保资金链路 100% 可信。",
|
||||
icon: "fa-shield-alt",
|
||||
progress: 33,
|
||||
status: "Establishing Secure Link..."
|
||||
},
|
||||
{
|
||||
title: "匹配最优承兑商...",
|
||||
desc: "正在为您筛选当前响应最快、额度最充足的合规承兑商账户。",
|
||||
icon: "fa-user-check",
|
||||
progress: 66,
|
||||
status: "Matching Liquidator..."
|
||||
},
|
||||
{
|
||||
title: "准备就绪",
|
||||
desc: "安全验证已通过。点击下方按钮,进入专属客服对话窗口获取收款信息。",
|
||||
icon: "fa-check-double",
|
||||
progress: 100,
|
||||
status: "Ready for Connection"
|
||||
}
|
||||
];
|
||||
|
||||
function handleDeposit(event, type) {
|
||||
event.preventDefault();
|
||||
currentFormId = type + '-form';
|
||||
document.getElementById('matching-modal').style.display = 'flex';
|
||||
|
||||
let step = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (step >= steps.length) {
|
||||
clearInterval(interval);
|
||||
const btn = document.getElementById('confirm-deposit-btn');
|
||||
btn.disabled = false;
|
||||
btn.style.background = 'var(--primary-color)';
|
||||
btn.style.color = 'white';
|
||||
btn.style.cursor = 'pointer';
|
||||
btn.innerText = '确认充值订单 / CONFIRM ORDER';
|
||||
return;
|
||||
}
|
||||
|
||||
const s = steps[step];
|
||||
document.getElementById('modal-progress').style.width = s.progress + '%';
|
||||
document.getElementById('matching-status-text').innerHTML = `<i class="fas fa-circle-notch fa-spin"></i> ${s.status}`;
|
||||
document.getElementById('step-title').innerText = s.title;
|
||||
document.getElementById('step-desc').innerText = s.desc;
|
||||
document.getElementById('step-icon').className = `fas ${s.icon}`;
|
||||
|
||||
step++;
|
||||
}, 1500);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
document.getElementById('confirm-deposit-btn').onclick = function() {
|
||||
this.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 正在连接客服...';
|
||||
this.disabled = true;
|
||||
setTimeout(() => {
|
||||
document.getElementById(currentFormId).submit();
|
||||
}, 800);
|
||||
};
|
||||
|
||||
function switchMethod(method) {
|
||||
if(method === 'fiat') {
|
||||
document.getElementById('fiat-section').style.display = 'block';
|
||||
@ -179,7 +320,7 @@ $fiat_currencies = [
|
||||
const rate = parseFloat(option.getAttribute('data-rate'));
|
||||
const amount = parseFloat(amountInput.value) || 0;
|
||||
|
||||
rateVal.innerText = rate;
|
||||
rateVal.innerText = rate.toFixed(4);
|
||||
rateCur.innerText = select.value;
|
||||
|
||||
if (rate > 0) {
|
||||
@ -191,8 +332,6 @@ $fiat_currencies = [
|
||||
|
||||
select.onchange = calculate;
|
||||
amountInput.oninput = calculate;
|
||||
|
||||
// Initial calculation
|
||||
calculate();
|
||||
|
||||
document.querySelectorAll('.network-label').forEach(label => {
|
||||
|
||||
199
futures.php
199
futures.php
@ -26,95 +26,62 @@ if ($user_id) {
|
||||
--input-bg: #1e2329;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.trading-layout {
|
||||
display: flex;
|
||||
height: calc(100vh - 60px);
|
||||
gap: 1px;
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--panel-bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Left: Markets */
|
||||
body { background-color: var(--bg-color); color: var(--text-primary); font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; margin: 0; overflow: hidden; }
|
||||
.trading-layout { display: flex; height: calc(100vh - 60px); gap: 1px; background: var(--border-color); }
|
||||
.panel { background: var(--panel-bg); display: flex; flex-direction: column; overflow: hidden; }
|
||||
.market-panel { width: 280px; flex-shrink: 0; }
|
||||
.search-box { padding: 12px; border-bottom: 1px solid var(--border-color); }
|
||||
.search-input-wrapper { position: relative; }
|
||||
.search-input-wrapper input { width: 100%; background: var(--input-bg); border: 1px solid var(--border-color); color: white; padding: 6px 12px 6px 32px; border-radius: 4px; font-size: 13px; outline: none; }
|
||||
.search-input-wrapper i { position: absolute; left: 10px; top: 8px; color: var(--text-secondary); }
|
||||
|
||||
.pair-item { display: flex; justify-content: space-between; padding: 10px 12px; cursor: pointer; transition: background 0.2s; border-bottom: 1px solid rgba(255,255,255,0.02); }
|
||||
.pair-item:hover { background: rgba(255,255,255,0.05); }
|
||||
.pair-item.active { background: rgba(79, 172, 254, 0.1); }
|
||||
|
||||
/* Center: Main Column */
|
||||
.center-panel { flex: 1; overflow-y: auto; }
|
||||
.info-bar { height: 50px; display: flex; align-items: center; padding: 0 15px; gap: 20px; border-bottom: 1px solid var(--border-color); }
|
||||
.chart-container { height: 400px; background: var(--bg-color); }
|
||||
|
||||
.order-form-panel { padding: 20px; background: var(--panel-bg); border-bottom: 1px solid var(--border-color); }
|
||||
.order-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
|
||||
|
||||
.margin-controls { display: flex; gap: 10px; margin-bottom: 15px; align-items: center; }
|
||||
.ctrl-btn { background: var(--input-bg); border: 1px solid var(--border-color); color: white; padding: 5px 12px; border-radius: 4px; font-size: 12px; cursor: pointer; transition: all 0.2s; }
|
||||
.ctrl-btn.active { border-color: var(--accent-color); color: var(--accent-color); }
|
||||
|
||||
.order-type-tabs { display: flex; gap: 15px; margin-left: 10px; }
|
||||
.order-type-btn { background: none; border: none; color: var(--text-secondary); font-size: 13px; cursor: pointer; padding: 0; }
|
||||
.order-type-btn.active { color: var(--accent-color); font-weight: bold; }
|
||||
|
||||
.input-row { background: var(--input-bg); border: 1px solid var(--border-color); border-radius: 4px; display: flex; align-items: center; margin-bottom: 12px; padding: 8px 12px; }
|
||||
.input-label { color: var(--text-secondary); font-size: 13px; width: 60px; }
|
||||
.input-row input { flex: 1; background: transparent; border: none; color: white; text-align: right; outline: none; font-size: 14px; }
|
||||
.input-row input:disabled { color: var(--text-secondary); cursor: not-allowed; }
|
||||
.input-unit { color: var(--text-secondary); font-size: 12px; margin-left: 8px; width: 40px; text-align: right; }
|
||||
|
||||
.slider-container { margin: 15px 0 25px 0; padding: 0 5px; position: relative; }
|
||||
.slider-marks { display: flex; justify-content: space-between; margin-top: -10px; position: relative; padding: 0 8px; pointer-events: none; }
|
||||
.mark-dot { width: 8px; height: 8px; background: var(--border-color); border: 2px solid var(--input-bg); border-radius: 50%; cursor: pointer; z-index: 2; pointer-events: auto; transform: translateY(-2px); }
|
||||
.mark-dot.active { background: var(--accent-color); border-color: var(--accent-color); }
|
||||
|
||||
.slider-labels { display: flex; justify-content: space-between; margin-top: 8px; padding: 0 2px; }
|
||||
.label-item { color: var(--text-secondary); font-size: 10px; cursor: pointer; user-select: none; width: 25px; text-align: center; }
|
||||
.label-item.active { color: var(--text-primary); }
|
||||
|
||||
.trade-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-top: 20px; }
|
||||
.btn-trade { padding: 12px; border: none; border-radius: 4px; font-weight: bold; font-size: 15px; cursor: pointer; color: white; transition: opacity 0.2s; }
|
||||
.btn-trade:active { opacity: 0.8; }
|
||||
.btn-trade.buy { background: var(--up-color); }
|
||||
.btn-trade.sell { background: var(--down-color); }
|
||||
|
||||
.tabs-section { flex: 1; }
|
||||
.tabs-header { display: flex; border-bottom: 1px solid var(--border-color); padding: 0 15px; }
|
||||
.tab-btn { background: none; border: none; color: var(--text-secondary); padding: 12px 15px; font-size: 14px; cursor: pointer; border-bottom: 2px solid transparent; }
|
||||
.tab-btn.active { color: var(--accent-color); border-bottom-color: var(--accent-color); }
|
||||
|
||||
/* Right: Order Book */
|
||||
.order-book-panel { width: 300px; flex-shrink: 0; }
|
||||
.ob-header { padding: 10px 15px; font-size: 11px; color: var(--text-secondary); border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; }
|
||||
.ob-row { display: flex; justify-content: space-between; padding: 3px 15px; font-size: 12px; position: relative; }
|
||||
.ob-bar { position: absolute; right: 0; top: 0; bottom: 0; z-index: 0; }
|
||||
.ob-val { z-index: 1; position: relative; }
|
||||
|
||||
input[type=range] { -webkit-appearance: none; width: 100%; background: transparent; margin: 0; padding: 0; }
|
||||
input[type=range]:focus { outline: none; }
|
||||
input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; cursor: pointer; background: var(--border-color); border-radius: 2px; }
|
||||
input[type=range]::-webkit-slider-thumb { height: 14px; width: 14px; border-radius: 50%; background: white; cursor: pointer; -webkit-appearance: none; margin-top: -5px; box-shadow: 0 0 2px rgba(0,0,0,0.5); border: 2px solid var(--accent-color); z-index: 3; position: relative; }
|
||||
|
||||
.modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); display: none; align-items: center; justify-content: center; z-index: 1000; }
|
||||
.modal-box { background: var(--panel-bg); padding: 30px; border-radius: 8px; width: 400px; border: 1px solid var(--border-color); }
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-thumb { background: #333; border-radius: 10px; }
|
||||
</style>
|
||||
|
||||
<div class="trading-layout">
|
||||
@ -221,23 +188,13 @@ if ($user_id) {
|
||||
|
||||
<div class="tabs-section">
|
||||
<div class="tabs-header">
|
||||
<button class="tab-btn active" onclick="switchTab(this, 'positions')">当前持仓</button>
|
||||
<button class="tab-btn" onclick="switchTab(this, 'orders')">当前委托</button>
|
||||
<button class="tab-btn" onclick="switchTab(this, 'tpsl')">止盈止损</button>
|
||||
<button class="tab-btn" onclick="switchTab(this, 'history')">历史委托</button>
|
||||
<button class="tab-btn active" id="tab-positions" onclick="switchTab(this, 'positions')">当前持仓</button>
|
||||
<button class="tab-btn" id="tab-orders" onclick="switchTab(this, 'open')">当前委托</button>
|
||||
<button class="tab-btn" id="tab-history" onclick="switchTab(this, 'history')">历史委托</button>
|
||||
</div>
|
||||
<div id="tab-content" style="padding: 0; min-height: 250px;">
|
||||
<table style="width: 100%; font-size: 12px; border-collapse: collapse;">
|
||||
<thead style="color: var(--text-secondary); text-align: left; background: rgba(0,0,0,0.1);">
|
||||
<tr>
|
||||
<th style="padding: 12px;">合约</th>
|
||||
<th style="padding: 12px;">仓位</th>
|
||||
<th style="padding: 12px;">开仓价</th>
|
||||
<th style="padding: 12px;">标记价</th>
|
||||
<th style="padding: 12px;">强平价</th>
|
||||
<th style="padding: 12px;">未实现盈亏</th>
|
||||
<th style="padding: 12px; text-align: right;">操作</th>
|
||||
</tr>
|
||||
<thead id="data-thead" style="color: var(--text-secondary); text-align: left; background: rgba(0,0,0,0.1);">
|
||||
</thead>
|
||||
<tbody id="data-tbody"></tbody>
|
||||
</table>
|
||||
@ -273,9 +230,10 @@ if ($user_id) {
|
||||
let currentPair = 'BTCUSDT';
|
||||
let currentPrice = 0;
|
||||
let leverage = 20;
|
||||
let balance = <?php echo $balance; ?>;
|
||||
let usdtBalance = <?php echo $balance; ?>;
|
||||
let marketData = {};
|
||||
let orderType = 'limit';
|
||||
let activeTab = 'positions';
|
||||
const faceValue = 10;
|
||||
|
||||
const pairs = [
|
||||
@ -295,12 +253,20 @@ if ($user_id) {
|
||||
function connectWS() {
|
||||
if (ws) ws.close();
|
||||
const streams = pairs.map(p => p.toLowerCase() + '@ticker').join('/');
|
||||
ws = new WebSocket(`wss://stream.binance.com:9443/ws/${streams}`);
|
||||
ws = new WebSocket(`wss://fstream.binance.com/ws/${streams}`);
|
||||
ws.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
marketData[data.s] = data;
|
||||
renderPairs();
|
||||
if (data.s === currentPair) {
|
||||
updateUIWithData(data);
|
||||
}
|
||||
if (activeTab === 'positions') fetchOrders();
|
||||
};
|
||||
}
|
||||
connectWS();
|
||||
|
||||
function updateUIWithData(data) {
|
||||
currentPrice = parseFloat(data.c);
|
||||
document.getElementById('last-price').innerText = currentPrice.toLocaleString();
|
||||
document.getElementById('last-price').style.color = data.P >= 0 ? 'var(--up-color)' : 'var(--down-color)';
|
||||
@ -313,13 +279,12 @@ if ($user_id) {
|
||||
document.getElementById('ob-index-price').innerText = (currentPrice * 0.9998).toFixed(2);
|
||||
updateOrderBook();
|
||||
|
||||
if (!document.getElementById('order-price').value) {
|
||||
document.getElementById('order-price').value = currentPrice;
|
||||
const op = document.getElementById('order-price');
|
||||
if (!op.value || op.getAttribute('data-auto') === 'true') {
|
||||
op.value = currentPrice;
|
||||
op.setAttribute('data-auto', 'true');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
connectWS();
|
||||
|
||||
function renderPairs() {
|
||||
const list = document.getElementById('pairs-list');
|
||||
@ -352,23 +317,25 @@ if ($user_id) {
|
||||
document.getElementById('current-pair-display').innerText = name + '/USDT 永续';
|
||||
document.getElementById('current-logo').src = `https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/${name.toLowerCase()}.png`;
|
||||
|
||||
if (currentPrice) {
|
||||
document.getElementById('order-price').value = currentPrice;
|
||||
if (marketData[p]) {
|
||||
updateUIWithData(marketData[p]);
|
||||
} else {
|
||||
document.getElementById('last-price').innerText = '--';
|
||||
document.getElementById('order-price').value = '';
|
||||
}
|
||||
|
||||
document.getElementById('order-price').setAttribute('data-auto', 'true');
|
||||
document.getElementById('order-amount').value = '';
|
||||
document.getElementById('order-slider').value = 0;
|
||||
document.getElementById('order-cost').innerText = '0.00';
|
||||
updateSliderMarks(0);
|
||||
|
||||
initChart(p);
|
||||
renderPairs();
|
||||
}
|
||||
|
||||
function setOrderType(type) {
|
||||
orderType = type;
|
||||
document.querySelectorAll('.order-type-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.innerText.includes(type === 'limit' ? '限价' : '市价'));
|
||||
});
|
||||
document.querySelectorAll('.order-type-btn').forEach(btn => btn.classList.toggle('active', btn.innerText.includes(type === 'limit' ? '限价' : '市价')));
|
||||
document.getElementById('price-row').style.display = type === 'limit' ? 'flex' : 'none';
|
||||
document.getElementById('market-price-row').style.display = type === 'market' ? 'flex' : 'none';
|
||||
updateFromSlider(document.getElementById('order-slider').value);
|
||||
@ -378,9 +345,10 @@ if ($user_id) {
|
||||
const asks = document.getElementById('asks-list');
|
||||
const bids = document.getElementById('bids-list');
|
||||
let asksHtml = ''; let bidsHtml = '';
|
||||
const step = currentPrice * 0.0001;
|
||||
for(let i=0; i<15; i++) {
|
||||
const askPrice = currentPrice * (1 + (i + 1) * 0.0001);
|
||||
const bidPrice = currentPrice * (1 - (i + 1) * 0.0001);
|
||||
const askPrice = currentPrice + (i + 1) * step;
|
||||
const bidPrice = currentPrice - (i + 1) * step;
|
||||
asksHtml += `<div class="ob-row"><div class="ob-bar" style="background: rgba(246, 70, 93, 0.1); width: ${Math.random()*100}%;"></div><span class="ob-val" style="color: var(--down-color);">${askPrice.toFixed(1)}</span><span class="ob-val">${(Math.random()*200).toFixed(0)}</span></div>`;
|
||||
bidsHtml += `<div class="ob-row"><div class="ob-bar" style="background: rgba(0, 192, 135, 0.1); width: ${Math.random()*100}%;"></div><span class="ob-val" style="color: var(--up-color);">${bidPrice.toFixed(1)}</span><span class="ob-val">${(Math.random()*200).toFixed(0)}</span></div>`;
|
||||
}
|
||||
@ -389,7 +357,7 @@ if ($user_id) {
|
||||
|
||||
function updateFromSlider(val) {
|
||||
val = parseFloat(val);
|
||||
const cost = balance * (val / 100);
|
||||
const cost = usdtBalance * (val / 100);
|
||||
const amount = Math.floor((cost * leverage) / faceValue);
|
||||
document.getElementById('order-amount').value = amount > 0 ? amount : '';
|
||||
document.getElementById('order-cost').innerText = cost.toFixed(2);
|
||||
@ -400,28 +368,24 @@ if ($user_id) {
|
||||
const amount = parseFloat(document.getElementById('order-amount').value) || 0;
|
||||
const cost = (amount * faceValue) / leverage;
|
||||
document.getElementById('order-cost').innerText = cost.toFixed(2);
|
||||
|
||||
let percentage = balance > 0 ? (cost / balance) * 100 : 0;
|
||||
let percentage = usdtBalance > 0 ? (cost / usdtBalance) * 100 : 0;
|
||||
percentage = Math.min(Math.max(percentage, 0), 100);
|
||||
document.getElementById('order-slider').value = percentage;
|
||||
updateSliderMarks(percentage);
|
||||
}
|
||||
|
||||
function updateSliderMarks(val) {
|
||||
document.querySelectorAll('.mark-dot').forEach((m, i) => {
|
||||
if (i * 25 <= val) m.classList.add('active'); else m.classList.remove('active');
|
||||
});
|
||||
document.querySelectorAll('.label-item').forEach((l, i) => {
|
||||
if (Math.abs(i * 25 - val) < 5) l.classList.add('active'); else l.classList.remove('active');
|
||||
});
|
||||
document.querySelectorAll('.mark-dot').forEach((m, i) => { if (i * 25 <= val) m.classList.add('active'); else m.classList.remove('active'); });
|
||||
document.querySelectorAll('.label-item').forEach((l, i) => { if (Math.abs(i * 25 - val) < 5) l.classList.add('active'); else l.classList.remove('active'); });
|
||||
}
|
||||
|
||||
function setSlider(val) {
|
||||
document.getElementById('order-slider').value = val;
|
||||
updateFromSlider(val);
|
||||
}
|
||||
function setSlider(val) { document.getElementById('order-slider').value = val; updateFromSlider(val); }
|
||||
|
||||
document.getElementById('order-amount').addEventListener('input', updateFromInputs);
|
||||
document.getElementById('order-price').addEventListener('input', (e) => {
|
||||
e.target.setAttribute('data-auto', 'false');
|
||||
updateFromInputs();
|
||||
});
|
||||
|
||||
function showLevModal() {
|
||||
document.getElementById('lev-range').value = leverage;
|
||||
@ -438,83 +402,116 @@ if ($user_id) {
|
||||
}
|
||||
|
||||
function setMargin(type) {
|
||||
document.querySelectorAll('.ctrl-btn').forEach(btn => {
|
||||
if (btn.innerText.includes('仓')) {
|
||||
btn.classList.toggle('active', btn.id.includes(type));
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('.ctrl-btn').forEach(btn => { if (btn.innerText.includes('仓')) btn.classList.toggle('active', btn.id.includes(type)); });
|
||||
}
|
||||
|
||||
async function placeOrder(side) {
|
||||
const amount = parseFloat(document.getElementById('order-amount').value);
|
||||
if (!amount) return alert('请输入数量');
|
||||
const price = orderType === 'limit' ? parseFloat(document.getElementById('order-price').value || currentPrice) : currentPrice;
|
||||
|
||||
const response = await fetch('api/place_order.php', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
symbol: currentPair, type: 'futures', side: side,
|
||||
order_type: orderType,
|
||||
price: price, amount: amount, leverage: leverage, total: price * amount
|
||||
order_type: orderType, price: price, amount: amount, leverage: leverage, total: amount * faceValue
|
||||
})
|
||||
});
|
||||
const res = await response.json();
|
||||
if (res.success) { alert('下单成功'); fetchOrders(); updateBalance(); } else { alert('失败: ' + res.error); }
|
||||
}
|
||||
|
||||
async function updateBalance() {
|
||||
const resp = await fetch('api/get_assets.php');
|
||||
const res = await resp.json();
|
||||
if (res.success) {
|
||||
alert('下单成功');
|
||||
fetchOrders();
|
||||
} else {
|
||||
alert('失败: ' + res.error);
|
||||
const usdt = res.data.find(a => a.symbol === 'USDT');
|
||||
if (usdt) {
|
||||
usdtBalance = usdt.amount;
|
||||
document.getElementById('available-bal').innerText = usdtBalance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchOrders() {
|
||||
const response = await fetch('api/get_orders.php?type=futures&status=open');
|
||||
const response = await fetch(`api/get_orders.php?type=futures&status=${activeTab}`);
|
||||
const res = await response.json();
|
||||
const tbody = document.getElementById('data-tbody');
|
||||
const thead = document.getElementById('data-thead');
|
||||
|
||||
if (activeTab === 'positions') {
|
||||
thead.innerHTML = `<tr><th style="padding: 12px;">合约</th><th style="padding: 12px;">仓位</th><th style="padding: 12px;">开仓价</th><th style="padding: 12px;">当前价</th><th style="padding: 12px;">强平价</th><th style="padding: 12px;">未实现盈亏</th><th style="padding: 12px; text-align: right;">操作</th></tr>`;
|
||||
} else {
|
||||
thead.innerHTML = `<tr><th style="padding: 12px;">时间</th><th style="padding: 12px;">合约</th><th style="padding: 12px;">方向</th><th style="padding: 12px;">委托价</th><th style="padding: 12px;">数量(张)</th><th style="padding: 12px;">状态</th><th style="padding: 12px; text-align: right;">操作</th></tr>`;
|
||||
}
|
||||
|
||||
if (res.success && res.data.length > 0) {
|
||||
let html = '';
|
||||
res.data.forEach(o => {
|
||||
if (activeTab === 'positions') {
|
||||
const price = marketData[o.symbol] ? parseFloat(marketData[o.symbol].c) : o.price;
|
||||
const nominal = o.amount * faceValue;
|
||||
let pnl = 0;
|
||||
if (o.side === 'buy') pnl = (price / o.price - 1) * nominal;
|
||||
else pnl = (1 - price / o.price) * nominal;
|
||||
|
||||
const color = o.side === 'buy' ? 'var(--up-color)' : 'var(--down-color)';
|
||||
html += `
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 12px;"><div style="font-weight: bold;">${o.symbol}</div><div style="font-size: 10px; color: var(--text-secondary);">永续</div></td>
|
||||
<td style="padding: 12px;"><span style="color: ${color};">${o.side === 'buy' ? '多' : '空'} ${o.leverage}x</span><br>${o.amount} 张</td>
|
||||
<td style="padding: 12px;"><span style="color: color;">${o.side === 'buy' ? '做多' : '做空'} ${o.leverage}x</span><br>${o.amount} 张</td>
|
||||
<td style="padding: 12px;">${parseFloat(o.price).toLocaleString()}</td>
|
||||
<td style="padding: 12px;">${currentPrice.toLocaleString()}</td>
|
||||
<td style="padding: 12px;">${(o.price * 0.85).toFixed(1)}</td>
|
||||
<td style="padding: 12px; color: var(--up-color);">+0.00</td>
|
||||
<td style="padding: 12px;">${price.toLocaleString()}</td>
|
||||
<td style="padding: 12px;">${(o.side === 'buy' ? o.price * (1 - 0.9/o.leverage) : o.price * (1 + 0.9/o.leverage)).toFixed(2)}</td>
|
||||
<td style="padding: 12px; color: ${pnl >= 0 ? 'var(--up-color)' : 'var(--down-color)'}">${(pnl >= 0 ? '+' : '') + pnl.toFixed(2)} USDT</td>
|
||||
<td style="padding: 12px; text-align: right;">
|
||||
<button onclick="closePosition(${o.id})" style="background: none; border: 1px solid var(--text-secondary); color: white; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px;">平仓</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
} else {
|
||||
html += `
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 12px;">${o.created_at}</td>
|
||||
<td style="padding: 12px; font-weight: bold;">${o.symbol}</td>
|
||||
<td style="padding: 12px; color: ${o.side === 'buy' ? 'var(--up-color)' : 'var(--down-color)'}">${o.side === 'buy' ? '开多' : '开空'}</td>
|
||||
<td style="padding: 12px;">${parseFloat(o.price).toLocaleString()}</td>
|
||||
<td style="padding: 12px;">${o.amount}</td>
|
||||
<td style="padding: 12px;">${o.status === 'open' ? '委托中' : '已完成'}</td>
|
||||
<td style="padding: 12px; text-align: right;">
|
||||
${o.status === 'open' ? `<button onclick="closePosition(${o.id})" style="background: none; border: 1px solid var(--down-color); color: var(--down-color); padding: 2px 8px; border-radius: 4px; cursor: pointer;">取消</button>` : '--'}
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
});
|
||||
tbody.innerHTML = html;
|
||||
} else {
|
||||
tbody.innerHTML = '<tr><td colspan="7" style="text-align: center; padding: 50px; color: var(--text-secondary); opacity: 0.5;">暂无持仓</td></tr>';
|
||||
tbody.innerHTML = `<tr><td colspan="7" style="text-align: center; padding: 50px; color: var(--text-secondary); opacity: 0.5;">暂无记录</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
function switchTab(btn, tab) {
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
activeTab = tab;
|
||||
fetchOrders();
|
||||
}
|
||||
|
||||
async function closePosition(id) {
|
||||
if (!confirm('确定平仓?')) return;
|
||||
if (!confirm('确定执行此操作?')) return;
|
||||
const resp = await fetch('api/cancel_order.php', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({order_id: id})
|
||||
});
|
||||
if ((await resp.json()).success) fetchOrders();
|
||||
if ((await resp.json()).success) { fetchOrders(); updateBalance(); }
|
||||
}
|
||||
|
||||
document.getElementById('market-search').addEventListener('input', renderPairs);
|
||||
fetchOrders();
|
||||
setInterval(fetchOrders, 5000);
|
||||
updateBalance();
|
||||
setInterval(() => { if (activeTab === 'positions') fetchOrders(); }, 3000);
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
33
includes/currency_helper.php
Normal file
33
includes/currency_helper.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
function get_fiat_rates() {
|
||||
$fiat_currencies = [
|
||||
'USD' => 1.00,
|
||||
'EUR' => 0.92,
|
||||
'GBP' => 0.79,
|
||||
'CNY' => 7.23,
|
||||
'HKD' => 7.82,
|
||||
'JPY' => 151.45,
|
||||
'KRW' => 1350.20,
|
||||
'SGD' => 1.35,
|
||||
'TWD' => 32.10,
|
||||
'THB' => 36.50,
|
||||
'VND' => 24800,
|
||||
'IDR' => 15850,
|
||||
'MYR' => 4.74,
|
||||
];
|
||||
|
||||
$api_url = 'https://api.exchangerate-api.com/v4/latest/USD';
|
||||
$ctx = stream_context_create(['http' => ['timeout' => 2]]);
|
||||
$json = @file_get_contents($api_url, false, $ctx);
|
||||
if ($json) {
|
||||
$data = json_decode($json, true);
|
||||
if (isset($data['rates'])) {
|
||||
foreach ($fiat_currencies as $code => $rate) {
|
||||
if (isset($data['rates'][$code])) {
|
||||
$fiat_currencies[$code] = $data['rates'][$code];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fiat_currencies;
|
||||
}
|
||||
502
matching.php
502
matching.php
@ -7,21 +7,28 @@ if (!isset($_SESSION['user_id'])) {
|
||||
}
|
||||
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/currency_helper.php';
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
// Handle initial order creation from deposit.php
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['amount'])) {
|
||||
$amount = $_POST['amount'];
|
||||
// Step 1: Handle initial order creation from deposit.php
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['amount']) && !isset($_GET['order_id'])) {
|
||||
$amount = (float)$_POST['amount'];
|
||||
$type = $_POST['type'] ?? 'fiat';
|
||||
$currency = $_POST['currency'] ?? 'USDT';
|
||||
|
||||
$fiat_rates = get_fiat_rates();
|
||||
$rate = $fiat_rates[$currency] ?? 1.0;
|
||||
$usdt_amount = ($rate > 0) ? ($amount / $rate) : $amount;
|
||||
|
||||
$expires_at = date('Y-m-d H:i:s', strtotime('+30 minutes'));
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO fiat_orders (user_id, amount, currency, status, expires_at, created_at) VALUES (?, ?, ?, 'matching', ?, CURRENT_TIMESTAMP)");
|
||||
$stmt->execute([$user_id, $amount, $currency, $expires_at]);
|
||||
// Status starts as 'matching' (待匹配)
|
||||
$stmt = db()->prepare("INSERT INTO fiat_orders (user_id, amount, usdt_amount, exchange_rate, currency, status, expires_at, created_at) VALUES (?, ?, ?, ?, ?, 'matching', ?, CURRENT_TIMESTAMP)");
|
||||
$stmt->execute([$user_id, $amount, $usdt_amount, $rate, $currency, $expires_at]);
|
||||
$order_id = db()->lastInsertId();
|
||||
|
||||
$msg = "[RECHARGE_NOTIFICATION] 用户 {$_SESSION['email']} (UID: {$_SESSION['uid']}) 正在发起充值匹配: $amount $currency。订单号: #$order_id。请尽快提供收款账户详情。";
|
||||
// Immediate notification to admin (用户一点击确认,后台立刻收到请求)
|
||||
$msg = "[RECHARGE_NOTIFICATION] 🚨 新充值申请! 用户 {$_SESSION['email']} (UID: {$_SESSION['uid']}) 正在等待匹配: **$amount $currency**。请尽快分配收款账户。";
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$user_id, $msg]);
|
||||
|
||||
@ -49,9 +56,13 @@ if ($order['status'] === 'completed') {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Step 6: Handle Proof Submission (已提交凭证)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['proof'])) {
|
||||
$file = $_FILES['proof'];
|
||||
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
|
||||
|
||||
if (in_array($ext, $allowed)) {
|
||||
$filename = 'proof_' . $order_id . '_' . time() . '.' . $ext;
|
||||
$target = 'assets/images/proofs/' . $filename;
|
||||
|
||||
@ -61,13 +72,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['proof'])) {
|
||||
$stmt = db()->prepare("UPDATE fiat_orders SET proof_image = ?, status = 'submitting' WHERE id = ?");
|
||||
$stmt->execute(['assets/images/proofs/' . $filename, $order_id]);
|
||||
|
||||
$msg = "[RECHARGE_SUBMITTED] 用户已上传支付凭证并确认转账 (订单 #$order_id)。请进入管理后台核对入账。";
|
||||
$msg = "[RECHARGE_SUBMITTED] ✅ 用户已完成转账并提交凭证 (订单 #$order_id)。请进入后台核对。";
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$user_id, $msg]);
|
||||
|
||||
header("Location: matching.php?order_id=" . $order_id);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM messages WHERE user_id = ? AND message NOT LIKE '[RECHARGE_%' ORDER BY created_at ASC");
|
||||
@ -75,235 +87,233 @@ $stmt->execute([$user_id]);
|
||||
$messages = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<main style="background: #0b0e11; min-height: calc(100vh - 64px); padding: 20px;">
|
||||
<div style="max-width: 1200px; margin: 0 auto; background: #161a1e; border: 1px solid #2b3139; border-radius: 24px; height: 85vh; display: grid; grid-template-columns: 1fr 380px; overflow: hidden; box-shadow: 0 20px 50px rgba(0,0,0,0.5);">
|
||||
<main style="background: #0b0e11; min-height: calc(100vh - 64px); padding: 40px 20px;">
|
||||
|
||||
<!-- Main Panel: Deposit Matching Workflow (The "Enlarged Dialog") -->
|
||||
<div style="display: flex; flex-direction: column; border-right: 1px solid #2b3139; background: #0b0e11;">
|
||||
<div style="padding: 20px 30px; border-bottom: 1px solid #2b3139; background: #161a1e; display: flex; align-items: center; justify-content: space-between;">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<div style="width: 45px; height: 45px; background: var(--primary-color); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 20px; color: white;">
|
||||
<i class="fas fa-wallet"></i>
|
||||
<!-- Step 4: MANDATORY LOCK OVERLAY for matched status (已匹配) -->
|
||||
<?php if ($order['status'] === 'matched' || ($order['bank_account_info'] && $order['status'] === 'matching')): ?>
|
||||
<div id="mandatory-lock" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #0b0e11; z-index: 100000; overflow-y: auto; padding: 20px;">
|
||||
<div style="max-width: 600px; margin: 20px auto;">
|
||||
<div style="background: #161a1e; border-radius: 32px; border: 1px solid rgba(255,255,255,0.1); overflow: hidden; box-shadow: 0 40px 100px rgba(0,0,0,0.8);">
|
||||
|
||||
<!-- Success Header -->
|
||||
<div style="background: linear-gradient(135deg, #00c087, #008960); padding: 40px 30px; text-align: center; color: white; position: relative; overflow: hidden;">
|
||||
<div style="position: absolute; top: -50px; right: -50px; width: 150px; height: 150px; background: rgba(255,255,255,0.05); border-radius: 50%;"></div>
|
||||
<div style="width: 80px; height: 80px; background: rgba(255,255,255,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; border: 3px solid white; box-shadow: 0 10px 20px rgba(0,0,0,0.2);">
|
||||
<i class="fas fa-check" style="font-size: 35px;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 800; font-size: 16px;">充值订单匹配 / Recharge Matching</div>
|
||||
<div style="font-size: 11px; color: #848e9c;">订单号: #<?php echo $order_id; ?></div>
|
||||
<h2 style="margin: 0; font-size: 2rem; font-weight: 900; letter-spacing: -0.5px;">账户匹配成功</h2>
|
||||
<p style="margin: 10px 0 0; opacity: 0.9; font-size: 15px; font-weight: 500;">请按以下信息完成转账并上传凭证</p>
|
||||
</div>
|
||||
|
||||
<div style="padding: 40px;">
|
||||
<!-- Step Indicator -->
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 40px; position: relative;">
|
||||
<div style="position: absolute; top: 12px; left: 0; width: 100%; height: 2px; background: rgba(255,255,255,0.05); z-index: 1;"></div>
|
||||
<div style="z-index: 2; text-align: center; width: 33%;">
|
||||
<div style="width: 26px; height: 26px; background: #00c087; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 8px; font-size: 12px; font-weight: bold; border: 4px solid #161a1e;">1</div>
|
||||
<div style="font-size: 11px; color: #00c087; font-weight: bold;">查看账户</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div id="countdown" style="font-size: 1.5rem; font-weight: 900; color: #f0b90b; font-family: 'Roboto Mono', monospace;">30:00</div>
|
||||
<div style="font-size: 10px; color: #848e9c;">剩余有效时间 / Time Left</div>
|
||||
<div style="z-index: 2; text-align: center; width: 33%;">
|
||||
<div id="step-dot-2" style="width: 26px; height: 26px; background: #2b3139; color: #848e9c; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 8px; font-size: 12px; font-weight: bold; border: 4px solid #161a1e;">2</div>
|
||||
<div id="step-text-2" style="font-size: 11px; color: #848e9c; font-weight: bold;">完成支付</div>
|
||||
</div>
|
||||
<div style="z-index: 2; text-align: center; width: 33%;">
|
||||
<div id="step-dot-3" style="width: 26px; height: 26px; background: #2b3139; color: #848e9c; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 8px; font-size: 12px; font-weight: bold; border: 4px solid #161a1e;">3</div>
|
||||
<div id="step-text-3" style="font-size: 11px; color: #848e9c; font-weight: bold;">上传凭证</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; overflow-y: auto; padding: 40px;">
|
||||
<?php if ($order['status'] === 'matching' && !$order['bank_account_info']): ?>
|
||||
<div style="text-align: center; max-width: 600px; margin: 40px auto;">
|
||||
<div class="matching-loader" style="margin-bottom: 30px;">
|
||||
<div class="ring"></div>
|
||||
<div class="ring"></div>
|
||||
<div class="ring"></div>
|
||||
<i class="fas fa-search-dollar" style="font-size: 40px; color: var(--primary-color);"></i>
|
||||
</div>
|
||||
<h2 style="font-size: 2rem; margin-bottom: 10px; color: white;">正在为您匹配充值账户...</h2>
|
||||
<p style="color: #848e9c; margin-bottom: 40px;">匹配过程中请勿关闭此页面,平均匹配时间 1-3 分钟。</p>
|
||||
|
||||
<div style="background: #161a1e; border-radius: 20px; padding: 30px; border: 1px solid #2b3139; text-align: left;">
|
||||
<h4 style="margin-top: 0; margin-bottom: 20px; color: #f0b90b;"><i class="fas fa-exclamation-triangle"></i> 转账说明 & 安全提示</h4>
|
||||
<div style="font-size: 14px; color: #EAECEF; line-height: 2;">
|
||||
<p>• 系统正在随机为您匹配承兑商账户,请稍等片刻。</p>
|
||||
<p>• 匹配成功后将显示收款银行卡、姓名及账号等详细信息。</p>
|
||||
<p>• 请务必使用<b>本人姓名</b>对应的账户进行转账,否则无法入账。</p>
|
||||
<p>• 转账时<b>严禁备注</b>任何关于加密货币、USDT等字眼。</p>
|
||||
</div>
|
||||
<!-- Amount Highlight -->
|
||||
<div style="background: rgba(240, 185, 11, 0.05); padding: 30px; border-radius: 24px; border: 1px solid rgba(240, 185, 11, 0.2); text-align: center; margin-bottom: 35px; box-shadow: inset 0 0 20px rgba(0,0,0,0.2);">
|
||||
<div style="font-size: 12px; color: #f0b90b; font-weight: bold; margin-bottom: 15px; text-transform: uppercase; letter-spacing: 2px;">应转账金额 / Amount Due</div>
|
||||
<div style="font-size: 3rem; font-weight: 900; color: white; display: flex; align-items: baseline; justify-content: center; gap: 10px;">
|
||||
<?php echo number_format($order['amount'], 2); ?> <span style="font-size: 20px; color: #f0b90b;"><?php echo $order['currency']; ?></span>
|
||||
</div>
|
||||
<div style="font-size: 13px; color: #848e9c; margin-top: 15px;"><i class="fas fa-exclamation-circle"></i> 请务必转入精确金额,包含小数点</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($order['bank_account_info'] && $order['status'] !== 'submitting' && $order['status'] !== 'completed'): ?>
|
||||
<div style="max-width: 700px; margin: 0 auto;">
|
||||
<div style="background: #161a1e; border-radius: 24px; border: 1px solid #2b3139; overflow: hidden;">
|
||||
<div style="background: rgba(0, 192, 135, 0.1); padding: 20px; display: flex; align-items: center; gap: 15px; border-bottom: 1px solid #2b3139;">
|
||||
<i class="fas fa-check-circle" style="color: #00c087; font-size: 24px;"></i>
|
||||
<div style="font-weight: bold; color: #00c087;">账户匹配成功,请在倒计时结束前完成转账</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 30px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px;">
|
||||
<div style="background: #0b0e11; padding: 15px; border-radius: 12px; border: 1px solid #2b3139;">
|
||||
<div style="font-size: 11px; color: #848e9c; margin-bottom: 5px;">充值金额 / Amount</div>
|
||||
<div style="font-size: 1.5rem; font-weight: 900; color: #f0b90b;"><?php echo number_format($order['amount'], 2); ?> <span style="font-size: 14px;"><?php echo $order['currency']; ?></span></div>
|
||||
</div>
|
||||
<div style="background: #0b0e11; padding: 15px; border-radius: 12px; border: 1px solid #2b3139;">
|
||||
<div style="font-size: 11px; color: #848e9c; margin-bottom: 5px;">收款方式 / Method</div>
|
||||
<div style="font-size: 1.1rem; font-weight: bold;">银行卡转账</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background: #0b0e11; padding: 25px; border-radius: 16px; border: 2px solid #2b3139; margin-bottom: 30px; position: relative;">
|
||||
<div style="font-size: 12px; color: var(--primary-color); font-weight: bold; margin-bottom: 15px;">收款账户信息 / RECEIVING ACCOUNT</div>
|
||||
<div style="font-size: 1.2rem; line-height: 2; color: white; font-family: 'Roboto Mono', monospace;">
|
||||
<?php echo nl2br(htmlspecialchars($order['bank_account_info'])); ?>
|
||||
</div>
|
||||
<button onclick="copyToClipboard('<?php echo str_replace(["\r", "\n"], ' ', $order['bank_account_info']); ?>')" style="position: absolute; top: 20px; right: 20px; background: #2b3139; border: none; color: white; padding: 5px 10px; border-radius: 6px; font-size: 12px; cursor: pointer;">
|
||||
<!-- Bank Details -->
|
||||
<div style="background: #0b0e11; padding: 30px; border-radius: 24px; border: 1px solid rgba(255,255,255,0.05); margin-bottom: 35px; position: relative;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px;">
|
||||
<div style="font-weight: 800; color: #848e9c; font-size: 14px; text-transform: uppercase; letter-spacing: 1px;"><i class="fas fa-university" style="margin-right: 8px;"></i> 收款账户信息</div>
|
||||
<button onclick="copyToClipboard('<?php echo str_replace(["\r", "\n"], ' ', $order['bank_account_info']); ?>')" style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: white; padding: 8px 16px; border-radius: 12px; font-size: 12px; cursor: pointer; transition: 0.2s; font-weight: bold;">
|
||||
<i class="fas fa-copy"></i> 复制
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-bottom: 30px;">
|
||||
<div style="font-size: 13px; color: #848e9c; line-height: 1.8;">
|
||||
<h5 style="color: white; margin-bottom: 10px;">安全提示:</h5>
|
||||
• 请勿在此聊天框外进行任何转账。<br>
|
||||
• 官方客服不会向您索要密码。<br>
|
||||
• 请确认收款人姓名后再汇款。
|
||||
</div>
|
||||
<div style="font-size: 13px; color: #848e9c; line-height: 1.8;">
|
||||
<h5 style="color: white; margin-bottom: 10px;">转账说明:</h5>
|
||||
• 转账完成后请务必保存截图。<br>
|
||||
• 点击下方按钮上传凭证确认。<br>
|
||||
• 资金通常在 15 分钟内到账。
|
||||
<div style="font-size: 1.2rem; line-height: 2; color: white; font-family: 'Roboto Mono', monospace; background: rgba(255,255,255,0.02); padding: 20px; border-radius: 16px;">
|
||||
<?php echo nl2br(htmlspecialchars($order['bank_account_info'])); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data" id="proof-form">
|
||||
<div style="background: #0b0e11; padding: 25px; border-radius: 16px; border: 2px dashed #2b3139; text-align: center; cursor: pointer; transition: 0.3s;" id="upload-zone" onclick="document.getElementById('proof-file').click()">
|
||||
<div id="upload-idle">
|
||||
<i class="fas fa-cloud-upload-alt" style="font-size: 30px; color: #5e6673; margin-bottom: 10px;"></i>
|
||||
<div style="font-weight: bold; margin-bottom: 5px; font-size: 14px;">上传支付凭证 / Upload Voucher</div>
|
||||
<div style="color: #f0b90b; font-size: 11px;">转账完成后点击此处上传凭证截图</div>
|
||||
</div>
|
||||
<div id="upload-preview" style="display: none;">
|
||||
<i class="fas fa-file-image" style="font-size: 30px; color: #00c087; margin-bottom: 10px;"></i>
|
||||
<div style="font-weight: bold; color: #00c087; font-size: 14px;">凭证已选择</div>
|
||||
<div id="filename-display" style="font-size: 12px; color: #848e9c;"></div>
|
||||
</div>
|
||||
<input type="file" name="proof" id="proof-file" accept="image/*" style="display: none;" onchange="handleFileSelect(this)">
|
||||
<!-- Action Buttons -->
|
||||
<div id="action-area">
|
||||
<button onclick="showUploadForm()" style="width: 100%; padding: 24px; background: #00c087; color: white; border: none; border-radius: 20px; font-size: 1.3rem; font-weight: 900; cursor: pointer; box-shadow: 0 15px 35px rgba(0,192,135,0.4); transition: 0.3s; transform: perspective(1px) translateZ(0);">
|
||||
我已完成转账,去上传凭证
|
||||
</button>
|
||||
<p style="text-align: center; color: #5e6673; font-size: 12px; margin-top: 20px;">
|
||||
<i class="fas fa-lock"></i> 资金安全由区块链技术加密保护
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="submit-btn" disabled class="btn-primary" style="width: 100%; padding: 18px; font-size: 1.1rem; border-radius: 12px; margin-top: 20px; opacity: 0.5; cursor: not-allowed; font-weight: bold;">
|
||||
完成转账 / Transfer Completed
|
||||
<!-- Step 5: Hidden Upload Form (上传转账凭证界面) -->
|
||||
<div id="upload-form-container" style="display: none; animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);">
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h3 style="margin: 0 0 10px 0; font-size: 1.5rem; font-weight: 900;">上传转账凭证</h3>
|
||||
<p style="color: #848e9c; font-size: 14px;">请上传包含交易单号、金额、时间的支付截图</p>
|
||||
</div>
|
||||
<form method="POST" enctype="multipart/form-data" id="main-proof-form">
|
||||
<div id="upload-dropzone" style="background: #0b0e11; border: 2px dashed rgba(255,255,255,0.1); border-radius: 24px; padding: 50px 30px; text-align: center; cursor: pointer; margin-bottom: 30px; transition: 0.3s;" onclick="document.getElementById('file-input').click()">
|
||||
<div id="file-idle">
|
||||
<div style="width: 80px; height: 80px; background: rgba(132, 142, 156, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px;">
|
||||
<i class="fas fa-cloud-upload-alt" style="font-size: 40px; color: #848e9c;"></i>
|
||||
</div>
|
||||
<div style="font-weight: 900; font-size: 18px; color: white;">点击选择转账截图</div>
|
||||
<div style="font-size: 13px; color: #5e6673; margin-top: 8px;">支持 JPG, PNG, JPEG 格式</div>
|
||||
</div>
|
||||
<div id="file-selected" style="display: none;">
|
||||
<div style="width: 80px; height: 80px; background: rgba(0, 192, 135, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px;">
|
||||
<i class="fas fa-file-image" style="font-size: 40px; color: #00c087;"></i>
|
||||
</div>
|
||||
<div style="font-weight: 900; font-size: 18px; color: #00c087;" id="selected-name"></div>
|
||||
<div style="font-size: 13px; color: #848e9c; margin-top: 8px;">文件已就绪,点击重新选择</div>
|
||||
</div>
|
||||
<input type="file" name="proof" id="file-input" accept="image/*" style="display: none;" onchange="updateFilePreview(this)">
|
||||
</div>
|
||||
|
||||
<!-- 逻辑限制:没有图片不允许提交 -->
|
||||
<button type="submit" id="final-submit" disabled style="width: 100%; padding: 22px; background: #2b3139; color: #5e6673; border: none; border-radius: 20px; font-size: 1.2rem; font-weight: 900; cursor: not-allowed; transition: 0.3s; box-shadow: 0 10px 30px rgba(0,0,0,0.2);">
|
||||
确认提交并通知客服
|
||||
</button>
|
||||
<button type="button" onclick="hideUploadForm()" style="width: 100%; margin-top: 20px; background: transparent; color: #848e9c; border: none; font-size: 14px; cursor: pointer; font-weight: bold;">
|
||||
<i class="fas fa-arrow-left"></i> 返回查看账户
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<div style="text-align: center; padding: 60px 0;">
|
||||
<div style="width: 120px; height: 120px; background: rgba(240, 185, 11, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 30px;">
|
||||
<i class="fas fa-history" style="font-size: 50px; color: #f0b90b; animation: pulse-scale 2s infinite;"></i>
|
||||
</div>
|
||||
<h2 style="font-size: 2.2rem; margin-bottom: 20px; color: white;">充值完成请等待确认</h2>
|
||||
<p style="color: #848e9c; line-height: 2; max-width: 500px; margin: 0 auto 40px; font-size: 15px;">
|
||||
您的支付凭证已成功提交。客服人员正在核实资金到账情况,请稍候。核实完成后,本页面将自动跳转至您的资产页面。
|
||||
</p>
|
||||
<div style="display: flex; gap: 20px; justify-content: center;">
|
||||
<a href="profile.php" style="background: var(--primary-color); color: white; padding: 15px 40px; border-radius: 12px; text-decoration: none; font-weight: bold;">查看我的资产</a>
|
||||
<button onclick="location.reload()" style="background: transparent; border: 1px solid #2b3139; color: white; padding: 15px 40px; border-radius: 12px; font-weight: bold; cursor: pointer;">刷新状态</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Side: Chat Sidebar (The "Dialog" sidebar) -->
|
||||
<div style="display: flex; flex-direction: column; background: #161a1e;">
|
||||
<div style="padding: 20px; border-bottom: 1px solid #2b3139; background: #1e2329; display: flex; align-items: center; gap: 12px;">
|
||||
<div style="position: relative;">
|
||||
<div style="width: 45px; height: 45px; background: var(--primary-color); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px;">
|
||||
<i class="fas fa-headset" style="color: white;"></i>
|
||||
</div>
|
||||
<div style="position: absolute; bottom: 0; right: 0; width: 12px; height: 12px; background: #00c087; border: 2px solid #1e2329; border-radius: 50%;"></div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; background: #161a1e; border: 1px solid rgba(255,255,255,0.08); border-radius: 32px; height: 85vh; display: grid; grid-template-columns: 1fr 400px; overflow: hidden; box-shadow: 0 40px 100px rgba(0,0,0,0.6);">
|
||||
|
||||
<!-- Left Panel: Status Info -->
|
||||
<div style="display: flex; flex-direction: column; background: #0b0e11; border-right: 1px solid rgba(255,255,255,0.08);">
|
||||
|
||||
<div style="padding: 25px 40px; border-bottom: 1px solid rgba(255,255,255,0.08); background: #161a1e; display: flex; align-items: center; justify-content: space-between;">
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<div style="width: 45px; height: 45px; background: var(--primary-color); border-radius: 12px; display: flex; align-items: center; justify-content: center; color: white;">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 800; font-size: 14px; color: white;">在线客服 (Support)</div>
|
||||
<div style="font-size: 10px; color: #848e9c;">24/7 实时响应中</div>
|
||||
<div style="font-weight: 800; font-size: 16px;">安全充值通道</div>
|
||||
<div style="font-size: 10px; color: #848e9c; font-family: monospace;">#<?php echo $order_id; ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div id="countdown" style="font-size: 1.4rem; font-weight: 900; color: #f0b90b; font-family: monospace;">30:00</div>
|
||||
<div style="font-size: 10px; color: #848e9c;">剩余支付时间</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chat-box" style="flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 15px; background: #0b0e11;">
|
||||
<?php foreach ($messages as $m):
|
||||
if (strpos($m['message'], '[SYSTEM]') === 0): ?>
|
||||
<div style="align-self: center; font-size: 10px; color: #5e6673; background: rgba(255,255,255,0.05); padding: 5px 15px; border-radius: 20px;">
|
||||
<?php echo htmlspecialchars(str_replace('[SYSTEM] ', '', $m['message'])); ?>
|
||||
<div style="flex: 1; overflow-y: auto; padding: 60px;">
|
||||
<!-- Step 2: 前端显示“匹配中” (待匹配) -->
|
||||
<?php if ($order['status'] === 'matching' && !$order['bank_account_info']): ?>
|
||||
<div style="text-align: center;">
|
||||
<div class="loader-container" style="margin-bottom: 40px;">
|
||||
<div class="loader"></div>
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center;">
|
||||
<i class="fas fa-user-clock" style="font-size: 40px; color: var(--primary-color);"></i>
|
||||
</div>
|
||||
<?php else:
|
||||
$align = $m['sender'] === 'user' ? 'flex-end' : 'flex-start';
|
||||
$bgColor = $m['sender'] === 'user' ? 'var(--primary-color)' : '#2b3139';
|
||||
$textColor = $m['sender'] === 'user' ? 'white' : '#EAECEF';
|
||||
$borderRadius = $m['sender'] === 'user' ? 'border-bottom-right-radius: 2px;' : 'border-bottom-left-radius: 2px;';
|
||||
?>
|
||||
<div style="display: flex; flex-direction: column; align-items: <?php echo $align; ?>;">
|
||||
<div style="max-width: 85%; padding: 10px 14px; border-radius: 14px; font-size: 13px; line-height: 1.5;
|
||||
background: <?php echo $bgColor; ?>; color: <?php echo $textColor; ?>; <?php echo $borderRadius; ?>">
|
||||
<?php echo nl2br(htmlspecialchars($m['message'])); ?>
|
||||
</div>
|
||||
<div style="font-size: 9px; color: #5e6673; margin-top: 4px;"><?php echo date('H:i', strtotime($m['created_at'])); ?></div>
|
||||
<h2 style="font-size: 2.2rem; font-weight: 900; margin-bottom: 20px;">正在为您匹配收款账户</h2>
|
||||
<div style="background: rgba(255,255,255,0.02); padding: 25px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.05); max-width: 500px; margin: 0 auto;">
|
||||
<p style="color: #848e9c; font-size: 16px; line-height: 1.8; margin: 0;">
|
||||
系统正在为您对接在线财务专员,匹配成功后将自动弹出收款账户详情,请保持页面开启不要离开。
|
||||
</p>
|
||||
</div>
|
||||
<div style="margin-top: 40px; display: flex; justify-content: center; gap: 15px;">
|
||||
<div class="dot-typing"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($order['status'] === 'submitting'): ?>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 140px; height: 140px; background: rgba(0, 192, 135, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 35px; border: 2px solid rgba(0, 192, 135, 0.2);">
|
||||
<i class="fas fa-file-invoice-dollar" style="font-size: 60px; color: #00c087;"></i>
|
||||
</div>
|
||||
<h2 style="font-size: 2.2rem; font-weight: 900; margin-bottom: 20px;">支付凭证已提交</h2>
|
||||
<p style="color: #848e9c; font-size: 16px; line-height: 1.8; max-width: 500px; margin: 0 auto;">
|
||||
我们已收到您的转账凭证,财务人员正在核实入账情况。通常在 5-10 分钟内完成,完成后您的余额将自动增加。
|
||||
</p>
|
||||
<div style="margin-top: 50px; display: flex; gap: 20px; justify-content: center;">
|
||||
<a href="profile.php" style="background: var(--primary-color); color: white; padding: 18px 45px; border-radius: 16px; text-decoration: none; font-weight: 900; box-shadow: 0 10px 25px rgba(0,82,255,0.3);">查看我的钱包</a>
|
||||
<a href="chat.php" style="background: #2b3139; color: white; padding: 18px 45px; border-radius: 16px; text-decoration: none; font-weight: 900;">联系人工客服</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($order['status'] === 'rejected'): ?>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 140px; height: 140px; background: rgba(246, 70, 93, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 35px; border: 2px solid rgba(246, 70, 93, 0.2);">
|
||||
<i class="fas fa-times-circle" style="font-size: 60px; color: #f6465d;"></i>
|
||||
</div>
|
||||
<h2 style="font-size: 2.2rem; font-weight: 900; margin-bottom: 20px; color: #f6465d;">订单审核未通过</h2>
|
||||
<p style="color: #848e9c; font-size: 16px; line-height: 1.8; max-width: 500px; margin: 0 auto;">
|
||||
非常抱歉,您的充值申请未能通过。可能是因为凭证不清晰、金额不匹配或转账未到账。
|
||||
</p>
|
||||
<a href="deposit.php" style="display: inline-block; margin-top: 50px; background: #f6465d; color: white; padding: 18px 45px; border-radius: 16px; text-decoration: none; font-weight: 900;">重新发起充值</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Panel: Chat -->
|
||||
<div style="display: flex; flex-direction: column; background: #161a1e;">
|
||||
<div style="padding: 20px 25px; border-bottom: 1px solid rgba(255,255,255,0.08); background: #1e2329; display: flex; align-items: center; gap: 12px;">
|
||||
<div style="width: 10px; height: 10px; background: #00c087; border-radius: 50%; box-shadow: 0 0 10px #00c087;"></div>
|
||||
<div style="font-weight: bold; font-size: 14px;">在线客服 (Online)</div>
|
||||
</div>
|
||||
|
||||
<div id="chat-box" style="flex: 1; overflow-y: auto; padding: 25px; display: flex; flex-direction: column; gap: 15px; background: #0b0e11;">
|
||||
<?php foreach ($messages as $m):
|
||||
$align = $m['sender'] === 'user' ? 'flex-end' : 'flex-start';
|
||||
$bg = $m['sender'] === 'user' ? 'var(--primary-color)' : '#2b3139';
|
||||
?>
|
||||
<div style="align-self: <?php echo $align; ?>; max-width: 85%;">
|
||||
<div style="padding: 12px 16px; border-radius: 15px; background: <?php echo $bg; ?>; color: white; font-size: 14px; line-height: 1.5;">
|
||||
<?php echo nl2br(htmlspecialchars($m['message'])); ?>
|
||||
</div>
|
||||
<div style="font-size: 10px; color: #5e6673; margin-top: 5px; text-align: <?php echo $m['sender'] === 'user' ? 'right' : 'left'; ?>;">
|
||||
<?php echo date('H:i', strtotime($m['created_at'])); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div style="padding: 20px; background: #1e2329; border-top: 1px solid #2b3139;">
|
||||
<div style="padding: 20px; background: #1e2329;">
|
||||
<form id="chat-form" style="display: flex; gap: 10px;">
|
||||
<input type="text" id="chat-input" placeholder="输入您的问题..." style="flex: 1; background: #0b0e11; border: 1px solid #2b3139; border-radius: 12px; padding: 12px 15px; color: white; outline: none; font-size: 13px;">
|
||||
<button type="submit" style="width: 45px; height: 45px; border-radius: 12px; background: var(--primary-color); border: none; color: white; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: 0.2s;">
|
||||
<input type="text" id="chat-input" placeholder="输入消息..." style="flex: 1; background: #0b0e11; border: 1px solid rgba(255,255,255,0.1); border-radius: 10px; padding: 12px 15px; color: white; outline: none;">
|
||||
<button type="submit" style="background: var(--primary-color); border: none; color: white; width: 45px; height: 45px; border-radius: 10px; cursor: pointer;">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.matching-loader { position: relative; width: 100px; height: 100px; margin: 0 auto; display: flex; align-items: center; justify-content: center; }
|
||||
.matching-loader .ring { position: absolute; width: 100%; height: 100%; border: 4px solid transparent; border-top: 4px solid var(--primary-color); border-radius: 50%; animation: spin 2s linear infinite; }
|
||||
.matching-loader .ring:nth-child(2) { width: 80%; height: 80%; border-top: 4px solid #f0b90b; animation: spin-reverse 1.5s linear infinite; }
|
||||
.matching-loader .ring:nth-child(3) { width: 60%; height: 60%; border-top: 4px solid #00c087; animation: spin 1s linear infinite; }
|
||||
|
||||
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
||||
@keyframes spin-reverse { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } }
|
||||
@keyframes pulse-scale { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.8; } 100% { transform: scale(1); opacity: 1; } }
|
||||
@keyframes slideUp { from { transform: translateY(40px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
||||
.loader-container { position: relative; width: 120px; height: 120px; margin: 0 auto; }
|
||||
.loader { width: 120px; height: 120px; border: 4px solid rgba(0,82,255,0.05); border-top-color: var(--primary-color); border-radius: 50%; animation: spin 1.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite; }
|
||||
|
||||
#upload-zone:hover { border-color: var(--primary-color); background: rgba(79, 172, 254, 0.05); }
|
||||
.btn-primary:active { transform: scale(0.98); }
|
||||
.dot-typing { position: relative; left: -9999px; width: 10px; height: 10px; border-radius: 5px; background-color: var(--primary-color); color: var(--primary-color); box-shadow: 9984px 0 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); animation: dot-typing 1.5s infinite linear; }
|
||||
@keyframes dot-typing { 0% { box-shadow: 9984px 0 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); } 16.667% { box-shadow: 9984px -10px 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); } 33.333% { box-shadow: 9984px 0 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); } 50% { box-shadow: 9984px 0 0 0 var(--primary-color), 9999px -10px 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); } 66.667% { box-shadow: 9984px 0 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); } 83.333% { box-shadow: 9984px 0 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px -10px 0 0 var(--primary-color); } 100% { box-shadow: 9984px 0 0 0 var(--primary-color), 9999px 0 0 0 var(--primary-color), 10014px 0 0 0 var(--primary-color); } }
|
||||
|
||||
body { overflow: hidden; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let expiresAt = new Date('<?php echo $order['expires_at']; ?>').getTime();
|
||||
function updateCountdown() {
|
||||
let now = new Date().getTime();
|
||||
let diff = expiresAt - now;
|
||||
let elements = document.querySelectorAll('#countdown');
|
||||
if (diff <= 0) { elements.forEach(el => { el.innerText = "00:00"; el.style.color = "#ff4d4f"; }); return; }
|
||||
let mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
let secs = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
let timeStr = (mins < 10 ? "0" + mins : mins) + ":" + (secs < 10 ? "0" + secs : secs);
|
||||
elements.forEach(el => el.innerText = timeStr);
|
||||
}
|
||||
if (document.querySelectorAll('#countdown').length > 0) { updateCountdown(); setInterval(updateCountdown, 1000); }
|
||||
|
||||
// Order status polling
|
||||
setInterval(() => {
|
||||
fetch('api/check_order_status.php?order_id=<?php echo $order_id; ?>')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'completed') { location.href = 'profile.php'; }
|
||||
else if (data.bank_account_info && !document.getElementById('upload-zone') && '<?php echo $order['status']; ?>' === 'matching') { location.reload(); }
|
||||
});
|
||||
}, 4000);
|
||||
|
||||
function handleFileSelect(input) {
|
||||
const btn = document.getElementById('submit-btn');
|
||||
if (input.files && input.files[0]) {
|
||||
btn.disabled = false; btn.style.opacity = "1"; btn.style.cursor = "pointer";
|
||||
document.getElementById('upload-idle').style.display = "none";
|
||||
document.getElementById('upload-preview').style.display = "block";
|
||||
document.getElementById('filename-display').innerText = input.files[0].name;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy function
|
||||
function copyToClipboard(text) {
|
||||
const el = document.createElement('textarea');
|
||||
el.value = text;
|
||||
@ -311,11 +321,105 @@ $messages = $stmt->fetchAll();
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
alert('已复制到剪贴板 / Copied to clipboard');
|
||||
alert('复制成功 / Copied');
|
||||
}
|
||||
|
||||
// Force Lock Logic
|
||||
const isMatched = <?php echo ($order['bank_account_info'] ? 'true' : 'false'); ?>;
|
||||
const status = '<?php echo $order['status']; ?>';
|
||||
|
||||
if (isMatched && status !== 'submitting' && status !== 'completed' && status !== 'rejected') {
|
||||
// Prevent back button and refresh alert
|
||||
window.history.pushState(null, null, window.location.href);
|
||||
window.onpopstate = function() {
|
||||
window.history.pushState(null, null, window.location.href);
|
||||
};
|
||||
window.onbeforeunload = function() {
|
||||
return "交易正在进行中,请勿离开!";
|
||||
};
|
||||
}
|
||||
|
||||
// Polling for status updates
|
||||
setInterval(() => {
|
||||
fetch('api/check_order_status.php?order_id=<?php echo $order_id; ?>')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'completed') {
|
||||
window.onbeforeunload = null;
|
||||
location.href = 'profile.php';
|
||||
} else if (data.status === 'rejected' && status !== 'rejected') {
|
||||
location.reload();
|
||||
} else if (data.status === 'matched' && status === 'matching') {
|
||||
location.reload(); // Trigger mandatory Step 4 popup
|
||||
} else if (data.bank_account_info && !isMatched) {
|
||||
location.reload(); // Admin matched while polling
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
// Countdown logic
|
||||
let expiresAt = new Date('<?php echo $order['expires_at']; ?>').getTime();
|
||||
function updateCountdown() {
|
||||
let now = new Date().getTime();
|
||||
let diff = expiresAt - now;
|
||||
let el = document.getElementById('countdown');
|
||||
if (!el) return;
|
||||
if (diff <= 0) { el.innerText = "00:00"; el.style.color = "#f6465d"; return; }
|
||||
let mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
let secs = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
el.innerText = (mins < 10 ? "0" + mins : mins) + ":" + (secs < 10 ? "0" + secs : secs);
|
||||
}
|
||||
updateCountdown();
|
||||
setInterval(updateCountdown, 1000);
|
||||
|
||||
// Upload Form Handlers
|
||||
function showUploadForm() {
|
||||
document.getElementById('action-area').style.display = 'none';
|
||||
document.getElementById('upload-form-container').style.display = 'block';
|
||||
|
||||
// Update Steps
|
||||
document.getElementById('step-dot-2').style.background = '#00c087';
|
||||
document.getElementById('step-dot-2').style.color = 'white';
|
||||
document.getElementById('step-text-2').style.color = '#00c087';
|
||||
}
|
||||
|
||||
function hideUploadForm() {
|
||||
document.getElementById('action-area').style.display = 'block';
|
||||
document.getElementById('upload-form-container').style.display = 'none';
|
||||
|
||||
// Revert Steps
|
||||
document.getElementById('step-dot-2').style.background = '#2b3139';
|
||||
document.getElementById('step-dot-2').style.color = '#848e9c';
|
||||
document.getElementById('step-text-2').style.color = '#848e9c';
|
||||
}
|
||||
|
||||
function updateFilePreview(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
document.getElementById('file-idle').style.display = 'none';
|
||||
document.getElementById('file-selected').style.display = 'block';
|
||||
document.getElementById('selected-name').innerText = input.files[0].name;
|
||||
|
||||
const btn = document.getElementById('final-submit');
|
||||
btn.disabled = false;
|
||||
btn.style.background = '#00c087';
|
||||
btn.style.color = 'white';
|
||||
btn.style.cursor = 'pointer';
|
||||
btn.style.boxShadow = '0 10px 20px rgba(0,192,135,0.2)';
|
||||
|
||||
// Step 3 active
|
||||
document.getElementById('step-dot-3').style.background = '#00c087';
|
||||
document.getElementById('step-dot-3').style.color = 'white';
|
||||
document.getElementById('step-text-3').style.color = '#00c087';
|
||||
|
||||
// Highlight dropzone
|
||||
document.getElementById('upload-dropzone').style.borderColor = '#00c087';
|
||||
document.getElementById('upload-dropzone').style.background = 'rgba(0,192,135,0.02)';
|
||||
}
|
||||
}
|
||||
|
||||
// Chat Logic
|
||||
const chatBox = document.getElementById('chat-box');
|
||||
if(chatBox) chatBox.scrollTop = chatBox.scrollHeight;
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
document.getElementById('chat-form').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
@ -323,25 +427,25 @@ $messages = $stmt->fetchAll();
|
||||
const msg = input.value.trim();
|
||||
if (!msg) return;
|
||||
|
||||
// Add message locally for instant feedback
|
||||
const time = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
||||
const msgHtml = `<div style="display: flex; flex-direction: column; align-items: flex-end;">
|
||||
<div style="max-width: 85%; padding: 10px 14px; border-radius: 14px; font-size: 13px; line-height: 1.5; background: var(--primary-color); color: white; border-bottom-right-radius: 2px;">${msg}</div>
|
||||
<div style="font-size: 9px; color: #5e6673; margin-top: 4px;">${time}</div>
|
||||
</div>`;
|
||||
chatBox.insertAdjacentHTML('beforeend', msgHtml);
|
||||
const html = `
|
||||
<div style="align-self: flex-end; max-width: 85%;">
|
||||
<div style="padding: 12px 16px; border-radius: 15px; background: var(--primary-color); color: white; font-size: 14px; line-height: 1.5;">${msg}</div>
|
||||
<div style="font-size: 10px; color: #5e6673; margin-top: 5px; text-align: right;">
|
||||
${time}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
chatBox.insertAdjacentHTML('beforeend', html);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
input.value = '';
|
||||
|
||||
fetch('chat.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'message=' + encodeURIComponent(msg) });
|
||||
fetch('chat.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: 'message=' + encodeURIComponent(msg)
|
||||
});
|
||||
};
|
||||
|
||||
let messageCount = <?php echo count($messages); ?>;
|
||||
setInterval(() => {
|
||||
fetch('api/get_messages.php')
|
||||
.then(r => r.json())
|
||||
.then(data => { if (data.count > messageCount) { location.reload(); } });
|
||||
}, 5000);
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
480
spot.php
480
spot.php
@ -50,287 +50,60 @@ if ($user_id) {
|
||||
}
|
||||
|
||||
/* Left: Markets */
|
||||
.market-panel {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-input-wrapper input {
|
||||
width: 100%;
|
||||
background: var(--input-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: white;
|
||||
padding: 6px 12px 6px 32px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-input-wrapper i {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 8px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.pairs-list-header {
|
||||
display: flex;
|
||||
padding: 8px 12px;
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.pair-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.02);
|
||||
}
|
||||
|
||||
.market-panel { width: 280px; flex-shrink: 0; }
|
||||
.search-box { padding: 12px; border-bottom: 1px solid var(--border-color); }
|
||||
.search-input-wrapper { position: relative; }
|
||||
.search-input-wrapper input { width: 100%; background: var(--input-bg); border: 1px solid var(--border-color); color: white; padding: 6px 12px 6px 32px; border-radius: 4px; font-size: 13px; outline: none; }
|
||||
.search-input-wrapper i { position: absolute; left: 10px; top: 8px; color: var(--text-secondary); }
|
||||
.pairs-list-header { display: flex; padding: 8px 12px; font-size: 11px; color: var(--text-secondary); border-bottom: 1px solid var(--border-color); }
|
||||
.pair-item { display: flex; justify-content: space-between; padding: 8px 12px; cursor: pointer; transition: background 0.2s; border-bottom: 1px solid rgba(255,255,255,0.02); }
|
||||
.pair-item:hover { background: rgba(255,255,255,0.05); }
|
||||
.pair-item.active { background: rgba(240, 185, 11, 0.1); }
|
||||
|
||||
/* Center: Chart, Form, Tabs */
|
||||
.center-panel {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.info-bar {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
gap: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 450px;
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.order-placement-panel {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
background: var(--panel-bg);
|
||||
}
|
||||
|
||||
.order-side-column {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.order-type-tabs {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.order-type-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
.center-panel { flex: 1; overflow-y: auto; }
|
||||
.info-bar { height: 50px; display: flex; align-items: center; padding: 0 15px; gap: 20px; border-bottom: 1px solid var(--border-color); }
|
||||
.chart-container { height: 450px; background: var(--bg-color); }
|
||||
.order-placement-panel { display: flex; gap: 20px; padding: 20px; border-bottom: 1px solid var(--border-color); background: var(--panel-bg); }
|
||||
.order-side-column { flex: 1; }
|
||||
.order-type-tabs { display: flex; gap: 15px; margin-bottom: 15px; }
|
||||
.order-type-btn { background: none; border: none; color: var(--text-secondary); font-size: 14px; cursor: pointer; padding: 0; }
|
||||
.order-type-btn.active { color: var(--accent-color); font-weight: bold; }
|
||||
.input-row { background: var(--input-bg); border: 1px solid var(--border-color); border-radius: 4px; display: flex; align-items: center; margin-bottom: 10px; padding: 8px 12px; }
|
||||
.input-label { color: var(--text-secondary); font-size: 13px; width: 50px; }
|
||||
.input-row input { flex: 1; background: transparent; border: none; color: white; text-align: right; outline: none; font-size: 14px; }
|
||||
.input-row input:disabled { color: var(--text-secondary); cursor: not-allowed; }
|
||||
.input-unit { color: var(--text-secondary); font-size: 12px; margin-left: 8px; width: 40px; text-align: right; }
|
||||
|
||||
.input-row {
|
||||
background: var(--input-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
width: 50px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.input-row input {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: white;
|
||||
text-align: right;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.input-row input:disabled {
|
||||
color: var(--text-secondary);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.input-unit {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
margin: 15px 0 25px 0;
|
||||
padding: 0 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slider-marks {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: -10px;
|
||||
position: relative;
|
||||
padding: 0 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mark-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--border-color);
|
||||
border: 2px solid var(--input-bg);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
pointer-events: auto;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.slider-container { margin: 15px 0 25px 0; padding: 0 5px; position: relative; }
|
||||
.slider-marks { display: flex; justify-content: space-between; margin-top: -10px; position: relative; padding: 0 8px; pointer-events: none; }
|
||||
.mark-dot { width: 8px; height: 8px; background: var(--border-color); border: 2px solid var(--input-bg); border-radius: 50%; cursor: pointer; z-index: 2; pointer-events: auto; transform: translateY(-2px); }
|
||||
.mark-dot.active { background: var(--accent-color); border-color: var(--accent-color); }
|
||||
|
||||
.slider-labels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
.label-item {
|
||||
color: var(--text-secondary);
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.slider-labels { display: flex; justify-content: space-between; margin-top: 8px; padding: 0 2px; }
|
||||
.label-item { color: var(--text-secondary); font-size: 10px; cursor: pointer; user-select: none; width: 20px; text-align: center; }
|
||||
.label-item.active { color: var(--text-primary); }
|
||||
|
||||
.execute-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.execute-btn { width: 100%; padding: 12px; border: none; border-radius: 4px; font-weight: bold; font-size: 15px; cursor: pointer; color: white; transition: opacity 0.2s; }
|
||||
.execute-btn:active { opacity: 0.8; }
|
||||
.execute-btn.buy { background: var(--up-color); }
|
||||
.execute-btn.sell { background: var(--down-color); }
|
||||
|
||||
.bottom-tabs {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tabs-header {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
padding: 12px 15px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.bottom-tabs { flex: 1; }
|
||||
.tabs-header { display: flex; border-bottom: 1px solid var(--border-color); padding: 0 15px; }
|
||||
.tab-btn { background: none; border: none; color: var(--text-secondary); padding: 12px 15px; font-size: 14px; cursor: pointer; border-bottom: 2px solid transparent; }
|
||||
.tab-btn.active { color: var(--accent-color); border-bottom-color: var(--accent-color); }
|
||||
|
||||
/* Right: Order Book */
|
||||
.order-book-panel {
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.order-book-header {
|
||||
padding: 10px 15px;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ob-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 3px 15px;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ob-bar {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.order-book-panel { width: 300px; flex-shrink: 0; }
|
||||
.order-book-header { padding: 10px 15px; font-size: 12px; color: var(--text-secondary); border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; }
|
||||
.ob-row { display: flex; justify-content: space-between; padding: 3px 15px; font-size: 12px; position: relative; }
|
||||
.ob-bar { position: absolute; right: 0; top: 0; bottom: 0; z-index: 0; }
|
||||
.ob-val { z-index: 1; position: relative; }
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
input[type=range] { -webkit-appearance: none; width: 100%; background: transparent; margin: 0; padding: 0; }
|
||||
input[type=range]:focus { outline: none; }
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
cursor: pointer;
|
||||
background: var(--border-color);
|
||||
border-radius: 2px;
|
||||
}
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: -5px;
|
||||
box-shadow: 0 0 2px rgba(0,0,0,0.5);
|
||||
border: 2px solid var(--accent-color);
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
}
|
||||
input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; cursor: pointer; background: var(--border-color); border-radius: 2px; }
|
||||
input[type=range]::-webkit-slider-thumb { height: 14px; width: 14px; border-radius: 50%; background: white; cursor: pointer; -webkit-appearance: none; margin-top: -5px; box-shadow: 0 0 2px rgba(0,0,0,0.5); border: 2px solid var(--accent-color); z-index: 3; position: relative; }
|
||||
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-thumb { background: #333; border-radius: 10px; }
|
||||
@ -379,7 +152,7 @@ if ($user_id) {
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 8px;">
|
||||
<span style="color: var(--text-secondary);">买入 <span class="asset-name">BTC</span></span>
|
||||
<span style="color: var(--text-secondary);">可用: <span id="buy-available" style="color: white;"><?php echo number_format($balance, 2); ?></span> USDT</span>
|
||||
<span style="color: var(--text-secondary);">可用: <span id="buy-available" style="color: white;">--</span> USDT</span>
|
||||
</div>
|
||||
<div class="input-row" id="buy-price-row">
|
||||
<span class="input-label">价格</span>
|
||||
@ -427,7 +200,7 @@ if ($user_id) {
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 8px;">
|
||||
<span style="color: var(--text-secondary);">卖出 <span class="asset-name">BTC</span></span>
|
||||
<span style="color: var(--text-secondary);">可用: <span id="sell-available" style="color: white;">0.00</span> <span class="asset-name">BTC</span></span>
|
||||
<span style="color: var(--text-secondary);">可用: <span id="sell-available" style="color: white;">--</span> <span class="asset-name">BTC</span></span>
|
||||
</div>
|
||||
<div class="input-row" id="sell-price-row">
|
||||
<span class="input-label">价格</span>
|
||||
@ -471,14 +244,14 @@ if ($user_id) {
|
||||
|
||||
<div class="bottom-tabs">
|
||||
<div class="tabs-header">
|
||||
<button class="tab-btn active" onclick="switchTab(this, 'open')">当前委托</button>
|
||||
<button class="tab-btn" onclick="switchTab(this, 'history')">历史委托</button>
|
||||
<button class="tab-btn" onclick="switchTab(this, 'assets')">资产余额</button>
|
||||
<button class="tab-btn active" id="tab-open" onclick="switchTab(this, 'open')">当前委托</button>
|
||||
<button class="tab-btn" id="tab-history" onclick="switchTab(this, 'history')">历史委托</button>
|
||||
<button class="tab-btn" id="tab-assets" onclick="switchTab(this, 'assets')">资产余额</button>
|
||||
</div>
|
||||
<div id="tab-content" style="padding: 15px;">
|
||||
<table style="width: 100%; font-size: 12px; border-collapse: collapse;">
|
||||
<div id="tab-content-area" style="padding: 15px;">
|
||||
<table id="orders-table" style="width: 100%; font-size: 12px; border-collapse: collapse;">
|
||||
<thead style="color: var(--text-secondary); text-align: left;">
|
||||
<tr>
|
||||
<tr id="table-header">
|
||||
<th style="padding: 8px;">时间</th>
|
||||
<th style="padding: 8px;">币对</th>
|
||||
<th style="padding: 8px;">类型</th>
|
||||
@ -515,10 +288,11 @@ if ($user_id) {
|
||||
<script>
|
||||
let currentPair = 'BTCUSDT';
|
||||
let currentPrice = 0;
|
||||
let usdtBalance = <?php echo $balance; ?>;
|
||||
let assetBalance = 1.25;
|
||||
let usdtBalance = 0;
|
||||
let userAssets = {};
|
||||
let marketData = {};
|
||||
let orderTypes = { buy: 'limit', sell: 'limit' };
|
||||
let activeTab = 'open';
|
||||
|
||||
const pairs = [
|
||||
'BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT', 'TRXUSDT', 'DOTUSDT',
|
||||
@ -545,6 +319,13 @@ if ($user_id) {
|
||||
marketData[data.s] = data;
|
||||
renderPairs();
|
||||
if (data.s === currentPair) {
|
||||
updateUIWithData(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
connectWS();
|
||||
|
||||
function updateUIWithData(data) {
|
||||
currentPrice = parseFloat(data.c);
|
||||
document.getElementById('last-price').innerText = currentPrice.toLocaleString();
|
||||
document.getElementById('last-price').style.color = data.P >= 0 ? 'var(--up-color)' : 'var(--down-color)';
|
||||
@ -559,12 +340,16 @@ if ($user_id) {
|
||||
|
||||
const bp = document.getElementById('buy-price');
|
||||
const sp = document.getElementById('sell-price');
|
||||
if (!bp.value) bp.value = currentPrice;
|
||||
if (!sp.value) sp.value = currentPrice;
|
||||
// Only set value if empty or if user hasn't typed anything yet (simple heuristic)
|
||||
if (!bp.value || bp.getAttribute('data-auto') === 'true') {
|
||||
bp.value = currentPrice;
|
||||
bp.setAttribute('data-auto', 'true');
|
||||
}
|
||||
if (!sp.value || sp.getAttribute('data-auto') === 'true') {
|
||||
sp.value = currentPrice;
|
||||
sp.setAttribute('data-auto', 'true');
|
||||
}
|
||||
};
|
||||
}
|
||||
connectWS();
|
||||
|
||||
function renderPairs() {
|
||||
const list = document.getElementById('pairs-list');
|
||||
@ -588,6 +373,25 @@ if ($user_id) {
|
||||
list.innerHTML = html;
|
||||
}
|
||||
|
||||
async function fetchAssets() {
|
||||
const resp = await fetch('api/get_assets.php');
|
||||
const res = await resp.json();
|
||||
if (res.success) {
|
||||
userAssets = {};
|
||||
res.data.forEach(a => {
|
||||
userAssets[a.symbol] = a.amount;
|
||||
});
|
||||
usdtBalance = userAssets['USDT'] || 0;
|
||||
updateAvailableDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function updateAvailableDisplay() {
|
||||
const coin = currentPair.replace('USDT', '');
|
||||
document.getElementById('buy-available').innerText = usdtBalance.toFixed(2);
|
||||
document.getElementById('sell-available').innerText = (userAssets[coin] || 0).toFixed(6);
|
||||
}
|
||||
|
||||
function switchPair(p) {
|
||||
currentPair = p;
|
||||
const name = p.replace('USDT', '');
|
||||
@ -598,13 +402,20 @@ if ($user_id) {
|
||||
document.querySelector('.execute-btn.buy').innerText = '买入 ' + name;
|
||||
document.querySelector('.execute-btn.sell').innerText = '卖出 ' + name;
|
||||
|
||||
assetBalance = (Math.random() * 2).toFixed(4);
|
||||
document.getElementById('sell-available').innerText = assetBalance;
|
||||
updateAvailableDisplay();
|
||||
|
||||
if (currentPrice) {
|
||||
document.getElementById('buy-price').value = currentPrice;
|
||||
document.getElementById('sell-price').value = currentPrice;
|
||||
if (marketData[p]) {
|
||||
updateUIWithData(marketData[p]);
|
||||
} else {
|
||||
document.getElementById('last-price').innerText = '--';
|
||||
document.getElementById('buy-price').value = '';
|
||||
document.getElementById('sell-price').value = '';
|
||||
}
|
||||
|
||||
// Reset auto flag on switch so it picks up the new price
|
||||
document.getElementById('buy-price').setAttribute('data-auto', 'true');
|
||||
document.getElementById('sell-price').setAttribute('data-auto', 'true');
|
||||
|
||||
document.getElementById('buy-amount').value = '';
|
||||
document.getElementById('sell-amount').value = '';
|
||||
document.getElementById('buy-slider').value = 0;
|
||||
@ -626,7 +437,6 @@ if ($user_id) {
|
||||
});
|
||||
document.getElementById(`${side}-price-row`).style.display = type === 'limit' ? 'flex' : 'none';
|
||||
document.getElementById(`${side}-market-price-row`).style.display = type === 'market' ? 'flex' : 'none';
|
||||
|
||||
updateFromSlider(side, document.getElementById(side + '-slider').value);
|
||||
}
|
||||
|
||||
@ -634,9 +444,10 @@ if ($user_id) {
|
||||
const asks = document.getElementById('asks-list');
|
||||
const bids = document.getElementById('bids-list');
|
||||
let asksHtml = ''; let bidsHtml = '';
|
||||
const step = currentPrice * 0.0002;
|
||||
for(let i=0; i<15; i++) {
|
||||
const askPrice = currentPrice * (1 + (i + 1) * 0.0002);
|
||||
const bidPrice = currentPrice * (1 - (i + 1) * 0.0002);
|
||||
const askPrice = currentPrice + (i + 1) * step;
|
||||
const bidPrice = currentPrice - (i + 1) * step;
|
||||
asksHtml += `<div class="ob-row"><div class="ob-bar" style="background: rgba(246, 70, 93, 0.1); width: ${Math.random()*100}%;"></div><span class="ob-val" style="color: var(--down-color);">${askPrice.toFixed(2)}</span><span class="ob-val">${(Math.random()*2).toFixed(4)}</span></div>`;
|
||||
bidsHtml += `<div class="ob-row"><div class="ob-bar" style="background: rgba(0, 192, 135, 0.1); width: ${Math.random()*100}%;"></div><span class="ob-val" style="color: var(--up-color);">${bidPrice.toFixed(2)}</span><span class="ob-val">${(Math.random()*2).toFixed(4)}</span></div>`;
|
||||
}
|
||||
@ -648,6 +459,7 @@ if ($user_id) {
|
||||
const isBuy = side === 'buy';
|
||||
const isLimit = orderTypes[side] === 'limit';
|
||||
const price = isLimit ? (parseFloat(document.getElementById(side + '-price').value) || currentPrice) : currentPrice;
|
||||
const coin = currentPair.replace('USDT', '');
|
||||
|
||||
if (isBuy) {
|
||||
const totalUSDT = usdtBalance * (val / 100);
|
||||
@ -655,7 +467,8 @@ if ($user_id) {
|
||||
document.getElementById('buy-amount').value = amount > 0 ? amount.toFixed(6) : '';
|
||||
document.getElementById('buy-total').innerText = totalUSDT.toFixed(2);
|
||||
} else {
|
||||
const amount = assetBalance * (val / 100);
|
||||
const coinBal = userAssets[coin] || 0;
|
||||
const amount = coinBal * (val / 100);
|
||||
const totalUSDT = amount * price;
|
||||
document.getElementById('sell-amount').value = amount > 0 ? amount.toFixed(6) : '';
|
||||
document.getElementById('sell-total').innerText = totalUSDT.toFixed(2);
|
||||
@ -666,8 +479,10 @@ if ($user_id) {
|
||||
function updateFromInputs(side) {
|
||||
const isBuy = side === 'buy';
|
||||
const isLimit = orderTypes[side] === 'limit';
|
||||
const price = isLimit ? (parseFloat(document.getElementById(side + '-price').value) || currentPrice) : currentPrice;
|
||||
const priceInput = document.getElementById(side + '-price');
|
||||
const price = isLimit ? (parseFloat(priceInput.value) || currentPrice) : currentPrice;
|
||||
const amount = parseFloat(document.getElementById(side + '-amount').value) || 0;
|
||||
const coin = currentPair.replace('USDT', '');
|
||||
|
||||
const total = price * amount;
|
||||
document.getElementById(side + '-total').innerText = total.toFixed(2);
|
||||
@ -676,7 +491,8 @@ if ($user_id) {
|
||||
if (isBuy) {
|
||||
percentage = usdtBalance > 0 ? (total / usdtBalance) * 100 : 0;
|
||||
} else {
|
||||
percentage = assetBalance > 0 ? (amount / assetBalance) * 100 : 0;
|
||||
const coinBal = userAssets[coin] || 0;
|
||||
percentage = coinBal > 0 ? (amount / coinBal) * 100 : 0;
|
||||
}
|
||||
percentage = Math.min(Math.max(percentage, 0), 100);
|
||||
document.getElementById(side + '-slider').value = percentage;
|
||||
@ -687,14 +503,8 @@ if ($user_id) {
|
||||
const column = document.getElementById(side + '-column');
|
||||
const marks = column.querySelectorAll('.mark-dot');
|
||||
const labels = column.querySelectorAll('.label-item');
|
||||
marks.forEach((m, i) => {
|
||||
if (i * 25 <= val) m.classList.add('active');
|
||||
else m.classList.remove('active');
|
||||
});
|
||||
labels.forEach((l, i) => {
|
||||
if (Math.abs(i * 25 - val) < 5) l.classList.add('active');
|
||||
else l.classList.remove('active');
|
||||
});
|
||||
marks.forEach((m, i) => { if (i * 25 <= val) m.classList.add('active'); else m.classList.remove('active'); });
|
||||
labels.forEach((l, i) => { if (Math.abs(i * 25 - val) < 5) l.classList.add('active'); else l.classList.remove('active'); });
|
||||
}
|
||||
|
||||
function setSlider(side, val) {
|
||||
@ -703,7 +513,10 @@ if ($user_id) {
|
||||
}
|
||||
|
||||
['buy', 'sell'].forEach(side => {
|
||||
document.getElementById(side + '-price').addEventListener('input', () => updateFromInputs(side));
|
||||
document.getElementById(side + '-price').addEventListener('input', (e) => {
|
||||
e.target.setAttribute('data-auto', 'false');
|
||||
updateFromInputs(side);
|
||||
});
|
||||
document.getElementById(side + '-amount').addEventListener('input', () => updateFromInputs(side));
|
||||
});
|
||||
|
||||
@ -726,6 +539,7 @@ if ($user_id) {
|
||||
const res = await response.json();
|
||||
if (res.success) {
|
||||
alert('下单成功');
|
||||
fetchAssets();
|
||||
fetchOrders();
|
||||
} else {
|
||||
alert('失败: ' + res.error);
|
||||
@ -733,12 +547,32 @@ if ($user_id) {
|
||||
}
|
||||
|
||||
async function fetchOrders() {
|
||||
const response = await fetch('api/get_orders.php?type=spot&status=open');
|
||||
if (activeTab === 'assets') return fetchAssetsList();
|
||||
|
||||
const response = await fetch(`api/get_orders.php?type=spot&status=${activeTab}`);
|
||||
const res = await response.json();
|
||||
const tbody = document.getElementById('orders-tbody');
|
||||
const thead = document.getElementById('table-header');
|
||||
|
||||
thead.innerHTML = `
|
||||
<th style="padding: 8px;">时间</th>
|
||||
<th style="padding: 8px;">币对</th>
|
||||
<th style="padding: 8px;">类型</th>
|
||||
<th style="padding: 8px;">方向</th>
|
||||
<th style="padding: 8px;">价格</th>
|
||||
<th style="padding: 8px;">数量</th>
|
||||
<th style="padding: 8px;">状态</th>
|
||||
<th style="padding: 8px; text-align: right;">操作</th>
|
||||
`;
|
||||
|
||||
if (res.success && res.data.length > 0) {
|
||||
let html = '';
|
||||
res.data.forEach(o => {
|
||||
let statusText = '';
|
||||
if (o.status === 'open') statusText = '进行中';
|
||||
else if (o.status === 'closed') statusText = '已成交';
|
||||
else statusText = '已取消';
|
||||
|
||||
html += `
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 10px 8px;">${o.created_at}</td>
|
||||
@ -747,9 +581,9 @@ if ($user_id) {
|
||||
<td style="padding: 10px 8px; color: ${o.side === 'buy' ? 'var(--up-color)' : 'var(--down-color)'}">${o.side === 'buy' ? '买入' : '卖出'}</td>
|
||||
<td style="padding: 10px 8px;">${parseFloat(o.price).toLocaleString()}</td>
|
||||
<td style="padding: 10px 8px;">${parseFloat(o.amount).toFixed(6)}</td>
|
||||
<td style="padding: 10px 8px;">待成交</td>
|
||||
<td style="padding: 10px 8px;">${statusText}</td>
|
||||
<td style="padding: 10px 8px; text-align: right;">
|
||||
<button onclick="cancelOrder(${o.id})" style="background: none; border: 1px solid var(--down-color); color: var(--down-color); padding: 2px 8px; border-radius: 4px; cursor: pointer;">取消</button>
|
||||
${o.status === 'open' ? `<button onclick="cancelOrder(${o.id})" style="background: none; border: 1px solid var(--down-color); color: var(--down-color); padding: 2px 8px; border-radius: 4px; cursor: pointer;">取消</button>` : '--'}
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
@ -760,9 +594,46 @@ if ($user_id) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAssetsList() {
|
||||
await fetchAssets();
|
||||
const tbody = document.getElementById('orders-tbody');
|
||||
const thead = document.getElementById('table-header');
|
||||
|
||||
thead.innerHTML = `
|
||||
<th style="padding: 8px;">资产</th>
|
||||
<th style="padding: 8px;">可用</th>
|
||||
<th style="padding: 8px;">冻结</th>
|
||||
<th style="padding: 8px;">折合(USDT)</th>
|
||||
<th style="padding: 8px; text-align: right;">操作</th>
|
||||
`;
|
||||
|
||||
let html = '';
|
||||
const assets = Object.entries(userAssets).filter(([sym, amt]) => amt > 0 || sym === 'USDT');
|
||||
assets.forEach(([sym, amt]) => {
|
||||
let price = 1;
|
||||
if (sym !== 'USDT') {
|
||||
const pair = sym + 'USDT';
|
||||
price = marketData[pair] ? parseFloat(marketData[pair].c) : 0;
|
||||
}
|
||||
html += `
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 10px 8px; font-weight: bold;">${sym}</td>
|
||||
<td style="padding: 10px 8px;">${amt.toFixed(sym === 'USDT' ? 2 : 6)}</td>
|
||||
<td style="padding: 10px 8px;">0.00</td>
|
||||
<td style="padding: 10px 8px;">${(amt * price).toFixed(2)}</td>
|
||||
<td style="padding: 10px 8px; text-align: right;">
|
||||
<button onclick="switchPair('${sym}USDT')" style="background: none; border: 1px solid var(--accent-color); color: var(--accent-color); padding: 2px 8px; border-radius: 4px; cursor: pointer;">去交易</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
tbody.innerHTML = html || '<tr><td colspan="5" style="text-align: center; padding: 40px; color: var(--text-secondary);">暂无资产</td></tr>';
|
||||
}
|
||||
|
||||
function switchTab(btn, tab) {
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
activeTab = tab;
|
||||
fetchOrders();
|
||||
}
|
||||
|
||||
@ -773,12 +644,19 @@ if ($user_id) {
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({order_id: id})
|
||||
});
|
||||
if ((await resp.json()).success) fetchOrders();
|
||||
if ((await resp.json()).success) {
|
||||
fetchAssets();
|
||||
fetchOrders();
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('market-search').addEventListener('input', renderPairs);
|
||||
fetchAssets();
|
||||
fetchOrders();
|
||||
setInterval(fetchOrders, 5000);
|
||||
setInterval(() => {
|
||||
if (activeTab === 'assets') fetchAssetsList();
|
||||
else fetchOrders();
|
||||
}, 5000);
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user