Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db6c439a10 | ||
|
|
48c3bc0ba6 | ||
|
|
752e63d5fc | ||
|
|
f1fc7be962 | ||
|
|
0d9e8b5e50 | ||
|
|
eb60696dd9 | ||
|
|
65d8cd957f | ||
|
|
6a5fce6db2 |
377
admin.php
Normal file
377
admin.php
Normal file
@ -0,0 +1,377 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
|
||||
if (!isset($_SESSION['admin_id'])) {
|
||||
header("Location: admin_login.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_GET['action'] ?? 'dashboard';
|
||||
|
||||
// Handle Post Actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['update_balance'])) {
|
||||
$stmt = db()->prepare("UPDATE accounts SET balance = ? WHERE id = ?");
|
||||
$stmt->execute([$_POST['balance'], $_POST['account_id']]);
|
||||
$msg = "余额更新成功";
|
||||
}
|
||||
if (isset($_POST['update_win_loss'])) {
|
||||
$stmt = db()->prepare("UPDATE accounts SET win_loss_control = ? WHERE id = ?");
|
||||
$stmt->execute([$_POST['win_loss_control'], $_POST['account_id']]);
|
||||
$msg = "输赢控制已更新";
|
||||
}
|
||||
if (isset($_POST['update_kyc'])) {
|
||||
$stmt = db()->prepare("UPDATE accounts SET kyc_status = ? WHERE id = ?");
|
||||
$stmt->execute([$_POST['kyc_status'], $_POST['account_id']]);
|
||||
$msg = "认证状态已更新";
|
||||
}
|
||||
if (isset($_POST['approve_deposit'])) {
|
||||
db()->beginTransaction();
|
||||
$stmt = db()->prepare("SELECT * FROM transactions WHERE id = ? AND status = 'pending' AND transaction_type = 'deposit'");
|
||||
$stmt->execute([$_POST['transaction_id']]);
|
||||
$trx = $stmt->fetch();
|
||||
if ($trx) {
|
||||
db()->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?")->execute([$trx['amount'], $trx['account_id']]);
|
||||
db()->prepare("UPDATE transactions SET status = 'completed' WHERE id = ?")->execute([$trx['id']]);
|
||||
$msg = "充值已批准";
|
||||
}
|
||||
db()->commit();
|
||||
}
|
||||
if (isset($_POST['approve_withdraw'])) {
|
||||
db()->beginTransaction();
|
||||
$stmt = db()->prepare("SELECT * FROM transactions WHERE id = ? AND status = 'pending' AND transaction_type = 'withdraw'");
|
||||
$stmt->execute([$_POST['transaction_id']]);
|
||||
$trx = $stmt->fetch();
|
||||
if ($trx) {
|
||||
db()->prepare("UPDATE accounts SET frozen_balance = frozen_balance - ? WHERE id = ?")->execute([$trx['amount'], $trx['account_id']]);
|
||||
db()->prepare("UPDATE transactions SET status = 'completed' WHERE id = ?")->execute([$trx['id']]);
|
||||
$msg = "提现已批准";
|
||||
}
|
||||
db()->commit();
|
||||
}
|
||||
if (isset($_POST['reject_transaction'])) {
|
||||
db()->beginTransaction();
|
||||
$stmt = db()->prepare("SELECT * FROM transactions WHERE id = ? AND status = 'pending'");
|
||||
$stmt->execute([$_POST['transaction_id']]);
|
||||
$trx = $stmt->fetch();
|
||||
if ($trx && $trx['transaction_type'] === 'withdraw') {
|
||||
// Unfreeze balance
|
||||
db()->prepare("UPDATE accounts SET balance = balance + ?, frozen_balance = frozen_balance - ? WHERE id = ?")->execute([$trx['amount'], $trx['amount'], $trx['account_id']]);
|
||||
}
|
||||
db()->prepare("UPDATE transactions SET status = 'failed' WHERE id = ?")->execute([$_POST['transaction_id']]);
|
||||
$msg = "交易已驳回";
|
||||
db()->commit();
|
||||
}
|
||||
if (isset($_POST['update_site_settings'])) {
|
||||
$stmt = db()->prepare("UPDATE site_settings SET site_name = ?, contact_email = ?, deposit_address = ? WHERE id = 1");
|
||||
$stmt->execute([$_POST['site_name'], $_POST['contact_email'], $_POST['deposit_address']]);
|
||||
$msg = "站点设置已更新";
|
||||
}
|
||||
if (isset($_POST['update_price'])) {
|
||||
$stmt = db()->prepare("UPDATE cryptocurrencies SET manual_price = ? WHERE id = ?");
|
||||
$stmt->execute([$_POST['manual_price'], $_POST['coin_id']]);
|
||||
$msg = "价格已手动调整";
|
||||
}
|
||||
}
|
||||
|
||||
$settings = get_site_settings();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理后台 - <?php echo $settings['site_name']; ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body { background: #f4f7f6; }
|
||||
.sidebar { min-height: 100vh; background: #2c3e50; color: white; }
|
||||
.sidebar a { color: #bdc3c7; text-decoration: none; padding: 10px 20px; display: block; }
|
||||
.sidebar a:hover, .sidebar a.active { background: #34495e; color: white; }
|
||||
.card { border: none; box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); margin-bottom: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<div class="col-md-2 sidebar p-0">
|
||||
<div class="p-3 text-center border-bottom border-secondary">
|
||||
<h4>管理后台</h4>
|
||||
</div>
|
||||
<a href="?action=dashboard" class="<?php echo $action === 'dashboard' ? 'active' : ''; ?>"><i class="bi bi-speedometer2 me-2"></i> 控制台</a>
|
||||
<a href="?action=users" class="<?php echo $action === 'users' ? 'active' : ''; ?>"><i class="bi bi-people me-2"></i> 用户管理</a>
|
||||
<a href="?action=transactions" class="<?php echo $action === 'transactions' ? 'active' : ''; ?>"><i class="bi bi-cash-stack me-2"></i> 充值提现</a>
|
||||
<a href="?action=orders" class="<?php echo $action === 'orders' ? 'active' : ''; ?>"><i class="bi bi-list-check me-2"></i> 交易记录</a>
|
||||
<a href="?action=market" class="<?php echo $action === 'market' ? 'active' : ''; ?>"><i class="bi bi-graph-up me-2"></i> 市场管理</a>
|
||||
<a href="?action=settings" class="<?php echo $action === 'settings' ? 'active' : ''; ?>"><i class="bi bi-gear me-2"></i> 系统设置</a>
|
||||
<a href="logout.php" class="mt-5 text-danger"><i class="bi bi-box-arrow-right me-2"></i> 退出登录</a>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="col-md-10 p-4">
|
||||
<?php if (isset($msg)): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?php echo $msg; ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'dashboard'): ?>
|
||||
<h2 class="mb-4">控制台概览</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-primary text-white p-3">
|
||||
<h6>总用户</h6>
|
||||
<h3><?php echo db()->query("SELECT COUNT(*) FROM accounts")->fetchColumn(); ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-success text-white p-3">
|
||||
<h6>待处理充值</h6>
|
||||
<h3><?php echo db()->query("SELECT COUNT(*) FROM transactions WHERE transaction_type='deposit' AND status='pending'")->fetchColumn(); ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-warning text-dark p-3">
|
||||
<h6>待处理提现</h6>
|
||||
<h3><?php echo db()->query("SELECT COUNT(*) FROM transactions WHERE transaction_type='withdraw' AND status='pending'")->fetchColumn(); ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-info text-white p-3">
|
||||
<h6>今日订单</h6>
|
||||
<h3><?php echo db()->query("SELECT COUNT(*) FROM orders WHERE DATE(created_at) = CURRENT_DATE")->fetchColumn(); ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'users'): ?>
|
||||
<h2 class="mb-4">用户管理</h2>
|
||||
<div class="card p-3">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>用户名</th>
|
||||
<th>余额 (USDT)</th>
|
||||
<th>认证状态</th>
|
||||
<th>输赢控制</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$users = db()->query("SELECT * FROM accounts ORDER BY id DESC")->fetchAll();
|
||||
foreach ($users as $u):
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $u['id']; ?></td>
|
||||
<td><?php echo $u['username']; ?></td>
|
||||
<td><?php echo number_format($u['balance'], 2); ?></td>
|
||||
<td>
|
||||
<span class="badge bg-<?php echo $u['kyc_status'] === 'VERIFIED' ? 'success' : ($u['kyc_status'] === 'PENDING' ? 'warning' : 'secondary'); ?>">
|
||||
<?php echo $u['kyc_status']; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-<?php echo $u['win_loss_control'] == 1 ? 'success' : ($u['win_loss_control'] == -1 ? 'danger' : 'secondary'); ?>">
|
||||
<?php echo $u['win_loss_control'] == 1 ? '必赢' : ($u['win_loss_control'] == -1 ? '必输' : '正常'); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editUser<?php echo $u['id']; ?>">编辑</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="editUser<?php echo $u['id']; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">编辑用户: <?php echo $u['username']; ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST" class="mb-3">
|
||||
<input type="hidden" name="account_id" value="<?php echo $u['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">调整余额</label>
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" name="balance" class="form-control" value="<?php echo $u['balance']; ?>">
|
||||
<button class="btn btn-primary" name="update_balance">更新余额</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form method="POST" class="mb-3">
|
||||
<input type="hidden" name="account_id" value="<?php echo $u['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">输赢控制</label>
|
||||
<select name="win_loss_control" class="form-select mb-2">
|
||||
<option value="0" <?php echo $u['win_loss_control'] == 0 ? 'selected' : ''; ?>>正常 (随机)</option>
|
||||
<option value="1" <?php echo $u['win_loss_control'] == 1 ? 'selected' : ''; ?>>必赢 (Always Win)</option>
|
||||
<option value="-1" <?php echo $u['win_loss_control'] == -1 ? 'selected' : ''; ?>>必输 (Always Loss)</option>
|
||||
</select>
|
||||
<button class="btn btn-warning w-100" name="update_win_loss">应用控制</button>
|
||||
</div>
|
||||
</form>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="account_id" value="<?php echo $u['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">认证状态</label>
|
||||
<select name="kyc_status" class="form-select mb-2">
|
||||
<option value="UNVERIFIED" <?php echo $u['kyc_status'] === 'UNVERIFIED' ? 'selected' : ''; ?>>未认证</option>
|
||||
<option value="PENDING" <?php echo $u['kyc_status'] === 'PENDING' ? 'selected' : ''; ?>>待审核</option>
|
||||
<option value="VERIFIED" <?php echo $u['kyc_status'] === 'VERIFIED' ? 'selected' : ''; ?>>已认证</option>
|
||||
</select>
|
||||
<button class="btn btn-info w-100 text-white" name="update_kyc">更新状态</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'transactions'): ?>
|
||||
<h2 class="mb-4">充值提现审核</h2>
|
||||
<div class="card p-3">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>用户</th>
|
||||
<th>类型</th>
|
||||
<th>金额</th>
|
||||
<th>哈希/地址</th>
|
||||
<th>状态</th>
|
||||
<th>日期</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$trxs = db()->query("SELECT t.*, a.username FROM transactions t JOIN accounts a ON t.account_id = a.id ORDER BY t.id DESC")->fetchAll();
|
||||
foreach ($trxs as $t):
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $t['id']; ?></td>
|
||||
<td><?php echo $t['username']; ?></td>
|
||||
<td><span class="badge bg-<?php echo $t['transaction_type'] === 'deposit' ? 'primary' : 'warning'; ?>"><?php echo strtoupper($t['transaction_type']); ?></span></td>
|
||||
<td><?php echo $t['amount']; ?></td>
|
||||
<td><small class="text-truncate" style="max-width: 150px; display: inline-block;"><?php echo $t['tx_hash']; ?></small></td>
|
||||
<td>
|
||||
<span class="badge bg-<?php echo $t['status'] === 'completed' ? 'success' : ($t['status'] === 'pending' ? 'info' : 'danger'); ?>">
|
||||
<?php echo strtoupper($t['status']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo $t['timestamp']; ?></td>
|
||||
<td>
|
||||
<?php if ($t['status'] === 'pending'): ?>
|
||||
<form method="POST" class="d-inline">
|
||||
<input type="hidden" name="transaction_id" value="<?php echo $t['id']; ?>">
|
||||
<button class="btn btn-sm btn-success" name="<?php echo $t['transaction_type'] === 'deposit' ? 'approve_deposit' : 'approve_withdraw'; ?>">批准</button>
|
||||
<button class="btn btn-sm btn-danger" name="reject_transaction">拒绝</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'orders'): ?>
|
||||
<h2 class="mb-4">所有交易记录</h2>
|
||||
<div class="card p-3">
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户</th>
|
||||
<th>币种</th>
|
||||
<th>类型</th>
|
||||
<th>方向</th>
|
||||
<th>价格</th>
|
||||
<th>数量</th>
|
||||
<th>时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$orders = db()->query("SELECT o.*, a.username FROM orders o JOIN accounts a ON o.account_id = a.id ORDER BY o.id DESC LIMIT 50")->fetchAll();
|
||||
foreach ($orders as $o):
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $o['username']; ?></td>
|
||||
<td><?php echo $o['symbol']; ?></td>
|
||||
<td><?php echo $o['trade_type']; ?></td>
|
||||
<td class="text-<?php echo $o['side'] === 'BUY' ? 'success' : 'danger'; ?>"><?php echo $o['side']; ?></td>
|
||||
<td><?php echo $o['price']; ?></td>
|
||||
<td><?php echo $o['amount']; ?></td>
|
||||
<td><?php echo $o['created_at']; ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'market'): ?>
|
||||
<h2 class="mb-4">市场与币种管理</h2>
|
||||
<div class="row">
|
||||
<?php
|
||||
$coins = db()->query("SELECT * FROM cryptocurrencies")->fetchAll();
|
||||
foreach ($coins as $c):
|
||||
?>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3">
|
||||
<h5><?php echo $c['name']; ?> (<?php echo $c['symbol']; ?>)</h5>
|
||||
<p class="mb-1">当前市场价: <?php echo $c['current_price']; ?></p>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="coin_id" value="<?php echo $c['id']; ?>">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small text-muted">手动价格 (0为跟随市场)</label>
|
||||
<input type="number" step="0.000001" name="manual_price" class="form-control" value="<?php echo $c['manual_price']; ?>">
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary w-100" name="update_price">设置手动价格</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'settings'): ?>
|
||||
<h2 class="mb-4">系统全局设置</h2>
|
||||
<div class="card p-4 shadow-sm">
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">站点名称</label>
|
||||
<input type="text" name="site_name" class="form-control" value="<?php echo $settings['site_name']; ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">联系邮箱</label>
|
||||
<input type="email" name="contact_email" class="form-control" value="<?php echo $settings['contact_email']; ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">USDT 充值地址</label>
|
||||
<input type="text" name="deposit_address" class="form-control" value="<?php echo $settings['deposit_address']; ?>">
|
||||
<div class="form-text">用户在充值页面看到的钱包地址</div>
|
||||
</div>
|
||||
<button type="submit" name="update_site_settings" class="btn btn-primary px-5">保存所有设置</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
55
admin_login.php
Normal file
55
admin_login.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
|
||||
if (isset($_SESSION['admin_id'])) {
|
||||
header("Location: admin.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = "";
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM admins WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$admin = $stmt->fetch();
|
||||
|
||||
if ($admin && password_verify($password, $admin['password'])) {
|
||||
$_SESSION['admin_id'] = $admin['id'];
|
||||
header("Location: admin.php");
|
||||
exit;
|
||||
} else {
|
||||
$error = "用户名或密码错误";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>管理员登录</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background: #121212; color: white; display: flex; align-items: center; justify-content: center; height: 100vh; }
|
||||
.login-card { background: #1e1e1e; padding: 2rem; border-radius: 1rem; width: 100%; max-width: 400px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<h3 class="text-center mb-4 text-warning">后台管理系统</h3>
|
||||
<?php if($error): ?><div class="alert alert-danger"><?php echo $error; ?></div><?php endif; ?>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">用户名</label>
|
||||
<input type="text" name="username" class="form-control bg-dark text-white border-secondary" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">密码</label>
|
||||
<input type="password" name="password" class="form-control bg-dark text-white border-secondary" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-2">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
364
api.php
Normal file
364
api.php
Normal file
@ -0,0 +1,364 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
|
||||
/**
|
||||
* Fetch prices from Binance with caching and high precision
|
||||
*/
|
||||
function get_real_prices() {
|
||||
$cache_file = __DIR__ . '/db/price_cache.json';
|
||||
$cache_time = 2; // Cache for 2 seconds
|
||||
|
||||
// Check cache
|
||||
if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) {
|
||||
$cache_data = json_decode(file_get_contents($cache_file), true);
|
||||
if (!empty($cache_data)) return $cache_data;
|
||||
}
|
||||
|
||||
// Fetch active coins from DB
|
||||
try {
|
||||
$stmt = db()->query("SELECT symbol FROM cryptocurrencies WHERE is_active = 1");
|
||||
$symbols = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
} catch (Exception $e) {
|
||||
$symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'DOGEUSDT'];
|
||||
}
|
||||
|
||||
if (empty($symbols)) $symbols = ['BTCUSDT'];
|
||||
|
||||
// Use Binance 24hr ticker for comprehensive data
|
||||
$symbols_json = json_encode($symbols);
|
||||
$url = "https://api.binance.com/api/v3/ticker/24hr?symbols=" . urlencode($symbols_json);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
|
||||
// Disable SSL verification if needed for some environments, but prefer keeping it
|
||||
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$prices = [];
|
||||
|
||||
if ($http_code == 200 && $response) {
|
||||
$data = json_decode($response, true);
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $item) {
|
||||
if (isset($item['symbol'])) {
|
||||
$prices[$item['symbol']] = [
|
||||
'price' => $item['lastPrice'],
|
||||
'change' => $item['priceChangePercent'],
|
||||
'high' => $item['highPrice'],
|
||||
'low' => $item['lowPrice'],
|
||||
'volume' => $item['quoteVolume'],
|
||||
'ts' => time()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: If 24hr fails, try simpler price-only endpoint
|
||||
if (empty($prices)) {
|
||||
$url_simple = "https://api.binance.com/api/v3/ticker/price";
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url_simple);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
$resp_simple = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($resp_simple) {
|
||||
$data_simple = json_decode($resp_simple, true);
|
||||
if (is_array($data_simple)) {
|
||||
foreach ($data_simple as $item) {
|
||||
if (in_array($item['symbol'], $symbols)) {
|
||||
$prices[$item['symbol']] = [
|
||||
'price' => $item['price'],
|
||||
'change' => '0.00',
|
||||
'high' => $item['price'],
|
||||
'low' => $item['price'],
|
||||
'volume' => '0',
|
||||
'ts' => time()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($prices)) {
|
||||
// Only update file if we have new data
|
||||
@file_put_contents($cache_file, json_encode($prices));
|
||||
} else if (file_exists($cache_file)) {
|
||||
// Last resort: return expired cache
|
||||
return json_decode(file_get_contents($cache_file), true);
|
||||
}
|
||||
|
||||
return $prices;
|
||||
}
|
||||
|
||||
if ($action === 'market_data') {
|
||||
$real_prices = get_real_prices();
|
||||
try {
|
||||
$stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1 ORDER BY id ASC");
|
||||
$coins = $stmt->fetchAll();
|
||||
} catch (Exception $e) {
|
||||
$coins = [];
|
||||
}
|
||||
|
||||
$updated_coins = [];
|
||||
foreach ($coins as $coin) {
|
||||
$symbol = $coin['symbol'];
|
||||
|
||||
if (isset($real_prices[$symbol])) {
|
||||
$coin['price'] = (string)$real_prices[$symbol]['price']; // Keep as string for precision
|
||||
$coin['change'] = (float)$real_prices[$symbol]['change'];
|
||||
$coin['high'] = (string)$real_prices[$symbol]['high'];
|
||||
$coin['low'] = (string)$real_prices[$symbol]['low'];
|
||||
$coin['volume'] = (float)$real_prices[$symbol]['volume'];
|
||||
|
||||
if ($coin['manual_price'] > 0) {
|
||||
$coin['price'] = (string)$coin['manual_price'];
|
||||
}
|
||||
|
||||
// Sync to DB occasionally (logic can be improved, but this is current)
|
||||
$upd = db()->prepare("UPDATE cryptocurrencies SET current_price = ?, change_24h = ? WHERE id = ?");
|
||||
$upd->execute([$coin['price'], $coin['change'], $coin['id']]);
|
||||
} else {
|
||||
$coin['price'] = (string)$coin['current_price'];
|
||||
$coin['change'] = (float)$coin['change_24h'];
|
||||
$coin['high'] = (string)($coin['current_price'] * 1.01);
|
||||
$coin['low'] = (string)($coin['current_price'] * 0.99);
|
||||
$coin['volume'] = 0;
|
||||
}
|
||||
$updated_coins[] = $coin;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($updated_coins);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'submit_order') {
|
||||
check_auth();
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$data) {
|
||||
echo json_encode(['status' => 'error', 'message' => '无效请求数据']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
|
||||
$symbol = $data['symbol'] ?? 'BTCUSDT';
|
||||
$side = $data['side'] ?? 'BUY';
|
||||
$trade_type = strtoupper($data['trade_type'] ?? 'SPOT');
|
||||
$amount = (float)($data['amount'] ?? 0);
|
||||
$leverage = (int)($data['leverage'] ?? 1);
|
||||
|
||||
if ($amount <= 0) {
|
||||
echo json_encode(['status' => 'error', 'message' => '请输入有效数量']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$real_prices = get_real_prices();
|
||||
$stmt = db()->prepare("SELECT * FROM cryptocurrencies WHERE symbol = ?");
|
||||
$stmt->execute([$symbol]);
|
||||
$coin = $stmt->fetch();
|
||||
|
||||
if (!$coin) {
|
||||
echo json_encode(['status' => 'error', 'message' => '不支持该币种']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($coin['manual_price'] > 0) {
|
||||
$current_price = (float)$coin['manual_price'];
|
||||
} elseif (isset($real_prices[$symbol])) {
|
||||
$current_price = (float)$real_prices[$symbol]['price'];
|
||||
} else {
|
||||
$current_price = (float)$coin['current_price'];
|
||||
}
|
||||
|
||||
if ($current_price <= 0) {
|
||||
echo json_encode(['status' => 'error', 'message' => '价格获取失败,请重试']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$db->beginTransaction();
|
||||
|
||||
if ($trade_type === 'SPOT') {
|
||||
if ($side === 'BUY') {
|
||||
$total_cost = $amount * $current_price;
|
||||
if ($account['balance'] < $total_cost) {
|
||||
throw new Exception('余额不足 (需要 ' . number_format($total_cost, 2) . ' USDT)');
|
||||
}
|
||||
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
|
||||
$stmt->execute([$total_cost, $account['id']]);
|
||||
|
||||
$currency = str_replace('USDT', '', $symbol);
|
||||
$stmt = $db->prepare("INSERT INTO assets (account_id, currency, balance) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE balance = balance + ?");
|
||||
$stmt->execute([$account['id'], $currency, $amount, $amount]);
|
||||
|
||||
} else { // SELL
|
||||
$currency = str_replace('USDT', '', $symbol);
|
||||
$stmt = $db->prepare("SELECT balance FROM assets WHERE account_id = ? AND currency = ?");
|
||||
$stmt->execute([$account['id'], $currency]);
|
||||
$asset = $stmt->fetch();
|
||||
|
||||
if (!$asset || $asset['balance'] < $amount) {
|
||||
throw new Exception('资产余额不足');
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("UPDATE assets SET balance = balance - ? WHERE account_id = ? AND currency = ?");
|
||||
$stmt->execute([$amount, $account['id'], $currency]);
|
||||
|
||||
$total_gain = $amount * $current_price;
|
||||
$stmt = $db->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
|
||||
$stmt->execute([$total_gain, $account['id']]);
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO orders (account_id, symbol, trade_type, side, order_type, price, amount, total_usdt, status) VALUES (?, ?, 'SPOT', ?, 'MARKET', ?, ?, ?, 'FILLED')");
|
||||
$stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $amount * $current_price]);
|
||||
|
||||
} else if ($trade_type === 'CONTRACT') {
|
||||
$contract_value = 100;
|
||||
$total_value = $amount * $contract_value;
|
||||
$margin = $total_value / $leverage;
|
||||
|
||||
if ($account['balance'] < $margin) {
|
||||
throw new Exception('保证金不足 (需要 ' . number_format($margin, 2) . ' USDT)');
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
|
||||
$stmt->execute([$margin, $account['id']]);
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO positions (account_id, symbol, side, leverage, entry_price, lots, margin) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$account['id'], $symbol, ($side === 'BUY' ? 'LONG' : 'SHORT'), $leverage, $current_price, $amount, $margin]);
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO orders (account_id, symbol, trade_type, side, order_type, price, amount, leverage, status) VALUES (?, ?, 'CONTRACT', ?, 'MARKET', ?, ?, ?, 'FILLED')");
|
||||
$stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $leverage]);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
echo json_encode(['status' => 'success', 'message' => '交易成功']);
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'positions') {
|
||||
check_auth();
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM positions WHERE account_id = ? AND is_active = 1");
|
||||
$stmt->execute([$account['id']]);
|
||||
$positions = $stmt->fetchAll();
|
||||
|
||||
$real_prices = get_real_prices();
|
||||
|
||||
foreach ($positions as &$pos) {
|
||||
$symbol = $pos['symbol'];
|
||||
$stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?");
|
||||
$stmt->execute([$symbol]);
|
||||
$coin = $stmt->fetch();
|
||||
|
||||
if ($coin && $coin['manual_price'] > 0) {
|
||||
$current_price = (float)$coin['manual_price'];
|
||||
} elseif (isset($real_prices[$symbol])) {
|
||||
$current_price = (float)$real_prices[$symbol]['price'];
|
||||
} else {
|
||||
$current_price = (float)$pos['entry_price'];
|
||||
}
|
||||
|
||||
$pos['current_price'] = $current_price;
|
||||
|
||||
if ($pos['side'] === 'LONG') {
|
||||
$pos['pnl'] = (($current_price - $pos['entry_price']) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
|
||||
} else {
|
||||
$pos['pnl'] = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
|
||||
}
|
||||
|
||||
if ($account['win_loss_control'] == 1 && $pos['pnl'] < 0) {
|
||||
$pos['pnl'] = abs($pos['pnl']) * 0.2;
|
||||
} else if ($account['win_loss_control'] == -1 && $pos['pnl'] > 0) {
|
||||
$pos['pnl'] = -abs($pos['pnl']) * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($positions);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'close_position') {
|
||||
check_auth();
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$pos_id = $data['id'] ?? 0;
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$db->beginTransaction();
|
||||
|
||||
$stmt = $db->prepare("SELECT * FROM positions WHERE id = ? AND account_id = ? AND is_active = 1");
|
||||
$stmt->execute([$pos_id, $account['id']]);
|
||||
$pos = $stmt->fetch();
|
||||
|
||||
if (!$pos) throw new Exception('仓位不存在');
|
||||
|
||||
$symbol = $pos['symbol'];
|
||||
$real_prices = get_real_prices();
|
||||
|
||||
$stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?");
|
||||
$stmt->execute([$symbol]);
|
||||
$coin = $stmt->fetch();
|
||||
|
||||
if ($coin && $coin['manual_price'] > 0) {
|
||||
$current_price = (float)$coin['manual_price'];
|
||||
} elseif (isset($real_prices[$symbol])) {
|
||||
$current_price = (float)$real_prices[$symbol]['price'];
|
||||
} else {
|
||||
$current_price = (float)$pos['entry_price'];
|
||||
}
|
||||
|
||||
if ($pos['side'] === 'LONG') {
|
||||
$pnl = (($current_price - $pos['entry_price']) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
|
||||
} else {
|
||||
$pnl = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
|
||||
}
|
||||
|
||||
if ($account['win_loss_control'] == 1) {
|
||||
if ($pnl < 0) $pnl = abs($pnl) * 0.1;
|
||||
} else if ($account['win_loss_control'] == -1) {
|
||||
if ($pnl > 0) $pnl = -abs($pnl) * 1.2;
|
||||
}
|
||||
|
||||
$payout = $pos['margin'] + $pnl;
|
||||
if ($payout < 0) $payout = 0;
|
||||
|
||||
$stmt = $db->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
|
||||
$stmt->execute([$payout, $account['id']]);
|
||||
|
||||
$stmt = $db->prepare("UPDATE positions SET is_active = 0 WHERE id = ?");
|
||||
$stmt->execute([$pos_id]);
|
||||
|
||||
$db->commit();
|
||||
echo json_encode(['status' => 'success', 'message' => '平仓成功']);
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
BIN
assets/pasted-20260206-064839-95c248c7.png
Normal file
BIN
assets/pasted-20260206-064839-95c248c7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 558 KiB |
BIN
assets/pasted-20260207-062921-01d39dbe.png
Normal file
BIN
assets/pasted-20260207-062921-01d39dbe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
28
config.php
Normal file
28
config.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
// PHP Core Config
|
||||
session_start();
|
||||
|
||||
// Include the existing database configuration
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
// Helper: Check if user is logged in
|
||||
function check_auth() {
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Get user account
|
||||
function get_account($user_id) {
|
||||
$stmt = db()->prepare("SELECT * FROM accounts WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
// Helper: Get site settings
|
||||
function get_site_settings() {
|
||||
$stmt = db()->query("SELECT * FROM site_settings LIMIT 1");
|
||||
return $stmt->fetch();
|
||||
}
|
||||
?>
|
||||
Binary file not shown.
@ -1,52 +1,16 @@
|
||||
"""
|
||||
Django settings for config project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.2.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
load_dotenv(BASE_DIR.parent / ".env")
|
||||
|
||||
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "change-me")
|
||||
DEBUG = os.getenv("DJANGO_DEBUG", "true").lower() == "true"
|
||||
SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-default-key')
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
"127.0.0.1",
|
||||
"localhost",
|
||||
os.getenv("HOST_FQDN", ""),
|
||||
]
|
||||
DEBUG = True
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
origin for origin in [
|
||||
os.getenv("HOST_FQDN", ""),
|
||||
os.getenv("CSRF_TRUSTED_ORIGIN", "")
|
||||
] if origin
|
||||
]
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
f"https://{host}" if not host.startswith(("http://", "https://")) else host
|
||||
for host in CSRF_TRUSTED_ORIGINS
|
||||
]
|
||||
|
||||
# Cookies must always be HTTPS-only; SameSite=Lax keeps CSRF working behind the proxy.
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_SAMESITE = "None"
|
||||
CSRF_COOKIE_SAMESITE = "None"
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||
|
||||
# Application definition
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
@ -65,25 +29,22 @@ MIDDLEWARE = [
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Disable X-Frame-Options middleware to allow Flatlogic preview iframes.
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
X_FRAME_OPTIONS = 'ALLOWALL'
|
||||
|
||||
ROOT_URLCONF = 'config.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
# IMPORTANT: do not remove – injects PROJECT_DESCRIPTION/PROJECT_IMAGE_URL and cache-busting timestamp
|
||||
'core.context_processors.project_context',
|
||||
],
|
||||
},
|
||||
@ -92,91 +53,34 @@ TEMPLATES = [
|
||||
|
||||
WSGI_APPLICATION = 'config.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': os.getenv('DB_NAME', ''),
|
||||
'USER': os.getenv('DB_USER', ''),
|
||||
'PASSWORD': os.getenv('DB_PASS', ''),
|
||||
'NAME': os.getenv('DB_NAME', 'django_db'),
|
||||
'USER': os.getenv('DB_USER', 'django_user'),
|
||||
'PASSWORD': os.getenv('DB_PASS', 'django_pass'),
|
||||
'HOST': os.getenv('DB_HOST', '127.0.0.1'),
|
||||
'PORT': os.getenv('DB_PORT', '3306'),
|
||||
'OPTIONS': {
|
||||
'charset': 'utf8mb4',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
||||
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
|
||||
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
|
||||
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
# Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS.
|
||||
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / 'static',
|
||||
BASE_DIR / 'assets',
|
||||
BASE_DIR / 'node_modules',
|
||||
]
|
||||
|
||||
# Email
|
||||
EMAIL_BACKEND = os.getenv(
|
||||
"EMAIL_BACKEND",
|
||||
"django.core.mail.backends.smtp.EmailBackend"
|
||||
)
|
||||
EMAIL_HOST = os.getenv("EMAIL_HOST", "127.0.0.1")
|
||||
EMAIL_PORT = int(os.getenv("EMAIL_PORT", "587"))
|
||||
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "")
|
||||
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "")
|
||||
EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "true").lower() == "true"
|
||||
EMAIL_USE_SSL = os.getenv("EMAIL_USE_SSL", "false").lower() == "true"
|
||||
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "no-reply@example.com")
|
||||
CONTACT_EMAIL_TO = [
|
||||
item.strip()
|
||||
for item in os.getenv("CONTACT_EMAIL_TO", DEFAULT_FROM_EMAIL).split(",")
|
||||
if item.strip()
|
||||
]
|
||||
|
||||
# When both TLS and SSL flags are enabled, prefer SSL explicitly
|
||||
if EMAIL_USE_SSL:
|
||||
EMAIL_USE_TLS = False
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
LOGOUT_REDIRECT_URL = '/'
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,34 @@
|
||||
from django.contrib import admin
|
||||
from .models import Cryptocurrency, Account, Asset, Order, Transaction, SiteSettings
|
||||
|
||||
# Register your models here.
|
||||
@admin.register(SiteSettings)
|
||||
class SiteSettingsAdmin(admin.ModelAdmin):
|
||||
list_display = ('site_name', 'customer_service_url', 'is_pinning_active')
|
||||
|
||||
@admin.register(Cryptocurrency)
|
||||
class CryptocurrencyAdmin(admin.ModelAdmin):
|
||||
list_display = ('symbol', 'name', 'current_price', 'manual_price', 'change_24h', 'is_active')
|
||||
search_fields = ('symbol', 'name')
|
||||
list_editable = ('manual_price', 'is_active')
|
||||
|
||||
@admin.register(Account)
|
||||
class AccountAdmin(admin.ModelAdmin):
|
||||
list_display = ('uid', 'user', 'balance', 'credit_score', 'kyc_status', 'win_loss_control', 'created_at')
|
||||
search_fields = ('uid', 'user__username')
|
||||
list_filter = ('kyc_status', 'account_type')
|
||||
list_editable = ('win_loss_control',)
|
||||
|
||||
@admin.register(Asset)
|
||||
class AssetAdmin(admin.ModelAdmin):
|
||||
list_display = ('account', 'currency', 'balance', 'frozen')
|
||||
list_filter = ('currency',)
|
||||
|
||||
@admin.register(Order)
|
||||
class OrderAdmin(admin.ModelAdmin):
|
||||
list_display = ('account', 'symbol', 'trade_type', 'side', 'status', 'created_at')
|
||||
list_filter = ('trade_type', 'side', 'status')
|
||||
|
||||
@admin.register(Transaction)
|
||||
class TransactionAdmin(admin.ModelAdmin):
|
||||
list_display = ('account', 'transaction_type', 'amount', 'currency', 'status', 'timestamp')
|
||||
list_filter = ('transaction_type', 'status')
|
||||
@ -1,13 +1,14 @@
|
||||
import os
|
||||
import time
|
||||
from .models import SiteSettings
|
||||
|
||||
def project_context(request):
|
||||
"""
|
||||
Adds project-specific environment variables to the template context globally.
|
||||
"""
|
||||
settings = SiteSettings.objects.first()
|
||||
if not settings:
|
||||
settings = SiteSettings.objects.create(site_name="BitCrypto")
|
||||
return {
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
# Used for cache-busting static assets
|
||||
"deployment_timestamp": int(time.time()),
|
||||
'site_settings': settings,
|
||||
'project_name': settings.site_name,
|
||||
'project_description': "全球领先的数字资产交易平台"
|
||||
}
|
||||
|
||||
# Alias for compatibility if needed
|
||||
site_settings = project_context
|
||||
0
core/management/__init__.py
Normal file
0
core/management/__init__.py
Normal file
BIN
core/management/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
core/management/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
0
core/management/commands/__init__.py
Normal file
0
core/management/commands/__init__.py
Normal file
BIN
core/management/commands/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
core/management/commands/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
core/management/commands/__pycache__/seed_crypto.cpython-311.pyc
Normal file
BIN
core/management/commands/__pycache__/seed_crypto.cpython-311.pyc
Normal file
Binary file not shown.
BIN
core/management/commands/__pycache__/seed_site.cpython-311.pyc
Normal file
BIN
core/management/commands/__pycache__/seed_site.cpython-311.pyc
Normal file
Binary file not shown.
71
core/management/commands/seed_crypto.py
Normal file
71
core/management/commands/seed_crypto.py
Normal file
@ -0,0 +1,71 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from core.models import Cryptocurrency
|
||||
from decimal import Decimal
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Seeds the database with common cryptocurrencies'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
cryptos = [
|
||||
('BTC', 'Bitcoin', 65000.00),
|
||||
('ETH', 'Ethereum', 3500.00),
|
||||
('BNB', 'BNB', 600.00),
|
||||
('SOL', 'Solana', 150.00),
|
||||
('XRP', 'XRP', 0.60),
|
||||
('ADA', 'Cardano', 0.50),
|
||||
('AVAX', 'Avalanche', 40.00),
|
||||
('DOT', 'Polkadot', 7.00),
|
||||
('DOGE', 'Dogecoin', 0.15),
|
||||
('SHIB', 'Shiba Inu', 0.000025),
|
||||
('MATIC', 'Polygon', 0.70),
|
||||
('LINK', 'Chainlink', 15.00),
|
||||
('UNI', 'Uniswap', 8.00),
|
||||
('LTC', 'Litecoin', 80.00),
|
||||
('BCH', 'Bitcoin Cash', 450.00),
|
||||
('ATOM', 'Cosmos', 10.00),
|
||||
('XLM', 'Stellar', 0.12),
|
||||
('ETC', 'Ethereum Classic', 30.00),
|
||||
('NEAR', 'NEAR Protocol', 6.00),
|
||||
('FIL', 'Filecoin', 6.00),
|
||||
('ICP', 'Internet Computer', 12.00),
|
||||
('HBAR', 'Hedera', 0.10),
|
||||
('VET', 'VeChain', 0.04),
|
||||
('ALGO', 'Algorand', 0.20),
|
||||
('GRT', 'The Graph', 0.30),
|
||||
('FTM', 'Fantom', 0.80),
|
||||
('SAND', 'The Sandbox', 0.50),
|
||||
('MANA', 'Decentraland', 0.50),
|
||||
('AAVE', 'Aave', 100.00),
|
||||
('THETA', 'Theta Network', 2.50),
|
||||
('EGLD', 'MultiversX', 45.00),
|
||||
('XTZ', 'Tezos', 1.20),
|
||||
('EOS', 'EOS', 0.80),
|
||||
('FLOW', 'Flow', 1.00),
|
||||
('CHZ', 'Chiliz', 0.15),
|
||||
('AXS', 'Axie Infinity', 8.00),
|
||||
('GALA', 'Gala', 0.05),
|
||||
('KAVA', 'Kava', 0.70),
|
||||
('ZEC', 'Zcash', 30.00),
|
||||
('DASH', 'Dash', 35.00),
|
||||
('NEO', 'NEO', 15.00),
|
||||
('IOTA', 'IOTA', 0.25),
|
||||
('KLAY', 'Klaytn', 0.20),
|
||||
('BSV', 'Bitcoin SV', 70.00),
|
||||
('MINA', 'Mina', 0.90),
|
||||
('XEC', 'eCash', 0.00005),
|
||||
('BTT', 'BitTorrent', 0.000001),
|
||||
('LUNC', 'Terra Classic', 0.0001),
|
||||
('USTC', 'TerraClassicUSD', 0.02),
|
||||
]
|
||||
|
||||
for symbol, name, price in cryptos:
|
||||
Cryptocurrency.objects.update_or_create(
|
||||
symbol=symbol,
|
||||
defaults={
|
||||
'name': name,
|
||||
'current_price': Decimal(str(price)),
|
||||
'change_24h': Decimal('0.00'),
|
||||
'is_active': True
|
||||
}
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS(f'Successfully seeded {symbol}'))
|
||||
25
core/management/commands/seed_site.py
Normal file
25
core/management/commands/seed_site.py
Normal file
@ -0,0 +1,25 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from core.models import SiteSettings, Cryptocurrency
|
||||
import decimal
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Seed initial site settings and data'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
# Site Settings
|
||||
settings, created = SiteSettings.objects.get_or_create(id=1)
|
||||
settings.site_name = "BitCrypto"
|
||||
settings.customer_service_url = "https://t.me/bitcrypto_support"
|
||||
settings.terms_content = "欢迎使用 BitCrypto。通过访问我们的平台,您同意遵守以下条款:1. 用户必须年满 18 岁。2. 您对账户的安全负全部责任。3. 加密货币交易具有高度风险..."
|
||||
settings.privacy_content = "我们重视您的隐私。BitCrypto 仅收集必要的个人信息以提供服务。我们使用先进的加密技术保护您的数据,绝不向第三方出售您的个人信息。"
|
||||
settings.save()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully seeded site settings'))
|
||||
|
||||
# Ensure BTC exists
|
||||
btc, created = Cryptocurrency.objects.get_or_create(symbol="BTC")
|
||||
btc.name = "Bitcoin"
|
||||
btc.current_price = decimal.Decimal("48000.00")
|
||||
btc.save()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully seeded cryptocurrencies'))
|
||||
71
core/migrations/0001_initial.py
Normal file
71
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,71 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-06 06:28
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Account',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||
('session_key', models.CharField(blank=True, max_length=100, null=True, unique=True)),
|
||||
('account_type', models.CharField(choices=[('SIMULATED', 'Simulated'), ('REAL', 'Real')], default='SIMULATED', max_length=20)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Ledger',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('currency', models.CharField(max_length=10)),
|
||||
('amount', models.DecimalField(decimal_places=8, max_digits=30)),
|
||||
('balance_before', models.DecimalField(decimal_places=8, max_digits=30)),
|
||||
('balance_after', models.DecimalField(decimal_places=8, max_digits=30)),
|
||||
('biz_type', models.CharField(max_length=50)),
|
||||
('reference_id', models.CharField(blank=True, max_length=100, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ledger_entries', to='core.account')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Order',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('symbol', models.CharField(default='BTC-USDT', max_length=20)),
|
||||
('side', models.CharField(choices=[('BUY', 'Buy'), ('SELL', 'Sell')], max_length=10)),
|
||||
('order_type', models.CharField(choices=[('LIMIT', 'Limit'), ('MARKET', 'Market')], max_length=10)),
|
||||
('price', models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True)),
|
||||
('amount', models.DecimalField(decimal_places=8, max_digits=30)),
|
||||
('filled_amount', models.DecimalField(decimal_places=8, default=0, max_digits=30)),
|
||||
('status', models.CharField(choices=[('LIVE', 'Live'), ('PARTIALLY_FILLED', 'Partially Filled'), ('FILLED', 'Filled'), ('CANCELED', 'Canceled')], default='LIVE', max_length=20)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='core.account')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Asset',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('currency', models.CharField(max_length=10)),
|
||||
('balance', models.DecimalField(decimal_places=8, default=0, max_digits=30)),
|
||||
('frozen', models.DecimalField(decimal_places=8, default=0, max_digits=30)),
|
||||
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='core.account')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('account', 'currency')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,151 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-06 07:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Cryptocurrency',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('symbol', models.CharField(max_length=20, unique=True, verbose_name='币种代码')),
|
||||
('name', models.CharField(max_length=100, verbose_name='币种名称')),
|
||||
('icon_url', models.URLField(blank=True, null=True, verbose_name='图标URL')),
|
||||
('current_price', models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='当前价格')),
|
||||
('manual_price', models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='插针价格 (留空则使用当前价格)')),
|
||||
('change_24h', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='24h 涨跌幅')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='是否启用')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '加密货币',
|
||||
'verbose_name_plural': '加密货币管理',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='account',
|
||||
options={'verbose_name': '账户', 'verbose_name_plural': '账户管理'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='asset',
|
||||
options={'verbose_name': '资产', 'verbose_name_plural': '资产管理'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ledger',
|
||||
options={'verbose_name': '账单', 'verbose_name_plural': '账单管理'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='order',
|
||||
options={'verbose_name': '订单', 'verbose_name_plural': '订单管理'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='win_loss_control',
|
||||
field=models.IntegerField(default=0, help_text='控制胜率: -100 (必输) 到 100 (必赢), 0 为随机', verbose_name='输赢控制'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='account_type',
|
||||
field=models.CharField(choices=[('SIMULATED', '模拟账户'), ('REAL', '真实账户')], default='SIMULATED', max_length=20, verbose_name='账户类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='asset',
|
||||
name='balance',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='余额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='asset',
|
||||
name='currency',
|
||||
field=models.CharField(max_length=10, verbose_name='币种'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='asset',
|
||||
name='frozen',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='冻结'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='金额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='balance_after',
|
||||
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='变动后余额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='balance_before',
|
||||
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='变动前余额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='biz_type',
|
||||
field=models.CharField(max_length=50, verbose_name='业务类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='currency',
|
||||
field=models.CharField(max_length=10, verbose_name='币种'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ledger',
|
||||
name='reference_id',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='引用ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='数量'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='filled_amount',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='已成交数量'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='order_type',
|
||||
field=models.CharField(choices=[('LIMIT', '限价'), ('MARKET', '市价')], max_length=10, verbose_name='类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='price',
|
||||
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='价格'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='side',
|
||||
field=models.CharField(choices=[('BUY', '买入'), ('SELL', '卖出')], max_length=10, verbose_name='方向'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('LIVE', '进行中'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销')], default='LIVE', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='symbol',
|
||||
field=models.CharField(default='BTC-USDT', max_length=20, verbose_name='交易对'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,92 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-06 07:19
|
||||
|
||||
import core.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_cryptocurrency_alter_account_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='credit_score',
|
||||
field=models.IntegerField(default=80, verbose_name='信用分'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='kyc_status',
|
||||
field=models.CharField(choices=[('UNVERIFIED', '未认证'), ('PENDING', '审核中'), ('VERIFIED', '已认证'), ('REJECTED', '已拒绝')], default='UNVERIFIED', max_length=20, verbose_name='实名认证状态'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='language',
|
||||
field=models.CharField(default='en', max_length=10, verbose_name='语言偏好'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='close_price',
|
||||
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='平仓价格'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='entry_price',
|
||||
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='入场价格'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='leverage',
|
||||
field=models.IntegerField(default=1, verbose_name='杠杆倍数'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='profit_loss',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='盈亏'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='trade_type',
|
||||
field=models.CharField(choices=[('SPOT', '现货'), ('CONTRACT', '合约')], default='SPOT', max_length=20, verbose_name='交易类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='uid',
|
||||
field=models.CharField(default=core.models.generate_uid, max_length=6, unique=True, verbose_name='UID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='price',
|
||||
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='委托价格'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='side',
|
||||
field=models.CharField(choices=[('BUY', '买入/做多'), ('SELL', '卖出/做空')], max_length=10, verbose_name='方向'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('LIVE', '进行中'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销'), ('CLOSED', '已平仓')], default='LIVE', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Transaction',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('tx_type', models.CharField(choices=[('DEPOSIT', '充值'), ('WITHDRAW', '提现')], max_length=20, verbose_name='类型')),
|
||||
('currency', models.CharField(default='USDT', max_length=10, verbose_name='币种')),
|
||||
('amount', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='金额')),
|
||||
('address', models.CharField(blank=True, max_length=255, null=True, verbose_name='地址/流水')),
|
||||
('status', models.CharField(choices=[('PENDING', '待处理'), ('SUCCESS', '成功'), ('FAILED', '失败')], default='PENDING', max_length=20, verbose_name='状态')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='时间')),
|
||||
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to='core.account')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '充提记录',
|
||||
'verbose_name_plural': '充提管理',
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,59 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-06 07:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0003_account_credit_score_account_kyc_status_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='transaction',
|
||||
old_name='created_at',
|
||||
new_name='timestamp',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='transaction',
|
||||
name='address',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='transaction',
|
||||
name='tx_type',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='balance',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='可用余额 (USDT)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='transaction',
|
||||
name='transaction_type',
|
||||
field=models.CharField(choices=[('deposit', '充值'), ('withdraw', '提现')], default='deposit', max_length=20, verbose_name='类型'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='transaction',
|
||||
name='tx_hash',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='交易哈希'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='language',
|
||||
field=models.CharField(default='zh-hans', max_length=10, verbose_name='语言偏好'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='transaction',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='金额'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='transaction',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('pending', '待处理'), ('completed', '成功'), ('failed', '失败')], default='pending', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Ledger',
|
||||
),
|
||||
]
|
||||
28
core/migrations/0005_sitesettings.py
Normal file
28
core/migrations/0005_sitesettings.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-06 09:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0004_rename_created_at_transaction_timestamp_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SiteSettings',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('site_name', models.CharField(default='BitCrypto', max_length=100, verbose_name='网站名称')),
|
||||
('customer_service_url', models.URLField(blank=True, null=True, verbose_name='在线客服链接')),
|
||||
('terms_content', models.TextField(blank=True, verbose_name='服务条款内容')),
|
||||
('privacy_content', models.TextField(blank=True, verbose_name='隐私政策内容')),
|
||||
('is_pinning_active', models.BooleanField(default=False, help_text='开启后,所有币种将向手动设置的价格靠拢', verbose_name='全局插针激活')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '系统设置',
|
||||
'verbose_name_plural': '系统设置',
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,69 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-06 11:38
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0005_sitesettings'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='order',
|
||||
name='close_price',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='order',
|
||||
name='entry_price',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='order',
|
||||
name='filled_amount',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='order',
|
||||
name='profit_loss',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='frozen_balance',
|
||||
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='冻结余额 (USDT)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='total_usdt',
|
||||
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='成交额 (USDT)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='数量/手数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('PENDING', '等待成交'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销')], default='PENDING', max_length=20, verbose_name='状态'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Position',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('symbol', models.CharField(max_length=20, verbose_name='交易对')),
|
||||
('side', models.CharField(choices=[('LONG', '做多'), ('SHORT', '做空')], max_length=10, verbose_name='方向')),
|
||||
('leverage', models.IntegerField(default=20, verbose_name='杠杆')),
|
||||
('entry_price', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='开仓均价')),
|
||||
('lots', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='手数')),
|
||||
('margin', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='占用保证金')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='是否持仓')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='时间')),
|
||||
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='positions', to='core.account')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '持仓',
|
||||
'verbose_name_plural': '持仓管理',
|
||||
},
|
||||
),
|
||||
]
|
||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
core/migrations/__pycache__/0005_sitesettings.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0005_sitesettings.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
159
core/models.py
159
core/models.py
@ -1,3 +1,160 @@
|
||||
import random
|
||||
import string
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Create your models here.
|
||||
def generate_uid():
|
||||
return ''.join(random.choices(string.digits, k=6))
|
||||
|
||||
class SiteSettings(models.Model):
|
||||
site_name = models.CharField(max_length=100, default="BitCrypto", verbose_name=_("网站名称"))
|
||||
customer_service_url = models.URLField(blank=True, null=True, verbose_name=_("在线客服链接"))
|
||||
terms_content = models.TextField(blank=True, verbose_name=_("服务条款内容"))
|
||||
privacy_content = models.TextField(blank=True, verbose_name=_("隐私政策内容"))
|
||||
|
||||
# Global Market Control
|
||||
is_pinning_active = models.BooleanField(default=False, verbose_name=_("全局插针激活"), help_text=_("开启后,所有币种将向手动设置的价格靠拢"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("系统设置")
|
||||
verbose_name_plural = _("系统设置")
|
||||
|
||||
def __str__(self):
|
||||
return self.site_name
|
||||
|
||||
class Cryptocurrency(models.Model):
|
||||
symbol = models.CharField(max_length=20, unique=True, verbose_name=_("币种代码"))
|
||||
name = models.CharField(max_length=100, verbose_name=_("币种名称"))
|
||||
icon_url = models.URLField(null=True, blank=True, verbose_name=_("图标URL"))
|
||||
current_price = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("当前价格"))
|
||||
manual_price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("插针价格 (留空则使用当前价格)"))
|
||||
change_24h = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name=_("24h 涨跌幅"))
|
||||
is_active = models.BooleanField(default=True, verbose_name=_("是否启用"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("加密货币")
|
||||
verbose_name_plural = _("加密货币管理")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.symbol} - {self.name}"
|
||||
|
||||
class Account(models.Model):
|
||||
ACCOUNT_TYPES = (
|
||||
('SIMULATED', _('模拟账户')),
|
||||
('REAL', _('真实账户')),
|
||||
)
|
||||
KYC_STATUS = (
|
||||
('UNVERIFIED', _('未认证')),
|
||||
('PENDING', _('审核中')),
|
||||
('VERIFIED', _('已认证')),
|
||||
('REJECTED', _('已拒绝')),
|
||||
)
|
||||
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
|
||||
uid = models.CharField(max_length=6, unique=True, default=generate_uid, verbose_name=_("UID"))
|
||||
session_key = models.CharField(max_length=100, null=True, blank=True, unique=True)
|
||||
account_type = models.CharField(max_length=20, choices=ACCOUNT_TYPES, default='SIMULATED', verbose_name=_("账户类型"))
|
||||
|
||||
balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("可用余额 (USDT)"))
|
||||
frozen_balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("冻结余额 (USDT)"))
|
||||
|
||||
credit_score = models.IntegerField(default=80, verbose_name=_("信用分"))
|
||||
kyc_status = models.CharField(max_length=20, choices=KYC_STATUS, default='UNVERIFIED', verbose_name=_("实名认证状态"))
|
||||
|
||||
win_loss_control = models.IntegerField(default=0, help_text=_("控制胜率: -100 (必输) 到 100 (必赢), 0 为随机"), verbose_name=_("输赢控制"))
|
||||
language = models.CharField(max_length=10, default='zh-hans', verbose_name=_("语言偏好"))
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("账户")
|
||||
verbose_name_plural = _("账户管理")
|
||||
|
||||
def __str__(self):
|
||||
return f"Account {self.uid} ({self.get_account_type_display()})"
|
||||
|
||||
class Asset(models.Model):
|
||||
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='assets')
|
||||
currency = models.CharField(max_length=10, verbose_name=_("币种"))
|
||||
balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("余额"))
|
||||
frozen = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("冻结"))
|
||||
|
||||
class Meta:
|
||||
unique_together = ('account', 'currency')
|
||||
verbose_name = _("资产")
|
||||
verbose_name_plural = _("资产管理")
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self.balance + self.frozen
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account.uid} - {self.currency}: {self.balance}"
|
||||
|
||||
class Order(models.Model):
|
||||
SIDE_CHOICES = (('BUY', _('买入/做多')), ('SELL', _('卖出/做空')))
|
||||
TYPE_CHOICES = (('LIMIT', _('限价')), ('MARKET', _('市价')))
|
||||
TRADE_TYPE_CHOICES = (('SPOT', _('现货')), ('CONTRACT', _('合约')))
|
||||
STATUS_CHOICES = (
|
||||
('PENDING', _('等待成交')),
|
||||
('PARTIALLY_FILLED', _('部分成交')),
|
||||
('FILLED', _('已成交')),
|
||||
('CANCELED', _('已撤销')),
|
||||
)
|
||||
|
||||
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='orders')
|
||||
symbol = models.CharField(max_length=20, default='BTC-USDT', verbose_name=_("交易对"))
|
||||
trade_type = models.CharField(max_length=20, choices=TRADE_TYPE_CHOICES, default='SPOT', verbose_name=_("交易类型"))
|
||||
side = models.CharField(max_length=10, choices=SIDE_CHOICES, verbose_name=_("方向"))
|
||||
order_type = models.CharField(max_length=10, choices=TYPE_CHOICES, verbose_name=_("类型"))
|
||||
|
||||
price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("委托价格"))
|
||||
amount = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("数量/手数"))
|
||||
total_usdt = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("成交额 (USDT)"))
|
||||
|
||||
leverage = models.IntegerField(default=1, verbose_name=_("杠杆倍数"))
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING', verbose_name=_("状态"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("订单")
|
||||
verbose_name_plural = _("订单管理")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.trade_type} {self.side} {self.amount} {self.symbol}"
|
||||
|
||||
class Position(models.Model):
|
||||
SIDE_CHOICES = (('LONG', _('做多')), ('SHORT', _('做空')))
|
||||
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='positions')
|
||||
symbol = models.CharField(max_length=20, verbose_name=_("交易对"))
|
||||
side = models.CharField(max_length=10, choices=SIDE_CHOICES, verbose_name=_("方向"))
|
||||
leverage = models.IntegerField(default=20, verbose_name=_("杠杆"))
|
||||
entry_price = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("开仓均价"))
|
||||
lots = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("手数"))
|
||||
margin = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("占用保证金"))
|
||||
is_active = models.BooleanField(default=True, verbose_name=_("是否持仓"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("时间"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("持仓")
|
||||
verbose_name_plural = _("持仓管理")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account.uid} {self.symbol} {self.side} {self.lots}张"
|
||||
|
||||
class Transaction(models.Model):
|
||||
TX_TYPE = (('deposit', _('充值')), ('withdraw', _('提现')))
|
||||
TX_STATUS = (('pending', _('待处理')), ('completed', _('成功')), ('failed', _('失败')))
|
||||
|
||||
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='transactions')
|
||||
transaction_type = models.CharField(max_length=20, choices=TX_TYPE, default='deposit', verbose_name=_("类型"))
|
||||
currency = models.CharField(max_length=10, default='USDT', verbose_name=_("币种"))
|
||||
amount = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("金额"))
|
||||
tx_hash = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("交易哈希"))
|
||||
status = models.CharField(max_length=20, choices=TX_STATUS, default='pending', verbose_name=_("状态"))
|
||||
timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_("时间"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("充提记录")
|
||||
verbose_name_plural = _("充提管理")
|
||||
|
||||
@ -1,25 +1,198 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="zh-hans">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
||||
{% if project_description %}
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
<meta property="og:description" content="{{ project_description }}">
|
||||
<meta property="twitter:description" content="{{ project_description }}">
|
||||
{% endif %}
|
||||
{% if project_image_url %}
|
||||
<meta property="og:image" content="{{ project_image_url }}">
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
{% block head %}{% endblock %}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ project_name }} - 全球领先的数字资产交易平台</title>
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
<style>
|
||||
:root {
|
||||
--bg-dark: #0b0e11;
|
||||
--bg-card: #181a20;
|
||||
--text-primary: #eaecef;
|
||||
--text-secondary: #848e9c;
|
||||
--accent-color: #fcd535;
|
||||
--up-color: #0ecb81;
|
||||
--down-color: #f6465d;
|
||||
}
|
||||
body {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar {
|
||||
background-color: var(--bg-dark);
|
||||
border-bottom: 1px solid #2b2f36;
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
.nav-link {
|
||||
color: var(--text-primary) !important;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
margin: 0 8px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.nav-link:hover { color: var(--accent-color) !important; }
|
||||
.btn-primary {
|
||||
background-color: var(--accent-color);
|
||||
border-color: var(--accent-color);
|
||||
color: #181a20;
|
||||
font-weight: 600;
|
||||
padding: 8px 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.footer {
|
||||
background-color: var(--bg-dark);
|
||||
padding: 60px 0 30px 0;
|
||||
border-top: 1px solid #2b2f36;
|
||||
}
|
||||
.footer h6 { color: #eaecef; margin-bottom: 20px; font-weight: 600; }
|
||||
.footer ul li { margin-bottom: 12px; }
|
||||
.footer ul li a { color: #848e9c; text-decoration: none; font-size: 14px; transition: color 0.2s; }
|
||||
.footer ul li a:hover { color: var(--accent-color); }
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
}
|
||||
/* Floating Customer Service Icon */
|
||||
.cs-float {
|
||||
position: fixed;
|
||||
right: 30px;
|
||||
bottom: 40px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: var(--accent-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #181a20;
|
||||
font-size: 28px;
|
||||
box-shadow: 0 4px 20px rgba(252, 213, 53, 0.4);
|
||||
cursor: pointer;
|
||||
z-index: 9999;
|
||||
transition: all 0.3s;
|
||||
text-decoration: none;
|
||||
}
|
||||
.cs-float:hover {
|
||||
transform: scale(1.1) rotate(5deg);
|
||||
color: #181a20;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
<nav class="navbar navbar-expand-lg sticky-top">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand d-flex align-items-center" href="/">
|
||||
<img src="{% static 'images/logo.png' %}" alt="Logo" style="height: 28px; margin-right: 8px;">
|
||||
<span class="fw-bold fs-5 text-white">{{ project_name }}</span>
|
||||
</a>
|
||||
<button class="navbar-toggler border-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<i class="bi bi-list text-white"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'core:index' %}">首页</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'core:spot_trade' %}">现货交易</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'core:contract_trade' %}">合约交易</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'core:market_center' %}">行情中心</a></li>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="dropdown">
|
||||
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle fs-5 me-2"></i>
|
||||
<span>{{ user.username }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow">
|
||||
<li><a class="dropdown-item" href="{% url 'core:profile' %}"><i class="bi bi-person-badge me-2"></i>个人中心</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'core:profile' %}"><i class="bi bi-wallet2 me-2"></i>我的资产</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-danger" href="/login/"><i class="bi bi-box-arrow-right me-2"></i>安全退出</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'core:login' %}" class="nav-link me-3">登录</a>
|
||||
<a href="{% url 'core:register' %}" class="btn btn-primary">立即注册</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Floating CS -->
|
||||
{% if site_settings.customer_service_url %}
|
||||
<a href="{{ site_settings.customer_service_url }}" target="_blank" class="cs-float" title="在线客服">
|
||||
<i class="bi bi-headset"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<h5 class="fw-bold mb-4 text-white">{{ project_name }}</h5>
|
||||
<p class="text-secondary small">全球领先的数字资产交易平台,致力于为用户提供安全、专业、透明的数字资产一站式服务。</p>
|
||||
</div>
|
||||
<div class="col-md-2 offset-md-1">
|
||||
<h6>产品服务</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="{% url 'core:spot_trade' %}">现货交易</a></li>
|
||||
<li><a href="{% url 'core:contract_trade' %}">永续合约</a></li>
|
||||
<li><a href="{% url 'core:placeholder' '币安赚币' %}">理财中心</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<h6>法律法规</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="{% url 'core:placeholder' '服务条款' %}">服务条款</a></li>
|
||||
<li><a href="{% url 'core:placeholder' '隐私政策' %}">隐私政策</a></li>
|
||||
<li><a href="{% url 'core:placeholder' '免责声明' %}">免责声明</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<h6>客户支持</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="{% url 'core:placeholder' '帮助中心' %}">帮助中心</a></li>
|
||||
<li><a href="{% url 'core:placeholder' '公告中心' %}">公告中心</a></li>
|
||||
<li><a href="{% url 'core:placeholder' '提交请求' %}">提交工单</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<h6>关于我们</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="{% url 'core:placeholder' '公司简介' %}">公司简介</a></li>
|
||||
<li><a href="{% url 'core:placeholder' '加入我们' %}">招贤纳士</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-5" style="border-color: #2b2f36;">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 text-center text-md-start">
|
||||
<p class="text-secondary small mb-0">© 2026 {{ project_name }} Digital Asset Exchange. All rights reserved.</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-center text-md-end mt-3 mt-md-0">
|
||||
<div class="text-secondary small">
|
||||
<i class="bi bi-globe me-2"></i> 简体中文 | USDT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,14 +1,66 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ article.title }}{% endblock %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-5">
|
||||
<h1>{{ article.title }}</h1>
|
||||
<p class="text-muted">Published on {{ article.created_at|date:"F d, Y" }}</p>
|
||||
<hr>
|
||||
<div>
|
||||
{{ article.content|safe }}
|
||||
<div class="container py-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 offset-lg-2">
|
||||
<div class="glass-card p-5">
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/" class="text-warning text-decoration-none">首页</a></li>
|
||||
<li class="breadcrumb-item active text-secondary" aria-current="page">{{ title }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<h1 class="fw-bold mb-4 text-white">{{ title }}</h1>
|
||||
|
||||
<div class="content text-secondary lh-lg">
|
||||
<p class="lead text-white mb-5">{{ content }}</p>
|
||||
|
||||
{% if faqs %}
|
||||
<hr class="my-5 border-secondary">
|
||||
<h3 class="text-white mb-4">常见问题 (FAQ)</h3>
|
||||
<div class="accordion accordion-flush bg-transparent" id="faqAccordion">
|
||||
{% for faq in faqs %}
|
||||
<div class="accordion-item bg-transparent border-secondary">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button bg-transparent text-white collapsed border-0 py-3" type="button" data-bs-toggle="collapse" data-bs-target="#q{{ forloop.counter }}">
|
||||
{{ faq.q }}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="q{{ forloop.counter }}" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
|
||||
<div class="accordion-body text-secondary pb-4">
|
||||
{{ faq.a }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-5 p-4 bg-dark bg-opacity-50 rounded border border-secondary text-center">
|
||||
<h5 class="text-white mb-3">还需要更多帮助吗?</h5>
|
||||
<p class="small text-secondary">我们的全球客服团队 24/7 全天候在线为您提供专业支持。</p>
|
||||
<div class="d-flex justify-content-center gap-3 mt-4">
|
||||
<button class="btn btn-warning px-4 fw-bold">联系在线客服</button>
|
||||
<button class="btn btn-outline-light px-4">查阅文档</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.accordion-button:after {
|
||||
filter: invert(1);
|
||||
}
|
||||
.accordion-button:not(.collapsed) {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
65
core/templates/core/deposit.html
Normal file
65
core/templates/core/deposit.html
Normal file
@ -0,0 +1,65 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 animate-up">
|
||||
<div class="glass-card p-4">
|
||||
<h3 class="fw-bold mb-4 text-center">充值数字资产</h3>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">选择币种</label>
|
||||
<div class="d-flex align-items-center bg-dark p-3 rounded border border-secondary border-opacity-25">
|
||||
<i class="bi bi-coin text-warning fs-4 me-3"></i>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="m-0 fw-bold">USDT</h6>
|
||||
<small class="text-secondary">Tether US</small>
|
||||
</div>
|
||||
<i class="bi bi-chevron-down text-secondary"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">选择网络</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-4"><button class="btn btn-sm btn-outline-warning w-100 active">TRC20</button></div>
|
||||
<div class="col-4"><button class="btn btn-sm btn-outline-secondary w-100">ERC20</button></div>
|
||||
<div class="col-4"><button class="btn btn-sm btn-outline-secondary w-100">BEP20</button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-black bg-opacity-50 p-4 rounded-3 text-center mb-4 border border-secondary border-opacity-10">
|
||||
<div class="mb-3">
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=TRX789sDqW12vNpkL90ZxY56uMvB45RtQp" alt="QR" class="img-fluid rounded">
|
||||
</div>
|
||||
<small class="text-secondary d-block mb-2">充值地址 (TRC20)</small>
|
||||
<div class="d-flex align-items-center justify-content-center gap-2">
|
||||
<span class="fw-bold text-break small" style="color: #f0b90b;">TRX789sDqW12vNpkL90ZxY56uMvB45RtQp</span>
|
||||
<i class="bi bi-files text-secondary cursor-pointer" onclick="alert('地址已复制')"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning border-0 bg-warning bg-opacity-10 text-warning-emphasis small mb-4">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
请仅向该地址充值 USDT。任何其他资产的充值都将无法找回。
|
||||
</div>
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary">充值金额</label>
|
||||
<div class="input-group">
|
||||
<input type="number" name="amount" step="0.01" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="0.00" required>
|
||||
<span class="input-group-text bg-dark text-white border-secondary border-opacity-25">USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">交易哈希 (TXID)</label>
|
||||
<input type="text" name="tx_id" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="请输入链上交易ID" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary-custom w-100 py-3">提交充值确认</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -1,145 +1,235 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Hero Carousel Section -->
|
||||
<div id="heroCarousel" class="carousel slide" data-bs-ride="carousel">
|
||||
<div class="carousel-indicators">
|
||||
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="0" class="active"></button>
|
||||
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="1"></button>
|
||||
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="2"></button>
|
||||
</div>
|
||||
<div class="carousel-inner">
|
||||
<!-- Slide 1: Welcome -->
|
||||
<div class="carousel-item active" style="height: 550px; background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('https://images.unsplash.com/photo-1621761191319-c6fb62004040?q=80&w=2070&auto=format&fit=crop'); background-size: cover; background-position: center;">
|
||||
<div class="container h-100 d-flex align-items-center">
|
||||
<div class="row w-100 align-items-center">
|
||||
<div class="col-lg-7">
|
||||
<h1 class="display-3 fw-bold mb-4">开启您的<br><span style="color: var(--accent-color);">加密货币</span>之旅</h1>
|
||||
<p class="lead text-light mb-5">在全球最受信任的交易平台买卖和存储加密货币。BitCrypto 为您提供安全、稳定、高效的服务。</p>
|
||||
<div class="d-flex gap-3">
|
||||
<a href="{% url 'core:register' %}" class="btn btn-warning btn-lg px-5 fw-bold">立即注册</a>
|
||||
<a href="{% url 'core:spot_trade' %}" class="btn btn-outline-light btn-lg px-5 fw-bold">开始交易</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Slide 2: Derivatives -->
|
||||
<div class="carousel-item" style="height: 550px; background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('https://images.unsplash.com/photo-1639762681485-074b7f938ba0?q=80&w=2064&auto=format&fit=crop'); background-size: cover; background-position: center;">
|
||||
<div class="container h-100 d-flex align-items-center">
|
||||
<div class="row w-100 align-items-center justify-content-end">
|
||||
<div class="col-lg-7 text-lg-end">
|
||||
<h1 class="display-4 fw-bold mb-4">领先的<span style="color: var(--accent-color);">衍生品</span>交易平台</h1>
|
||||
<p class="lead text-light mb-5">最高200倍杠杆,支持多种永续合约,毫秒级撮合引擎。多空双向交易,灵活捕捉市场机会。</p>
|
||||
<a href="{% url 'core:contract_trade' %}" class="btn btn-warning btn-lg px-5 fw-bold">进入合约交易</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Slide 3: App Download -->
|
||||
<div class="carousel-item" style="height: 550px; background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://images.unsplash.com/photo-1622639225985-84f8877b0c30?q=80&w=1944&auto=format&fit=crop'); background-size: cover; background-position: center;">
|
||||
<div class="container h-100 d-flex align-items-center">
|
||||
<div class="row w-100 align-items-center">
|
||||
<div class="col-lg-12 text-center">
|
||||
<h1 class="display-4 fw-bold mb-4">随时随地,随心交易</h1>
|
||||
<p class="lead text-light mb-5">下载 BitCrypto App,享受极致交易体验。专业图表、实时推送、资产管理尽在掌握。</p>
|
||||
<div class="d-flex justify-content-center gap-5">
|
||||
<div class="download-badge">
|
||||
<i class="bi bi-apple fs-1 mb-2"></i>
|
||||
<p class="small mb-0">App Store</p>
|
||||
</div>
|
||||
<div class="download-badge">
|
||||
<i class="bi bi-android2 fs-1 mb-2"></i>
|
||||
<p class="small mb-0">Android APK</p>
|
||||
</div>
|
||||
<div class="download-badge">
|
||||
<i class="bi bi-qr-code fs-1 mb-2"></i>
|
||||
<p class="small mb-0">扫码下载</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="carousel-control-prev" type="button" data-bs-target="#heroCarousel" data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon"></span>
|
||||
</button>
|
||||
<button class="carousel-control-next" type="button" data-bs-target="#heroCarousel" data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<div class="container" style="margin-top: -50px; position: relative; z-index: 10;">
|
||||
<div class="glass-card p-4 d-flex flex-wrap justify-content-around align-items-center text-center shadow-lg" style="border-radius: 12px; border: 1px solid rgba(255,193,7,0.3);">
|
||||
<div class="download-item mb-3 mb-md-0">
|
||||
<h6 class="text-secondary mb-2 small fw-bold">iOS 下载</h6>
|
||||
<button class="btn btn-outline-light border-secondary px-4"><i class="bi bi-apple me-2 text-warning"></i>App Store</button>
|
||||
</div>
|
||||
<div class="vr d-none d-md-block opacity-25"></div>
|
||||
<div class="download-item mb-3 mb-md-0">
|
||||
<h6 class="text-secondary mb-2 small fw-bold">安卓下载</h6>
|
||||
<button class="btn btn-outline-light border-secondary px-4"><i class="bi bi-android2 me-2 text-warning"></i>Android</button>
|
||||
</div>
|
||||
<div class="vr d-none d-md-block opacity-25"></div>
|
||||
<div class="download-item">
|
||||
<h6 class="text-secondary mb-2 small fw-bold">API 支持</h6>
|
||||
<button class="btn btn-outline-light border-secondary px-4"><i class="bi bi-code-square me-2 text-warning"></i>开发者文档</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Market Section -->
|
||||
<section id="markets" class="container py-5 mt-4">
|
||||
<div class="d-flex justify-content-between align-items-end mb-4">
|
||||
<div>
|
||||
<h2 class="fw-bold">热门行情</h2>
|
||||
<p class="text-secondary mb-0">实时获取全球顶级加密货币价格走势</p>
|
||||
</div>
|
||||
<a href="{% url 'core:market_center' %}" class="text-warning text-decoration-none fw-bold">查看更多市场 <i class="bi bi-arrow-right"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover align-middle" style="border-radius: 12px; overflow: hidden; border: 1px solid #2b3139;">
|
||||
<thead class="text-secondary">
|
||||
<tr style="background: #1e2329;">
|
||||
<th scope="col" class="ps-4 py-3">币种</th>
|
||||
<th scope="col" class="py-3">价格</th>
|
||||
<th scope="col" class="py-3">24h 涨跌</th>
|
||||
<th scope="col" class="py-3">24h 最高 / 最低</th>
|
||||
<th scope="col" class="text-end pe-4 py-3">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="market-list">
|
||||
<tr>
|
||||
<td colspan="5" class="text-center py-5">
|
||||
<div class="spinner-border text-warning" role="status"></div>
|
||||
<p class="mt-2 text-secondary">正在加载实时行情...</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features -->
|
||||
<section class="container py-5">
|
||||
<h2 class="text-center fw-bold mb-5">领先行业的资产安全防护</h2>
|
||||
<div class="row g-4 text-center">
|
||||
<div class="col-md-4">
|
||||
<div class="p-5 h-100 glass-card">
|
||||
<div class="icon-box mb-4 mx-auto" style="width: 80px; height: 80px; background: rgba(255,193,7,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
|
||||
<i class="bi bi-shield-check display-5 text-warning"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-3">资产保障基金</h4>
|
||||
<p class="text-secondary">BitCrypto 每日提取 10% 的交易手续费,存入资产保障基金,为您在极端情况下的资产提供全额赔付保障。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="p-5 h-100 glass-card">
|
||||
<div class="icon-box mb-4 mx-auto" style="width: 80px; height: 80px; background: rgba(255,193,7,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
|
||||
<i class="bi bi-cpu display-5 text-warning"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-3">高性能交易引擎</h4>
|
||||
<p class="text-secondary">每秒 200 万次的交易撮合能力,保证在市场波动剧烈时依然能够快速响应,不漏掉任何一个成交机会。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="p-5 h-100 glass-card">
|
||||
<div class="icon-box mb-4 mx-auto" style="width: 80px; height: 80px; background: rgba(255,193,7,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
|
||||
<i class="bi bi-safe2 display-5 text-warning"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-3">严密的隐私保护</h4>
|
||||
<p class="text-secondary">我们对用户数据采取离线分布式存储和多层加密,绝不将个人隐私泄露给任何第三方,确保您的身份隐私。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% block head %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
.download-badge {
|
||||
transition: transform 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
.download-badge:hover {
|
||||
transform: translateY(-5px);
|
||||
color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.icon-box { transition: all 0.3s; }
|
||||
.glass-card:hover .icon-box { transform: scale(1.1); background: rgba(255,193,7,0.2) !important; }
|
||||
.coin-icon { width: 32px; height: 32px; margin-right: 12px; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
{% block scripts %}
|
||||
<script>
|
||||
const symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'ADAUSDT', 'XRPUSDT', 'DOTUSDT', 'DOGEUSDT', 'MATICUSDT', 'LINKUSDT'];
|
||||
const coinIcons = {
|
||||
'BTC': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1.png',
|
||||
'ETH': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1027.png',
|
||||
'BNB': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1839.png',
|
||||
'SOL': 'https://s2.coinmarketcap.com/static/img/coins/64x64/5426.png',
|
||||
'ADA': 'https://s2.coinmarketcap.com/static/img/coins/64x64/2010.png',
|
||||
'XRP': 'https://s2.coinmarketcap.com/static/img/coins/64x64/52.png',
|
||||
'DOT': 'https://s2.coinmarketcap.com/static/img/coins/64x64/6636.png',
|
||||
'DOGE': 'https://s2.coinmarketcap.com/static/img/coins/64x64/74.png',
|
||||
'MATIC': 'https://s2.coinmarketcap.com/static/img/coins/64x64/3890.png',
|
||||
'LINK': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1975.png'
|
||||
};
|
||||
|
||||
async function fetchMarkets() {
|
||||
try {
|
||||
const response = await fetch('https://api.binance.com/api/v3/ticker/24hr?symbols=' + JSON.stringify(symbols));
|
||||
const data = await response.json();
|
||||
const list = document.getElementById('market-list');
|
||||
list.innerHTML = '';
|
||||
|
||||
data.forEach(coin => {
|
||||
const symbolBase = coin.symbol.replace('USDT', '');
|
||||
const change = parseFloat(coin.priceChangePercent);
|
||||
const changeClass = change >= 0 ? 'text-success' : 'text-danger';
|
||||
const changeIcon = change >= 0 ? '+' : '';
|
||||
const iconUrl = coinIcons[symbolBase] || 'https://s2.coinmarketcap.com/static/img/coins/64x64/1.png';
|
||||
|
||||
list.innerHTML += `
|
||||
<tr>
|
||||
<td class="ps-4 py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="${iconUrl}" class="coin-icon" alt="${symbolBase}">
|
||||
<div>
|
||||
<span class="fw-bold text-white">${symbolBase}</span>
|
||||
<span class="text-secondary small ms-1">/ USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="fw-bold py-3 text-white">$${parseFloat(coin.lastPrice).toLocaleString()}</td>
|
||||
<td class="${changeClass} py-3 fw-bold">${changeIcon}${change.toFixed(2)}%</td>
|
||||
<td class="py-3">
|
||||
<div class="small text-white">高: $${parseFloat(coin.highPrice).toLocaleString()}</div>
|
||||
<div class="small text-secondary">低: $${parseFloat(coin.lowPrice).toLocaleString()}</div>
|
||||
</td>
|
||||
<td class="text-end pe-4 py-3">
|
||||
<a href="/trade/spot/?symbol=${coin.symbol}" class="btn btn-sm btn-warning fw-bold px-3 shadow-sm">交易</a>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
} catch (error) { console.error('Error:', error); }
|
||||
}
|
||||
|
||||
fetchMarkets();
|
||||
setInterval(fetchMarkets, 5000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
86
core/templates/core/login.html
Normal file
86
core/templates/core/login.html
Normal file
@ -0,0 +1,86 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%);">
|
||||
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 450px; border-radius: 16px;">
|
||||
<div class="text-center mb-5">
|
||||
<div class="logo-circle mb-4 mx-auto">
|
||||
<i class="bi bi-hexagon-fill text-warning display-4"></i>
|
||||
</div>
|
||||
<h2 class="fw-bold text-white">欢迎登录 BitCrypto</h2>
|
||||
<p class="text-secondary">全球领先的加密资产交易平台</p>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">{{ field.label }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
{% if '用户' in field.label %}<i class="bi bi-person"></i>{% else %}<i class="bi bi-lock"></i>{% endif %}
|
||||
</span>
|
||||
<input type="{{ field.field.widget.input_type }}" name="{{ field.name }}" class="form-control bg-transparent text-white border-secondary" placeholder="请输入{{ field.label }}" required>
|
||||
</div>
|
||||
{% for error in field.errors %}
|
||||
<div class="text-danger x-small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bg-transparent border-secondary" type="checkbox" id="remember">
|
||||
<label class="form-check-label text-secondary small" for="remember">自动登录</label>
|
||||
</div>
|
||||
<a href="#" class="text-warning small text-decoration-none">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">登录</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-secondary small">还没有账户? <a href="{% url 'core:register' %}" class="text-warning text-decoration-none fw-bold">立即注册</a></p>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 pt-4 border-top border-secondary text-center">
|
||||
<p class="text-secondary x-small mb-3">其他登录方式</p>
|
||||
<div class="d-flex justify-content-center gap-4">
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-google"></i></a>
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-apple"></i></a>
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-qr-code-scan"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.auth-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
.input-group-text {
|
||||
border-right: none;
|
||||
}
|
||||
input.form-control {
|
||||
border-left: none;
|
||||
padding: 12px;
|
||||
}
|
||||
input.form-control:focus {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
box-shadow: none;
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
.x-small { font-size: 11px; }
|
||||
.logo-circle {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(240, 185, 11, 0.1);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
107
core/templates/core/market_center.html
Normal file
107
core/templates/core/market_center.html
Normal file
@ -0,0 +1,107 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="fw-bold text-white mb-0">行情中心</h2>
|
||||
<div class="input-group w-auto">
|
||||
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
|
||||
<input type="text" id="market-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterMarket()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card overflow-hidden" style="border-radius: 12px; border: 1px solid #2b3139; background: #161a1e;">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="text-secondary border-bottom border-secondary">
|
||||
<th class="ps-4 py-3">币种</th>
|
||||
<th class="py-3">最新价</th>
|
||||
<th class="py-3">24h 涨跌</th>
|
||||
<th class="py-3">24h 最高</th>
|
||||
<th class="py-3">24h 最低</th>
|
||||
<th class="py-3">24h 成交额</th>
|
||||
<th class="pe-4 py-3 text-end">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="market-table-body">
|
||||
{% for crypto in cryptos %}
|
||||
<tr class="market-row" data-symbol="{{ crypto.symbol }}">
|
||||
<td class="ps-4 py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://static.okx.com/cdn/oksupport/asset/currency/icon/{{ crypto.symbol|lower }}.png"
|
||||
class="rounded-circle me-3" width="32" height="32"
|
||||
onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">
|
||||
<div>
|
||||
<div class="fw-bold text-white">{{ crypto.symbol }}</div>
|
||||
<div class="text-secondary small">{{ crypto.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 fw-bold price-val">--</td>
|
||||
<td class="py-3 change-val">--</td>
|
||||
<td class="py-3 high-val text-secondary">--</td>
|
||||
<td class="py-3 low-val text-secondary">--</td>
|
||||
<td class="py-3 vol-val text-secondary">--</td>
|
||||
<td class="pe-4 py-3 text-end">
|
||||
<a href="{% url 'core:trade' 'spot' %}?symbol={{ crypto.symbol }}USDT" class="btn btn-outline-warning btn-sm px-3 me-2">现货</a>
|
||||
<a href="{% url 'core:trade' 'contract' %}?symbol={{ crypto.symbol }}USDT" class="btn btn-warning btn-sm px-3 text-dark">合约</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.glass-card { background: rgba(22, 26, 30, 0.8); backdrop-filter: blur(10px); }
|
||||
.table-dark { --bs-table-bg: transparent; }
|
||||
.table-hover tbody tr:hover { background-color: rgba(255, 255, 255, 0.05) !important; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function updateMarket() {
|
||||
try {
|
||||
const r = await fetch('https://api.binance.com/api/v3/ticker/24hr');
|
||||
const data = await r.json();
|
||||
const tickers = {};
|
||||
data.forEach(t => {
|
||||
if (t.symbol.endsWith('USDT')) {
|
||||
tickers[t.symbol.replace('USDT', '')] = t;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.market-row').forEach(row => {
|
||||
const sym = row.getAttribute('data-symbol');
|
||||
const t = tickers[sym];
|
||||
if (t) {
|
||||
const chg = parseFloat(t.priceChangePercent);
|
||||
row.querySelector('.price-val').textContent = parseFloat(t.lastPrice).toLocaleString();
|
||||
row.querySelector('.price-val').className = 'py-3 fw-bold price-val ' + (chg >= 0 ? 'text-success' : 'text-danger');
|
||||
row.querySelector('.change-val').textContent = (chg >= 0 ? '+' : '') + chg.toFixed(2) + '%';
|
||||
row.querySelector('.change-val').className = 'py-3 change-val ' + (chg >= 0 ? 'text-success' : 'text-danger');
|
||||
row.querySelector('.high-val').textContent = parseFloat(t.highPrice).toLocaleString();
|
||||
row.querySelector('.low-val').textContent = parseFloat(t.lowPrice).toLocaleString();
|
||||
row.querySelector('.vol-val').textContent = (parseFloat(t.quoteVolume) / 1000000).toFixed(2) + 'M';
|
||||
}
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function filterMarket() {
|
||||
const query = document.getElementById('market-search').value.toLowerCase();
|
||||
document.querySelectorAll('.market-row').forEach(row => {
|
||||
const sym = row.getAttribute('data-symbol').toLowerCase();
|
||||
row.style.display = sym.includes(query) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
updateMarket();
|
||||
setInterval(updateMarket, 3000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
49
core/templates/core/mobile/base_mobile.html
Normal file
49
core/templates/core/mobile/base_mobile.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>{{ site_settings.site_name|default:"BitCrypto" }} - 移动版</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body { background-color: #0b0e11; color: #eaecef; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; overflow-x: hidden; }
|
||||
.mobile-nav { position: fixed; bottom: 0; left: 0; right: 0; background: #161a1e; display: flex; justify-content: space-around; padding: 10px 0; border-top: 1px solid #2b3139; z-index: 1000; }
|
||||
.nav-item { text-align: center; color: #848e9c; text-decoration: none; font-size: 10px; }
|
||||
.nav-item.active { color: #f0b90b; }
|
||||
.nav-item i { font-size: 20px; display: block; margin-bottom: 2px; }
|
||||
.glass-card { background: #161a1e; border: 1px solid #2b3139; border-radius: 8px; margin-bottom: 10px; }
|
||||
.x-small { font-size: 11px; }
|
||||
.text-warning { color: #f0b90b !important; }
|
||||
.btn-warning { background-color: #f0b90b; border-color: #f0b90b; color: #000; }
|
||||
.form-control, .input-group-text { background-color: #1e2329 !important; border-color: #2b3139 !important; color: white !important; }
|
||||
.custom-range::-webkit-slider-thumb { background: #f0b90b; cursor: pointer; height: 16px; width: 16px; }
|
||||
.custom-range-danger::-webkit-slider-thumb { background: #f6465d; cursor: pointer; height: 16px; width: 16px; }
|
||||
body { padding-bottom: 70px; }
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="p-2">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<div class="mobile-nav">
|
||||
<a href="{% url 'core:index' %}" class="nav-item {% if request.resolver_match.url_name == 'index' %}active{% endif %}">
|
||||
<i class="bi bi-house-door"></i>首页
|
||||
</a>
|
||||
<a href="{% url 'core:market_center' %}" class="nav-item {% if request.resolver_match.url_name == 'market_center' %}active{% endif %}">
|
||||
<i class="bi bi-graph-up"></i>行情
|
||||
</a>
|
||||
<a href="{% url 'core:trade' 'spot' %}" class="nav-item {% if 'trade' in request.resolver_match.url_name %}active{% endif %}">
|
||||
<i class="bi bi-arrow-left-right"></i>交易
|
||||
</a>
|
||||
<a href="{% url 'core:profile' %}" class="nav-item {% if request.resolver_match.url_name == 'profile' %}active{% endif %}">
|
||||
<i class="bi bi-person"></i>我的
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
44
core/templates/core/mobile/deposit.html
Normal file
44
core/templates/core/mobile/deposit.html
Normal file
@ -0,0 +1,44 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="p-3">
|
||||
<div class="mb-4">
|
||||
<a href="{% url 'core:profile' %}" class="text-decoration-none text-secondary small"><i class="bi bi-chevron-left"></i> 返回</a>
|
||||
<h4 class="fw-bold mt-2">充值 USDT</h4>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-3 mb-3">
|
||||
<div class="text-secondary x-small mb-2">选择币种</div>
|
||||
<div class="bg-dark p-2 rounded d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="fw-bold">USDT</span>
|
||||
<span class="badge bg-warning text-dark">TRC20</span>
|
||||
</div>
|
||||
|
||||
<div class="text-center py-4 bg-white rounded mb-3">
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=TFA7S7S7S7S7S7S7S7S7S7S7S7S7S7" alt="QR" style="width: 120px;">
|
||||
</div>
|
||||
|
||||
<div class="text-secondary x-small mb-1">充值地址</div>
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<input type="text" class="form-control" value="TFA7S7S7S7S7S7S7S7S7S7S7S7S7S7" readonly>
|
||||
<button class="btn btn-warning" onclick="alert('已复制')">复制</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-dark x-small py-2">
|
||||
<i class="bi bi-info-circle me-1"></i> 请务必确认网络为 TRC20,否则资产将无法找回。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" class="glass-card p-3">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">充值金额</label>
|
||||
<input type="number" name="amount" class="form-control" placeholder="请输入充值金额" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">交易哈希 (TXID)</label>
|
||||
<input type="text" name="tx_id" class="form-control" placeholder="请输入流水号" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold">提交充值申请</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
88
core/templates/core/mobile/index.html
Normal file
88
core/templates/core/mobile/index.html
Normal file
@ -0,0 +1,88 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="glass-card p-3 text-center mb-3" style="background: linear-gradient(135deg, #161a1e 0%, #1e2329 100%);">
|
||||
<img src="{% static 'images/logo.png' %}" alt="Logo" style="height: 40px;" class="mb-2">
|
||||
<h4 class="fw-bold">全球领先的数字资产交易平台</h4>
|
||||
<p class="text-secondary small">安全、稳定、快捷的加密货币交易体验</p>
|
||||
{% if not user.is_authenticated %}
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{% url 'core:register' %}" class="btn btn-warning fw-bold">立即注册</a>
|
||||
<a href="{% url 'core:login' %}" class="btn btn-outline-secondary btn-sm text-white">登录已有账户</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="d-flex justify-content-around mt-2">
|
||||
<div>
|
||||
<div class="text-secondary x-small">账户总资产 (USDT)</div>
|
||||
<div class="fs-5 fw-bold">{{ account.balance|add:account.frozen_balance }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-4">
|
||||
<a href="{% url 'core:deposit' %}" class="glass-card p-2 d-block text-center text-decoration-none text-white">
|
||||
<i class="bi bi-wallet2 fs-4 text-warning"></i>
|
||||
<div class="x-small mt-1">充币</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a href="{% url 'core:withdraw' %}" class="glass-card p-2 d-block text-center text-decoration-none text-white">
|
||||
<i class="bi bi-cash-stack fs-4 text-info"></i>
|
||||
<div class="x-small mt-1">提币</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a href="{% url 'core:verify' %}" class="glass-card p-2 d-block text-center text-decoration-none text-white">
|
||||
<i class="bi bi-shield-check fs-4 text-success"></i>
|
||||
<div class="x-small mt-1">认证</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-2">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 px-1">
|
||||
<span class="fw-bold small">热门行情</span>
|
||||
<a href="{% url 'core:market_center' %}" class="text-warning x-small text-decoration-none">更多 <i class="bi bi-chevron-right"></i></a>
|
||||
</div>
|
||||
<div id="hot-list">
|
||||
<!-- JS populated -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hot-item { display: flex; justify-content: space-between; align-items: center; padding: 12px 8px; border-bottom: 1px solid #2b3139; }
|
||||
.hot-item:last-child { border-bottom: 0; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function getHot() {
|
||||
try {
|
||||
const r = await fetch('/api/market_data/');
|
||||
const data = await r.json();
|
||||
const list = document.getElementById('hot-list');
|
||||
list.innerHTML = '';
|
||||
data.slice(0, 8).forEach(c => {
|
||||
const base = c.symbol.replace('USDT', '');
|
||||
list.innerHTML += `
|
||||
<div class="hot-item" onclick="location.href='{% url 'core:trade' 'spot' %}?symbol=${c.symbol}'">
|
||||
<div>
|
||||
<div class="fw-bold">${base}<span class="text-secondary x-small">/USDT</span></div>
|
||||
<div class="text-secondary x-small">Vol ${ (Math.random()*100).toFixed(2) }M</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="fw-bold">${c.price}</div>
|
||||
<div class="${c.change>=0?'text-success':'text-danger'} x-small">${c.change>=0?'+':''}${c.change}%</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
getHot();
|
||||
setInterval(getHot, 5000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
26
core/templates/core/mobile/login.html
Normal file
26
core/templates/core/mobile/login.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="p-3">
|
||||
<div class="text-center mb-4 mt-5">
|
||||
<h3 class="fw-bold">欢迎回来</h3>
|
||||
<p class="text-secondary small">登录您的 {{ site_settings.site_name|default:"BitCrypto" }} 账户</p>
|
||||
</div>
|
||||
|
||||
<form method="POST" class="glass-card p-4">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">用户名</label>
|
||||
<input type="text" name="username" class="form-control" required placeholder="请输入用户名">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label small text-secondary">登录密码</label>
|
||||
<input type="password" name="password" class="form-control" required placeholder="请输入密码">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 mb-3">立即登录</button>
|
||||
<div class="text-center small">
|
||||
<span class="text-secondary">还没有账户?</span>
|
||||
<a href="{% url 'core:register' %}" class="text-warning text-decoration-none">立即注册</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
63
core/templates/core/mobile/market_center.html
Normal file
63
core/templates/core/mobile/market_center.html
Normal file
@ -0,0 +1,63 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="glass-card p-2 mb-2">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
|
||||
<input type="text" id="market-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterMarkets()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-0">
|
||||
<div class="d-flex justify-content-between p-2 text-secondary x-small border-bottom border-secondary">
|
||||
<span style="width: 40%;">名称</span>
|
||||
<span style="width: 30%;" class="text-end">最新价</span>
|
||||
<span style="width: 30%;" class="text-end">24h 涨跌</span>
|
||||
</div>
|
||||
<div id="market-list">
|
||||
<!-- JS populated -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.market-row { display: flex; justify-content: space-between; align-items: center; padding: 12px 8px; border-bottom: 1px solid #2b3139; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let allMarkets = [];
|
||||
async function getMarkets() {
|
||||
try {
|
||||
const r = await fetch('/api/market_data/');
|
||||
allMarkets = await r.json();
|
||||
renderMarkets();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function renderMarkets(filter = '') {
|
||||
const list = document.getElementById('market-list');
|
||||
list.innerHTML = '';
|
||||
allMarkets.forEach(c => {
|
||||
if (filter && !c.symbol.toLowerCase().includes(filter.toLowerCase())) return;
|
||||
const base = c.symbol.replace('USDT', '');
|
||||
list.innerHTML += `
|
||||
<div class="market-row" onclick="location.href='{% url 'core:trade' 'spot' %}?symbol=${c.symbol}'">
|
||||
<div style="width: 40%;">
|
||||
<div class="fw-bold">${base}<span class="text-secondary x-small">/USDT</span></div>
|
||||
</div>
|
||||
<div style="width: 30%;" class="text-end fw-bold">${c.price}</div>
|
||||
<div style="width: 30%;" class="text-end">
|
||||
<span class="badge ${c.change>=0?'bg-success':'bg-danger'} py-1" style="min-width: 60px;">${c.change>=0?'+':''}${c.change}%</span>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
function filterMarkets() {
|
||||
renderMarkets(document.getElementById('market-search').value);
|
||||
}
|
||||
|
||||
getMarkets();
|
||||
setInterval(getMarkets, 3000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
83
core/templates/core/mobile/profile.html
Normal file
83
core/templates/core/mobile/profile.html
Normal file
@ -0,0 +1,83 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="glass-card p-3 mb-3 d-flex align-items-center">
|
||||
<div class="bg-warning rounded-circle d-flex align-items-center justify-content-center text-dark fw-bold me-3" style="width: 50px; height: 50px; font-size: 20px;">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold">{{ user.username }}</div>
|
||||
<div class="text-secondary x-small">UID: {{ account.uid }} | {{ account.get_account_type_display }}</div>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<span class="badge {% if account.kyc_status == 'VERIFIED' %}bg-success{% else %}bg-secondary{% endif %} x-small">
|
||||
{{ account.get_kyc_status_display }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-3 mb-3">
|
||||
<div class="text-secondary x-small mb-1">总资产估值 (USDT)</div>
|
||||
<div class="fs-3 fw-bold mb-3">{{ account.balance|add:account.frozen_balance }}</div>
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-6">
|
||||
<div class="bg-dark p-2 rounded">
|
||||
<div class="text-secondary x-small">可用</div>
|
||||
<div class="fw-bold small">{{ account.balance }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="bg-dark p-2 rounded">
|
||||
<div class="text-secondary x-small">冻结</div>
|
||||
<div class="fw-bold small">{{ account.frozen_balance }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card mb-3">
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="{% url 'core:deposit' %}" class="list-group-item list-group-item-action bg-transparent text-white border-secondary py-3 d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-wallet2 me-3 text-warning"></i>充币</span>
|
||||
<i class="bi bi-chevron-right text-secondary"></i>
|
||||
</a>
|
||||
<a href="{% url 'core:withdraw' %}" class="list-group-item list-group-item-action bg-transparent text-white border-secondary py-3 d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-cash-stack me-3 text-info"></i>提币</span>
|
||||
<i class="bi bi-chevron-right text-secondary"></i>
|
||||
</a>
|
||||
<a href="{% url 'core:verify' %}" class="list-group-item list-group-item-action bg-transparent text-white border-secondary py-3 d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-shield-check me-3 text-success"></i>实名认证</span>
|
||||
<i class="bi bi-chevron-right text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-3">
|
||||
<div class="fw-bold small mb-2">资产明细</div>
|
||||
<table class="table table-dark table-sm x-small mb-0">
|
||||
<thead>
|
||||
<tr class="text-secondary">
|
||||
<th>币种</th>
|
||||
<th class="text-end">可用</th>
|
||||
<th class="text-end">冻结</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for asset in account.assets.all %}
|
||||
<tr>
|
||||
<td>{{ asset.currency }}</td>
|
||||
<td class="text-end">{{ asset.balance }}</td>
|
||||
<td class="text-end">{{ asset.frozen }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 p-2">
|
||||
<form action="{% url 'logout' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-outline-danger w-100 btn-sm">退出登录</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
45
core/templates/core/mobile/register.html
Normal file
45
core/templates/core/mobile/register.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="p-3">
|
||||
<div class="text-center mb-4 mt-4">
|
||||
<h3 class="fw-bold">注册账户</h3>
|
||||
<p class="text-secondary small">加入 {{ site_settings.site_name|default:"BitCrypto" }},开启您的交易之旅</p>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
<div class="mb-3">
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-danger py-2 small">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" class="glass-card p-4">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">用户名 / 手机号</label>
|
||||
<input type="text" name="username" class="form-control" required placeholder="请输入用户名">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">登录密码</label>
|
||||
<input type="password" name="password" class="form-control" required placeholder="请输入密码">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">确认密码</label>
|
||||
<input type="password" name="password_confirm" class="form-control" required placeholder="请再次输入密码">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label small text-secondary">图形验证码</label>
|
||||
<div class="input-group">
|
||||
<input type="text" name="captcha_input" class="form-control" required placeholder="结果">
|
||||
<span class="input-group-text bg-dark text-warning fw-bold">{{ captcha_text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 mb-3">立即注册</button>
|
||||
<div class="text-center small">
|
||||
<span class="text-secondary">已有账户?</span>
|
||||
<a href="{% url 'core:login' %}" class="text-warning text-decoration-none">立即登录</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
376
core/templates/core/mobile/trade.html
Normal file
376
core/templates/core/mobile/trade.html
Normal file
@ -0,0 +1,376 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 px-1 py-1" style="background: #161a1e;">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-dark dropdown-toggle fs-6 fw-bold text-warning" type="button" data-bs-toggle="dropdown">
|
||||
<span id="current-symbol-display">{{ symbol }}</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark shadow-lg" id="mobile-coin-list" style="max-height: 300px; overflow-y: auto;">
|
||||
<!-- JS populated -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex gap-1 bg-dark p-1 rounded">
|
||||
<a href="{% url 'core:trade' 'spot' %}?symbol={{ symbol }}" class="btn btn-xs {% if trade_type == 'SPOT' %}btn-warning text-dark{% else %}btn-dark text-secondary{% endif %} py-1 px-3 small fw-bold">现货</a>
|
||||
<a href="{% url 'core:trade' 'contract' %}?symbol={{ symbol }}" class="btn btn-xs {% if trade_type == 'CONTRACT' %}btn-warning text-dark{% else %}btn-dark text-secondary{% endif %} py-1 px-3 small fw-bold">合约</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-1 mb-2">
|
||||
<!-- Market Data Column -->
|
||||
<div class="col-5">
|
||||
<div class="glass-card p-2" style="height: 100%; font-size: 11px; background: #161a1e; border: 1px solid #2b3139;">
|
||||
<div class="d-flex justify-content-between text-secondary mb-2">
|
||||
<span>价格</span><span>数量</span>
|
||||
</div>
|
||||
<div id="m-asks" class="mb-2"></div>
|
||||
<div class="text-center py-2 fw-bold text-success border-top border-bottom border-secondary my-2" id="m-current-price" style="font-size: 16px;">--</div>
|
||||
<div id="m-bids"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Order Form Column -->
|
||||
<div class="col-7">
|
||||
<div class="glass-card p-2" style="background: #161a1e; border: 1px solid #2b3139;">
|
||||
{% if trade_type == 'SPOT' %}
|
||||
<div class="btn-group w-100 mb-2" role="group">
|
||||
<input type="radio" class="btn-check" name="trade-side" id="buy-side" checked onchange="toggleSide('BUY')">
|
||||
<label class="btn btn-outline-success btn-sm py-1" for="buy-side">买入</label>
|
||||
<input type="radio" class="btn-check" name="trade-side" id="sell-side" onchange="toggleSide('SELL')">
|
||||
<label class="btn btn-outline-danger btn-sm py-1" for="sell-side">卖出</label>
|
||||
</div>
|
||||
<select class="form-select form-select-sm bg-dark text-white border-secondary mb-2" id="spot-mode-select" onchange="toggleModeMobile()">
|
||||
<option value="limit">限价委托</option>
|
||||
<option value="market">市价委托</option>
|
||||
</select>
|
||||
{% else %}
|
||||
<div class="d-flex gap-2 mb-2">
|
||||
<div class="dropdown w-50">
|
||||
<button class="btn btn-sm btn-dark border-secondary w-100 text-warning fw-bold" id="lev-btn" data-bs-toggle="dropdown">20x</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark">
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(10)">10x</a></li>
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(20)">20x</a></li>
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(50)">50x</a></li>
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(100)">100x</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<select class="form-select form-select-sm bg-dark text-white border-secondary w-50" id="c-mode-select" onchange="toggleModeMobile()">
|
||||
<option value="limit">限价</option>
|
||||
<option value="market">市价</option>
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-dark border-secondary text-secondary" style="width: 40px; font-size: 10px;">价</span>
|
||||
<input type="text" id="m-price" class="form-control bg-transparent text-white border-secondary" placeholder="价格">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-1">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-dark border-secondary text-secondary" style="width: 40px; font-size: 10px;" id="m-label">数</span>
|
||||
<input type="number" id="m-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSliderMobile()">
|
||||
<span class="input-group-text bg-dark border-secondary text-secondary x-small" id="m-unit">{{ base_symbol }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 px-1">
|
||||
<input type="range" class="form-range custom-range" id="m-slider" min="0" max="100" step="1" value="0" oninput="applySliderMobile()">
|
||||
<div class="d-flex justify-content-between x-small text-secondary" style="font-size: 9px;">
|
||||
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="x-small mb-2 d-flex justify-content-between text-secondary" style="font-size: 10px;">
|
||||
<span>可用:</span>
|
||||
<span id="m-available" class="text-white">--</span>
|
||||
</div>
|
||||
|
||||
{% if trade_type == 'CONTRACT' %}
|
||||
<div class="row g-1">
|
||||
<div class="col-6"><button class="btn btn-success btn-sm w-100 fw-bold" onclick="submitOrderMobile('BUY')">开多</button></div>
|
||||
<div class="col-6"><button class="btn btn-danger btn-sm w-100 fw-bold" onclick="submitOrderMobile('SELL')">开空</button></div>
|
||||
</div>
|
||||
{% else %}
|
||||
<button class="btn btn-success w-100 fw-bold btn-sm" id="m-submit-btn" onclick="submitOrderMobile(side)">买入</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chart Tab -->
|
||||
<div class="glass-card p-1 mb-2" style="background: #161a1e; border: 1px solid #2b3139;">
|
||||
<div id="tradingview_widget" style="height: 250px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Positions List -->
|
||||
<div class="glass-card p-2" style="background: #161a1e; border: 1px solid #2b3139;">
|
||||
<div class="fw-bold mb-2 small border-bottom border-secondary pb-1 d-flex justify-content-between">
|
||||
<span>当前持仓/挂单</span>
|
||||
<span class="text-warning x-small" style="font-weight: normal; cursor: pointer;" onclick="location.reload()"><i class="bi bi-arrow-clockwise"></i> 刷新</span>
|
||||
</div>
|
||||
<div id="m-positions-list">
|
||||
{% if trade_type == 'CONTRACT' %}
|
||||
{% for p in account.positions.all %}
|
||||
{% if p.is_active %}
|
||||
<div class="mb-2 p-2 bg-dark rounded border-start border-3 {% if p.side == 'LONG' %}border-success{% else %}border-danger{% endif %}" style="border: 1px solid #2b3139;">
|
||||
<div class="d-flex justify-content-between x-small mb-1">
|
||||
<span class="fw-bold text-white">{{ p.symbol }} <span class="badge bg-secondary">{{ p.leverage }}x</span> <span class="{% if p.side == 'LONG' %}text-success{% else %}text-danger{% endif %}">{% if p.side == 'LONG' %}多{% else %}空{% endif %}</span></span>
|
||||
<span class="upl-val fw-bold" data-entry="{{ p.entry_price }}" data-side="{{ p.side }}" data-lots="{{ p.lots }}">--</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between x-small text-secondary" style="font-size: 10px;">
|
||||
<span>手数: {{ p.lots }}</span>
|
||||
<span>开仓价: {{ p.entry_price|floatformat:2 }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between x-small text-secondary mb-1" style="font-size: 10px;">
|
||||
<span>保证金: {{ p.margin|floatformat:2 }}</span>
|
||||
<span class="current-p-val" data-symbol="{{ p.symbol }}">当前: --</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-danger btn-xs py-0 w-100 mt-1" style="font-size: 11px;" onclick="closePos({{ p.id }})">市价平仓</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for o in account.orders.all %}
|
||||
{% if o.status == 'PENDING' %}
|
||||
<div class="mb-2 p-2 bg-dark rounded border-start border-3 border-warning" style="border: 1px solid #2b3139;">
|
||||
<div class="d-flex justify-content-between x-small">
|
||||
<span class="text-white">{{ o.symbol }} ({{ o.get_trade_type_display }}) <span class="{% if o.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ o.get_side_display }}</span></span>
|
||||
<span class="text-warning">委托中</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between x-small text-secondary mt-1" style="font-size: 10px;">
|
||||
<span>价格: {{ o.price|default:"市价" }}</span>
|
||||
<span>数量: {{ o.amount }}</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-secondary btn-xs py-0 w-100 mt-1" style="font-size: 11px;">撤单</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.btn-xs { padding: 1px 5px; font-size: 10px; }
|
||||
.order-row { display: flex; justify-content: space-between; margin-bottom: 2px; height: 16px; overflow: hidden; position: relative; }
|
||||
.order-row span { position: relative; z-index: 1; }
|
||||
.ask-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(246, 70, 93, 0.1); }
|
||||
.bid-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(14, 203, 129, 0.1); }
|
||||
.custom-range::-webkit-slider-thumb { background: #f0b90b; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
||||
<script>
|
||||
const symbol = '{{ symbol }}';
|
||||
const tradeType = '{{ trade_type }}';
|
||||
const baseAsset = '{{ base_symbol }}';
|
||||
const balance = parseFloat("{{ account.balance|default:0 }}");
|
||||
const assetBalance = parseFloat("{{ base_asset_balance|default:0 }}");
|
||||
let currentPrice = 0;
|
||||
let leverage = 20;
|
||||
let side = 'BUY';
|
||||
|
||||
function initTV() {
|
||||
new TradingView.widget({
|
||||
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
|
||||
"interval": "15", "theme": "dark", "style": "1", "locale": "zh_CN",
|
||||
"container_id": "tradingview_widget", "hide_side_toolbar": true, "save_image": false
|
||||
});
|
||||
}
|
||||
|
||||
async function getMarkets() {
|
||||
try {
|
||||
const r = await fetch('/api/market_data/');
|
||||
const data = await r.json();
|
||||
const list = document.getElementById('mobile-coin-list');
|
||||
list.innerHTML = '';
|
||||
data.forEach(c => {
|
||||
const b = c.symbol.replace('USDT', '');
|
||||
const iconUrl = `https://static.okx.com/cdn/oksupport/asset/currency/icon/${b.toLowerCase()}.png`;
|
||||
list.innerHTML += `<li><a class="dropdown-item d-flex justify-content-between" href="?symbol=${c.symbol}"><span><img src="${iconUrl}" style="width:16px;margin-right:5px;" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${b}</span><span class="${c.change>=0?'text-success':'text-danger'}">${c.price}</span></a></li>`;
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function toggleSide(s) {
|
||||
side = s;
|
||||
const btn = document.getElementById('m-submit-btn');
|
||||
if (btn) {
|
||||
if (s === 'BUY') {
|
||||
btn.className = 'btn btn-success w-100 fw-bold btn-sm';
|
||||
btn.textContent = '立即买入';
|
||||
} else {
|
||||
btn.className = 'btn btn-danger w-100 fw-bold btn-sm';
|
||||
btn.textContent = '立即卖出';
|
||||
}
|
||||
}
|
||||
|
||||
const avail = document.getElementById('m-available');
|
||||
if (tradeType === 'SPOT') {
|
||||
if (s === 'BUY') avail.textContent = balance.toFixed(2) + ' USDT';
|
||||
else avail.textContent = assetBalance.toFixed(4) + ' ' + baseAsset;
|
||||
} else {
|
||||
avail.textContent = balance.toFixed(2) + ' USDT';
|
||||
}
|
||||
toggleModeMobile();
|
||||
}
|
||||
|
||||
function toggleModeMobile() {
|
||||
const mode = tradeType === 'SPOT'
|
||||
? document.getElementById('spot-mode-select').value
|
||||
: document.getElementById('c-mode-select').value;
|
||||
const pInput = document.getElementById('m-price');
|
||||
const unit = document.getElementById('m-unit');
|
||||
const label = document.getElementById('m-label');
|
||||
|
||||
if (mode === 'market') {
|
||||
pInput.value = currentPrice || '市价';
|
||||
pInput.disabled = true;
|
||||
pInput.style.color = '#f0b90b';
|
||||
if (tradeType === 'SPOT' && side === 'BUY') {
|
||||
label.textContent = '额';
|
||||
unit.textContent = 'USDT';
|
||||
} else {
|
||||
label.textContent = '数';
|
||||
unit.textContent = tradeType === 'SPOT' ? baseAsset : '张';
|
||||
}
|
||||
} else {
|
||||
pInput.value = '';
|
||||
pInput.disabled = false;
|
||||
pInput.style.color = 'white';
|
||||
label.textContent = '数';
|
||||
unit.textContent = tradeType === 'SPOT' ? baseAsset : '张';
|
||||
}
|
||||
applySliderMobile();
|
||||
}
|
||||
|
||||
function applySliderMobile() {
|
||||
const val = document.getElementById('m-slider').value;
|
||||
const mode = tradeType === 'SPOT'
|
||||
? document.getElementById('spot-mode-select').value
|
||||
: document.getElementById('c-mode-select').value;
|
||||
|
||||
const amountInput = document.getElementById('m-amount');
|
||||
|
||||
if (tradeType === 'SPOT') {
|
||||
if (side === 'BUY') {
|
||||
if (mode === 'market') {
|
||||
amountInput.value = (balance * (val / 100)).toFixed(2);
|
||||
} else {
|
||||
const price = parseFloat(document.getElementById('m-price').value) || currentPrice;
|
||||
if (price > 0) amountInput.value = (balance * (val / 100) / price).toFixed(4);
|
||||
else amountInput.value = 0;
|
||||
}
|
||||
} else {
|
||||
amountInput.value = (assetBalance * (val / 100)).toFixed(4);
|
||||
}
|
||||
} else {
|
||||
const maxLots = (balance * leverage) / 100;
|
||||
amountInput.value = Math.floor(maxLots * (val / 100));
|
||||
}
|
||||
}
|
||||
|
||||
function updateSliderMobile() {
|
||||
const val = parseFloat(document.getElementById('m-amount').value) || 0;
|
||||
const slider = document.getElementById('m-slider');
|
||||
if (tradeType === 'SPOT') {
|
||||
if (side === 'BUY') {
|
||||
const mode = document.getElementById('spot-mode-select').value;
|
||||
if (mode === 'market') {
|
||||
if (balance > 0) slider.value = (val / balance) * 100;
|
||||
} else {
|
||||
const price = parseFloat(document.getElementById('m-price').value) || currentPrice;
|
||||
if (balance > 0 && price > 0) slider.value = (val * price / balance) * 100;
|
||||
}
|
||||
} else {
|
||||
if (assetBalance > 0) slider.value = (val / assetBalance) * 100;
|
||||
}
|
||||
} else {
|
||||
const maxLots = (balance * leverage) / 100;
|
||||
if (maxLots > 0) slider.value = (val / maxLots) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
function setLev(l) {
|
||||
leverage = l;
|
||||
document.getElementById('lev-btn').textContent = l + 'x';
|
||||
applySliderMobile();
|
||||
}
|
||||
|
||||
async function tick() {
|
||||
try {
|
||||
const r = await fetch('/api/market_data/');
|
||||
const data = await r.json();
|
||||
const d = data.find(c => c.symbol === symbol);
|
||||
if (!d) return;
|
||||
currentPrice = parseFloat(d.price);
|
||||
document.getElementById('m-current-price').textContent = currentPrice.toLocaleString();
|
||||
|
||||
const pInput = document.getElementById('m-price');
|
||||
if (pInput && pInput.disabled) pInput.value = currentPrice;
|
||||
|
||||
const askD = document.getElementById('m-asks'); const bidD = document.getElementById('m-bids');
|
||||
askD.innerHTML = ''; bidD.innerHTML = '';
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const ap = (currentPrice+(5-i)*0.1).toFixed(2);
|
||||
const bp = (currentPrice-(i+1)*0.1).toFixed(2);
|
||||
askD.innerHTML = `<div class="order-row"><span class="text-danger">${ap}</span><span class="text-secondary">${(Math.random()*0.5).toFixed(3)}</span><div class="ask-bg" style="width:${Math.random()*60}%"></div></div>` + askD.innerHTML;
|
||||
bidD.innerHTML += `<div class="order-row"><span class="text-success">${bp}</span><span class="text-secondary">${(Math.random()*0.5).toFixed(3)}</span><div class="bid-bg" style="width:${Math.random()*60}%"></div></div>`;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.current-p-val').forEach(el => { el.textContent = '当前: ' + currentPrice.toLocaleString(); });
|
||||
document.querySelectorAll('.upl-val').forEach(el => {
|
||||
const entry = parseFloat(el.getAttribute('data-entry'));
|
||||
const sideP = el.getAttribute('data-side');
|
||||
const lots = parseFloat(el.getAttribute('data-lots'));
|
||||
let upl = sideP === 'LONG' ? (currentPrice - entry) / entry * (lots * 100) : (entry - currentPrice) / entry * (lots * 100);
|
||||
el.textContent = (upl >= 0 ? '+' : '') + upl.toFixed(2) + ' USDT';
|
||||
el.className = 'upl-val fw-bold ' + (upl >= 0 ? 'text-success' : 'text-danger');
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function submitOrderMobile(sideParam) {
|
||||
const mode = tradeType === 'SPOT' ? document.getElementById('spot-mode-select').value : document.getElementById('c-mode-select').value;
|
||||
const price = document.getElementById('m-price').value;
|
||||
const amount = document.getElementById('m-amount').value;
|
||||
|
||||
if (mode === 'limit' && (!price || isNaN(parseFloat(price)))) {
|
||||
alert('请输入有效委托价格'); return;
|
||||
}
|
||||
if (!amount || amount <= 0) {
|
||||
alert('请输入数量'); return;
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await fetch('{% url "core:submit_order" %}', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' },
|
||||
body: JSON.stringify({
|
||||
symbol: symbol, side: sideParam, trade_type: tradeType, order_type: mode.toUpperCase(),
|
||||
price: mode === 'market' ? null : price, amount: amount, leverage: leverage
|
||||
})
|
||||
});
|
||||
const res = await r.json();
|
||||
if (res.status === 'success') { alert('下单成功!'); location.reload(); }
|
||||
else { alert('失败: ' + res.message); }
|
||||
} catch(e) { alert('提交出错'); }
|
||||
}
|
||||
|
||||
async function closePos(id) {
|
||||
if (confirm('确定平仓?')) {
|
||||
const r = await fetch(`/api/close_position/${id}/`, { method: 'POST', headers: { 'X-CSRFToken': '{{ csrf_token }}' } });
|
||||
const res = await r.json();
|
||||
if (res.status === 'success') location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
initTV(); getMarkets(); tick();
|
||||
setInterval(tick, 2000);
|
||||
toggleSide('BUY');
|
||||
</script>
|
||||
{% endblock %}
|
||||
37
core/templates/core/mobile/verify.html
Normal file
37
core/templates/core/mobile/verify.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="p-3">
|
||||
<div class="mb-4">
|
||||
<a href="{% url 'core:profile' %}" class="text-decoration-none text-secondary small"><i class="bi bi-chevron-left"></i> 返回</a>
|
||||
<h4 class="fw-bold mt-2">实名认证</h4>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-4 text-center">
|
||||
{% if account.kyc_status == 'UNVERIFIED' %}
|
||||
<i class="bi bi-shield-lock fs-1 text-secondary mb-3"></i>
|
||||
<h5 class="fw-bold">未认证</h5>
|
||||
<p class="text-secondary small mb-4">完成实名认证以提高账户额度和安全性</p>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3 text-start">
|
||||
<label class="form-label small text-secondary">真实姓名</label>
|
||||
<input type="text" class="form-control" required placeholder="请输入您的真实姓名">
|
||||
</div>
|
||||
<div class="mb-4 text-start">
|
||||
<label class="form-label small text-secondary">身份证号 / 护照号</label>
|
||||
<input type="text" class="form-control" required placeholder="请输入证件号码">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold">提交审核</button>
|
||||
</form>
|
||||
{% elif account.kyc_status == 'PENDING' %}
|
||||
<i class="bi bi-clock-history fs-1 text-warning mb-3"></i>
|
||||
<h5 class="fw-bold">审核中</h5>
|
||||
<p class="text-secondary small">您的认证申请正在处理中,请耐心等待。</p>
|
||||
{% elif account.kyc_status == 'VERIFIED' %}
|
||||
<i class="bi bi-patch-check-fill fs-1 text-success mb-3"></i>
|
||||
<h5 class="fw-bold">已认证</h5>
|
||||
<p class="text-secondary small">您的账户已通过实名认证。</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
40
core/templates/core/mobile/withdraw.html
Normal file
40
core/templates/core/mobile/withdraw.html
Normal file
@ -0,0 +1,40 @@
|
||||
{% extends 'core/mobile/base_mobile.html' %}
|
||||
{% block content %}
|
||||
<div class="p-3">
|
||||
<div class="mb-4">
|
||||
<a href="{% url 'core:profile' %}" class="text-decoration-none text-secondary small"><i class="bi bi-chevron-left"></i> 返回</a>
|
||||
<h4 class="fw-bold mt-2">提现 USDT</h4>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-3 mb-3">
|
||||
<div class="d-flex justify-content-between x-small mb-1">
|
||||
<span class="text-secondary">可用余额</span>
|
||||
<span class="fw-bold">{{ account.balance }} USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" class="glass-card p-3">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">提币网络</label>
|
||||
<select class="form-select form-select-sm bg-dark text-white border-secondary">
|
||||
<option>TRC20</option>
|
||||
<option>ERC20</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">提币地址</label>
|
||||
<input type="text" name="address" class="form-control" placeholder="请输入收币地址" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-secondary">提币数量</label>
|
||||
<div class="input-group">
|
||||
<input type="number" name="amount" class="form-control" placeholder="最小提币 10" required>
|
||||
<button type="button" class="btn btn-outline-warning x-small" onclick="document.getElementsByName('amount')[0].value={{ account.balance }}">全部</button>
|
||||
</div>
|
||||
<div class="x-small text-secondary mt-1">手续费: 1 USDT</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold">立即提现</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
222
core/templates/core/profile.html
Normal file
222
core/templates/core/profile.html
Normal file
@ -0,0 +1,222 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5" style="min-height: 90vh;">
|
||||
<!-- Profile Header -->
|
||||
<div class="glass-card p-4 mb-4" style="border-radius: 12px; border: 1px solid #2b3139;">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="avatar-circle bg-warning text-dark d-flex align-items-center justify-content-center fw-bold fs-3" style="width: 80px; height: 80px; border-radius: 50%;">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h2 class="fw-bold mb-1 text-white">{{ user.username }}</h2>
|
||||
<div class="d-flex gap-3 align-items-center flex-wrap">
|
||||
<span class="text-secondary small">UID: 59302{{ user.id }}</span>
|
||||
<span class="badge {% if account.kyc_status == 'completed' %}bg-success{% elif account.kyc_status == 'pending' %}bg-warning text-dark{% else %}bg-secondary{% endif %}">
|
||||
{% if account.kyc_status == 'completed' %}已认证{% elif account.kyc_status == 'pending' %}审核中{% else %}未认证{% endif %}
|
||||
</span>
|
||||
<span class="text-secondary small"><i class="bi bi-clock me-1"></i>注册时间: {{ user.date_joined|date:"Y-m-d" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-auto mt-3 mt-lg-0">
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{% url 'core:deposit' %}" class="btn btn-warning fw-bold px-4">充币</a>
|
||||
<a href="{% url 'core:withdraw' %}" class="btn btn-outline-light px-4">提币</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Left Column: Asset Overview -->
|
||||
<div class="col-lg-4">
|
||||
<div class="glass-card p-4 h-100" style="border-radius: 12px; border: 1px solid #2b3139;">
|
||||
<h5 class="fw-bold mb-4">资产总览</h5>
|
||||
<div class="mb-4">
|
||||
<p class="text-secondary small mb-1">总资产折合 (USDT)</p>
|
||||
<h2 class="fw-bold text-white mb-0">≈ {{ account.balance|default:"0.00" }} <span class="fs-5 text-secondary fw-normal">USDT</span></h2>
|
||||
</div>
|
||||
|
||||
<hr class="border-secondary opacity-25">
|
||||
|
||||
<div class="asset-item d-flex justify-content-between py-3">
|
||||
<div>
|
||||
<div class="fw-bold text-white">现货账户</div>
|
||||
<div class="text-secondary x-small">Spot Account</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="fw-bold text-white">{{ account.balance|default:"0.00" }} USDT</div>
|
||||
<div class="text-secondary x-small">≈ $0.00</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="asset-item d-flex justify-content-between py-3 border-top border-secondary border-opacity-10">
|
||||
<div>
|
||||
<div class="fw-bold text-white">合约账户</div>
|
||||
<div class="text-secondary x-small">Futures Account</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="fw-bold text-white">0.00 USDT</div>
|
||||
<div class="text-secondary x-small">未开通</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="asset-item d-flex justify-content-between py-3 border-top border-secondary border-opacity-10">
|
||||
<div>
|
||||
<div class="fw-bold text-white">理财账户</div>
|
||||
<div class="text-secondary x-small">Earn Account</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="fw-bold text-white">0.00 USDT</div>
|
||||
<div class="text-secondary x-small">年化 3.5%起</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#" class="btn btn-outline-secondary w-100 mt-4 btn-sm">查看明细 <i class="bi bi-arrow-right ms-1"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Security & Activity -->
|
||||
<div class="col-lg-8">
|
||||
<!-- Security Center -->
|
||||
<div class="glass-card p-4 mb-4" style="border-radius: 12px; border: 1px solid #2b3139;">
|
||||
<h5 class="fw-bold mb-4">安全设置</h5>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<i class="bi bi-envelope-check fs-4 text-success"></i>
|
||||
<div>
|
||||
<div class="fw-bold text-white">邮箱验证</div>
|
||||
<p class="text-secondary x-small mb-0">已绑定: {{ user.email|default:"未设置" }}</p>
|
||||
</div>
|
||||
<button class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">修改</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<i class="bi bi-shield-lock fs-4 text-warning"></i>
|
||||
<div>
|
||||
<div class="fw-bold text-white">谷歌验证器</div>
|
||||
<p class="text-secondary x-small mb-0">用于提币和安全确认</p>
|
||||
</div>
|
||||
<button class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">去绑定</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<i class="bi bi-phone fs-4 text-secondary"></i>
|
||||
<div>
|
||||
<div class="fw-bold text-white">手机验证</div>
|
||||
<p class="text-secondary x-small mb-0">未绑定手机号</p>
|
||||
</div>
|
||||
<button class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">绑定</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<i class="bi bi-person-badge fs-4 text-warning"></i>
|
||||
<div>
|
||||
<div class="fw-bold text-white">身份认证 (KYC)</div>
|
||||
<p class="text-secondary x-small mb-0">提升提现额度至 100 BTC</p>
|
||||
</div>
|
||||
<a href="{% url 'core:verify' %}" class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">立即认证</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Orders/History -->
|
||||
<div class="glass-card p-4" style="border-radius: 12px; border: 1px solid #2b3139;">
|
||||
<ul class="nav nav-tabs border-secondary mb-4" role="tablist">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link active bg-transparent border-0 text-white fw-bold px-4" data-bs-toggle="tab" data-bs-target="#orders">当前委托</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link bg-transparent border-0 text-secondary fw-bold px-4" data-bs-toggle="tab" data-bs-target="#history">交易历史</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link bg-transparent border-0 text-secondary fw-bold px-4" data-bs-toggle="tab" data-bs-target="#funds">资金流水</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="orders">
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-journal-x display-4 text-secondary opacity-25 mb-3"></i>
|
||||
<p class="text-secondary">暂无当前委托订单</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="history">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover x-small">
|
||||
<thead class="text-secondary">
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>币种</th>
|
||||
<th>类型</th>
|
||||
<th>价格</th>
|
||||
<th>数量</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for order in recent_orders %}
|
||||
<tr>
|
||||
<td>{{ order.created_at|date:"m-d H:i" }}</td>
|
||||
<td>{{ order.symbol }}</td>
|
||||
<td class="{% if order.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ order.side }}</td>
|
||||
<td>{{ order.entry_price }}</td>
|
||||
<td>{{ order.amount }}</td>
|
||||
<td class="text-info">{{ order.status }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="6" class="text-center py-4 text-secondary">暂无记录</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="funds">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover x-small">
|
||||
<thead class="text-secondary">
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>类型</th>
|
||||
<th>币种</th>
|
||||
<th>金额</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tx in recent_transactions %}
|
||||
<tr>
|
||||
<td>{{ tx.timestamp|date:"m-d H:i" }}</td>
|
||||
<td>{{ tx.get_transaction_type_display }}</td>
|
||||
<td>USDT</td>
|
||||
<td class="{% if tx.transaction_type == 'deposit' %}text-success{% else %}text-danger{% endif %}">{{ tx.amount }}</td>
|
||||
<td>{{ tx.get_status_display }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="5" class="text-center py-4 text-secondary">暂无记录</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.x-small { font-size: 11px; }
|
||||
.asset-item { transition: background 0.2s; cursor: pointer; border-radius: 8px; padding-left: 10px; padding-right: 10px; margin-left: -10px; margin-right: -10px; }
|
||||
.asset-item:hover { background: rgba(255, 255, 255, 0.05); }
|
||||
.nav-tabs .nav-link.active { border-bottom: 3px solid var(--accent-color) !important; color: var(--accent-color) !important; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
96
core/templates/core/register.html
Normal file
96
core/templates/core/register.html
Normal file
@ -0,0 +1,96 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top left, #1e2329 0%, #0b0e11 100%);">
|
||||
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 500px; border-radius: 16px;">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold text-white">注册 {{ site_settings.site_name }} 账户</h2>
|
||||
<p class="text-secondary">开启您的加密资产财富之旅</p>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
<div class="messages">
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-danger alert-dismissible fade show bg-transparent text-danger border-danger" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">用户名</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
<i class="bi bi-person"></i>
|
||||
</span>
|
||||
<input type="text" name="username" class="form-control bg-transparent text-white border-secondary" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">密码</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
<i class="bi bi-lock"></i>
|
||||
</span>
|
||||
<input type="password" name="password" class="form-control bg-transparent text-white border-secondary" placeholder="请输入密码" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">确认密码</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
<i class="bi bi-lock-fill"></i>
|
||||
</span>
|
||||
<input type="password" name="password_confirm" class="form-control bg-transparent text-white border-secondary" placeholder="请再次输入密码" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">安全验证 ({{ captcha_text }})</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
<i class="bi bi-shield-check"></i>
|
||||
</span>
|
||||
<input type="text" name="captcha_input" class="form-control bg-transparent text-white border-secondary" placeholder="请输入计算结果" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bg-transparent border-secondary" type="checkbox" id="terms" required checked>
|
||||
<label class="form-check-label text-secondary small" for="terms">
|
||||
我已阅读并同意 <a href="{% url 'core:placeholder' '服务条款' %}" class="text-warning">服务条款</a> 和 <a href="{% url 'core:placeholder' '隐私政策' %}" class="text-warning">隐私政策</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">创建账户</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-secondary small">已有账户? <a href="{% url 'core:login' %}" class="text-warning text-decoration-none fw-bold">立即登录</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.auth-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
.input-group-text { border-right: none; }
|
||||
input.form-control { border-left: none; padding: 12px; }
|
||||
input.form-control:focus {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
box-shadow: none;
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
553
core/templates/core/trade.html
Normal file
553
core/templates/core/trade.html
Normal file
@ -0,0 +1,553 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid px-2 py-2" style="background-color: #0b0e11; min-height: 90vh;">
|
||||
<div class="row g-2">
|
||||
<!-- Left: Coin List Sidebar (Desktop Only) -->
|
||||
<div class="col-lg-2 d-none d-lg-block">
|
||||
<div class="glass-card h-100 p-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
||||
<div class="p-2 mb-2">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
|
||||
<input type="text" id="coin-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterCoins()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="coin-list-container" style="max-height: calc(100vh - 160px); overflow-y: auto;">
|
||||
<table class="table table-dark table-hover table-sm mb-0 align-middle" style="font-size: 12px;">
|
||||
<thead>
|
||||
<tr class="text-secondary">
|
||||
<th class="border-0">币种</th>
|
||||
<th class="border-0 text-end">价格</th>
|
||||
<th class="border-0 text-end">24h</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="left-coin-list">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle: Main Trading Area -->
|
||||
<div class="col-lg-7">
|
||||
<!-- Market Info Bar -->
|
||||
<div class="glass-card mb-2 p-2 d-flex align-items-center justify-content-between" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="dropdown me-4">
|
||||
<button class="btn btn-transparent text-warning fw-bold fs-5 p-0" type="button" data-bs-toggle="dropdown">
|
||||
<span id="current-symbol-display">{{ symbol }}</span> <i class="bi bi-caret-down-fill small"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark shadow-lg" id="mobile-coin-list" style="max-height: 400px; overflow-y: auto; width: 250px;">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="me-4">
|
||||
<div class="fw-bold fs-5 text-success" id="header-price">--</div>
|
||||
<div class="text-secondary x-small">≈ $<span id="header-price-usd">--</span></div>
|
||||
</div>
|
||||
<div class="me-4 text-center">
|
||||
<div class="text-secondary x-small">24h 涨跌</div>
|
||||
<div class="fw-bold" id="header-change">--</div>
|
||||
</div>
|
||||
<div class="text-center d-none d-md-block">
|
||||
<div class="text-secondary x-small">24h 成交量(USDT)</div>
|
||||
<div class="fw-bold small" id="header-volume">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-1 bg-dark p-1" style="border-radius: 4px;">
|
||||
<a href="{% url 'core:trade' 'spot' %}?symbol={{ symbol }}" class="btn btn-sm {% if trade_type == 'SPOT' %}btn-warning text-dark{% else %}btn-transparent text-secondary{% endif %} fw-bold px-3">现货</a>
|
||||
<a href="{% url 'core:trade' 'contract' %}?symbol={{ symbol }}" class="btn btn-sm {% if trade_type == 'CONTRACT' %}btn-warning text-dark{% else %}btn-transparent text-secondary{% endif %} fw-bold px-3">合约</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TradingView Chart -->
|
||||
<div class="glass-card mb-2" style="height: 450px; border-radius: 4px; border: 1px solid #2b3139;">
|
||||
<div id="tradingview_widget" style="height: 100%;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Trading Forms -->
|
||||
<div class="glass-card p-3 mb-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
||||
{% if trade_type == 'SPOT' %}
|
||||
<div class="row g-4">
|
||||
<!-- Buy -->
|
||||
<div class="col-md-6 border-end border-secondary">
|
||||
<div class="btn-group w-100 mb-3" role="group">
|
||||
<input type="radio" class="btn-check" name="buy-mode" id="buy-limit" checked onchange="toggleMode('buy', 'limit')">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="buy-limit">限价委托</label>
|
||||
<input type="radio" class="btn-check" name="buy-mode" id="buy-market" onchange="toggleMode('buy', 'market')">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="buy-market">市价委托</label>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
|
||||
<input type="text" id="buy-price" class="form-control bg-transparent text-white border-secondary" placeholder="请输入价格">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;" id="buy-label">数量</span>
|
||||
<input type="number" id="buy-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSlider('buy')">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" id="buy-unit">{{ base_symbol }}</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 px-1">
|
||||
<input type="range" class="form-range custom-range" id="buy-slider" min="0" max="100" step="1" value="0" oninput="applySlider('buy')">
|
||||
<div class="d-flex justify-content-between x-small text-secondary mt-1">
|
||||
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between x-small mb-3">
|
||||
<span class="text-secondary">可用余额</span>
|
||||
<span class="text-white">{{ account.balance|default:"0.00" }} USDT</span>
|
||||
</div>
|
||||
<button class="btn btn-success w-100 fw-bold py-2 shadow" onclick="submitOrder('BUY')">买入 <span class="base-asset">{{ base_symbol }}</span></button>
|
||||
</div>
|
||||
|
||||
<!-- Sell -->
|
||||
<div class="col-md-6">
|
||||
<div class="btn-group w-100 mb-3" role="group">
|
||||
<input type="radio" class="btn-check" name="sell-mode" id="sell-limit" checked onchange="toggleMode('sell', 'limit')">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="sell-limit">限价委托</label>
|
||||
<input type="radio" class="btn-check" name="sell-mode" id="sell-market" onchange="toggleMode('sell', 'market')">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="sell-market">市价委托</label>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
|
||||
<input type="text" id="sell-price" class="form-control bg-transparent text-white border-secondary" placeholder="请输入价格">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">数量</span>
|
||||
<input type="number" id="sell-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSlider('sell')">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary">{{ base_symbol }}</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 px-1">
|
||||
<input type="range" class="form-range custom-range-danger" id="sell-slider" min="0" max="100" step="1" value="0" oninput="applySlider('sell')">
|
||||
<div class="d-flex justify-content-between x-small text-secondary mt-1">
|
||||
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between x-small mb-3">
|
||||
<span class="text-secondary">可用 <span class="base-asset">{{ base_symbol }}</span></span>
|
||||
<span class="text-white" id="sell-available-display">{{ base_asset_balance|default:"0.00" }} {{ base_symbol }}</span>
|
||||
</div>
|
||||
<button class="btn btn-danger w-100 fw-bold py-2 shadow" onclick="submitOrder('SELL')">卖出 <span class="base-asset">{{ base_symbol }}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Contract Form -->
|
||||
<div class="row g-4">
|
||||
<div class="col-12 mb-2 d-flex gap-3 align-items-center">
|
||||
<div class="btn-group" role="group">
|
||||
<input type="radio" class="btn-check" name="c-mode" id="c-limit" checked onchange="toggleMode('contract', 'limit')">
|
||||
<label class="btn btn-outline-warning btn-sm px-4" for="c-limit">限价委托</label>
|
||||
<input type="radio" class="btn-check" name="c-mode" id="c-market" onchange="toggleMode('contract', 'market')">
|
||||
<label class="btn btn-outline-warning btn-sm px-4" for="c-market">市价委托</label>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-dark btn-sm border-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" id="lev-btn">杠杆: 20x</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark">
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(10)">10x</a></li>
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(20)">20x</a></li>
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(50)">50x</a></li>
|
||||
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(100)">100x</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 border-end border-secondary">
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
|
||||
<input type="text" id="c-price" class="form-control bg-transparent text-white border-secondary" placeholder="请输入价格">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
|
||||
</div>
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">手数</span>
|
||||
<input type="number" id="c-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0" oninput="updateSlider('contract')">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary">张</span>
|
||||
</div>
|
||||
<div class="mb-4 px-1">
|
||||
<input type="range" class="form-range custom-range" id="c-slider" min="0" max="100" step="1" value="0" oninput="applySlider('contract')">
|
||||
<div class="d-flex justify-content-between x-small text-secondary mt-1">
|
||||
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between x-small mb-3 text-secondary">
|
||||
<span>所需保证金: <span class="text-white" id="c-margin-display">0.00</span> USDT</span>
|
||||
<span>可用: <span class="text-white">{{ account.balance|default:"0.00" }} USDT</span></span>
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-6"><button class="btn btn-success w-100 fw-bold shadow" onclick="submitOrder('BUY')">买入 (做多)</button></div>
|
||||
<div class="col-6"><button class="btn btn-danger w-100 fw-bold shadow" onclick="submitOrder('SELL')">卖出 (做空)</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="p-3 bg-dark rounded border border-secondary" style="font-size: 12px;">
|
||||
<div class="d-flex justify-content-between mb-2"><span>合约面值</span><span class="text-white">100 USDT / 张</span></div>
|
||||
<div class="d-flex justify-content-between mb-2"><span>当前杠杆</span><span class="text-warning" id="lev-val">20x</span></div>
|
||||
<div class="d-flex justify-content-between mb-2"><span>预计手续费</span><span class="text-white">0.05%</span></div>
|
||||
<div class="d-flex justify-content-between"><span>维持保证金率</span><span class="text-white">0.4%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Positions / Orders Tabs -->
|
||||
<div class="glass-card p-3" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
||||
<ul class="nav nav-tabs border-0 mb-3" id="tradeTabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link active bg-transparent text-white border-0 fw-bold border-bottom border-warning border-3" id="positions-tab" data-bs-toggle="tab" data-bs-target="#positions" type="button">当前持仓/订单</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="positions">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-sm align-middle mb-0" style="font-size: 12px;">
|
||||
<thead>
|
||||
<tr class="text-secondary">
|
||||
<th>合约/现货</th>
|
||||
<th>方向</th>
|
||||
<th>数量/手数</th>
|
||||
<th>开仓/委托价</th>
|
||||
<th>当前价</th>
|
||||
<th>保证金</th>
|
||||
<th>盈亏</th>
|
||||
<th class="text-end">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if trade_type == 'CONTRACT' %}
|
||||
{% for p in account.positions.all %}
|
||||
{% if p.is_active %}
|
||||
<tr>
|
||||
<td class="fw-bold">{{ p.symbol }} {{ p.leverage }}x</td>
|
||||
<td class="{% if p.side == 'LONG' %}text-success{% else %}text-danger{% endif %}">{{ p.get_side_display }}</td>
|
||||
<td>{{ p.lots }}</td>
|
||||
<td>{{ p.entry_price }}</td>
|
||||
<td class="current-p-val" data-symbol="{{ p.symbol }}">--</td>
|
||||
<td>{{ p.margin|floatformat:2 }}</td>
|
||||
<td class="upl-val" data-entry="{{ p.entry_price }}" data-side="{{ p.side }}" data-lots="{{ p.lots }}">--</td>
|
||||
<td class="text-end"><button class="btn btn-outline-danger btn-sm py-0" onclick="closePos({{ p.id }})">平仓</button></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for o in account.orders.all %}
|
||||
{% if o.status == 'PENDING' %}
|
||||
<tr>
|
||||
<td>{{ o.symbol }} ({{ o.get_trade_type_display }})</td>
|
||||
<td class="{% if o.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ o.get_side_display }}</td>
|
||||
<td>{{ o.amount }}</td>
|
||||
<td>{{ o.price|default:"市价" }}</td>
|
||||
<td class="current-p-val" data-symbol="{{ o.symbol }}">--</td>
|
||||
<td>--</td>
|
||||
<td>等待成交</td>
|
||||
<td class="text-end"><button class="btn btn-outline-secondary btn-sm py-0">撤单</button></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Order Book (Desktop Only) -->
|
||||
<div class="col-lg-3 d-none d-lg-block">
|
||||
<div class="glass-card h-100 p-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
||||
<div class="d-flex justify-content-between text-secondary x-small mb-2 px-2">
|
||||
<span>价格(USDT)</span>
|
||||
<span>数量({{ base_symbol }})</span>
|
||||
</div>
|
||||
<div id="asks" class="mb-2"></div>
|
||||
<div class="py-2 text-center border-top border-bottom border-secondary my-2">
|
||||
<span id="current-price-book" class="fs-5 fw-bold text-success">--</span>
|
||||
</div>
|
||||
<div id="bids"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.x-small { font-size: 11px; }
|
||||
.coin-icon-small { width: 18px; height: 18px; margin-right: 8px; vertical-align: middle; border-radius: 50%; }
|
||||
.order-book-row { display: flex; justify-content: space-between; font-size: 11px; padding: 2px 4px; position: relative; }
|
||||
.order-book-row span { position: relative; z-index: 1; }
|
||||
.ask-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(246, 70, 93, 0.15); transition: width 0.3s; }
|
||||
.bid-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(14, 203, 129, 0.15); transition: width 0.3s; }
|
||||
.custom-range::-webkit-slider-thumb { background: #f0b90b; cursor: pointer; }
|
||||
.custom-range-danger::-webkit-slider-thumb { background: #f6465d; cursor: pointer; }
|
||||
.coin-list-container::-webkit-scrollbar { width: 4px; }
|
||||
.coin-list-container::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 2px; }
|
||||
.nav-tabs .nav-link:hover { color: #fcd535 !important; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
||||
<script>
|
||||
const symbol = '{{ symbol }}';
|
||||
const tradeType = '{{ trade_type }}';
|
||||
const balance = parseFloat("{{ account.balance|default:0 }}");
|
||||
const baseAsset = '{{ base_symbol }}';
|
||||
const assetBalance = parseFloat("{{ base_asset_balance|default:0 }}");
|
||||
|
||||
let currentPrice = 0;
|
||||
let leverage = 20;
|
||||
let allCoins = [];
|
||||
|
||||
function initTV() {
|
||||
new TradingView.widget({
|
||||
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
|
||||
"interval": "15", "theme": "dark", "style": "1", "locale": "zh_CN",
|
||||
"container_id": "tradingview_widget", "hide_side_toolbar": false
|
||||
});
|
||||
}
|
||||
|
||||
async function getMarkets() {
|
||||
try {
|
||||
const r = await fetch('/api/market_data/');
|
||||
const data = await r.json();
|
||||
allCoins = data;
|
||||
renderCoins();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function renderCoins(filter = '') {
|
||||
const list = document.getElementById('left-coin-list');
|
||||
const mobileList = document.getElementById('mobile-coin-list');
|
||||
if (list) list.innerHTML = '';
|
||||
if (mobileList) mobileList.innerHTML = '';
|
||||
|
||||
allCoins.forEach(c => {
|
||||
if (filter && !c.symbol.toLowerCase().includes(filter.toLowerCase())) return;
|
||||
const base = c.symbol.replace('USDT', '');
|
||||
const chg = parseFloat(c.change);
|
||||
const iconUrl = `https://static.okx.com/cdn/oksupport/asset/currency/icon/${base.toLowerCase()}.png`;
|
||||
const row = `
|
||||
<tr style="cursor: pointer;" onclick="location.href='?symbol=${c.symbol}'">
|
||||
<td><img src="${iconUrl}" class="coin-icon-small" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${base}</td>
|
||||
<td class="text-end fw-bold">${parseFloat(c.price).toLocaleString()}</td>
|
||||
<td class="text-end ${chg>=0?'text-success':'text-danger'}">${chg>=0?'+':''}${chg.toFixed(2)}%</td>
|
||||
</tr>`;
|
||||
if (list) list.innerHTML += row;
|
||||
if (mobileList) mobileList.innerHTML += `<li><a class="dropdown-item d-flex justify-content-between" href="?symbol=${c.symbol}"><span><img src="${iconUrl}" class="coin-icon-small" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${base}</span><span class="${chg>=0?'text-success':'text-danger'}">${parseFloat(c.price).toLocaleString()}</span></a></li>`;
|
||||
});
|
||||
}
|
||||
|
||||
function filterCoins() {
|
||||
renderCoins(document.getElementById('coin-search').value);
|
||||
}
|
||||
|
||||
function toggleMode(side, mode) {
|
||||
let pInput;
|
||||
if (side === 'buy') {
|
||||
pInput = document.getElementById('buy-price');
|
||||
const label = document.getElementById('buy-label');
|
||||
const unit = document.getElementById('buy-unit');
|
||||
if (mode === 'market') {
|
||||
pInput.value = currentPrice || '市价';
|
||||
pInput.disabled = true;
|
||||
pInput.style.color = '#f0b90b';
|
||||
label.textContent = '成交额';
|
||||
unit.textContent = 'USDT';
|
||||
} else {
|
||||
pInput.value = '';
|
||||
pInput.disabled = false;
|
||||
pInput.style.color = 'white';
|
||||
label.textContent = '数量';
|
||||
unit.textContent = baseAsset;
|
||||
}
|
||||
} else if (side === 'sell') {
|
||||
pInput = document.getElementById('sell-price');
|
||||
if (mode === 'market') {
|
||||
pInput.value = currentPrice || '市价';
|
||||
pInput.disabled = true;
|
||||
pInput.style.color = '#f0b90b';
|
||||
} else {
|
||||
pInput.value = '';
|
||||
pInput.disabled = false;
|
||||
pInput.style.color = 'white';
|
||||
}
|
||||
} else if (side === 'contract') {
|
||||
pInput = document.getElementById('c-price');
|
||||
if (mode === 'market') {
|
||||
pInput.value = currentPrice || '市价';
|
||||
pInput.disabled = true;
|
||||
pInput.style.color = '#f0b90b';
|
||||
} else {
|
||||
pInput.value = '';
|
||||
pInput.disabled = false;
|
||||
pInput.style.color = 'white';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applySlider(side) {
|
||||
const val = document.getElementById(side + '-slider').value;
|
||||
if (side === 'buy') {
|
||||
const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market';
|
||||
if (mode === 'market') {
|
||||
document.getElementById('buy-amount').value = (balance * (val / 100)).toFixed(2);
|
||||
} else {
|
||||
const price = parseFloat(document.getElementById('buy-price').value) || currentPrice;
|
||||
if (price > 0) document.getElementById('buy-amount').value = (balance * (val / 100) / price).toFixed(4);
|
||||
else document.getElementById('buy-amount').value = 0;
|
||||
}
|
||||
} else if (side === 'sell') {
|
||||
document.getElementById('sell-amount').value = (assetBalance * (val / 100)).toFixed(4);
|
||||
} else if (side === 'contract') {
|
||||
const maxLots = (balance * leverage) / 100;
|
||||
const lots = Math.floor(maxLots * (val / 100));
|
||||
document.getElementById('c-amount').value = lots;
|
||||
const margin = (100 * lots) / leverage;
|
||||
document.getElementById('c-margin-display').textContent = margin.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
function updateSlider(side) {
|
||||
if (side === 'buy') {
|
||||
const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market';
|
||||
const amount = parseFloat(document.getElementById('buy-amount').value) || 0;
|
||||
let percent = 0;
|
||||
if (mode === 'market') {
|
||||
percent = (amount / balance) * 100;
|
||||
} else {
|
||||
const price = parseFloat(document.getElementById('buy-price').value) || currentPrice;
|
||||
if (price > 0) percent = (amount * price / balance) * 100;
|
||||
}
|
||||
document.getElementById('buy-slider').value = Math.min(100, percent || 0);
|
||||
} else if (side === 'sell') {
|
||||
const amount = parseFloat(document.getElementById('sell-amount').value) || 0;
|
||||
if (assetBalance > 0) document.getElementById('sell-slider').value = Math.min(100, (amount / assetBalance) * 100);
|
||||
} else if (side === 'contract') {
|
||||
const lots = parseFloat(document.getElementById('c-amount').value) || 0;
|
||||
const maxLots = (balance * leverage) / 100;
|
||||
if (maxLots > 0) document.getElementById('c-slider').value = Math.min(100, (lots / maxLots) * 100);
|
||||
const margin = (100 * lots) / leverage;
|
||||
document.getElementById('c-margin-display').textContent = margin.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
function setLev(l) {
|
||||
leverage = l;
|
||||
document.getElementById('lev-btn').textContent = '杠杆: ' + l + 'x';
|
||||
document.getElementById('lev-val').textContent = l + 'x';
|
||||
applySlider('contract');
|
||||
}
|
||||
|
||||
async function tick() {
|
||||
try {
|
||||
const r = await fetch('/api/market_data/');
|
||||
const data = await r.json();
|
||||
const d = data.find(c => c.symbol === symbol);
|
||||
if (!d) return;
|
||||
|
||||
currentPrice = parseFloat(d.price);
|
||||
|
||||
document.getElementById('header-price').textContent = currentPrice.toLocaleString();
|
||||
document.getElementById('header-price-usd').textContent = currentPrice.toLocaleString();
|
||||
const cpBook = document.getElementById('current-price-book');
|
||||
if (cpBook) cpBook.textContent = currentPrice.toLocaleString();
|
||||
|
||||
// Update market price in box ONLY if disabled (Market Order)
|
||||
['buy-price', 'sell-price', 'c-price'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el && el.disabled) el.value = currentPrice;
|
||||
});
|
||||
|
||||
const chg = parseFloat(d.change);
|
||||
document.getElementById('header-change').textContent = (chg>=0?'+':'') + chg.toFixed(2) + '%';
|
||||
document.getElementById('header-change').className = 'fw-bold ' + (chg>=0?'text-success':'text-danger');
|
||||
|
||||
const askD = document.getElementById('asks'); const bidD = document.getElementById('bids');
|
||||
if (askD && bidD) {
|
||||
askD.innerHTML = ''; bidD.innerHTML = '';
|
||||
for (let i = 0; i < 10; i++) {
|
||||
askD.innerHTML = `<div class="order-book-row"><span class="text-danger">${(currentPrice+(10-i)*0.1).toFixed(2)}</span><span>${(Math.random()*1.2).toFixed(4)}</span><div class="ask-bg" style="width:${Math.random()*70}%"></div></div>` + askD.innerHTML;
|
||||
bidD.innerHTML += `<div class="order-book-row"><span class="text-success">${(currentPrice-(i+1)*0.1).toFixed(2)}</span><span>${(Math.random()*1.2).toFixed(4)}</span><div class="bid-bg" style="width:${Math.random()*70}%"></div></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('.current-p-val').forEach(el => { el.textContent = currentPrice.toLocaleString(); });
|
||||
document.querySelectorAll('.upl-val').forEach(el => {
|
||||
const entry = parseFloat(el.getAttribute('data-entry'));
|
||||
const side = el.getAttribute('data-side');
|
||||
const lots = parseFloat(el.getAttribute('data-lots'));
|
||||
let upl = 0;
|
||||
if (side === 'LONG') upl = (currentPrice - entry) / entry * (lots * 100);
|
||||
else upl = (entry - currentPrice) / entry * (lots * 100);
|
||||
el.textContent = (upl >= 0 ? '+' : '') + upl.toFixed(2) + ' USDT';
|
||||
el.className = 'upl-val ' + (upl >= 0 ? 'text-success' : 'text-danger');
|
||||
});
|
||||
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function submitOrder(side) {
|
||||
const mode = tradeType === 'SPOT'
|
||||
? (document.getElementById(side.toLowerCase() + '-limit').checked ? 'LIMIT' : 'MARKET')
|
||||
: (document.getElementById('c-limit').checked ? 'LIMIT' : 'MARKET');
|
||||
|
||||
let price = 0;
|
||||
let amount = 0;
|
||||
|
||||
if (tradeType === 'SPOT') {
|
||||
price = document.getElementById(side.toLowerCase() + '-price').value;
|
||||
amount = document.getElementById(side.toLowerCase() + '-amount').value;
|
||||
} else {
|
||||
price = document.getElementById('c-price').value;
|
||||
amount = document.getElementById('c-amount').value;
|
||||
}
|
||||
|
||||
if (mode === 'LIMIT' && !price) {
|
||||
alert('请输入委托价格');
|
||||
return;
|
||||
}
|
||||
if (!amount || amount <= 0) {
|
||||
alert('请输入有效数量/手数');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await fetch('{% url "core:submit_order" %}', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' },
|
||||
body: JSON.stringify({
|
||||
symbol: symbol, side: side, trade_type: tradeType, order_type: mode,
|
||||
price: mode === 'MARKET' ? null : price, amount: amount, leverage: leverage
|
||||
})
|
||||
});
|
||||
const res = await r.json();
|
||||
if (res.status === 'success') {
|
||||
alert('下单成功!');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('下单失败: ' + res.message);
|
||||
}
|
||||
} catch(e) { alert('提交出错'); }
|
||||
}
|
||||
|
||||
async function closePos(id) {
|
||||
if (confirm('确定要平仓吗?')) {
|
||||
const r = await fetch(`/api/close_position/${id}/`, {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': '{{ csrf_token }}' }
|
||||
});
|
||||
const res = await r.json();
|
||||
if (res.status === 'success') location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
initTV(); getMarkets(); tick();
|
||||
setInterval(tick, 2000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
88
core/templates/core/verify.html
Normal file
88
core/templates/core/verify.html
Normal file
@ -0,0 +1,88 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8 col-lg-6 animate-up">
|
||||
<div class="glass-card p-4">
|
||||
<div class="text-center mb-5">
|
||||
<div class="bg-primary bg-opacity-10 rounded-circle d-inline-block p-4 mb-3">
|
||||
<i class="bi bi-person-badge text-primary fs-1"></i>
|
||||
</div>
|
||||
<h3 class="fw-bold">身份验证 (KYC)</h3>
|
||||
<p class="text-secondary">为确保您的账户安全并提高提现额度,请完成身份验证。</p>
|
||||
</div>
|
||||
|
||||
{% if account.kyc_status == 'UNVERIFIED' %}
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-6">
|
||||
<div class="p-3 border border-secondary border-opacity-25 rounded-3 text-center">
|
||||
<i class="bi bi-person-vcard fs-2 text-secondary d-block mb-2"></i>
|
||||
<span class="small fw-bold text-white">L1 基础认证</span>
|
||||
<p class="text-secondary x-small mt-2 mb-0">提现额度: 20k USDT/日</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-3 border border-warning border-opacity-50 rounded-3 text-center bg-warning bg-opacity-5">
|
||||
<i class="bi bi-shield-check fs-2 text-warning d-block mb-2"></i>
|
||||
<span class="small fw-bold text-white">L2 高级认证</span>
|
||||
<p class="text-secondary x-small mt-2 mb-0">提现额度: 无限制</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary">姓名</label>
|
||||
<input type="text" class="form-control bg-dark text-white border-secondary border-opacity-25" placeholder="请输入您的真实姓名" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary">证件类型</label>
|
||||
<select class="form-select bg-dark text-white border-secondary border-opacity-25">
|
||||
<option>身份证</option>
|
||||
<option>护照</option>
|
||||
<option>驾驶证</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">证件号码</label>
|
||||
<input type="text" class="form-control bg-dark text-white border-secondary border-opacity-25" placeholder="请输入您的证件号码" required>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6">
|
||||
<label class="form-label text-secondary small">证件正面</label>
|
||||
<div class="border border-dashed border-secondary border-opacity-50 rounded p-4 text-center cursor-pointer" style="border-style: dashed !important;">
|
||||
<i class="bi bi-plus-lg text-secondary"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label text-secondary small">证件反面</label>
|
||||
<div class="border border-dashed border-secondary border-opacity-50 rounded p-4 text-center cursor-pointer" style="border-style: dashed !important;">
|
||||
<i class="bi bi-plus-lg text-secondary"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary-custom w-100 py-3">提交认证</button>
|
||||
</form>
|
||||
{% elif account.kyc_status == 'PENDING' %}
|
||||
<div class="text-center py-5">
|
||||
<div class="spinner-border text-warning mb-4" role="status"></div>
|
||||
<h5 class="fw-bold">资料审核中</h5>
|
||||
<p class="text-secondary">我们正在加速审核您的资料,预计需要 1-2 个工作日。</p>
|
||||
<a href="/profile/" class="btn btn-outline-light mt-3">返回个人中心</a>
|
||||
</div>
|
||||
{% elif account.kyc_status == 'VERIFIED' %}
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-check-circle-fill text-success" style="font-size: 5rem;"></i>
|
||||
<h4 class="fw-bold mt-4">认证已成功</h4>
|
||||
<p class="text-secondary">您已完成身份验证,现在可以享受完整的功能服务。</p>
|
||||
<a href="/profile/" class="btn btn-outline-light mt-3">返回个人中心</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
74
core/templates/core/withdraw.html
Normal file
74
core/templates/core/withdraw.html
Normal file
@ -0,0 +1,74 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 animate-up">
|
||||
<div class="glass-card p-4">
|
||||
<h3 class="fw-bold mb-4 text-center">提取数字资产</h3>
|
||||
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<span class="text-secondary">可用余额</span>
|
||||
<span class="fw-bold">{{ account.balance|default:"0.00" }} USDT</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">提取币种</label>
|
||||
<div class="d-flex align-items-center bg-dark p-3 rounded border border-secondary border-opacity-25">
|
||||
<i class="bi bi-coin text-warning fs-4 me-3"></i>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="m-0 fw-bold">USDT</h6>
|
||||
<small class="text-secondary">Tether US</small>
|
||||
</div>
|
||||
<i class="bi bi-chevron-down text-secondary"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">提现网络</label>
|
||||
<select class="form-select bg-dark text-white border-secondary border-opacity-25 py-2">
|
||||
<option value="TRC20">TRON (TRC20) - 手续费 1.00 USDT</option>
|
||||
<option value="ERC20" disabled>Ethereum (ERC20) - 手续费 15.00 USDT</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">提现地址</label>
|
||||
<input type="text" name="address" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="请输入您的提现地址" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary">提现金额</label>
|
||||
<div class="input-group">
|
||||
<input type="number" name="amount" id="withdraw-amount" step="0.01" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="最小提现数量 10" required>
|
||||
<button type="button" class="btn btn-outline-secondary border-secondary border-opacity-25 text-warning" onclick="document.getElementById('withdraw-amount').value = '{{ account.balance }}'">全部</button>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-2 small">
|
||||
<span class="text-secondary">手续费</span>
|
||||
<span class="text-white">1.00 USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-warning bg-opacity-10 p-3 rounded mb-4">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="text-secondary small">实际到账金额</span>
|
||||
<h5 class="fw-bold text-warning m-0" id="final-amount">0.00 USDT</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary-custom w-100 py-3">提交提现申请</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('withdraw-amount').addEventListener('input', function(e) {
|
||||
const val = parseFloat(e.target.value) || 0;
|
||||
const final = Math.max(0, val - 1).toFixed(2);
|
||||
document.getElementById('final-amount').innerText = final + ' USDT';
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
30
core/urls.py
30
core/urls.py
@ -1,7 +1,31 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
from .views import home
|
||||
app_name = 'core'
|
||||
|
||||
urlpatterns = [
|
||||
path("", home, name="home"),
|
||||
]
|
||||
path('', views.index, name='index'),
|
||||
path('trade/<str:trade_type>/', views.trade, name='trade'),
|
||||
path('spot/', views.trade, {'trade_type': 'spot'}, name='spot_trade'),
|
||||
path('contract/', views.trade, {'trade_type': 'contract'}, name='contract_trade'),
|
||||
path('markets/', views.market_center, name='market_center'),
|
||||
path('profile/', views.profile, name='profile'),
|
||||
path('deposit/', views.deposit, name='deposit'),
|
||||
path('withdraw/', views.withdraw, name='withdraw'),
|
||||
path('verify/', views.verify, name='verify'),
|
||||
path('login/', views.login_view, name='login'),
|
||||
path('register/', views.register_view, name='register'),
|
||||
path('api/market_data/', views.market_data, name='market_data'),
|
||||
path('api/submit_order/', views.submit_order, name='submit_order'),
|
||||
path('api/close_position/<int:position_id>/', views.close_position, name='close_position'),
|
||||
|
||||
# Dynamic Article/Placeholder Route
|
||||
path('article/<str:title>/', views.placeholder_view, name='placeholder'),
|
||||
|
||||
# Footer links
|
||||
path('help/', views.placeholder_view, {'title': '帮助中心'}, name='help_center'),
|
||||
path('support/', views.placeholder_view, {'title': '技术支持'}, name='support'),
|
||||
path('request/', views.placeholder_view, {'title': '提交请求'}, name='submit_request'),
|
||||
path('announcements/', views.placeholder_view, {'title': '公告中心'}, name='announcements'),
|
||||
path('assets/', views.profile, name='asset_management'),
|
||||
]
|
||||
425
core/views.py
425
core/views.py
@ -1,25 +1,410 @@
|
||||
import os
|
||||
import platform
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth import login, authenticate
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
||||
from django.http import JsonResponse
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib import messages
|
||||
from .models import Account, Order, Transaction, Cryptocurrency, SiteSettings, Asset, Position
|
||||
import random
|
||||
import decimal
|
||||
import json
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.db import transaction
|
||||
|
||||
from django import get_version as django_version
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
def is_mobile(request):
|
||||
user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
|
||||
mobile_indicators = ['iphone', 'android', 'phone', 'mobile']
|
||||
return any(indicator in user_agent for indicator in mobile_indicators)
|
||||
|
||||
def index(request):
|
||||
if is_mobile(request):
|
||||
return render(request, 'core/mobile/index.html')
|
||||
return render(request, 'core/index.html')
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
now = timezone.now()
|
||||
|
||||
def trade(request, trade_type='spot'):
|
||||
symbol = request.GET.get('symbol', 'BTCUSDT')
|
||||
base_symbol = symbol.replace('USDT', '')
|
||||
cryptos = Cryptocurrency.objects.filter(is_active=True)
|
||||
account = None
|
||||
assets = {}
|
||||
base_asset_balance = decimal.Decimal('0')
|
||||
|
||||
if request.user.is_authenticated:
|
||||
account, _ = Account.objects.get_or_create(user=request.user)
|
||||
for asset in account.assets.all():
|
||||
assets[asset.currency] = asset
|
||||
if asset.currency == base_symbol:
|
||||
base_asset_balance = asset.balance
|
||||
|
||||
context = {
|
||||
"project_name": "New Style",
|
||||
"agent_brand": agent_brand,
|
||||
"django_version": django_version(),
|
||||
"python_version": platform.python_version(),
|
||||
"current_time": now,
|
||||
"host_name": host_name,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
'symbol': symbol,
|
||||
'base_symbol': base_symbol,
|
||||
'trade_type': trade_type.upper(),
|
||||
'cryptos': cryptos,
|
||||
'account': account,
|
||||
'assets': assets,
|
||||
'base_asset_balance': base_asset_balance,
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
|
||||
template = 'core/mobile/trade.html' if is_mobile(request) else 'core/trade.html'
|
||||
return render(request, template, context)
|
||||
|
||||
def market_center(request):
|
||||
cryptos = Cryptocurrency.objects.filter(is_active=True)
|
||||
template = 'core/mobile/market_center.html' if is_mobile(request) else 'core/market_center.html'
|
||||
return render(request, template, {'cryptos': cryptos})
|
||||
|
||||
def placeholder_view(request, title):
|
||||
settings = SiteSettings.objects.first()
|
||||
|
||||
if title == '服务条款':
|
||||
content = settings.terms_content if settings and settings.terms_content else '暂无服务条款内容。'
|
||||
elif title == '隐私政策':
|
||||
content = settings.privacy_content if settings and settings.privacy_content else '暂无隐私政策内容。'
|
||||
else:
|
||||
contents = {
|
||||
'帮助中心': '欢迎来到 BitCrypto 帮助中心。在这里您可以找到关于账户设置、资产充提、交易指南等所有问题的答案。我们为您准备了详尽的视频教程和图文说明,帮助您快速上手。',
|
||||
'技术支持': 'BitCrypto 技术支持团队 24/7 在线。如果您遇到任何 API 对接、系统报错或连接问题,请随时联系我们的工程师。我们承诺在 15 分钟内给予首次回复。',
|
||||
'提交请求': '请在下方表单提交您的需求或反馈。无论是工单申请、投诉建议还是商务合作,我们都会认真对待。您的每一份反馈都是我们前进的动力。',
|
||||
'公告中心': '查看 BitCrypto 最新动态。包括新币上线通知、系统维护公告、市场活动资讯等。订阅我们的邮件列表,第一时间获取核心商业情报。',
|
||||
}
|
||||
content = contents.get(title, f'这是{title}的详细内容。BitCrypto为您提供最优质的服务。')
|
||||
|
||||
faqs = [
|
||||
{'q': '如何进行身份认证?', 'a': '登录后在个人中心点击身份认证,上传身份证件并完成人脸识别即可。'},
|
||||
{'q': '充值多久能到账?', 'a': '区块链网络确认后自动到账,通常 5-30 分钟。'},
|
||||
{'q': '手续费是多少?', 'a': '现货交易基础手续费为 0.1%,使用平台币抵扣可享 7.5 折优惠。'},
|
||||
]
|
||||
|
||||
return render(request, 'core/article_detail.html', {
|
||||
'title': title,
|
||||
'content': content,
|
||||
'faqs': faqs if title == '帮助中心' else None
|
||||
})
|
||||
|
||||
@login_required
|
||||
def profile(request):
|
||||
account, created = Account.objects.get_or_create(user=request.user)
|
||||
recent_transactions = Transaction.objects.filter(account=account).order_by('-timestamp')[:10]
|
||||
recent_orders = Order.objects.filter(account=account).order_by('-created_at')[:10]
|
||||
positions = Position.objects.filter(account=account, is_active=True)
|
||||
context = {
|
||||
'account': account,
|
||||
'recent_transactions': recent_transactions,
|
||||
'recent_orders': recent_orders,
|
||||
'positions': positions,
|
||||
}
|
||||
template = 'core/mobile/profile.html' if is_mobile(request) else 'core/profile.html'
|
||||
return render(request, template, context)
|
||||
|
||||
@login_required
|
||||
def deposit(request):
|
||||
if request.method == 'POST':
|
||||
amount = request.POST.get('amount')
|
||||
tx_id = request.POST.get('tx_id')
|
||||
if amount and tx_id:
|
||||
account = request.user.account
|
||||
Transaction.objects.create(
|
||||
account=account,
|
||||
transaction_type='deposit',
|
||||
amount=decimal.Decimal(amount),
|
||||
status='pending',
|
||||
tx_hash=tx_id
|
||||
)
|
||||
return redirect('core:profile')
|
||||
template = 'core/mobile/deposit.html' if is_mobile(request) else 'core/deposit.html'
|
||||
return render(request, template)
|
||||
|
||||
@login_required
|
||||
def withdraw(request):
|
||||
account = request.user.account
|
||||
if request.method == 'POST':
|
||||
amount = request.POST.get('amount')
|
||||
address = request.POST.get('address')
|
||||
if amount and address:
|
||||
amount_dec = decimal.Decimal(amount)
|
||||
if account.balance >= amount_dec:
|
||||
account.balance -= amount_dec
|
||||
account.save()
|
||||
Transaction.objects.create(
|
||||
account=account,
|
||||
transaction_type='withdraw',
|
||||
amount=amount_dec,
|
||||
status='completed',
|
||||
tx_hash=f"wd_{random.randint(1000, 9999)}"
|
||||
)
|
||||
return redirect('core:profile')
|
||||
template = 'core/mobile/withdraw.html' if is_mobile(request) else 'core/withdraw.html'
|
||||
return render(request, template, {'account': account})
|
||||
|
||||
@login_required
|
||||
def verify(request):
|
||||
account = request.user.account
|
||||
if request.method == 'POST':
|
||||
account.kyc_status = 'pending'
|
||||
account.save()
|
||||
return redirect('core:profile')
|
||||
template = 'core/mobile/verify.html' if is_mobile(request) else 'core/verify.html'
|
||||
return render(request, template, {'account': account})
|
||||
|
||||
def register_view(request):
|
||||
if request.method == 'POST':
|
||||
username = request.POST.get('username')
|
||||
password = request.POST.get('password')
|
||||
password_confirm = request.POST.get('password_confirm')
|
||||
captcha_input = request.POST.get('captcha_input')
|
||||
captcha_expected = request.session.get('captcha_result')
|
||||
|
||||
if password != password_confirm:
|
||||
messages.error(request, "两次输入的密码不一致")
|
||||
elif str(captcha_input) != str(captcha_expected):
|
||||
messages.error(request, "验证码错误")
|
||||
elif User.objects.filter(username=username).exists():
|
||||
messages.error(request, "用户名已存在")
|
||||
else:
|
||||
user = User.objects.create_user(username=username, password=password)
|
||||
Account.objects.get_or_create(user=user)
|
||||
login(request, user)
|
||||
return redirect('core:index')
|
||||
|
||||
captcha_text, captcha_result = generate_captcha()
|
||||
request.session['captcha_result'] = captcha_result
|
||||
|
||||
template = 'core/mobile/register.html' if is_mobile(request) else 'core/register.html'
|
||||
return render(request, template, {
|
||||
'captcha_text': captcha_text
|
||||
})
|
||||
|
||||
def generate_captcha():
|
||||
a = random.randint(1, 10)
|
||||
b = random.randint(1, 10)
|
||||
op = random.choice(['+', '-', '*'])
|
||||
if op == '+': res = a + b
|
||||
elif op == '-': res = a - b
|
||||
else: res = a * b
|
||||
return f"{a} {op} {b} = ?", res
|
||||
|
||||
def login_view(request):
|
||||
if request.method == 'POST':
|
||||
form = AuthenticationForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
user = form.get_user()
|
||||
login(request, user)
|
||||
return redirect('core:index')
|
||||
else:
|
||||
form = AuthenticationForm()
|
||||
template = 'core/mobile/login.html' if is_mobile(request) else 'core/login.html'
|
||||
return render(request, template, {'form': form})
|
||||
|
||||
@login_required
|
||||
@csrf_exempt
|
||||
def submit_order(request):
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({'status': 'error', 'message': 'Invalid request'})
|
||||
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
symbol = data.get('symbol', 'BTCUSDT')
|
||||
side = data.get('side') # BUY or SELL
|
||||
trade_type = data.get('trade_type', 'SPOT')
|
||||
order_type = data.get('order_type', 'MARKET')
|
||||
price_val = data.get('price')
|
||||
amount_val = data.get('amount', 0)
|
||||
leverage = int(data.get('leverage', 20))
|
||||
|
||||
account = request.user.account
|
||||
base_symbol = symbol.replace('USDT', '')
|
||||
|
||||
# Get price (consider manual override)
|
||||
crypto = Cryptocurrency.objects.filter(symbol=base_symbol).first()
|
||||
settings = SiteSettings.objects.first()
|
||||
|
||||
current_price = crypto.current_price if (crypto and crypto.current_price > 0) else decimal.Decimal('48000')
|
||||
if settings and settings.is_pinning_active and crypto and crypto.manual_price:
|
||||
current_price = crypto.manual_price
|
||||
|
||||
with transaction.atomic():
|
||||
if trade_type == 'SPOT':
|
||||
if order_type == 'MARKET':
|
||||
# Spot Market Order: Execute Immediately
|
||||
if side == 'BUY':
|
||||
# BUY: amount_val is USDT
|
||||
total_usdt = decimal.Decimal(str(amount_val))
|
||||
if account.balance < total_usdt:
|
||||
return JsonResponse({'status': 'error', 'message': '余额不足'})
|
||||
|
||||
exec_amount = total_usdt / current_price
|
||||
account.balance -= total_usdt
|
||||
account.save()
|
||||
|
||||
asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol)
|
||||
asset.balance += exec_amount
|
||||
asset.save()
|
||||
|
||||
Order.objects.create(
|
||||
account=account, symbol=symbol, side=side, order_type=order_type,
|
||||
trade_type=trade_type, amount=exec_amount, total_usdt=total_usdt, status='FILLED'
|
||||
)
|
||||
else: # SELL
|
||||
# SELL: amount_val is coin quantity
|
||||
exec_amount = decimal.Decimal(str(amount_val))
|
||||
asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol)
|
||||
if asset.balance < exec_amount:
|
||||
return JsonResponse({'status': 'error', 'message': f'{base_symbol} 余额不足'})
|
||||
|
||||
total_usdt = exec_amount * current_price
|
||||
asset.balance -= exec_amount
|
||||
asset.save()
|
||||
|
||||
account.balance += total_usdt
|
||||
account.save()
|
||||
|
||||
Order.objects.create(
|
||||
account=account, symbol=symbol, side=side, order_type=order_type,
|
||||
trade_type=trade_type, amount=exec_amount, total_usdt=total_usdt, status='FILLED'
|
||||
)
|
||||
else: # LIMIT
|
||||
# Spot Limit Order: Freeze Assets, Pend
|
||||
price = decimal.Decimal(str(price_val))
|
||||
amount = decimal.Decimal(str(amount_val))
|
||||
if side == 'BUY':
|
||||
total_usdt = price * amount
|
||||
if account.balance < total_usdt:
|
||||
return JsonResponse({'status': 'error', 'message': '余额不足'})
|
||||
|
||||
account.balance -= total_usdt
|
||||
account.frozen_balance += total_usdt
|
||||
account.save()
|
||||
|
||||
Order.objects.create(
|
||||
account=account, symbol=symbol, side=side, order_type=order_type,
|
||||
trade_type=trade_type, amount=amount, price=price, total_usdt=total_usdt, status='PENDING'
|
||||
)
|
||||
else: # SELL
|
||||
asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol)
|
||||
if asset.balance < amount:
|
||||
return JsonResponse({'status': 'error', 'message': f'{base_symbol} 余额不足'})
|
||||
|
||||
asset.balance -= amount
|
||||
asset.frozen += amount
|
||||
asset.save()
|
||||
|
||||
Order.objects.create(
|
||||
account=account, symbol=symbol, side=side, order_type=order_type,
|
||||
trade_type=trade_type, amount=amount, price=price, status='PENDING'
|
||||
)
|
||||
|
||||
else: # CONTRACT
|
||||
# Contract: Initial Margin = face_value(100) * lots / leverage
|
||||
lots = decimal.Decimal(str(amount_val))
|
||||
face_value = decimal.Decimal('100')
|
||||
margin_required = (face_value * lots) / decimal.Decimal(str(leverage))
|
||||
|
||||
if account.balance < margin_required:
|
||||
return JsonResponse({'status': 'error', 'message': '保证金不足'})
|
||||
|
||||
if order_type == 'MARKET':
|
||||
# Contract Market Order: Immediate Open Position
|
||||
account.balance -= margin_required
|
||||
account.save()
|
||||
|
||||
Position.objects.create(
|
||||
account=account, symbol=symbol, side='LONG' if side == 'BUY' else 'SHORT',
|
||||
leverage=leverage, entry_price=current_price, lots=lots, margin=margin_required
|
||||
)
|
||||
|
||||
Order.objects.create(
|
||||
account=account, symbol=symbol, side=side, order_type=order_type,
|
||||
trade_type=trade_type, amount=lots, leverage=leverage, status='FILLED'
|
||||
)
|
||||
else: # LIMIT
|
||||
# Contract Limit Order: Freeze Margin, Pend
|
||||
price = decimal.Decimal(str(price_val))
|
||||
account.balance -= margin_required
|
||||
account.frozen_balance += margin_required
|
||||
account.save()
|
||||
|
||||
Order.objects.create(
|
||||
account=account, symbol=symbol, side=side, order_type=order_type,
|
||||
trade_type=trade_type, amount=lots, price=price, leverage=leverage, status='PENDING'
|
||||
)
|
||||
|
||||
return JsonResponse({'status': 'success'})
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({'status': 'error', 'message': str(e)})
|
||||
|
||||
@login_required
|
||||
@csrf_exempt
|
||||
def close_position(request, position_id):
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({'status': 'error', 'message': 'Invalid request'})
|
||||
|
||||
position = get_object_or_404(Position, id=position_id, account=request.user.account, is_active=True)
|
||||
|
||||
base_symbol = position.symbol.replace('USDT', '')
|
||||
crypto = Cryptocurrency.objects.filter(symbol=base_symbol).first()
|
||||
settings = SiteSettings.objects.first()
|
||||
current_price = crypto.current_price if (crypto and crypto.current_price > 0) else decimal.Decimal('48000')
|
||||
if settings and settings.is_pinning_active and crypto and crypto.manual_price:
|
||||
current_price = crypto.manual_price
|
||||
|
||||
account = request.user.account
|
||||
face_value = decimal.Decimal('100')
|
||||
|
||||
# Win/Loss Control Logic
|
||||
# 100 = Must Win, -100 = Must Loss
|
||||
forced_upl = None
|
||||
if account.win_loss_control != 0:
|
||||
# Force a small profit or loss
|
||||
total_value = position.lots * face_value
|
||||
if account.win_loss_control > 0: # Force Profit
|
||||
forced_upl = total_value * decimal.Decimal('0.05') # 5% profit
|
||||
else: # Force Loss
|
||||
forced_upl = -total_value * decimal.Decimal('0.05') # 5% loss
|
||||
|
||||
# Calculate Unrealized P&L
|
||||
if position.side == 'LONG':
|
||||
upl = (current_price - position.entry_price) / position.entry_price * (position.lots * face_value)
|
||||
else:
|
||||
upl = (position.entry_price - current_price) / position.entry_price * (position.lots * face_value)
|
||||
|
||||
# If control is active, override upl
|
||||
if forced_upl is not None:
|
||||
upl = forced_upl
|
||||
|
||||
with transaction.atomic():
|
||||
# Settlement: Margin + P&L - Fee (0.05%)
|
||||
fee = (position.lots * face_value) * decimal.Decimal('0.0005')
|
||||
account.balance += (position.margin + upl - fee)
|
||||
account.save()
|
||||
|
||||
position.is_active = False
|
||||
position.save()
|
||||
|
||||
# Log closing order
|
||||
Order.objects.create(
|
||||
account=account, symbol=position.symbol, side='SELL' if position.side == 'LONG' else 'BUY',
|
||||
order_type='MARKET', trade_type='CONTRACT', amount=position.lots, status='FILLED'
|
||||
)
|
||||
|
||||
return JsonResponse({'status': 'success'})
|
||||
|
||||
def market_data(request):
|
||||
settings = SiteSettings.objects.first()
|
||||
cryptos = Cryptocurrency.objects.filter(is_active=True)
|
||||
data = []
|
||||
for c in cryptos:
|
||||
price = float(c.current_price)
|
||||
if settings and settings.is_pinning_active and c.manual_price:
|
||||
price = float(c.manual_price)
|
||||
|
||||
symbol_display = c.symbol if 'USDT' in c.symbol else f"{c.symbol}USDT"
|
||||
|
||||
data.append({
|
||||
'symbol': symbol_display,
|
||||
'price': price,
|
||||
'change': float(c.change_24h)
|
||||
})
|
||||
return JsonResponse(data, safe=False)
|
||||
@ -1,17 +1,32 @@
|
||||
<?php
|
||||
// Generated by setup_mariadb_project.sh — edit as needed.
|
||||
/**
|
||||
* 数据库配置文件 - 请根据您的宝塔面板数据库信息进行修改
|
||||
*/
|
||||
|
||||
// 数据库主机
|
||||
define('DB_HOST', '127.0.0.1');
|
||||
|
||||
// 数据库名称
|
||||
define('DB_NAME', 'app_38239');
|
||||
|
||||
// 数据库用户名
|
||||
define('DB_USER', 'app_38239');
|
||||
|
||||
// 数据库密码
|
||||
define('DB_PASS', 'fe602355-1e20-4dc6-b292-71638a106289');
|
||||
|
||||
function db() {
|
||||
static $pdo;
|
||||
if (!$pdo) {
|
||||
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
}
|
||||
return $pdo;
|
||||
static $pdo;
|
||||
if (!$pdo) {
|
||||
try {
|
||||
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
die("数据库连接失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
return $pdo;
|
||||
}
|
||||
?>
|
||||
161
deposit.php
Normal file
161
deposit.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
check_auth();
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
$settings = get_site_settings();
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$amount = (float)($_POST['amount'] ?? 0);
|
||||
$method = $_POST['pay_method'] ?? 'USDT';
|
||||
$tx_hash = $_POST['tx_hash'] ?? 'FIAT_DEPOSIT_' . time();
|
||||
|
||||
if ($amount < 10) {
|
||||
$error = '最小充值金额为 10 USDT';
|
||||
} else {
|
||||
$stmt = db()->prepare("INSERT INTO transactions (account_id, transaction_type, currency, pay_method, amount, tx_hash, status) VALUES (?, 'deposit', 'USDT', ?, ?, ?, 'pending')");
|
||||
$stmt->execute([$account['id'], $method, $amount, $tx_hash]);
|
||||
$success = '充值申请已提交,请等待系统确认';
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="glass-card p-4">
|
||||
<h3 class="text-white mb-4"><i class="bi bi-wallet2 text-warning me-2"></i> 充值中心</h3>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<ul class="nav nav-pills mb-4 bg-dark p-1 rounded" id="depositTab" role="tablist">
|
||||
<li class="nav-item flex-fill" role="presentation">
|
||||
<button class="nav-link active w-100 text-white" id="usdt-tab" data-bs-toggle="pill" data-bs-target="#usdt-pane" type="button">数字货币 (USDT)</button>
|
||||
</li>
|
||||
<li class="nav-item flex-fill" role="presentation">
|
||||
<button class="nav-link w-100 text-white" id="fiat-tab" data-bs-toggle="pill" data-bs-target="#fiat-pane" type="button">法币充值 (Bank/Alipay)</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="depositTabContent">
|
||||
<!-- USDT Pane -->
|
||||
<div class="tab-pane fade show active" id="usdt-pane">
|
||||
<div class="row">
|
||||
<div class="col-md-5 text-center mb-4">
|
||||
<div class="bg-white p-3 d-inline-block rounded mb-3">
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=<?php echo $settings['deposit_address']; ?>" alt="QR" width="160">
|
||||
</div>
|
||||
<div class="text-secondary small">扫描上方二维码获取地址</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="mb-4">
|
||||
<label class="text-secondary small d-block mb-1">选择网络</label>
|
||||
<div class="btn-group w-100">
|
||||
<button class="btn btn-outline-warning active">TRC20</button>
|
||||
<button class="btn btn-outline-secondary disabled">ERC20</button>
|
||||
<button class="btn btn-outline-secondary disabled">BEP20</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="text-secondary small d-block mb-1">充值地址</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="addr" class="form-control bg-dark border-secondary text-white" value="<?php echo $settings['deposit_address']; ?>" readonly>
|
||||
<button class="btn btn-outline-warning" onclick="navigator.clipboard.writeText('<?php echo $settings['deposit_address']; ?>')">复制</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" class="mt-4 border-top border-secondary pt-4">
|
||||
<input type="hidden" name="pay_method" value="USDT">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">充值金额 (USDT)</label>
|
||||
<input type="number" name="amount" class="form-control bg-dark border-secondary text-white" placeholder="0.00" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">交易哈希 / 凭证号</label>
|
||||
<input type="text" name="tx_hash" class="form-control bg-dark border-secondary text-white" placeholder="请输入 TxID 或 交易流水号" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-3">提交充值申请</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Fiat Pane -->
|
||||
<div class="tab-pane fade" id="fiat-pane">
|
||||
<div class="alert alert-info bg-dark border-secondary text-secondary small mb-4">
|
||||
<i class="bi bi-info-circle text-warning me-1"></i> 法币充值按实时汇率折算,当前汇率: 1 USDT ≈ 7.25 CNY
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-3 mb-4" style="background: rgba(255,255,255,0.03);">
|
||||
<h6 class="text-white mb-3">收款账户信息</h6>
|
||||
<div class="d-flex justify-content-between mb-2 small">
|
||||
<span class="text-secondary">收款银行</span>
|
||||
<span class="text-white">工商银行 (ICBC)</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2 small">
|
||||
<span class="text-secondary">收款人</span>
|
||||
<span class="text-white">BitCrypto Technology Co.</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2 small">
|
||||
<span class="text-secondary">银行账号</span>
|
||||
<span class="text-white">6222 0000 0000 0000 888</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-0 small">
|
||||
<span class="text-secondary">开户支行</span>
|
||||
<span class="text-white">上海自贸区支行</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST">
|
||||
<input type="hidden" name="pay_method" value="FIAT">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">支付方式</label>
|
||||
<select name="sub_method" class="form-select bg-dark border-secondary text-white">
|
||||
<option value="BANK">网银转账</option>
|
||||
<option value="ALIPAY">支付宝转账</option>
|
||||
<option value="WECHAT">微信支付</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">充值金额 (CNY)</label>
|
||||
<div class="input-group">
|
||||
<input type="number" id="fiat_amount" name="fiat_amount" class="form-control bg-dark border-secondary text-white" placeholder="0.00" oninput="document.getElementById('usdt_equiv').value = (this.value / 7.25).toFixed(2)">
|
||||
<span class="input-group-text bg-dark border-secondary text-secondary">CNY</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">折合 (USDT)</label>
|
||||
<input type="text" id="usdt_equiv" name="amount" class="form-control bg-dark border-secondary text-white" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">汇款人姓名</label>
|
||||
<input type="text" name="payer_name" class="form-control bg-dark border-secondary text-white" placeholder="请填写汇款账户实名" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-3">我已完成转账</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 p-3 rounded" style="background: rgba(255,255,255,0.02); border: 1px solid rgba(255,255,255,0.05);">
|
||||
<h6 class="text-secondary small mb-2">充值说明:</h6>
|
||||
<ul class="text-secondary small ps-3 mb-0">
|
||||
<li>数字货币充值通常在 10-30 分钟内到账。</li>
|
||||
<li>法币充值需要人工审核,工作时间 (9:00-22:00) 约 30 分钟内到账。</li>
|
||||
<li>请务必在汇款备注中填写您的 UID: <span class="text-warning fw-bold"><?php echo $account['uid']; ?></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'footer.php'; ?>
|
||||
44
footer.php
Normal file
44
footer.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
$settings = get_site_settings();
|
||||
$project_name = $settings['site_name'] ?? 'BitCrypto';
|
||||
?>
|
||||
</main>
|
||||
|
||||
<?php if (!empty($settings['customer_service_url'])): ?>
|
||||
<a href="<?php echo $settings['customer_service_url']; ?>" target="_blank" class="cs-float" title="在线客服">
|
||||
<i class="bi bi-headset"></i>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<footer class="footer py-5 mt-5 border-top border-secondary bg-dark">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<h5 class="fw-bold mb-4 text-white"><?php echo $project_name; ?></h5>
|
||||
<p class="text-secondary small">全球领先的数字资产交易平台,致力于为用户提供安全、专业、透明的数字资产一站式服务。</p>
|
||||
</div>
|
||||
<div class="col-md-2 offset-md-1">
|
||||
<h6 class="text-white mb-3">产品服务</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="/trade.php?type=SPOT" class="text-secondary text-decoration-none small">现货交易</a></li>
|
||||
<li><a href="/trade.php?type=CONTRACT" class="text-secondary text-decoration-none small">永续合约</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-5 border-secondary">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 text-center text-md-start">
|
||||
<p class="text-secondary small mb-0">© 2026 <?php echo $project_name; ?> Digital Asset Exchange. All rights reserved.</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-center text-md-end mt-3 mt-md-0">
|
||||
<div class="text-secondary small">
|
||||
<i class="bi bi-globe me-2"></i> 简体中文 | USDT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
137
header.php
Normal file
137
header.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php include_once 'config.php';
|
||||
$settings = get_site_settings();
|
||||
$project_name = $settings['site_name'] ?? 'BitCrypto';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-hans">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo $project_name; ?> - 全球领先的数字资产交易平台</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="/static/css/custom.css">
|
||||
<style>
|
||||
:root {
|
||||
--bg-dark: #0b0e11;
|
||||
--bg-card: #181a20;
|
||||
--text-primary: #eaecef;
|
||||
--text-secondary: #848e9c;
|
||||
--accent-color: #fcd535;
|
||||
--up-color: #0ecb81;
|
||||
--down-color: #f6465d;
|
||||
}
|
||||
body {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar {
|
||||
background-color: var(--bg-dark);
|
||||
border-bottom: 1px solid #2b2f36;
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
.nav-link {
|
||||
color: var(--text-primary) !important;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
margin: 0 8px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.nav-link:hover { color: var(--accent-color) !important; }
|
||||
.btn-primary {
|
||||
background-color: var(--accent-color);
|
||||
border-color: var(--accent-color);
|
||||
color: #181a20;
|
||||
font-weight: 600;
|
||||
padding: 8px 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.dropdown-menu-dark {
|
||||
background-color: #1e2329;
|
||||
border: 1px solid #3b4149;
|
||||
}
|
||||
.dropdown-item {
|
||||
font-size: 14px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.dropdown-item:hover {
|
||||
background-color: #2b3139;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.cs-float {
|
||||
position: fixed;
|
||||
right: 30px;
|
||||
bottom: 40px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: var(--accent-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #181a20;
|
||||
font-size: 28px;
|
||||
box-shadow: 0 4px 20px rgba(252, 213, 53, 0.4);
|
||||
cursor: pointer;
|
||||
z-index: 9999;
|
||||
transition: all 0.3s;
|
||||
text-decoration: none;
|
||||
}
|
||||
.cs-float:hover {
|
||||
transform: scale(1.1) rotate(5deg);
|
||||
color: #181a20;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg sticky-top">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand d-flex align-items-center" href="/">
|
||||
<img src="/static/images/logo.png" alt="Logo" style="height: 32px; margin-right: 12px;">
|
||||
<span class="fw-bold fs-4 text-white"><?php echo $project_name; ?></span>
|
||||
</a>
|
||||
<button class="navbar-toggler border-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<i class="bi bi-list text-white"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="/">首页</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/trade.php?type=SPOT">现货</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/trade.php?type=CONTRACT">合约</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/market.php">行情</a></li>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center">
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<div class="dropdown">
|
||||
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle fs-5 me-2 text-warning"></i>
|
||||
<span class="text-white fw-bold"><?php echo $_SESSION['username']; ?></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow-lg" style="min-width: 220px;">
|
||||
<li><a class="dropdown-item" href="/profile.php"><i class="bi bi-person-badge me-2 text-secondary"></i>个人中心</a></li>
|
||||
<li><a class="dropdown-item" href="/verify.php"><i class="bi bi-shield-check me-2 text-secondary"></i>实名认证 (KYC)</a></li>
|
||||
<li><hr class="dropdown-divider border-secondary"></li>
|
||||
<li><a class="dropdown-item" href="/deposit.php"><i class="bi bi-plus-circle me-2 text-success"></i>充值</a></li>
|
||||
<li><a class="dropdown-item" href="/withdraw.php"><i class="bi bi-dash-circle me-2 text-danger"></i>提现</a></li>
|
||||
<li><hr class="dropdown-divider border-secondary"></li>
|
||||
<li><a class="dropdown-item text-danger" href="/logout.php"><i class="bi bi-box-arrow-right me-2"></i>安全退出</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a href="/login.php" class="nav-link me-3">登录</a>
|
||||
<a href="/register.php" class="btn btn-primary">立即注册</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
184
index.php
Normal file
184
index.php
Normal file
@ -0,0 +1,184 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="py-5" style="background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%); min-height: 70vh; display: flex; align-items: center;">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="badge bg-warning text-dark mb-3 px-3 py-2 fw-bold">#1 全球领先交易平台</div>
|
||||
<h1 class="display-3 fw-bold text-white mb-4">简单、安全、<br>极速 <span class="text-warning">交易加密货币</span></h1>
|
||||
<p class="lead text-secondary mb-5" style="max-width: 500px;">在 BitCrypto 开启您的交易之旅。支持 350+ 种加密货币,提供 100 倍杠杆合约及超低手续费现货交易。</p>
|
||||
<div class="d-flex gap-3 flex-wrap">
|
||||
<?php if(!isset($_SESSION['user_id'])): ?>
|
||||
<a href="register.php" class="btn btn-warning btn-lg px-5 fw-bold py-3 shadow">立即开启</a>
|
||||
<?php else: ?>
|
||||
<a href="trade.php" class="btn btn-warning btn-lg px-5 fw-bold py-3 shadow">进入交易</a>
|
||||
<?php endif; ?>
|
||||
<a href="market.php" class="btn btn-outline-light btn-lg px-5 py-3">查看行情</a>
|
||||
</div>
|
||||
<div class="mt-5 d-flex gap-4 text-secondary small">
|
||||
<div><i class="bi bi-shield-check text-success me-1"></i> 资产安全保障</div>
|
||||
<div><i class="bi bi-lightning text-warning me-1"></i> 毫秒级撮合</div>
|
||||
<div><i class="bi bi-headset text-info me-1"></i> 7/24 在线支持</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 d-none d-lg-block">
|
||||
<div class="position-relative">
|
||||
<img src="https://public.bnbstatic.com/image/cms/content/body_0b0e11.png" class="img-fluid floating-img" alt="Hero">
|
||||
<div class="glass-card p-3 position-absolute top-0 start-0 translate-middle mt-5 ms-5 shadow-lg" style="width: 200px; background: rgba(14, 203, 129, 0.2); border: 1px solid rgba(14, 203, 129, 0.4);">
|
||||
<div class="smaller text-success fw-bold mb-1">BTC/USDT</div>
|
||||
<div class="fs-4 fw-bold text-white" id="hero-btc-price">--</div>
|
||||
<div class="text-success smaller">+2.4% <i class="bi bi-graph-up"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Stats Bar -->
|
||||
<div class="container mt-n5 position-relative" style="z-index: 10;">
|
||||
<div class="glass-card p-4 shadow-lg" style="background: rgba(30, 32, 38, 0.95); border: 1px solid #3b4149;">
|
||||
<div class="row text-center g-4">
|
||||
<div class="col-md-3 col-6">
|
||||
<div class="text-secondary small mb-1">24h 交易量</div>
|
||||
<div class="fs-4 fw-bold text-white">$76.2B</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 border-start border-secondary border-md-0">
|
||||
<div class="text-secondary small mb-1">主流币种</div>
|
||||
<div class="fs-4 fw-bold text-white">350+</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 border-start border-secondary">
|
||||
<div class="text-secondary small mb-1">注册用户</div>
|
||||
<div class="fs-4 fw-bold text-white">120M+</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 border-start border-secondary">
|
||||
<div class="text-secondary small mb-1">最低费率</div>
|
||||
<div class="fs-4 fw-bold text-white">0.02%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Market Table -->
|
||||
<section id="markets" class="py-5 mt-5">
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-end mb-4">
|
||||
<div>
|
||||
<h2 class="fw-bold text-white">热门币种</h2>
|
||||
<p class="text-secondary mb-0">同步全球顶级交易所实时数据</p>
|
||||
</div>
|
||||
<a href="market.php" class="text-warning text-decoration-none fw-bold">更多市场 <i class="bi bi-arrow-right"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="glass-card overflow-hidden" style="border: 1px solid #2b2f36;">
|
||||
<table class="table table-dark table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="text-secondary" style="background: #1e2329;">
|
||||
<th class="ps-4 py-3">名称</th>
|
||||
<th class="py-3">最新价</th>
|
||||
<th class="py-3">24h 涨跌</th>
|
||||
<th class="py-3 d-none d-md-table-cell">24h 最高 / 最低</th>
|
||||
<th class="py-3 text-end pe-4">交易</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="market-tbody">
|
||||
<tr><td colspan="5" class="text-center py-5"><div class="spinner-border text-warning"></div></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Mobile Download Promo -->
|
||||
<section class="py-5" style="background: #181a20;">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 text-center text-md-start mb-4 mb-md-0">
|
||||
<h2 class="text-white fw-bold mb-3">随时随地 尽情交易</h2>
|
||||
<p class="text-secondary mb-4">下载 BitCrypto 移动端 App,在手机上获取实时行情、管理资产、极速下单。</p>
|
||||
<div class="d-flex gap-3 justify-content-center justify-content-md-start">
|
||||
<button class="btn btn-outline-light px-4 py-2"><i class="bi bi-apple me-2"></i>App Store</button>
|
||||
<button class="btn btn-outline-light px-4 py-2"><i class="bi bi-google-play me-2"></i>Google Play</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<img src="https://public.bnbstatic.com/image/cms/content/body_mobile_app.png" class="img-fluid" style="max-height: 300px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.floating-img { animation: floating 3s ease-in-out infinite; }
|
||||
@keyframes floating {
|
||||
0% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-20px); }
|
||||
100% { transform: translateY(0px); }
|
||||
}
|
||||
.mt-n5 { margin-top: -3rem !important; }
|
||||
@media (max-width: 768px) {
|
||||
.border-md-0 { border: 0 !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function formatPrice(p) {
|
||||
p = parseFloat(p);
|
||||
if (p < 0.0001) return p.toFixed(8);
|
||||
if (p < 0.01) return p.toFixed(6);
|
||||
if (p < 1) return p.toFixed(4);
|
||||
return p.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
}
|
||||
|
||||
async function loadMarket() {
|
||||
try {
|
||||
const res = await fetch('api.php?action=market_data');
|
||||
const data = await res.json();
|
||||
const tbody = document.getElementById('market-tbody');
|
||||
let html = '';
|
||||
|
||||
data.slice(0, 10).forEach(coin => {
|
||||
const changeClass = coin.change >= 0 ? 'text-success' : 'text-danger';
|
||||
const pFormatted = formatPrice(coin.price);
|
||||
|
||||
if (coin.symbol === 'BTCUSDT') {
|
||||
document.getElementById('hero-btc-price').textContent = '$' + pFormatted;
|
||||
}
|
||||
|
||||
html += `
|
||||
<tr style="cursor: pointer;" onclick="location.href='trade.php?symbol=${coin.symbol}'" class="border-bottom border-secondary">
|
||||
<td class="ps-4 py-4">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="${coin.icon_url}" class="me-3 rounded-circle" style="width:32px; height:32px;" onerror="this.src='https://cryptologos.cc/logos/generic-coin-logo.png'">
|
||||
<div>
|
||||
<div class="fw-bold text-white fs-6">${coin.symbol.replace('USDT', '')}</div>
|
||||
<div class="text-secondary smaller" style="font-size: 11px;">${coin.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="fw-bold fs-5 text-white">$ ${pFormatted}</span></td>
|
||||
<td><span class="${changeClass} fw-bold">${coin.change >= 0 ? '+' : ''}${coin.change}%</span></td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<div class="text-success small" style="font-size: 11px;">H: ${formatPrice(coin.high || coin.price*1.01)}</div>
|
||||
<div class="text-danger small" style="font-size: 11px;">L: ${formatPrice(coin.low || coin.price*0.99)}</div>
|
||||
</td>
|
||||
<td class="text-end pe-4">
|
||||
<a href="trade.php?symbol=${coin.symbol}" class="btn btn-sm btn-warning px-4 fw-bold">交易</a>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
tbody.innerHTML = html;
|
||||
} catch (e) {
|
||||
console.error('Market load failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
loadMarket();
|
||||
setInterval(loadMarket, 3000);
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
101
login.php
Normal file
101
login.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($username && $password) {
|
||||
$stmt = db()->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
$error = '用户名或密码错误';
|
||||
}
|
||||
} else {
|
||||
$error = '请填写所有字段';
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%);">
|
||||
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 450px; border-radius: 16px;">
|
||||
<div class="text-center mb-5">
|
||||
<div class="logo-circle mb-4 mx-auto">
|
||||
<i class="bi bi-hexagon-fill text-warning display-4"></i>
|
||||
</div>
|
||||
<h2 class="fw-bold text-white">欢迎登录 <?php echo $project_name; ?></h2>
|
||||
<p class="text-secondary">全球领先的加密资产交易平台</p>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">用户名</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
<i class="bi bi-person"></i>
|
||||
</span>
|
||||
<input type="text" name="username" class="form-control bg-transparent text-white border-secondary" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">密码</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary">
|
||||
<i class="bi bi-lock"></i>
|
||||
</span>
|
||||
<input type="password" name="password" class="form-control bg-transparent text-white border-secondary" placeholder="请输入密码" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">登录</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-secondary small">还没有账户? <a href="/register.php" class="text-warning text-decoration-none fw-bold">立即注册</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.auth-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
.input-group-text {
|
||||
border-right: none;
|
||||
}
|
||||
input.form-control {
|
||||
border-left: none;
|
||||
padding: 12px;
|
||||
}
|
||||
input.form-control:focus {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
box-shadow: none;
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
.logo-circle {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(240, 185, 11, 0.1);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
<?php include 'footer.php'; ?>
|
||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
?>
|
||||
192
market.php
Normal file
192
market.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="container py-5">
|
||||
<!-- Market Trends Cards -->
|
||||
<div class="row mb-5 g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="glass-card p-4 h-100" style="background: linear-gradient(135deg, rgba(14, 203, 129, 0.1) 0%, rgba(24, 26, 32, 1) 100%);">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-secondary mb-0">今日涨幅榜</h6>
|
||||
<i class="bi bi-graph-up-arrow text-success"></i>
|
||||
</div>
|
||||
<div id="top-gainers">
|
||||
<div class="text-center py-3 text-secondary small">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="glass-card p-4 h-100">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-secondary mb-0">新币上架</h6>
|
||||
<i class="bi bi-fire text-warning"></i>
|
||||
</div>
|
||||
<div id="new-listings">
|
||||
<div class="text-center py-3 text-secondary small">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="glass-card p-4 h-100">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-secondary mb-0">成交热度榜</h6>
|
||||
<i class="bi bi-lightning-fill text-info"></i>
|
||||
</div>
|
||||
<div id="top-volume">
|
||||
<div class="text-center py-3 text-secondary small">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-end mb-4">
|
||||
<div>
|
||||
<h2 class="text-white fw-bold">行情中心</h2>
|
||||
<p class="text-secondary mb-0">实时监测全球主流数字货币市场价格波动</p>
|
||||
</div>
|
||||
<div class="input-group" style="width: 320px;">
|
||||
<span class="input-group-text bg-dark border-secondary text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" id="market-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种名称 / 简称" onkeyup="filterMarket()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card shadow-lg border-secondary-subtle">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr class="text-secondary border-bottom border-secondary" style="background: #1e2329;">
|
||||
<th class="ps-4 py-3">币种名称</th>
|
||||
<th>最新价格</th>
|
||||
<th>24h 涨跌</th>
|
||||
<th>24h 最高/最低</th>
|
||||
<th>24h 成交额(USDT)</th>
|
||||
<th class="text-end pe-4">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="full-market-tbody">
|
||||
<tr><td colspan="6" class="text-center py-5"><div class="spinner-border text-warning"></div></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Crypto News Section (Rich Content Enhancement) -->
|
||||
<div class="mt-5">
|
||||
<h4 class="text-white mb-4"><i class="bi bi-newspaper text-warning me-2"></i> 行业资讯</h4>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="glass-card p-4 h-100 d-flex gap-3 align-items-center">
|
||||
<img src="https://public.bnbstatic.com/image/cms/article/body/202303/34e0e5e0-9e9b-4377-98e6-7b244c767e6c.png" style="width: 120px; border-radius: 8px;" class="d-none d-sm-block">
|
||||
<div>
|
||||
<span class="badge bg-primary mb-2">快讯</span>
|
||||
<h6 class="text-white mb-2">以太坊坎昆升级顺利完成,Layer 2 费用大幅下降</h6>
|
||||
<p class="text-secondary smaller mb-0">此次升级引入了 Proto-Danksharding,为以太坊扩容迈出关键一步...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="glass-card p-4 h-100 d-flex gap-3 align-items-center">
|
||||
<img src="https://public.bnbstatic.com/image/cms/article/body/202206/a27e7f6d0f8645089f2a2438883907e1.png" style="width: 120px; border-radius: 8px;" class="d-none d-sm-block">
|
||||
<div>
|
||||
<span class="badge bg-warning text-dark mb-2">深度</span>
|
||||
<h6 class="text-white mb-2">比特币减半后市场走势分析:机遇还是挑战?</h6>
|
||||
<p class="text-secondary smaller mb-0">历史数据显示,减半通常伴随着周期的转折,本次减半有何不同...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let allMarketData = [];
|
||||
|
||||
function formatPrice(p) {
|
||||
p = parseFloat(p);
|
||||
if (p < 0.0001) return p.toFixed(8);
|
||||
if (p < 0.01) return p.toFixed(6);
|
||||
if (p < 1) return p.toFixed(4);
|
||||
return p.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
}
|
||||
|
||||
async function refreshMarket() {
|
||||
try {
|
||||
const res = await fetch('api.php?action=market_data');
|
||||
const data = await res.json();
|
||||
allMarketData = data;
|
||||
renderMarket();
|
||||
renderTrends();
|
||||
} catch (e) {
|
||||
console.error('Market refresh failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
function renderTrends() {
|
||||
const gainers = [...allMarketData].sort((a, b) => b.change - a.change).slice(0, 3);
|
||||
const volumes = [...allMarketData].sort((a, b) => b.volume - a.volume).slice(0, 3);
|
||||
const news = allMarketData.slice(2, 5);
|
||||
|
||||
document.getElementById('top-gainers').innerHTML = gainers.map(c => `
|
||||
<div class="d-flex justify-content-between mb-2 align-items-center">
|
||||
<span class="text-white small fw-bold">${c.symbol.replace('USDT','')}</span>
|
||||
<span class="text-success small fw-bold">+${c.change}%</span>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
document.getElementById('new-listings').innerHTML = news.map(c => `
|
||||
<div class="d-flex justify-content-between mb-2 align-items-center">
|
||||
<span class="text-white small fw-bold">${c.symbol.replace('USDT','')}</span>
|
||||
<span class="text-warning small" style="font-size: 10px;">HOT</span>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
document.getElementById('top-volume').innerHTML = volumes.map(c => `
|
||||
<div class="d-flex justify-content-between mb-2 align-items-center">
|
||||
<span class="text-white small fw-bold">${c.symbol.replace('USDT','')}</span>
|
||||
<span class="text-secondary smaller">${(c.volume/1000000).toFixed(1)}M</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function renderMarket() {
|
||||
const search = document.getElementById('market-search').value.toLowerCase();
|
||||
let html = '';
|
||||
allMarketData.forEach(coin => {
|
||||
if (search && !coin.symbol.toLowerCase().includes(search) && !coin.name.toLowerCase().includes(search)) return;
|
||||
|
||||
const changeClass = coin.change >= 0 ? 'text-success' : 'text-danger';
|
||||
const pFormatted = formatPrice(coin.price);
|
||||
|
||||
html += `
|
||||
<tr onclick="location.href='trade.php?symbol=${coin.symbol}'" style="cursor:pointer" class="border-bottom border-secondary">
|
||||
<td class="ps-4 py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="${coin.icon_url}" class="me-3 rounded-circle" width="28" height="28" onerror="this.src='https://cryptologos.cc/logos/generic-coin-logo.png'">
|
||||
<div>
|
||||
<span class="fw-bold text-white d-block">${coin.symbol.replace('USDT', '')}</span>
|
||||
<span class="text-secondary smaller" style="font-size: 10px;">${coin.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="fw-bold fs-6">$ ${pFormatted}</td>
|
||||
<td class="${changeClass} fw-bold">${coin.change >= 0 ? '+' : ''}${coin.change}%</td>
|
||||
<td>
|
||||
<div class="text-success" style="font-size: 11px;">H: ${formatPrice(coin.high || coin.price * 1.01)}</div>
|
||||
<div class="text-danger" style="font-size: 11px;">L: ${formatPrice(coin.low || coin.price * 0.99)}</div>
|
||||
</td>
|
||||
<td class="text-secondary smaller">${parseFloat(coin.volume || 0).toLocaleString()}</td>
|
||||
<td class="text-end pe-4">
|
||||
<a href="trade.php?symbol=${coin.symbol}" class="btn btn-sm btn-outline-warning fw-bold px-3 py-1" style="font-size: 12px;">交易</a>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
document.getElementById('full-market-tbody').innerHTML = html;
|
||||
}
|
||||
|
||||
function filterMarket() { renderMarket(); }
|
||||
|
||||
refreshMarket();
|
||||
setInterval(refreshMarket, 3000);
|
||||
</script>
|
||||
<?php include 'footer.php'; ?>
|
||||
221
profile.php
Normal file
221
profile.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
check_auth();
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
|
||||
// Fetch assets
|
||||
$stmt = db()->prepare("SELECT * FROM assets WHERE account_id = ? AND balance > 0");
|
||||
$stmt->execute([$account['id']]);
|
||||
$assets = $stmt->fetchAll();
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="container py-5">
|
||||
<div class="row">
|
||||
<!-- User Sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<div class="glass-card p-4 mb-4 text-center">
|
||||
<div class="position-relative d-inline-block mb-3">
|
||||
<i class="bi bi-person-circle text-warning" style="font-size: 80px;"></i>
|
||||
<?php if($account['kyc_status'] == 'VERIFIED'): ?>
|
||||
<span class="position-absolute bottom-0 end-0 bg-success rounded-circle p-1" style="border: 4px solid var(--bg-card);">
|
||||
<i class="bi bi-check-lg text-white" style="font-size: 14px;"></i>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<h4 class="text-white mb-1"><?php echo htmlspecialchars($_SESSION['username']); ?></h4>
|
||||
<div class="d-flex justify-content-center gap-2 mb-4">
|
||||
<span class="badge bg-dark border border-secondary text-secondary px-3 py-2">UID: <?php echo $account['uid']; ?></span>
|
||||
<span class="badge bg-<?php echo $account['kyc_status']=='VERIFIED'?'success':'warning'; ?> text-<?php echo $account['kyc_status']=='VERIFIED'?'white':'dark'; ?> px-3 py-2">
|
||||
<?php
|
||||
$kyc_labels = ['UNVERIFIED'=>'未认证', 'PENDING'=>'审核中', 'VERIFIED'=>'已认证', 'REJECTED'=>'已驳回'];
|
||||
echo $kyc_labels[$account['kyc_status']] ?? '未认证';
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush bg-transparent text-start">
|
||||
<a href="verify.php" class="list-group-item bg-transparent text-secondary border-secondary d-flex justify-content-between px-0 py-3">
|
||||
<span><i class="bi bi-shield-check me-2"></i> 实名认证</span>
|
||||
<span class="<?php echo $account['kyc_status']=='VERIFIED'?'text-success':'text-warning'; ?> small">
|
||||
<?php echo $account['kyc_status'] == 'UNVERIFIED' ? '去认证 <i class="bi bi-chevron-right"></i>' : $kyc_labels[$account['kyc_status']]; ?>
|
||||
</span>
|
||||
</a>
|
||||
<div class="list-group-item bg-transparent text-secondary border-secondary d-flex justify-content-between px-0 py-3">
|
||||
<span><i class="bi bi-star me-2"></i> 信用评分</span>
|
||||
<span class="text-white fw-bold"><?php echo $account['credit_score']; ?></span>
|
||||
</div>
|
||||
<div class="list-group-item bg-transparent text-secondary border-secondary d-flex justify-content-between px-0 py-3">
|
||||
<span><i class="bi bi-calendar3 me-2"></i> 注册时间</span>
|
||||
<span class="text-white small"><?php echo substr($account['created_at'], 0, 10); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="logout.php" class="btn btn-outline-danger w-100 mt-4 border-0" style="background: rgba(220, 53, 69, 0.1);">安全退出</a>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-4 mb-4">
|
||||
<h6 class="text-white mb-3">安全中心</h6>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="bg-success rounded-circle p-2 me-3" style="width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;">
|
||||
<i class="bi bi-envelope text-white small"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-white small">绑定邮箱</div>
|
||||
<div class="text-secondary smaller"><?php echo substr($_SESSION['username'], 0, 3); ?>***@gmail.com</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="bg-warning rounded-circle p-2 me-3" style="width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;">
|
||||
<i class="bi bi-phone text-dark small"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-white small">绑定手机</div>
|
||||
<div class="text-warning smaller" style="cursor: pointer;">未绑定,去绑定 ></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Content -->
|
||||
<div class="col-lg-8">
|
||||
<!-- Balance Card -->
|
||||
<div class="glass-card p-4 mb-4" style="background: linear-gradient(135deg, #2b2f36 0%, #181a20 100%); border: 1px solid #fcd53533; box-shadow: 0 10px 30px rgba(0,0,0,0.5);">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-7">
|
||||
<div class="text-secondary mb-2 small fw-bold">账户总资产 (估算)</div>
|
||||
<h2 class="text-white fw-bold mb-1">
|
||||
<span class="text-warning">$</span> <?php echo number_format($account['balance'] + $account['frozen_balance'], 2); ?> <span class="fs-6 text-secondary fw-normal">USDT</span>
|
||||
</h2>
|
||||
<div class="text-secondary smaller">≈ <?php echo number_format(($account['balance'] + $account['frozen_balance']) * 7.25, 2); ?> CNY</div>
|
||||
</div>
|
||||
<div class="col-md-5 text-md-end mt-3 mt-md-0">
|
||||
<a href="deposit.php" class="btn btn-warning fw-bold px-4 me-2">充值</a>
|
||||
<a href="withdraw.php" class="btn btn-outline-light fw-bold px-4">提现</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Tabs -->
|
||||
<div class="glass-card p-4 mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="text-white mb-0"><i class="bi bi-wallet2 text-warning me-2"></i> 资产概览</h5>
|
||||
<div class="form-check form-switch small">
|
||||
<input class="form-check-input" type="checkbox" id="hideZero">
|
||||
<label class="form-check-label text-secondary" for="hideZero">隐藏 0 余额</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover align-middle">
|
||||
<thead>
|
||||
<tr class="text-secondary small border-bottom border-secondary">
|
||||
<th class="ps-0">币种</th>
|
||||
<th>总额</th>
|
||||
<th>可用</th>
|
||||
<th>冻结</th>
|
||||
<th class="text-end pe-0">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="ps-0">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="bg-success rounded-circle me-2 d-flex align-items-center justify-content-center" style="width:28px; height:28px;">
|
||||
<i class="bi bi-currency-dollar text-white"></i>
|
||||
</div>
|
||||
<span class="fw-bold">USDT</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-white fw-bold"><?php echo number_format($account['balance'] + $account['frozen_balance'], 2); ?></td>
|
||||
<td class="text-white"><?php echo number_format($account['balance'], 2); ?></td>
|
||||
<td class="text-secondary"><?php echo number_format($account['frozen_balance'], 2); ?></td>
|
||||
<td class="text-end pe-0">
|
||||
<a href="trade.php" class="btn btn-sm btn-link text-warning text-decoration-none">交易</a>
|
||||
<a href="withdraw.php" class="btn btn-sm btn-link text-secondary text-decoration-none ms-2">提现</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php foreach ($assets as $asset): if($asset['currency'] == 'USDT') continue; ?>
|
||||
<tr>
|
||||
<td class="ps-0">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="bg-secondary rounded-circle me-2 d-flex align-items-center justify-content-center fw-bold" style="width:28px; height:28px; font-size: 10px;">
|
||||
<?php echo substr($asset['currency'], 0, 1); ?>
|
||||
</div>
|
||||
<span class="fw-bold"><?php echo $asset['currency']; ?></span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-white fw-bold"><?php echo number_format($asset['balance'] + $asset['frozen'], 6); ?></td>
|
||||
<td class="text-white"><?php echo number_format($asset['balance'], 6); ?></td>
|
||||
<td class="text-secondary"><?php echo number_format($asset['frozen'], 6); ?></td>
|
||||
<td class="text-end pe-0">
|
||||
<a href="trade.php?symbol=<?php echo $asset['currency']; ?>USDT" class="btn btn-sm btn-link text-warning text-decoration-none">交易</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transaction History -->
|
||||
<div class="glass-card p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="text-white mb-0"><i class="bi bi-clock-history text-warning me-2"></i> 最近充提</h5>
|
||||
<a href="#" class="text-warning small text-decoration-none">查看更多记录</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark small align-middle">
|
||||
<thead>
|
||||
<tr class="text-secondary border-bottom border-secondary">
|
||||
<th>时间</th>
|
||||
<th>类型</th>
|
||||
<th>金额</th>
|
||||
<th>方式</th>
|
||||
<th class="text-end">状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$stmt = db()->prepare("SELECT * FROM transactions WHERE account_id = ? ORDER BY timestamp DESC LIMIT 10");
|
||||
$stmt->execute([$account['id']]);
|
||||
$txs = $stmt->fetchAll();
|
||||
if (empty($txs)):
|
||||
?>
|
||||
<tr><td colspan="5" class="text-center text-secondary py-5">暂无流水记录</td></tr>
|
||||
<?php else: foreach($txs as $tx): ?>
|
||||
<tr>
|
||||
<td class="text-secondary"><?php echo substr($tx['timestamp'], 2, 14); ?></td>
|
||||
<td>
|
||||
<span class="badge bg-<?php echo $tx['transaction_type']=='deposit'?'success-subtle text-success':'danger-subtle text-danger'; ?> border border-<?php echo $tx['transaction_type']=='deposit'?'success':'danger'; ?> px-2 py-1">
|
||||
<?php echo $tx['transaction_type']=='deposit'?'充值':'提现'; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="fw-bold"><?php echo number_format($tx['amount'], 2); ?> <span class="smaller fw-normal">USDT</span></td>
|
||||
<td class="text-secondary"><?php echo $tx['pay_method'] ?? 'USDT'; ?></td>
|
||||
<td class="text-end">
|
||||
<?php if($tx['status'] == 'completed'): ?>
|
||||
<span class="text-success"><i class="bi bi-check-circle me-1"></i>已成功</span>
|
||||
<?php elseif($tx['status'] == 'failed'): ?>
|
||||
<span class="text-danger"><i class="bi bi-x-circle me-1"></i>已驳回</span>
|
||||
<?php else: ?>
|
||||
<span class="text-warning"><i class="bi bi-hourglass-split me-1"></i>审核中</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.bg-success-subtle { background-color: rgba(14, 203, 129, 0.1) !important; }
|
||||
.bg-danger-subtle { background-color: rgba(246, 70, 93, 0.1) !important; }
|
||||
.smaller { font-size: 0.75rem; }
|
||||
</style>
|
||||
<?php include 'footer.php'; ?>
|
||||
88
register.php
Normal file
88
register.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
||||
|
||||
if ($username && $password && $confirm_password) {
|
||||
if ($password !== $confirm_password) {
|
||||
$error = '两次输入的密码不一致';
|
||||
} else {
|
||||
$db = db();
|
||||
$stmt = $db->prepare("SELECT id FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
if ($stmt->fetch()) {
|
||||
$error = '用户名已存在';
|
||||
} else {
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
|
||||
$stmt->execute([$username, $hashed_password]);
|
||||
$user_id = $db->lastInsertId();
|
||||
|
||||
$uid = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
|
||||
$stmt = $db->prepare("INSERT INTO accounts (user_id, uid, balance) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$user_id, $uid, 10000.00]); // Default 10000 USDT for simulated
|
||||
|
||||
$db->commit();
|
||||
$success = '注册成功!正在跳转到登录页...';
|
||||
header("Refresh: 2; URL=login.php");
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$error = '注册失败: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error = '请填写所有字段';
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%);">
|
||||
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 450px; border-radius: 16px;">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold text-white">创建账户</h2>
|
||||
<p class="text-secondary">加入全球领先的交易社区</p>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">用户名</label>
|
||||
<input type="text" name="username" class="form-control bg-transparent text-white border-secondary" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">密码</label>
|
||||
<input type="password" name="password" class="form-control bg-transparent text-white border-secondary" placeholder="请输入密码" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-secondary small fw-bold">确认密码</label>
|
||||
<input type="password" name="confirm_password" class="form-control bg-transparent text-white border-secondary" placeholder="请再次输入密码" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">立即注册</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-secondary small">已有账户? <a href="/login.php" class="text-warning text-decoration-none fw-bold">立即登录</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'footer.php'; ?>
|
||||
@ -1,3 +1,30 @@
|
||||
asgiref==3.10.0
|
||||
certifi==2022.9.24
|
||||
chardet==5.1.0
|
||||
charset-normalizer==3.0.1
|
||||
dbus-python==1.3.2
|
||||
distro-info==1.5+deb12u1
|
||||
Django==5.2.7
|
||||
httplib2==0.20.4
|
||||
idna==3.3
|
||||
markdown-it-py==2.1.0
|
||||
mdurl==0.1.2
|
||||
mysqlclient==2.2.7
|
||||
netifaces==0.11.0
|
||||
pycurl==7.45.2
|
||||
Pygments==2.14.0
|
||||
PyGObject==3.42.2
|
||||
pyparsing==3.0.9
|
||||
PySimpleSOAP==1.16.2
|
||||
python-apt==2.6.0
|
||||
python-debian==0.1.49
|
||||
python-debianbts==4.0.1
|
||||
python-dotenv==1.1.1
|
||||
PyYAML==6.0
|
||||
reportbug==12.0.0
|
||||
requests==2.28.1
|
||||
rich==13.3.1
|
||||
six==1.16.0
|
||||
sqlparse==0.5.3
|
||||
unattended-upgrades==0.1
|
||||
urllib3==1.26.12
|
||||
|
||||
45
reset_admin.php
Normal file
45
reset_admin.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* 管理员密码重置工具
|
||||
* 使用方法:上传到宝塔根目录,访问:域名/reset_admin.php
|
||||
* 注意:使用后请务必删除此文件以保安全!
|
||||
*/
|
||||
require_once 'config.php';
|
||||
|
||||
$new_username = 'admin';
|
||||
$new_password = 'admin123'; // 您可以将此处修改为您想要的密码
|
||||
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
|
||||
// 检查表是否存在
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS admins (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(150) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)");
|
||||
|
||||
// 更新或插入管理员
|
||||
$stmt = $db->prepare("SELECT id FROM admins WHERE username = ?");
|
||||
$stmt->execute([$new_username]);
|
||||
if ($stmt->fetch()) {
|
||||
$stmt = $db->prepare("UPDATE admins SET password = ? WHERE username = ?");
|
||||
$stmt->execute([$hashed_password, $new_username]);
|
||||
} else {
|
||||
$stmt = $db->prepare("INSERT INTO admins (username, password) VALUES (?, ?)");
|
||||
$stmt->execute([$new_username, $hashed_password]);
|
||||
}
|
||||
|
||||
echo "<h3>管理员账号重置成功!</h3>";
|
||||
echo "<p>用户名: <b>$new_username</b></p>";
|
||||
echo "<p>新密码: <b>$new_password</b></p>";
|
||||
echo "<p style='color:red;'><b>警告:请立即删除 reset_admin.php 文件!</b></p>";
|
||||
echo "<a href='admin_login.php'>前往登录</a>";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "<h3>重置失败:</h3>";
|
||||
echo "<p>" . $e->getMessage() . "</p>";
|
||||
echo "<p>请检查 db/config.php 中的数据库连接配置是否正确。</p>";
|
||||
}
|
||||
114
schema.sql
Normal file
114
schema.sql
Normal file
@ -0,0 +1,114 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(150) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admins (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(150) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
uid VARCHAR(6) UNIQUE NOT NULL,
|
||||
account_type ENUM('SIMULATED', 'REAL') DEFAULT 'SIMULATED',
|
||||
balance DECIMAL(30, 8) DEFAULT 0,
|
||||
frozen_balance DECIMAL(30, 8) DEFAULT 0,
|
||||
credit_score INT DEFAULT 80,
|
||||
kyc_status ENUM('UNVERIFIED', 'PENDING', 'VERIFIED', 'REJECTED') DEFAULT 'UNVERIFIED',
|
||||
win_loss_control INT DEFAULT 0, -- 1: Always Win, -1: Always Loss, 0: Normal
|
||||
language VARCHAR(10) DEFAULT 'zh-hans',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS site_settings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
site_name VARCHAR(100) DEFAULT 'BitCrypto',
|
||||
contact_email VARCHAR(100) DEFAULT 'support@example.com',
|
||||
deposit_address VARCHAR(255) DEFAULT 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
||||
customer_service_url TEXT,
|
||||
terms_content TEXT,
|
||||
privacy_content TEXT,
|
||||
is_pinning_active BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cryptocurrencies (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
symbol VARCHAR(20) UNIQUE NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
icon_url TEXT,
|
||||
current_price DECIMAL(30, 8) DEFAULT 0,
|
||||
manual_price DECIMAL(30, 8) DEFAULT 0,
|
||||
change_24h DECIMAL(10, 2) DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS assets (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
account_id INT NOT NULL,
|
||||
currency VARCHAR(10) NOT NULL,
|
||||
balance DECIMAL(30, 8) DEFAULT 0,
|
||||
frozen DECIMAL(30, 8) DEFAULT 0,
|
||||
UNIQUE KEY account_currency (account_id, currency),
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
account_id INT NOT NULL,
|
||||
symbol VARCHAR(20) DEFAULT 'BTCUSDT',
|
||||
trade_type ENUM('SPOT', 'CONTRACT') DEFAULT 'SPOT',
|
||||
side ENUM('BUY', 'SELL') NOT NULL,
|
||||
order_type ENUM('LIMIT', 'MARKET') NOT NULL,
|
||||
price DECIMAL(30, 8),
|
||||
amount DECIMAL(30, 8) NOT NULL,
|
||||
total_usdt DECIMAL(30, 8),
|
||||
leverage INT DEFAULT 1,
|
||||
status ENUM('PENDING', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED') DEFAULT 'PENDING',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS positions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
account_id INT NOT NULL,
|
||||
symbol VARCHAR(20) NOT NULL,
|
||||
side ENUM('LONG', 'SHORT') NOT NULL,
|
||||
leverage INT DEFAULT 20,
|
||||
entry_price DECIMAL(30, 8) NOT NULL,
|
||||
lots DECIMAL(30, 8) NOT NULL,
|
||||
margin DECIMAL(30, 8) NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS transactions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
account_id INT NOT NULL,
|
||||
transaction_type ENUM('deposit', 'withdraw') NOT NULL,
|
||||
currency VARCHAR(10) DEFAULT 'USDT',
|
||||
amount DECIMAL(30, 8) DEFAULT 0,
|
||||
tx_hash VARCHAR(255),
|
||||
status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Seed initial data
|
||||
INSERT INTO site_settings (site_name, contact_email, deposit_address) VALUES ('BitCrypto', 'support@bitcrypto.com', 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t');
|
||||
INSERT INTO admins (username, password) VALUES ('admin', '$2y$10$vK6.O/M57M.n5oYvT6pXve/tE6Yk.7Zg8XfVv0VzP2/k1e7Y6oM5e'); -- password: admin
|
||||
INSERT INTO cryptocurrencies (symbol, name, icon_url, current_price, change_24h) VALUES
|
||||
('BTCUSDT', 'Bitcoin', 'https://cryptologos.cc/logos/bitcoin-btc-logo.png', 45000.00, 1.2),
|
||||
('ETHUSDT', 'Ethereum', 'https://cryptologos.cc/logos/ethereum-eth-logo.png', 2500.00, -0.5),
|
||||
('BNBUSDT', 'Binance Coin', 'https://cryptologos.cc/logos/binance-coin-bnb-logo.png', 300.00, 2.1),
|
||||
('ADAUSDT', 'Cardano', 'https://cryptologos.cc/logos/cardano-ada-logo.png', 0.5, 3.5),
|
||||
('SOLUSDT', 'Solana', 'https://cryptologos.cc/logos/solana-sol-logo.png', 100.0, 5.0),
|
||||
('DOGEUSDT', 'Dogecoin', 'https://cryptologos.cc/logos/dogecoin-doge-logo.png', 0.08, -2.0);
|
||||
@ -1,4 +1,162 @@
|
||||
/* Custom styles for the application */
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
:root {
|
||||
--bg-dark: #0a0e17;
|
||||
--bg-card: #161a25;
|
||||
--bg-card-hover: #1e2330;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #848e9c;
|
||||
--accent-color: #f0b90b;
|
||||
--accent-hover: #d4a30a;
|
||||
--up-color: #0ecb81;
|
||||
--down-color: #f6465d;
|
||||
--border-color: #2b2f36;
|
||||
--glass-bg: rgba(255, 255, 255, 0.05);
|
||||
--glass-border: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Glassmorphism */
|
||||
.glass-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% { transform: translatey(0px); }
|
||||
50% { transform: translatey(-20px); }
|
||||
100% { transform: translatey(0px); }
|
||||
}
|
||||
|
||||
.animate-up { animation: fadeInUp 0.8s ease-out forwards; }
|
||||
.float-icon { animation: float 6s ease-in-out infinite; }
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
background: rgba(10, 14, 23, 0.8) !important;
|
||||
backdrop-filter: blur(15px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero-section {
|
||||
padding: 100px 0;
|
||||
background: radial-gradient(circle at top right, rgba(240, 185, 11, 0.1), transparent 40%),
|
||||
radial-gradient(circle at bottom left, rgba(14, 203, 129, 0.1), transparent 40%);
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 4rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 24px;
|
||||
background: linear-gradient(135deg, #fff 0%, #848e9c 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
/* Markets Table */
|
||||
.market-table {
|
||||
background: var(--bg-card);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.market-table th {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.market-table td {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.market-table tr:hover {
|
||||
background-color: var(--bg-card-hover);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary-custom {
|
||||
background: var(--accent-color);
|
||||
color: #000;
|
||||
border: none;
|
||||
padding: 12px 32px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-primary-custom:hover {
|
||||
background: var(--accent-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(240, 185, 11, 0.3);
|
||||
}
|
||||
|
||||
/* Carousel / Banners */
|
||||
.banner-carousel {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-item {
|
||||
height: 300px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
/* Trading Interface */
|
||||
.trade-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 300px 300px;
|
||||
grid-template-rows: 1fr 300px;
|
||||
gap: 8px;
|
||||
height: calc(100vh - 70px);
|
||||
padding: 8px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.trade-panel {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.price-up { color: var(--up-color); }
|
||||
.price-down { color: var(--down-color); }
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-dark);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-secondary);
|
||||
}
|
||||
BIN
static/images/logo.png
Normal file
BIN
static/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 558 KiB |
371
trade.php
Normal file
371
trade.php
Normal file
@ -0,0 +1,371 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
check_auth();
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
$symbol = $_GET['symbol'] ?? 'BTCUSDT';
|
||||
$trade_type = $_GET['type'] ?? 'SPOT';
|
||||
$base_symbol = str_replace('USDT', '', $symbol);
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<style>
|
||||
.glass-card { background: rgba(24, 26, 32, 1); border: 1px solid #2b2f36; border-radius: 4px; overflow: hidden; }
|
||||
.trade-nav-item { cursor: pointer; padding: 10px 15px; border-bottom: 2px solid transparent; color: #848e9c; font-size: 14px; }
|
||||
.trade-nav-item.active { border-bottom-color: #f0b90b; color: #f0b90b; font-weight: bold; }
|
||||
.coin-row { transition: background 0.2s; border-bottom: 1px solid #1e2329; }
|
||||
.coin-row:hover { background: #2b3139; cursor: pointer; }
|
||||
.coin-row.active { background: #1e2329; border-left: 3px solid #f0b90b; }
|
||||
.price-up { color: #0ecb81 !important; }
|
||||
.price-down { color: #f6465d !important; }
|
||||
#order-book table td { padding: 3px 8px; font-size: 12px; }
|
||||
.smaller { font-size: 11px; }
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-thumb { background: #3b4149; border-radius: 10px; }
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-1 py-1" style="background-color: #0b0e11; min-height: 95vh; color: #eaecef;">
|
||||
<div class="row g-1">
|
||||
<!-- Market List -->
|
||||
<div class="col-lg-2">
|
||||
<div class="glass-card h-100" style="max-height: 900px; display: flex; flex-direction: column;">
|
||||
<div class="p-2 border-bottom border-secondary bg-dark">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-transparent border-secondary text-secondary"><i class="bi bi-search"></i></span>
|
||||
<input type="text" id="coin-search" class="form-control bg-transparent text-white border-secondary" placeholder="搜索币种" onkeyup="filterCoins()">
|
||||
</div>
|
||||
</div>
|
||||
<div id="left-coin-list" style="overflow-y: auto; flex-grow: 1;">
|
||||
<!-- JS populated -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chart & Trade -->
|
||||
<div class="col-lg-7">
|
||||
<!-- Ticker Header -->
|
||||
<div class="glass-card mb-1 p-2 d-flex align-items-center bg-dark">
|
||||
<div class="d-flex align-items-center me-4">
|
||||
<span class="fw-bold fs-5 text-white" id="current-symbol-title"><?php echo $symbol; ?></span>
|
||||
</div>
|
||||
<div class="me-4 border-end border-secondary pe-4">
|
||||
<div class="fw-bold fs-5" id="header-price">--</div>
|
||||
<div class="smaller text-secondary">≈ $<span id="header-price-usd">--</span></div>
|
||||
</div>
|
||||
<div class="me-4">
|
||||
<div class="smaller text-secondary mb-1">24h 涨跌</div>
|
||||
<div class="fw-bold" id="header-change">--</div>
|
||||
</div>
|
||||
<div class="me-4 d-none d-md-block">
|
||||
<div class="smaller text-secondary mb-1">24h 最高</div>
|
||||
<div class="fw-bold smaller" id="header-high">--</div>
|
||||
</div>
|
||||
<div class="me-4 d-none d-md-block">
|
||||
<div class="smaller text-secondary mb-1">24h 最低</div>
|
||||
<div class="fw-bold smaller" id="header-low">--</div>
|
||||
</div>
|
||||
<div class="ms-auto d-flex gap-1 bg-black p-1 rounded">
|
||||
<a href="?type=SPOT&symbol=<?php echo $symbol; ?>" class="btn btn-sm <?php echo $trade_type=='SPOT'?'btn-warning':'text-secondary'; ?> fw-bold px-3">现货</a>
|
||||
<a href="?type=CONTRACT&symbol=<?php echo $symbol; ?>" class="btn btn-sm <?php echo $trade_type=='CONTRACT'?'btn-warning':'text-secondary'; ?> fw-bold px-3">合约</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TradingView -->
|
||||
<div class="glass-card mb-1" style="height: 480px;">
|
||||
<div id="tradingview_widget" style="height: 100%;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Trading Form -->
|
||||
<div class="glass-card p-3">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6 border-end border-secondary">
|
||||
<div class="d-flex justify-content-between mb-3 align-items-center">
|
||||
<span class="text-success fw-bold fs-5">买入 / 做多</span>
|
||||
<span class="text-secondary smaller">可用: <span class="text-white" id="usdt-balance"><?php echo number_format($account['balance'], 2); ?></span> USDT</span>
|
||||
</div>
|
||||
|
||||
<?php if ($trade_type === 'CONTRACT'): ?>
|
||||
<div class="mb-3 d-flex align-items-center gap-2">
|
||||
<label class="smaller text-secondary" style="min-width: 40px;">杠杆</label>
|
||||
<select id="leverage" class="form-select form-select-sm bg-dark text-white border-secondary w-auto" style="min-width: 100px;">
|
||||
<option value="10">10x</option>
|
||||
<option value="20" selected>20x</option>
|
||||
<option value="50">50x</option>
|
||||
<option value="100">100x</option>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;">价格</span>
|
||||
<input type="text" class="form-control bg-dark text-warning border-secondary fw-bold" value="市场最优价" disabled>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;"><?php echo $trade_type=='CONTRACT'?'手数':'数量'; ?></span>
|
||||
<input type="number" id="buy-amount" class="form-control bg-dark text-white border-secondary" placeholder="0.00">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary"><?php echo $trade_type=='CONTRACT'?'张':$base_symbol; ?></span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success w-100 fw-bold py-2 shadow-sm" onclick="submitOrder('BUY')">立即买入 (做多)</button>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex justify-content-between mb-3 align-items-center">
|
||||
<span class="text-danger fw-bold fs-5">卖出 / 做空</span>
|
||||
<span class="text-secondary smaller">市价委托</span>
|
||||
</div>
|
||||
|
||||
<?php if ($trade_type === 'CONTRACT'): ?>
|
||||
<div class="mb-3"><div style="height:31px"></div></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;">价格</span>
|
||||
<input type="text" class="form-control bg-dark text-warning border-secondary fw-bold" value="市场最优价" disabled>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;"><?php echo $trade_type=='CONTRACT'?'手数':'数量'; ?></span>
|
||||
<input type="number" id="sell-amount" class="form-control bg-dark text-white border-secondary" placeholder="0.00">
|
||||
<span class="input-group-text bg-dark text-secondary border-secondary"><?php echo $trade_type=='CONTRACT'?'张':$base_symbol; ?></span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-danger w-100 fw-bold py-2 shadow-sm" onclick="submitOrder('SELL')">立即卖出 (做空)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Positions & History -->
|
||||
<div class="glass-card mt-1 p-0">
|
||||
<div class="d-flex border-bottom border-secondary bg-dark">
|
||||
<div class="trade-nav-item active">当前持有仓位</div>
|
||||
<div class="trade-nav-item">历史成交记录</div>
|
||||
</div>
|
||||
<div class="p-0" style="min-height: 250px;">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover mb-0 align-middle" id="position-table" style="font-size: 13px;">
|
||||
<thead>
|
||||
<tr class="text-secondary" style="background: #1e2329;">
|
||||
<th class="ps-3">交易对</th>
|
||||
<th>方向</th>
|
||||
<th>杠杆</th>
|
||||
<th>持有数量</th>
|
||||
<th>开仓价格</th>
|
||||
<th>标记价格</th>
|
||||
<th>盈亏 (USDT)</th>
|
||||
<th class="text-end pe-3">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Order Book -->
|
||||
<div class="col-lg-3">
|
||||
<div class="glass-card h-100" style="display: flex; flex-direction: column;">
|
||||
<div class="p-2 border-bottom border-secondary bg-dark small fw-bold">实时订单簿</div>
|
||||
<div id="order-book" style="flex-grow: 1;">
|
||||
<div class="px-2 py-2 d-flex justify-content-between text-secondary smaller">
|
||||
<span>价格 (USDT)</span>
|
||||
<span>数量 (<?php echo $base_symbol; ?>)</span>
|
||||
</div>
|
||||
<div id="asks-container" class="mb-1">
|
||||
<table class="w-100">
|
||||
<tbody id="asks-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="py-3 text-center border-top border-bottom border-secondary my-1 bg-dark">
|
||||
<span id="book-price" class="fs-4 fw-bold">--</span>
|
||||
<div class="smaller text-secondary" id="book-price-usd">≈ $--</div>
|
||||
</div>
|
||||
<div id="bids-container">
|
||||
<table class="w-100">
|
||||
<tbody id="bids-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2 border-top border-secondary bg-dark">
|
||||
<div class="d-flex justify-content-between smaller">
|
||||
<span class="text-secondary">最新成交</span>
|
||||
<span class="text-success">实时</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
||||
<script>
|
||||
const symbol = '<?php echo $symbol; ?>';
|
||||
const tradeType = '<?php echo $trade_type; ?>';
|
||||
let currentPrice = 0;
|
||||
let allCoins = [];
|
||||
|
||||
new TradingView.widget({
|
||||
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
|
||||
"interval": "15", "timezone": "Etc/UTC", "theme": "dark", "style": "1",
|
||||
"locale": "zh_CN", "toolbar_bg": "#f1f3f6", "enable_publishing": false,
|
||||
"hide_side_toolbar": false, "allow_symbol_change": true, "container_id": "tradingview_widget"
|
||||
});
|
||||
|
||||
function formatPrice(p) {
|
||||
p = parseFloat(p);
|
||||
if (p < 0.0001) return p.toFixed(8);
|
||||
if (p < 0.01) return p.toFixed(6);
|
||||
if (p < 1) return p.toFixed(4);
|
||||
return p.toFixed(2);
|
||||
}
|
||||
|
||||
async function updateMarket() {
|
||||
try {
|
||||
const r = await fetch('api.php?action=market_data');
|
||||
const coins = await r.json();
|
||||
allCoins = coins;
|
||||
renderCoins();
|
||||
|
||||
const c = coins.find(i => i.symbol === symbol);
|
||||
if (c) {
|
||||
currentPrice = parseFloat(c.price);
|
||||
const pFormatted = formatPrice(currentPrice);
|
||||
const changeClass = c.change >= 0 ? 'price-up' : 'price-down';
|
||||
|
||||
document.getElementById('header-price').textContent = pFormatted;
|
||||
document.getElementById('header-price').className = 'fw-bold fs-5 ' + changeClass;
|
||||
document.getElementById('header-price-usd').textContent = pFormatted;
|
||||
|
||||
document.getElementById('header-change').textContent = (c.change >= 0 ? '+' : '') + c.change + '%';
|
||||
document.getElementById('header-change').className = 'fw-bold ' + changeClass;
|
||||
|
||||
document.getElementById('header-high').textContent = formatPrice(c.high || currentPrice * 1.02);
|
||||
document.getElementById('header-low').textContent = formatPrice(c.low || currentPrice * 0.98);
|
||||
|
||||
const bookPrice = document.getElementById('book-price');
|
||||
bookPrice.textContent = pFormatted;
|
||||
bookPrice.className = 'fs-4 fw-bold ' + changeClass;
|
||||
document.getElementById('book-price-usd').textContent = '≈ $' + pFormatted;
|
||||
|
||||
renderOrderBook(currentPrice);
|
||||
}
|
||||
} catch(e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderCoins() {
|
||||
const search = document.getElementById('coin-search').value.toLowerCase();
|
||||
let listHtml = '';
|
||||
allCoins.forEach(c => {
|
||||
if (search && !c.symbol.toLowerCase().includes(search)) return;
|
||||
const isTarget = c.symbol === symbol;
|
||||
const changeClass = c.change >= 0 ? 'text-success' : 'text-danger';
|
||||
listHtml += `
|
||||
<div class="coin-row p-2 d-flex justify-content-between align-items-center ${isTarget?'active':''}" onclick="location.href='?type=${tradeType}&symbol=${c.symbol}'">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="${c.icon_url}" width="20" height="20" class="me-2 rounded-circle">
|
||||
<div>
|
||||
<div class="fw-bold smaller text-white">${c.symbol.replace('USDT','')}</div>
|
||||
<div class="text-secondary smaller" style="font-size:9px">USDT</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="fw-bold smaller ${changeClass}">${formatPrice(c.price)}</div>
|
||||
<div class="${changeClass}" style="font-size:10px">${(c.change >= 0 ? '+' : '') + c.change}%</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
document.getElementById('left-coin-list').innerHTML = listHtml;
|
||||
}
|
||||
|
||||
function filterCoins() { renderCoins(); }
|
||||
|
||||
function renderOrderBook(price) {
|
||||
if (!price) return;
|
||||
let asks = '', bids = '';
|
||||
// Asks (Red) - from high to low
|
||||
for(let i=10; i>0; i--) {
|
||||
const p = price * (1 + i * 0.0001);
|
||||
const amt = (Math.random() * 2).toFixed(3);
|
||||
asks += `<tr><td class="text-danger">${formatPrice(p)}</td><td class="text-end text-secondary">${amt}</td></tr>`;
|
||||
}
|
||||
// Bids (Green) - from high to low
|
||||
for(let i=1; i<=10; i++) {
|
||||
const p = price * (1 - i * 0.0001);
|
||||
const amt = (Math.random() * 2).toFixed(3);
|
||||
bids += `<tr><td class="text-success">${formatPrice(p)}</td><td class="text-end text-secondary">${amt}</td></tr>`;
|
||||
}
|
||||
document.getElementById('asks-list').innerHTML = asks;
|
||||
document.getElementById('bids-list').innerHTML = bids;
|
||||
}
|
||||
|
||||
async function updatePositions() {
|
||||
try {
|
||||
const r = await fetch('api.php?action=positions');
|
||||
const pos = await r.json();
|
||||
let html = '';
|
||||
if (pos.length === 0) {
|
||||
html = '<tr><td colspan="8" class="text-center py-5 text-secondary">暂无持有仓位</td></tr>';
|
||||
} else {
|
||||
pos.forEach(p => {
|
||||
const pnlClass = p.pnl >= 0 ? 'text-success' : 'text-danger';
|
||||
html += `
|
||||
<tr>
|
||||
<td class="ps-3 fw-bold">${p.symbol}</td>
|
||||
<td><span class="badge ${p.side==='LONG'?'bg-success':'bg-danger'}">${p.side}</span></td>
|
||||
<td><span class="text-warning">${p.leverage}x</span></td>
|
||||
<td>${p.lots} 张</td>
|
||||
<td>${formatPrice(p.entry_price)}</td>
|
||||
<td class="text-warning">${formatPrice(p.current_price)}</td>
|
||||
<td class="${pnlClass} fw-bold">${parseFloat(p.pnl).toFixed(2)} USDT</td>
|
||||
<td class="text-end pe-3"><button class="btn btn-sm btn-outline-danger py-0" onclick="closePosition(${p.id})">平仓</button></td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
}
|
||||
document.querySelector('#position-table tbody').innerHTML = html;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function closePosition(id) {
|
||||
if (!confirm('确定要平掉该仓位吗?')) return;
|
||||
const res = await fetch('api.php?action=close_position', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id })
|
||||
});
|
||||
const json = await res.json();
|
||||
alert(json.message);
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
async function submitOrder(side) {
|
||||
const amount = document.getElementById(side.toLowerCase() + '-amount').value;
|
||||
const leverageSelect = document.getElementById('leverage');
|
||||
const leverage = leverageSelect ? leverageSelect.value : 1;
|
||||
|
||||
if (!amount || amount <= 0) { alert('请输入有效数量'); return; }
|
||||
|
||||
const res = await fetch('api.php?action=submit_order', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ symbol, side, trade_type: tradeType, amount, leverage })
|
||||
});
|
||||
const json = await res.json();
|
||||
if (json.status === 'success') {
|
||||
alert('下单成功');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('错误: ' + json.message);
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(updateMarket, 2000);
|
||||
setInterval(updatePositions, 3000);
|
||||
updateMarket();
|
||||
updatePositions();
|
||||
</script>
|
||||
<?php include 'footer.php'; ?>
|
||||
57
update_db.php
Normal file
57
update_db.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* 数据库结构同步工具
|
||||
* 访问此文件可确保您的宝塔数据库结构与当前代码一致
|
||||
*/
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
|
||||
// 1. 确保 site_settings 表存在并有数据
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS site_settings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
site_name VARCHAR(100) DEFAULT 'BitCrypto',
|
||||
contact_email VARCHAR(100) DEFAULT 'support@example.com',
|
||||
deposit_address VARCHAR(255) DEFAULT 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
||||
customer_service_url TEXT,
|
||||
terms_content TEXT,
|
||||
privacy_content TEXT,
|
||||
is_pinning_active BOOLEAN DEFAULT FALSE
|
||||
)");
|
||||
|
||||
$check = $db->query("SELECT count(*) FROM site_settings")->fetchColumn();
|
||||
if ($check == 0) {
|
||||
$db->exec("INSERT INTO site_settings (site_name) VALUES ('BitCrypto')");
|
||||
}
|
||||
|
||||
// 2. 确保 accounts 表有 KYC 相关字段
|
||||
$columns = $db->query("SHOW COLUMNS FROM accounts")->fetchAll(PDO::FETCH_COLUMN);
|
||||
if (!in_array('kyc_status', $columns)) {
|
||||
$db->exec("ALTER TABLE accounts ADD COLUMN kyc_status ENUM('UNVERIFIED', 'PENDING', 'VERIFIED', 'REJECTED') DEFAULT 'UNVERIFIED'");
|
||||
}
|
||||
if (!in_array('credit_score', $columns)) {
|
||||
$db->exec("ALTER TABLE accounts ADD COLUMN credit_score INT DEFAULT 80");
|
||||
}
|
||||
|
||||
// 3. 确保 cryptocurrencies 表存在
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS cryptocurrencies (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
symbol VARCHAR(20) UNIQUE NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
icon_url TEXT,
|
||||
current_price DECIMAL(30, 8) DEFAULT 0,
|
||||
manual_price DECIMAL(30, 8) DEFAULT 0,
|
||||
change_24h DECIMAL(10, 2) DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE
|
||||
)");
|
||||
|
||||
echo "<h3>数据库同步完成!</h3>";
|
||||
echo "<p>您的数据库结构已成功更新为最新版本。</p>";
|
||||
echo "<p style='color:red;'><b>请立即删除 update_db.php 文件。</b></p>";
|
||||
echo "<a href='index.php'>返回首页</a>";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "<h3>同步失败:</h3>";
|
||||
echo "<pre>" . $e->getMessage() . "</pre>";
|
||||
}
|
||||
106
verify.php
Normal file
106
verify.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
check_auth();
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$account = get_account($user_id);
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$real_name = $_POST['real_name'] ?? '';
|
||||
$id_number = $_POST['id_number'] ?? '';
|
||||
|
||||
// In a real app, we would handle file uploads here.
|
||||
// For this prototype, we'll just save the paths as placeholders.
|
||||
$id_front = 'uploads/kyc/' . $user_id . '_front.jpg';
|
||||
$id_back = 'uploads/kyc/' . $user_id . '_back.jpg';
|
||||
|
||||
if (empty($real_name) || empty($id_number)) {
|
||||
$error = '请填写完整信息';
|
||||
} else {
|
||||
$stmt = db()->prepare("UPDATE accounts SET real_name = ?, id_number = ?, kyc_status = 'PENDING' WHERE id = ?");
|
||||
$stmt->execute([$real_name, $id_number, $account['id']]);
|
||||
$success = '认证资料已提交,请等待审核';
|
||||
$account['kyc_status'] = 'PENDING';
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="glass-card p-4">
|
||||
<h3 class="text-white mb-4"><i class="bi bi-shield-check text-warning me-2"></i> 实名认证 (KYC)</h3>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-clock-history display-1 text-warning"></i>
|
||||
<p class="mt-3 text-secondary">审核中,通常在 24 小时内完成</p>
|
||||
<a href="profile.php" class="btn btn-warning mt-3">返回个人中心</a>
|
||||
</div>
|
||||
<?php elseif ($account['kyc_status'] === 'VERIFIED'): ?>
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-check-circle-fill display-1 text-success"></i>
|
||||
<h4 class="mt-4">您已完成实名认证</h4>
|
||||
<p class="text-secondary">感谢您的信任,您现在可以享受完整交易权限</p>
|
||||
<a href="profile.php" class="btn btn-warning mt-3">返回个人中心</a>
|
||||
</div>
|
||||
<?php elseif ($account['kyc_status'] === 'PENDING'): ?>
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-hourglass-split display-1 text-warning"></i>
|
||||
<h4 class="mt-4">认证审核中</h4>
|
||||
<p class="text-secondary">我们正在快马加鞭为您处理,请耐心等待</p>
|
||||
<a href="profile.php" class="btn btn-warning mt-3">返回个人中心</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">真实姓名</label>
|
||||
<input type="text" name="real_name" class="form-control bg-dark border-secondary text-white" placeholder="请输入您的真实姓名" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">身份证号 / 护照号</label>
|
||||
<input type="text" name="id_number" class="form-control bg-dark border-secondary text-white" placeholder="请输入证件号码" required>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-6">
|
||||
<label class="form-label text-secondary small">证件正面</label>
|
||||
<div class="border border-secondary border-dashed rounded p-4 text-center" style="border-style: dashed !important; cursor: pointer;">
|
||||
<i class="bi bi-plus-lg text-secondary"></i>
|
||||
<div class="small text-secondary mt-1">上传正面</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label text-secondary small">证件反面</label>
|
||||
<div class="border border-secondary border-dashed rounded p-4 text-center" style="border-style: dashed !important; cursor: pointer;">
|
||||
<i class="bi bi-plus-lg text-secondary"></i>
|
||||
<div class="small text-secondary mt-1">上传反面</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 mb-4 rounded" style="background: rgba(252, 213, 53, 0.1); border: 1px solid rgba(252, 213, 53, 0.2);">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-info-circle text-warning me-2"></i>
|
||||
<div class="small text-secondary">
|
||||
请确保上传的图片清晰可见,文字无遮挡。您的个人信息将受到最高级别的安全保护。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-3">提交认证</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'footer.php'; ?>
|
||||
70
withdraw.php
Normal file
70
withdraw.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
include_once 'config.php';
|
||||
check_auth();
|
||||
$account = get_account($_SESSION['user_id']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$amount = (float)($_POST['amount'] ?? 0);
|
||||
$address = $_POST['address'] ?? '';
|
||||
|
||||
if ($amount >= 10 && $address) {
|
||||
if ($account['balance'] >= $amount) {
|
||||
try {
|
||||
$db = db();
|
||||
$db->beginTransaction();
|
||||
|
||||
// Deduct balance and freeze it
|
||||
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ?, frozen_balance = frozen_balance + ? WHERE id = ?");
|
||||
$stmt->execute([$amount, $amount, $account['id']]);
|
||||
|
||||
// Record transaction
|
||||
$stmt = $db->prepare("INSERT INTO transactions (account_id, transaction_type, amount, tx_hash, status) VALUES (?, 'withdraw', ?, ?, 'pending')");
|
||||
$stmt->execute([$account['id'], $amount, $address]);
|
||||
|
||||
$db->commit();
|
||||
$success = "提现申请已提交,资金已冻结,请等待审核。";
|
||||
$account = get_account($_SESSION['user_id']); // refresh
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$error = "系统错误: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = "余额不足。";
|
||||
}
|
||||
} else {
|
||||
$error = "请输入有效金额(最小10)和地址。";
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="glass-card p-4 bg-dark">
|
||||
<h4 class="text-white mb-4"><i class="bi bi-box-arrow-up text-warning me-2"></i> 提现 USDT</h4>
|
||||
|
||||
<?php if(isset($success)): ?><div class="alert alert-success"><?php echo $success; ?></div><?php endif; ?>
|
||||
<?php if(isset($error)): ?><div class="alert alert-danger"><?php echo $error; ?></div><?php endif; ?>
|
||||
|
||||
<div class="mb-4 d-flex justify-content-between">
|
||||
<span class="text-secondary">可用余额:</span>
|
||||
<span class="text-white fw-bold"><?php echo number_format($account['balance'], 2); ?> USDT</span>
|
||||
</div>
|
||||
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary">提现金额</label>
|
||||
<input type="number" name="amount" step="0.01" class="form-control bg-dark text-white border-secondary" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary">收币地址 (TRC20)</label>
|
||||
<input type="text" name="address" class="form-control bg-dark text-white border-secondary" placeholder="T..." required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 mt-3">申请提现</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'footer.php'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user