Autosave: 20260210-070025
This commit is contained in:
parent
030c796cf4
commit
d7eac0da6a
250
admin.php
Normal file
250
admin.php
Normal file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Ensure user is admin
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$user = $stmt->fetch();
|
||||
if ($user['role'] !== 'admin') {
|
||||
die('Access Denied');
|
||||
}
|
||||
|
||||
$action = $_GET['action'] ?? 'dashboard';
|
||||
|
||||
// Handle Actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($action === 'confirm_recharge') {
|
||||
$id = $_POST['id'];
|
||||
$pdo->beginTransaction();
|
||||
$stmt = $pdo->prepare("SELECT * FROM recharges WHERE id = ? AND status = 'pending'");
|
||||
$stmt->execute([$id]);
|
||||
$recharge = $stmt->fetch();
|
||||
if ($recharge) {
|
||||
$stmt = $pdo->prepare("UPDATE recharges SET status = 'completed' WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?");
|
||||
$stmt->execute([$recharge['amount'], $recharge['user_id']]);
|
||||
}
|
||||
$pdo->commit();
|
||||
} elseif ($action === 'reject_recharge') {
|
||||
$id = $_POST['id'];
|
||||
$stmt = $pdo->prepare("UPDATE recharges SET status = 'rejected' WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
} elseif ($action === 'reply_support') {
|
||||
$user_id = $_POST['user_id'];
|
||||
$message = $_POST['message'];
|
||||
$stmt = $pdo->prepare("INSERT INTO support_messages (user_id, sender, message) VALUES (?, 'admin', ?)");
|
||||
$stmt->execute([$user_id, $message]);
|
||||
} elseif ($action === 'update_settings') {
|
||||
foreach ($_POST['settings'] as $key => $value) {
|
||||
$stmt = $pdo->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = ?");
|
||||
$stmt->execute([$value, $key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Data
|
||||
$stats = [
|
||||
'users' => $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(),
|
||||
'pending_recharges' => $pdo->query("SELECT COUNT(*) FROM recharges WHERE status = 'pending'")->fetchColumn(),
|
||||
'total_orders' => $pdo->query("SELECT COUNT(*) FROM sms_orders")->fetchColumn(),
|
||||
];
|
||||
|
||||
$pending_recharges = $pdo->query("SELECT r.*, u.username FROM recharges r JOIN users u ON r.user_id = u.id WHERE r.status = 'pending' ORDER BY r.created_at DESC")->fetchAll();
|
||||
$support_requests = $pdo->query("SELECT m.*, u.username FROM support_messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND m.id IN (SELECT MAX(id) FROM support_messages GROUP BY user_id) ORDER BY m.created_at DESC")->fetchAll();
|
||||
$settings = $pdo->query("SELECT * FROM settings")->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<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">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #ff4d94;
|
||||
--bg-light: #fff5f7;
|
||||
--surface-light: #ffffff;
|
||||
--text-main: #2d1a1e;
|
||||
--text-muted: #8a6d71;
|
||||
--border-color: #ffd1dc;
|
||||
}
|
||||
body { background-color: var(--bg-light); font-family: 'Plus Jakarta Sans', sans-serif; }
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
background: var(--surface-light);
|
||||
border-right: 2px solid var(--border-color);
|
||||
color: var(--text-main);
|
||||
padding: 30px 20px;
|
||||
}
|
||||
.main { margin-left: 250px; padding: 40px; }
|
||||
.nav-link {
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.nav-link:hover, .nav-link.active {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(255, 77, 148, 0.2);
|
||||
}
|
||||
.card {
|
||||
background-color: var(--surface-light);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 20px;
|
||||
box-shadow: 6px 6px 0px var(--border-color);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.btn-primary { background-color: var(--primary-color); border: none; border-radius: 10px; }
|
||||
.btn-primary:hover { background-color: #ff1a75; }
|
||||
.form-control { border: 2px solid var(--border-color); border-radius: 10px; }
|
||||
.form-control:focus { border-color: var(--primary-color); box-shadow: none; }
|
||||
.table { color: var(--text-main); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<h4 class="fw-bold text-primary mb-4"><i class="fas fa-shield-alt me-2"></i>管理中心</h4>
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link <?= $action === 'dashboard' ? 'active' : '' ?>" href="?action=dashboard"><i class="fas fa-chart-pie me-2"></i> 数据概览</a>
|
||||
<a class="nav-link <?= $action === 'recharges' ? 'active' : '' ?>" href="?action=recharges"><i class="fas fa-receipt me-2"></i> 充值审核</a>
|
||||
<a class="nav-link <?= $action === 'support' ? 'active' : '' ?>" href="?action=support"><i class="fas fa-comments me-2"></i> 客服消息</a>
|
||||
<a class="nav-link <?= $action === 'settings' ? 'active' : '' ?>" href="?action=settings"><i class="fas fa-tools me-2"></i> 系统参数</a>
|
||||
<hr class="text-muted">
|
||||
<a class="nav-link" href="dashboard.php"><i class="fas fa-external-link-alt me-2"></i> 回到前台</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="d-flex justify-content-between align-items-center mb-5">
|
||||
<h2 class="fw-bold"><?= [
|
||||
'dashboard' => '数据大盘',
|
||||
'recharges' => '充值申请列表',
|
||||
'support' => '用户咨询回复',
|
||||
'settings' => '全局系统设置'
|
||||
][$action] ?></h2>
|
||||
<div class="text-muted small">系统时间: <?= date('Y-m-d H:i') ?></div>
|
||||
</div>
|
||||
|
||||
<?php if ($action === 'dashboard'): ?>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card p-4 text-center">
|
||||
<div class="text-muted mb-2">注册用户总量</div>
|
||||
<h2 class="fw-bold text-primary"><?= $stats['users'] ?></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-4 text-center">
|
||||
<div class="text-muted mb-2">待审核充值</div>
|
||||
<h2 class="fw-bold text-warning"><?= $stats['pending_recharges'] ?></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-4 text-center">
|
||||
<div class="text-muted mb-2">累计成交订单</div>
|
||||
<h2 class="fw-bold text-success"><?= $stats['total_orders'] ?></h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($action === 'recharges'): ?>
|
||||
<div class="card p-4">
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
<th>申请金额</th>
|
||||
<th>交易 TXID</th>
|
||||
<th>提交时间</th>
|
||||
<th>操作决策</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($pending_recharges as $r): ?>
|
||||
<tr>
|
||||
<td class="fw-bold"><?= htmlspecialchars($r['username']) ?></td>
|
||||
<td class="text-success fw-bold">$<?= $r['amount'] ?></td>
|
||||
<td><code class="small text-muted"><?= htmlspecialchars($r['txid']) ?></code></td>
|
||||
<td class="small"><?= $r['created_at'] ?></td>
|
||||
<td>
|
||||
<form method="POST" action="?action=confirm_recharge" class="d-inline">
|
||||
<input type="hidden" name="id" value="<?= $r['id'] ?>">
|
||||
<button class="btn btn-sm btn-success px-3">通过</button>
|
||||
</form>
|
||||
<form method="POST" action="?action=reject_recharge" class="d-inline">
|
||||
<input type="hidden" name="id" value="<?= $r['id'] ?>">
|
||||
<button class="btn btn-sm btn-outline-danger px-3">驳回</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($pending_recharges)): ?>
|
||||
<tr><td colspan="5" class="text-center py-4 text-muted">暂无待处理充值</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($action === 'support'): ?>
|
||||
<div class="row">
|
||||
<?php foreach ($support_requests as $s): ?>
|
||||
<div class="col-12">
|
||||
<div class="card p-4">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h5 class="fw-bold mb-0 text-primary"><?= htmlspecialchars($s['username']) ?></h5>
|
||||
<span class="small text-muted"><?= $s['created_at'] ?></span>
|
||||
</div>
|
||||
<div class="p-3 rounded-3 mb-4" style="background-color: var(--bg-light); border: 1px solid var(--border-color);">
|
||||
<?= htmlspecialchars($s['message']) ?>
|
||||
</div>
|
||||
<form method="POST" action="?action=reply_support">
|
||||
<input type="hidden" name="user_id" value="<?= $s['user_id'] ?>">
|
||||
<div class="input-group">
|
||||
<input type="text" name="message" class="form-control" placeholder="输入回复内容..." required>
|
||||
<button class="btn btn-primary px-4">发送回复</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($support_requests)): ?>
|
||||
<div class="col-12 text-center py-5 text-muted">暂无待回复消息</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php elseif ($action === 'settings'): ?>
|
||||
<div class="card p-4">
|
||||
<form method="POST" action="?action=update_settings">
|
||||
<div class="row">
|
||||
<?php foreach ($settings as $key => $val): ?>
|
||||
<div class="col-md-6 mb-4">
|
||||
<label class="form-label fw-bold"><?= $key ?></label>
|
||||
<input type="text" name="settings[<?= $key ?>]" value="<?= htmlspecialchars($val) ?>" class="form-control p-3">
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="text-end mt-4">
|
||||
<button class="btn btn-primary btn-lg px-5">保存所有系统参数</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
201
ajax_handler.php
Normal file
201
ajax_handler.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
require_once __DIR__ . '/api/LocalLubanApi.php';
|
||||
|
||||
$pdo = db();
|
||||
$api = new LubanSMS();
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isset($_SESSION['user_id']) && $action !== 'login') {
|
||||
echo json_encode(['code' => 401, 'msg' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'get_balance':
|
||||
$stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$balance = $stmt->fetchColumn();
|
||||
echo json_encode(['code' => 0, 'balance' => number_format($balance, 2)]);
|
||||
break;
|
||||
|
||||
case 'get_countries':
|
||||
echo json_encode($api->getCountries());
|
||||
break;
|
||||
|
||||
case 'get_services':
|
||||
$country = $_GET['country'] ?? '';
|
||||
$service = $_GET['service'] ?? '';
|
||||
$res = $api->getServices($country, $service);
|
||||
echo json_encode($res);
|
||||
break;
|
||||
|
||||
case 'get_number':
|
||||
$service_id = $_GET['service_id'] ?? '';
|
||||
$country_name = $_GET['country_name'] ?? '未知国家';
|
||||
$service_name = $_GET['service_name'] ?? '未知项目';
|
||||
$price = (float)($_GET['price'] ?? 1.0);
|
||||
|
||||
if (!$service_id) {
|
||||
echo json_encode(['code' => 400, 'msg' => 'Service ID is required']);
|
||||
break;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$balance = $stmt->fetchColumn();
|
||||
|
||||
if ($balance < $price) {
|
||||
echo json_encode(['code' => 400, 'msg' => '余额不足,请先充值']);
|
||||
break;
|
||||
}
|
||||
|
||||
$res = $api->getNumber($service_id);
|
||||
if ($res['code'] == 0) {
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE users SET balance = balance - ? WHERE id = ?");
|
||||
$stmt->execute([$price, $_SESSION['user_id']]);
|
||||
|
||||
// User requested 10 minutes countdown
|
||||
$stmt = $pdo->prepare("INSERT INTO sms_orders (user_id, request_id, number, service_name, country_name, cost, status, expire_at) VALUES (?, ?, ?, ?, ?, ?, 'pending', DATE_ADD(NOW(), INTERVAL 10 MINUTE))");
|
||||
$stmt->execute([$_SESSION['user_id'], $res['request_id'], $res['number'], $service_name, $country_name, $price]);
|
||||
$pdo->commit();
|
||||
echo json_encode($res);
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode(['code' => 500, 'msg' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode($res);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'check_sms':
|
||||
$request_id = $_GET['request_id'] ?? '';
|
||||
if (!$request_id) {
|
||||
echo json_encode(['code' => 400, 'msg' => 'Request ID is required']);
|
||||
break;
|
||||
}
|
||||
|
||||
$res = $api->getSms($request_id);
|
||||
if ($res['code'] == 0 && $res['msg'] == 'success') {
|
||||
$stmt = $pdo->prepare("UPDATE sms_orders SET sms_content = ?, status = 'received' WHERE request_id = ?");
|
||||
$stmt->execute([$res['sms_code'], $request_id]);
|
||||
}
|
||||
echo json_encode($res);
|
||||
break;
|
||||
|
||||
case 'release_number':
|
||||
$request_id = $_GET['request_id'] ?? '';
|
||||
|
||||
// Manual release requires > 2 minutes
|
||||
$stmt = $pdo->prepare("SELECT created_at, status FROM sms_orders WHERE request_id = ? AND user_id = ?");
|
||||
$stmt->execute([$request_id, $_SESSION['user_id']]);
|
||||
$order = $stmt->fetch();
|
||||
|
||||
if (!$order) {
|
||||
echo json_encode(['code' => 404, 'msg' => 'Order not found']);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($order['status'] !== 'pending') {
|
||||
echo json_encode(['code' => 400, 'msg' => 'Invalid order status']);
|
||||
break;
|
||||
}
|
||||
|
||||
$createdAt = strtotime($order['created_at']);
|
||||
if (time() - $createdAt < 120) {
|
||||
echo json_encode(['code' => 400, 'msg' => '获取号码不足2分钟,暂时无法手动释放。请稍候或等待系统自动释放。']);
|
||||
break;
|
||||
}
|
||||
|
||||
$res = $api->setStatus($request_id, 'reject');
|
||||
if ($res['code'] == 0) {
|
||||
$stmt = $pdo->prepare("UPDATE sms_orders SET status = 'canceled' WHERE request_id = ?");
|
||||
$stmt->execute([$request_id]);
|
||||
}
|
||||
echo json_encode($res);
|
||||
break;
|
||||
|
||||
case 'get_active_orders':
|
||||
// Auto-expire orders
|
||||
$stmt = $pdo->prepare("UPDATE sms_orders SET status = 'expired' WHERE status = 'pending' AND expire_at < NOW()");
|
||||
$stmt->execute();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM sms_orders WHERE user_id = ? AND status = 'pending' ORDER BY created_at DESC");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
echo json_encode(['code' => 0, 'data' => $stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
case 'create_recharge':
|
||||
$amount = (float)($_POST['amount'] ?? 0);
|
||||
if ($amount < 10) {
|
||||
echo json_encode(['code' => 400, 'msg' => '最低充值金额为 10 USDT']);
|
||||
break;
|
||||
}
|
||||
|
||||
// Add random decimal to help identify payment (e.g. 10.42)
|
||||
// If it already has decimals, we might want to keep it or refine it.
|
||||
// The user said "any recharge add decimal".
|
||||
$base = floor($amount);
|
||||
$random_decimal = rand(1, 99) / 100;
|
||||
$final_amount = $base + $random_decimal;
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO recharges (user_id, amount, txid, status) VALUES (?, ?, 'Auto-Detect', 'pending')");
|
||||
$stmt->execute([$_SESSION['user_id'], $final_amount]);
|
||||
echo json_encode(['code' => 0, 'recharge_id' => $pdo->lastInsertId(), 'amount' => $final_amount]);
|
||||
break;
|
||||
|
||||
case 'check_recharge_status':
|
||||
$recharge_id = $_GET['recharge_id'] ?? '';
|
||||
if (!$recharge_id) {
|
||||
echo json_encode(['code' => 400, 'msg' => 'Recharge ID is required']);
|
||||
break;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM recharges WHERE id = ? AND user_id = ?");
|
||||
$stmt->execute([$recharge_id, $_SESSION['user_id']]);
|
||||
$recharge = $stmt->fetch();
|
||||
|
||||
if (!$recharge) {
|
||||
echo json_encode(['code' => 404, 'msg' => 'Order not found']);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($recharge['status'] === 'completed') {
|
||||
echo json_encode(['code' => 0, 'status' => 'completed']);
|
||||
break;
|
||||
}
|
||||
|
||||
// SIMULATION: In a real app, this would query a blockchain API for the address.
|
||||
// For testing, we'll auto-complete after 15 seconds.
|
||||
$createdAt = strtotime($recharge['created_at']);
|
||||
if (time() - $createdAt > 15) {
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE recharges SET status = 'completed' WHERE id = ?");
|
||||
$stmt->execute([$recharge_id]);
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?");
|
||||
$stmt->execute([$recharge['amount'], $_SESSION['user_id']]);
|
||||
|
||||
$pdo->commit();
|
||||
echo json_encode(['code' => 0, 'status' => 'completed']);
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode(['code' => 500, 'msg' => 'Database error']);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['code' => 0, 'status' => 'pending']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['code' => 404, 'msg' => 'Action not found']);
|
||||
break;
|
||||
}
|
||||
70
api/LocalLubanApi.php
Normal file
70
api/LocalLubanApi.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
class LubanSMS {
|
||||
private $apikey;
|
||||
private $baseUrl = 'https://lubansms.com/v2/api/';
|
||||
|
||||
public function __construct($apikey = null) {
|
||||
if ($apikey) {
|
||||
$this->apikey = $apikey;
|
||||
} else {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = 'lubansms_apikey'");
|
||||
$stmt->execute();
|
||||
$this->apikey = $stmt->fetchColumn();
|
||||
}
|
||||
}
|
||||
|
||||
private function request($endpoint, $params = []) {
|
||||
$params['apikey'] = $this->apikey;
|
||||
$url = $this->baseUrl . $endpoint . '?' . http_build_query($params);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
return ['code' => 500, 'msg' => 'Network error or server down'];
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
public function getBalance() {
|
||||
return $this->request('getBalance');
|
||||
}
|
||||
|
||||
public function getCountries() {
|
||||
return $this->request('countries');
|
||||
}
|
||||
|
||||
public function getServices($countryName = '', $serviceName = '', $page = 1) {
|
||||
$params = [
|
||||
'page' => $page,
|
||||
'language' => 'zh'
|
||||
];
|
||||
if ($countryName) $params['country'] = $countryName;
|
||||
if ($serviceName) $params['service'] = $serviceName;
|
||||
return $this->request('List', $params);
|
||||
}
|
||||
|
||||
public function getNumber($service_id) {
|
||||
return $this->request('getNumber', ['service_id' => $service_id]);
|
||||
}
|
||||
|
||||
public function getSms($request_id) {
|
||||
return $this->request('getSms', ['request_id' => $request_id]);
|
||||
}
|
||||
|
||||
public function setStatus($request_id, $status = 'reject') {
|
||||
return $this->request('setStatus', [
|
||||
'request_id' => $request_id,
|
||||
'status' => $status
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
:root {
|
||||
--color-bg: #ffffff;
|
||||
--color-text: #1a1a1a;
|
||||
--color-primary: #2563EB; /* Vibrant Blue */
|
||||
--color-secondary: #000000;
|
||||
--color-accent: #A3E635; /* Lime Green */
|
||||
--color-surface: #f8f9fa;
|
||||
--color-bg: #fff5f7; /* Very Light Pink Background */
|
||||
--color-text: #2d1a1e; /* Dark Brownish Pink for Text */
|
||||
--color-primary: #ff4d94; /* Vibrant Pink */
|
||||
--color-secondary: #ff1a75; /* Deep Pink */
|
||||
--color-accent: #ffd1dc; /* Soft Pastel Pink */
|
||||
--color-surface: #ffffff;
|
||||
--font-heading: 'Space Grotesk', sans-serif;
|
||||
--font-body: 'Inter', sans-serif;
|
||||
--border-width: 2px;
|
||||
--shadow-hard: 5px 5px 0px #000;
|
||||
--shadow-hover: 8px 8px 0px #000;
|
||||
--shadow-hard: 5px 5px 0px #ff4d94;
|
||||
--shadow-hover: 8px 8px 0px #ff1a75;
|
||||
--radius-pill: 50rem;
|
||||
--radius-card: 1rem;
|
||||
}
|
||||
@ -28,15 +28,15 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
|
||||
/* Utilities */
|
||||
.text-primary { color: var(--color-primary) !important; }
|
||||
.bg-black { background-color: #000 !important; }
|
||||
.bg-pink { background-color: var(--color-primary) !important; }
|
||||
.text-white { color: #fff !important; }
|
||||
.shadow-hard { box-shadow: var(--shadow-hard); }
|
||||
.border-2-black { border: var(--border-width) solid #000; }
|
||||
.border-2-pink { border: var(--border-width) solid var(--color-primary); }
|
||||
.py-section { padding-top: 5rem; padding-bottom: 5rem; }
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(255, 245, 247, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: var(--border-width) solid transparent;
|
||||
transition: all 0.3s;
|
||||
@ -45,7 +45,7 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
}
|
||||
|
||||
.navbar.scrolled {
|
||||
border-bottom-color: #000;
|
||||
border-bottom-color: var(--color-primary);
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
@ -72,7 +72,7 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
font-family: var(--font-heading);
|
||||
padding: 0.8rem 2rem;
|
||||
border-radius: var(--radius-pill);
|
||||
border: var(--border-width) solid #000;
|
||||
border: var(--border-width) solid var(--color-primary);
|
||||
transition: all 0.2s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
box-shadow: var(--shadow-hard);
|
||||
}
|
||||
@ -84,34 +84,29 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
|
||||
.btn:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: 0 0 0 #000;
|
||||
box-shadow: 0 0 0 var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--color-primary);
|
||||
border-color: #000;
|
||||
border-color: var(--color-secondary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #1d4ed8;
|
||||
border-color: #000;
|
||||
background-color: var(--color-secondary);
|
||||
border-color: var(--color-secondary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-outline-dark {
|
||||
.btn-outline-pink {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-cta {
|
||||
background-color: var(--color-accent);
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-cta:hover {
|
||||
background-color: #8cc629;
|
||||
color: #000;
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
@ -152,53 +147,9 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.dot { color: var(--color-primary); }
|
||||
|
||||
.badge-pill {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid #000;
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
background: #fff;
|
||||
box-shadow: 4px 4px 0 #000;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Marquee */
|
||||
.marquee-container {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
border-top: 2px solid #000;
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
|
||||
.rotate-divider {
|
||||
transform: rotate(-2deg) scale(1.05);
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
margin-top: -50px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.marquee-content {
|
||||
display: inline-block;
|
||||
animation: marquee 20s linear infinite;
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
@keyframes marquee {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-50%); }
|
||||
}
|
||||
|
||||
/* Portfolio Cards */
|
||||
.project-card {
|
||||
border: 2px solid #000;
|
||||
border: 2px solid var(--color-primary);
|
||||
border-radius: var(--radius-card);
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
@ -211,37 +162,14 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
|
||||
.project-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 8px 8px 0 #000;
|
||||
box-shadow: var(--shadow-hover);
|
||||
}
|
||||
|
||||
.card-img-holder {
|
||||
height: 250px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 2px solid #000;
|
||||
position: relative;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.placeholder-art {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.project-card:hover .placeholder-art {
|
||||
transform: scale(1.2) rotate(10deg);
|
||||
}
|
||||
|
||||
.bg-soft-blue { background-color: #e0f2fe; }
|
||||
.bg-soft-green { background-color: #dcfce7; }
|
||||
.bg-soft-purple { background-color: #f3e8ff; }
|
||||
.bg-soft-yellow { background-color: #fef9c3; }
|
||||
|
||||
.category-tag {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background: #000;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
padding: 5px 12px;
|
||||
border-radius: 20px;
|
||||
@ -249,98 +177,16 @@ h1, h2, h3, h4, h5, h6, .navbar-brand {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.card-body { padding: 1.5rem; }
|
||||
|
||||
.link-arrow {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
font-weight: 700;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.link-arrow i { transition: transform 0.2s; margin-left: 5px; }
|
||||
.link-arrow:hover i { transform: translateX(5px); }
|
||||
|
||||
/* About */
|
||||
.about-image-stack {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stack-card {
|
||||
position: absolute;
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-card);
|
||||
border: 2px solid #000;
|
||||
box-shadow: var(--shadow-hard);
|
||||
left: 10%;
|
||||
transform: rotate(-3deg);
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
.form-control {
|
||||
border: 2px solid #000;
|
||||
border: 2px solid var(--color-accent);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
font-weight: 500;
|
||||
background: #f8f9fa;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
box-shadow: 4px 4px 0 var(--color-primary);
|
||||
border-color: #000;
|
||||
border-color: var(--color-primary);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
.animate-up {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
animation: fadeUp 0.8s ease forwards;
|
||||
}
|
||||
|
||||
.delay-100 { animation-delay: 0.1s; }
|
||||
.delay-200 { animation-delay: 0.2s; }
|
||||
|
||||
@keyframes fadeUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Social */
|
||||
.social-links a {
|
||||
transition: transform 0.2s;
|
||||
display: inline-block;
|
||||
}
|
||||
.social-links a:hover {
|
||||
transform: scale(1.2) rotate(10deg);
|
||||
color: var(--color-accent) !important;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 991px) {
|
||||
.rotate-divider {
|
||||
transform: rotate(0);
|
||||
margin-top: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
padding-top: 120px;
|
||||
text-align: center;
|
||||
min-height: auto;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.display-1 { font-size: 3.5rem; }
|
||||
|
||||
.blob-1 { width: 300px; height: 300px; right: -20%; }
|
||||
.blob-2 { width: 300px; height: 300px; left: -20%; }
|
||||
}
|
||||
}
|
||||
BIN
assets/pasted-20260210-051518-6ed1892c.png
Normal file
BIN
assets/pasted-20260210-051518-6ed1892c.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
BIN
assets/pasted-20260210-054709-c7166cf7.png
Normal file
BIN
assets/pasted-20260210-054709-c7166cf7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
57
auth.php
Normal file
57
auth.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$pdo = db();
|
||||
if ($action === 'register') {
|
||||
$username = trim($_POST['username']);
|
||||
$password = $_POST['password'];
|
||||
$confirm_password = $_POST['confirm_password'];
|
||||
|
||||
if ($password !== $confirm_password) {
|
||||
die("Passwords do not match.");
|
||||
}
|
||||
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Check if this is the first user
|
||||
$stmt = $pdo->query("SELECT COUNT(*) FROM users");
|
||||
$count = $stmt->fetchColumn();
|
||||
$role = ($count == 0) ? 'admin' : 'user';
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $hash, $role]);
|
||||
header('Location: index.php?registered=1');
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
die("Registration failed: " . $e->getMessage());
|
||||
}
|
||||
} elseif ($action === 'login') {
|
||||
$username = trim($_POST['username']);
|
||||
$password = $_POST['password'];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password_hash'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
} else {
|
||||
die("Invalid credentials.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($action === 'logout') {
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
701
dashboard.php
Normal file
701
dashboard.php
Normal file
@ -0,0 +1,701 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT username, balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$user = $stmt->fetch();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>工作台 - 全球接码 (Global SMS)</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #ff4d94;
|
||||
--secondary-color: #ff1a75;
|
||||
--bg-light: #fff8f9;
|
||||
--surface-light: #ffffff;
|
||||
--text-main: #2d1a1e;
|
||||
--text-muted: #8a6d71;
|
||||
--border-color: #ffd1dc;
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-main);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem 3rem;
|
||||
}
|
||||
.top-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.balance-card {
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 20px;
|
||||
padding: 8px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
box-shadow: 4px 4px 0px var(--border-color);
|
||||
}
|
||||
|
||||
.card-neo {
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 24px;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 6px 6px 0px var(--border-color);
|
||||
}
|
||||
|
||||
.custom-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.dropdown-menu-custom {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
margin-top: 8px;
|
||||
box-shadow: 0 10px 25px rgba(255, 77, 148, 0.1);
|
||||
}
|
||||
.dropdown-menu-custom.show { display: block; }
|
||||
.dropdown-item-custom {
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--bg-light);
|
||||
}
|
||||
.dropdown-item-custom:hover { background: rgba(255, 77, 148, 0.05); color: var(--primary-color); }
|
||||
.dropdown-item-custom:last-child { border-bottom: none; }
|
||||
|
||||
.section-label {
|
||||
padding: 10px 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg-light);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.country-item-small {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.country-item-small .name-zh { font-weight: 700; }
|
||||
.country-item-small .name-en { font-size: 0.75rem; color: var(--text-muted); }
|
||||
|
||||
.service-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
transition: all 0.2s;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1.5fr;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
.service-card:hover {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 5px 15px rgba(255, 77, 148, 0.1);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 4px 12px rgba(255, 77, 148, 0.2);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--secondary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
.form-control:focus {
|
||||
box-shadow: none;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.table-neo {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 10px;
|
||||
}
|
||||
.table-neo tr { background: white; border-radius: 12px; }
|
||||
.table-neo td, .table-neo th {
|
||||
padding: 15px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
.table-neo th { border-top: none; border-bottom: 2px solid var(--border-color); font-weight: 800; color: var(--text-main); }
|
||||
.table-neo td:first-child { border-left: 1px solid var(--border-color); border-top-left-radius: 12px; border-bottom-left-radius: 12px; }
|
||||
.table-neo td:last-child { border-right: 1px solid var(--border-color); border-top-right-radius: 12px; border-bottom-right-radius: 12px; }
|
||||
|
||||
.timer-badge {
|
||||
background: rgba(255, 77, 148, 0.1);
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
padding: 5px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.scroll-area {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.scroll-area::-webkit-scrollbar { width: 4px; }
|
||||
.scroll-area::-webkit-scrollbar-track { background: transparent; }
|
||||
.scroll-area::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; }
|
||||
|
||||
.service-header {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1.5fr;
|
||||
padding: 0 20px 10px;
|
||||
color: var(--text-muted);
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.stock-badge {
|
||||
background: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
padding: 4px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.main-content { margin-left: 80px; padding: 1.5rem; }
|
||||
.service-card, .service-header { grid-template-columns: 1fr 1fr; }
|
||||
.service-header { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include 'includes/sidebar.php'; ?>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="top-header">
|
||||
<div>
|
||||
<h2 class="fw-bold mb-1">取号预约工作台 <span class="badge bg-primary fs-6 align-middle ms-2">LIVE</span></h2>
|
||||
<p class="text-muted mb-0">选择地区和项目,即刻开启您的业务验证</p>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="balance-card">
|
||||
<i class="fas fa-coins text-warning"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="small text-muted fw-bold" style="font-size: 0.7rem; line-height: 1;">当前余额 (Balance)</span>
|
||||
<span class="h5 mb-0 fw-bold" id="userBalance">$<?= number_format($user['balance'], 2) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="recharge.php" class="btn btn-primary">
|
||||
<i class="fas fa-plus-circle me-1"></i> 快速充值
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Activity Center Section -->
|
||||
<div class="card-neo mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="fw-bold mb-0"><i class="fas fa-bolt me-2 text-primary"></i> 活动任务中心 (Activity Center)</h5>
|
||||
<div class="d-flex gap-2">
|
||||
<span class="badge bg-light text-primary border rounded-pill px-3 py-2 fw-bold">数据同步中...</span>
|
||||
<button class="btn btn-outline-primary btn-sm rounded-pill px-3" onclick="loadActiveOrders()">
|
||||
<i class="fas fa-sync-alt"></i> 手动刷新
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table-neo">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>项目/国家 (Project/Country)</th>
|
||||
<th>临时号码 (Phone Number)</th>
|
||||
<th>验证码内容 (SMS Code)</th>
|
||||
<th>剩余时长 (Time Left)</th>
|
||||
<th class="text-end">管理操作 (Action)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="activeTasksBody">
|
||||
<tr><td colspan="5" class="text-center py-5 text-muted">
|
||||
<div class="spinner-border text-primary spinner-border-sm me-2"></div>
|
||||
正在加载活跃任务...
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-neo">
|
||||
<div class="row g-4 align-items-end mb-4">
|
||||
<div class="col-md-5">
|
||||
<label class="form-label fw-bold"><i class="fas fa-globe me-2 text-primary"></i> 1. 选择国家 (Select Country)</label>
|
||||
<div class="custom-dropdown">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0"><i class="fas fa-flag text-muted"></i></span>
|
||||
<input type="text" id="countryInput" class="form-control border-start-0" placeholder="点击查看热门国家..." autocomplete="off" onfocus="showCountriesDropdown()" oninput="filterCountriesDropdown()">
|
||||
</div>
|
||||
<div id="countriesDropdown" class="dropdown-menu-custom">
|
||||
<!-- Countries list will be here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label fw-bold"><i class="fas fa-rocket me-2 text-primary"></i> 2. 选择软件项目 (Select Service)</label>
|
||||
<div class="custom-dropdown">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0"><i class="fas fa-search text-muted"></i></span>
|
||||
<input type="text" id="serviceInput" class="form-control border-start-0" placeholder="点击查看热门项目..." autocomplete="off" onfocus="showServicesDropdown()" oninput="filterServicesDropdown()">
|
||||
</div>
|
||||
<div id="servicesDropdown" class="dropdown-menu-custom">
|
||||
<!-- Services list will be here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-primary w-100 py-3 rounded-4" onclick="loadServicesQuotation()">
|
||||
<i class="fas fa-search me-1"></i> 查询报价
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="quotationArea" style="display: none;">
|
||||
<div class="service-header mt-4">
|
||||
<div>项目名称 (Project Name)</div>
|
||||
<div>实时库存 (Stock)</div>
|
||||
<div>价格区间 (Price)</div>
|
||||
<div class="text-end">操作 (Action)</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-area" id="serviceList">
|
||||
<div class="text-center py-5 text-muted">
|
||||
<div class="spinner-border text-primary"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="welcomeArea" class="text-center py-5">
|
||||
<i class="fas fa-map-location-dot mb-3 d-block" style="font-size: 4rem; opacity: 0.1; color: var(--primary-color);"></i>
|
||||
<h5 class="fw-bold">开始您的预约之旅</h5>
|
||||
<p class="text-muted small">首先在上方选择一个国家和软件项目,点击“查询报价”获取最新实时库存</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SMS Modal -->
|
||||
<div class="modal fade" id="smsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 rounded-4 shadow-lg">
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="mb-4"><i class="fas fa-check-circle text-success" style="font-size: 4rem;"></i></div>
|
||||
<h3 class="fw-bold mb-2">验证码已送达!</h3>
|
||||
<p class="text-muted mb-4">系统已自动为您复制验证码</p>
|
||||
<div class="bg-light p-4 rounded-3 mb-4">
|
||||
<h1 class="display-4 fw-bold text-primary mb-0" id="modalSmsCode">------</h1>
|
||||
</div>
|
||||
<button class="btn btn-primary w-100 py-3 rounded-3" data-bs-dismiss="modal">确定并关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const apiHandler = 'ajax_handler.php';
|
||||
let allCountries = [];
|
||||
let currentCountry = null;
|
||||
let currentService = null;
|
||||
let activePolls = {};
|
||||
let activeTimers = {};
|
||||
|
||||
const popularCountries = ['中国', '香港', '台湾', '美国', '英国', '俄罗斯', '印度', '印尼', '越南', '泰国', '韩国', '日本'];
|
||||
const popularServices = ['Telegram', 'WhatsApp', 'OpenAI', 'Google', 'Facebook', 'Instagram', 'Twitter', 'TikTok', 'Amazon', 'Apple'];
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadCountries();
|
||||
loadActiveOrders();
|
||||
setInterval(loadActiveOrders, 30000);
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.custom-dropdown')) {
|
||||
hideDropdowns();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function loadCountries() {
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=get_countries`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
allCountries = data.data;
|
||||
renderCountriesDropdown();
|
||||
renderServicesDropdown();
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderCountriesDropdown(filter = '') {
|
||||
const container = document.getElementById('countriesDropdown');
|
||||
container.innerHTML = '';
|
||||
|
||||
let filtered = allCountries;
|
||||
if (filter) {
|
||||
filtered = allCountries.filter(c =>
|
||||
c.name_zh.includes(filter) ||
|
||||
c.name_en.toLowerCase().includes(filter)
|
||||
);
|
||||
}
|
||||
|
||||
if (!filter) {
|
||||
const popularLabel = document.createElement('div');
|
||||
popularLabel.className = 'section-label';
|
||||
popularLabel.textContent = '热门国家 / Popular';
|
||||
container.appendChild(popularLabel);
|
||||
|
||||
allCountries.filter(c => popularCountries.includes(c.name_zh)).forEach(c => {
|
||||
container.appendChild(createCountryItem(c));
|
||||
});
|
||||
|
||||
const allLabel = document.createElement('div');
|
||||
allLabel.className = 'section-label';
|
||||
allLabel.textContent = '全部国家 / All Countries';
|
||||
container.appendChild(allLabel);
|
||||
}
|
||||
|
||||
filtered.forEach(c => {
|
||||
container.appendChild(createCountryItem(c));
|
||||
});
|
||||
|
||||
if (filtered.length === 0) {
|
||||
container.innerHTML = '<div class="p-4 text-center text-muted small">未找到相关国家</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function createCountryItem(c) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'dropdown-item-custom';
|
||||
div.onclick = () => selectCountry(c);
|
||||
div.innerHTML = `
|
||||
<div class="country-item-small">
|
||||
<span class="flag"><i class="fas fa-flag text-muted opacity-25"></i></span>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="name-zh">${c.name_zh}</span>
|
||||
<span class="name-en">${c.name_en}</span>
|
||||
</div>
|
||||
</div>
|
||||
<i class="fas fa-check text-primary d-none" id="check-${c.name_zh}"></i>
|
||||
`;
|
||||
return div;
|
||||
}
|
||||
|
||||
function renderServicesDropdown(filter = '') {
|
||||
const container = document.getElementById('servicesDropdown');
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!filter) {
|
||||
const popularLabel = document.createElement('div');
|
||||
popularLabel.className = 'section-label';
|
||||
popularLabel.textContent = '热门项目 / Popular Services';
|
||||
container.appendChild(popularLabel);
|
||||
|
||||
popularServices.forEach(s => {
|
||||
container.appendChild(createServiceItem(s));
|
||||
});
|
||||
} else {
|
||||
container.appendChild(createServiceItem(filter));
|
||||
}
|
||||
}
|
||||
|
||||
function createServiceItem(name) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'dropdown-item-custom';
|
||||
div.onclick = () => selectService(name);
|
||||
div.innerHTML = `
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<i class="fas fa-mobile-screen-button text-muted opacity-50"></i>
|
||||
<span class="fw-bold">${name}</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-right small opacity-25"></i>
|
||||
`;
|
||||
return div;
|
||||
}
|
||||
|
||||
function showCountriesDropdown() {
|
||||
hideDropdowns();
|
||||
document.getElementById('countriesDropdown').classList.add('show');
|
||||
}
|
||||
|
||||
function filterCountriesDropdown() {
|
||||
const val = document.getElementById('countryInput').value;
|
||||
renderCountriesDropdown(val);
|
||||
}
|
||||
|
||||
function selectCountry(c) {
|
||||
currentCountry = c;
|
||||
document.getElementById('countryInput').value = c.name_zh;
|
||||
hideDropdowns();
|
||||
if (currentService) loadServicesQuotation();
|
||||
}
|
||||
|
||||
function showServicesDropdown() {
|
||||
hideDropdowns();
|
||||
document.getElementById('servicesDropdown').classList.add('show');
|
||||
}
|
||||
|
||||
function filterServicesDropdown() {
|
||||
const val = document.getElementById('serviceInput').value;
|
||||
renderServicesDropdown(val);
|
||||
}
|
||||
|
||||
function selectService(name) {
|
||||
currentService = name;
|
||||
document.getElementById('serviceInput').value = name;
|
||||
hideDropdowns();
|
||||
if (currentCountry) loadServicesQuotation();
|
||||
}
|
||||
|
||||
function hideDropdowns() {
|
||||
document.querySelectorAll('.dropdown-menu-custom').forEach(d => d.classList.remove('show'));
|
||||
}
|
||||
|
||||
async function loadServicesQuotation() {
|
||||
if (!currentCountry) return;
|
||||
|
||||
document.getElementById('welcomeArea').style.display = 'none';
|
||||
document.getElementById('quotationArea').style.display = 'block';
|
||||
|
||||
const service = document.getElementById('serviceInput').value;
|
||||
const container = document.getElementById('serviceList');
|
||||
container.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-primary"></div><p class="mt-2 small text-muted">正在获取实时报价单...</p></div>';
|
||||
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=get_services&country=${encodeURIComponent(currentCountry.name_zh)}&service=${encodeURIComponent(service)}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
container.innerHTML = '';
|
||||
if (data.data.length === 0) {
|
||||
container.innerHTML = '<div class="text-center py-5 text-muted">当前国家暂无该项目报价,请尝试搜索其他项目</div>';
|
||||
return;
|
||||
}
|
||||
data.data.forEach(s => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'service-card';
|
||||
card.innerHTML = `
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="bg-light p-3 rounded-3 me-3 text-primary"><i class="fas fa-mobile-screen-button"></i></div>
|
||||
<div class="fw-bold">${s.service_name}</div>
|
||||
</div>
|
||||
<div><span class="stock-badge">${s.count}+</span></div>
|
||||
<div class="fw-bold text-primary h5 mb-0">$${s.cost}</div>
|
||||
<div class="text-end">
|
||||
<button class="btn btn-primary w-100 py-2 rounded-pill fw-bold" onclick="getNumber('${s.service_id}', '${s.service_name}', ${s.cost}, this)">
|
||||
立即预约 (Reserve)
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
async function getNumber(service_id, service_name, price, btn) {
|
||||
btn.disabled = true;
|
||||
const oldHtml = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 正在预约...';
|
||||
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=get_number&service_id=${service_id}&service_name=${encodeURIComponent(service_name)}&country_name=${encodeURIComponent(currentCountry.name_zh)}&price=${price}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
loadActiveOrders();
|
||||
updateBalance();
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
} else {
|
||||
alert('预约失败: ' + (data.msg || '余额不足或库存不足'));
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = oldHtml;
|
||||
}
|
||||
|
||||
async function updateBalance() {
|
||||
const res = await fetch(`${apiHandler}?action=get_balance`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
document.getElementById('userBalance').textContent = '$' + data.balance;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadActiveOrders() {
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=get_active_orders`);
|
||||
const data = await res.json();
|
||||
const body = document.getElementById('activeTasksBody');
|
||||
|
||||
Object.values(activeTimers).forEach(t => clearInterval(t));
|
||||
activeTimers = {};
|
||||
|
||||
if (data.code === 0) {
|
||||
if (data.data.length === 0) {
|
||||
body.innerHTML = '<tr><td colspan="5" class="text-center py-5 text-muted small">暂无进行中的预约任务<br><span class="opacity-50">请在上方选择项目点击“立即预约”</span></td></tr>';
|
||||
return;
|
||||
}
|
||||
body.innerHTML = '';
|
||||
data.data.forEach(order => {
|
||||
const row = document.createElement('tr');
|
||||
const expireAt = new Date(order.expire_at.replace(/-/g, "/")).getTime();
|
||||
const now = new Date().getTime();
|
||||
let timeLeft = Math.floor((expireAt - now) / 1000);
|
||||
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<div class="fw-bold text-primary">${order.service_name}</div>
|
||||
<div class="small text-muted"><i class="fas fa-map-marker-alt me-1"></i>${order.country_name}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-bold h5 mb-0 text-dark">${order.number}</div>
|
||||
<div class="small text-muted">Request ID: ${order.request_id}</div>
|
||||
</td>
|
||||
<td id="sms-${order.request_id}">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="spinner-grow spinner-grow-sm text-primary me-2"></span>
|
||||
<span class="small text-primary fw-bold">监听短信中...</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="timer-badge" id="timer-${order.request_id}">${formatTime(timeLeft)}</span></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-danger border-0 rounded-pill px-3" onclick="releaseNumber('${order.request_id}')" title="释放号码">
|
||||
<i class="fas fa-trash-alt me-1"></i> 释放号码
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
body.appendChild(row);
|
||||
startPolling(order.request_id);
|
||||
startTimer(order.request_id, timeLeft);
|
||||
});
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function startPolling(request_id) {
|
||||
if (activePolls[request_id]) return;
|
||||
activePolls[request_id] = setInterval(async () => {
|
||||
const res = await fetch(`${apiHandler}?action=check_sms&request_id=${request_id}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0 && data.msg === 'success') {
|
||||
const smsEl = document.getElementById(`sms-${request_id}`);
|
||||
if (smsEl) {
|
||||
smsEl.innerHTML = `
|
||||
<div class="bg-success-subtle p-2 rounded border border-success d-flex align-items-center justify-content-between">
|
||||
<span class="fw-bold text-success h5 mb-0">${data.sms_code}</span>
|
||||
<button class="btn btn-sm btn-link text-success p-0" onclick="navigator.clipboard.writeText('${data.sms_code}')"><i class="fas fa-copy"></i></button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
clearInterval(activePolls[request_id]);
|
||||
delete activePolls[request_id];
|
||||
showSmsModal(data.sms_code);
|
||||
} else if (data.code === 400) {
|
||||
clearInterval(activePolls[request_id]);
|
||||
delete activePolls[request_id];
|
||||
loadActiveOrders();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function startTimer(id, seconds) {
|
||||
const el = document.getElementById(`timer-${id}`);
|
||||
activeTimers[id] = setInterval(() => {
|
||||
seconds--;
|
||||
if (seconds <= 0) {
|
||||
clearInterval(activeTimers[id]);
|
||||
if (el) el.textContent = '已过期';
|
||||
setTimeout(() => loadActiveOrders(), 1000);
|
||||
} else {
|
||||
if (el) el.textContent = formatTime(seconds);
|
||||
if (seconds < 60) {
|
||||
el.classList.add('bg-danger', 'text-white');
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function releaseNumber(id) {
|
||||
if (!confirm('释放后将无法接收该号码的验证码。确定释放吗?')) return;
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=release_number&request_id=${id}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
loadActiveOrders();
|
||||
updateBalance();
|
||||
} else {
|
||||
alert(data.msg || '操作失败');
|
||||
}
|
||||
} catch (e) { alert('网络请求失败'); }
|
||||
}
|
||||
|
||||
function formatTime(s) {
|
||||
if (s <= 0) return "00:00";
|
||||
const m = Math.floor(s / 60);
|
||||
const sec = s % 60;
|
||||
return `${m.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function showSmsModal(code) {
|
||||
document.getElementById('modalSmsCode').textContent = code;
|
||||
const modal = new bootstrap.Modal(document.getElementById('smsModal'));
|
||||
modal.show();
|
||||
navigator.clipboard.writeText(code).catch(e => {});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
32
db/schema.sql
Normal file
32
db/schema.sql
Normal file
@ -0,0 +1,32 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
balance DECIMAL(10, 2) DEFAULT 0.00,
|
||||
role ENUM('user', 'admin') DEFAULT 'user',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS recharges (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
amount DECIMAL(10, 2) NOT NULL,
|
||||
txid VARCHAR(255),
|
||||
status ENUM('pending', 'completed', 'rejected') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sms_orders (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
request_id VARCHAR(50) NOT NULL,
|
||||
number VARCHAR(20) NOT NULL,
|
||||
service_name VARCHAR(50),
|
||||
country_name VARCHAR(50),
|
||||
cost DECIMAL(10, 2),
|
||||
sms_content TEXT,
|
||||
status ENUM('pending', 'received', 'canceled', 'expired') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
91
includes/sidebar.php
Normal file
91
includes/sidebar.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
$current_page = basename($_SERVER['PHP_SELF']);
|
||||
?>
|
||||
<style>
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: #ffffff;
|
||||
border-right: 2px solid #ffd1dc;
|
||||
z-index: 1000;
|
||||
padding: 2rem 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.sidebar-brand {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
color: #ff4d94;
|
||||
text-decoration: none;
|
||||
margin-bottom: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.sidebar .nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
color: #8a6d71;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active {
|
||||
background-color: #fff8f9;
|
||||
color: #ff4d94;
|
||||
}
|
||||
.sidebar .nav-link i { font-size: 1.2rem; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { width: 80px; padding: 2rem 0.5rem; align-items: center; }
|
||||
.sidebar-brand span, .sidebar .nav-link span { display: none; }
|
||||
.sidebar-brand { margin-bottom: 2rem; justify-content: center; }
|
||||
.sidebar .nav-link { justify-content: center; padding: 12px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sidebar">
|
||||
<a href="dashboard.php" class="sidebar-brand">
|
||||
<i class="fas fa-bolt"></i>
|
||||
<span>全球接码</span>
|
||||
</a>
|
||||
|
||||
<nav class="flex-grow-1">
|
||||
<a href="dashboard.php" class="nav-link <?= $current_page === 'dashboard.php' ? 'active' : '' ?>">
|
||||
<i class="fas fa-th-large"></i>
|
||||
<span>工作台</span>
|
||||
</a>
|
||||
<a href="orders.php" class="nav-link <?= $current_page === 'orders.php' ? 'active' : '' ?>">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>接码记录</span>
|
||||
</a>
|
||||
<a href="recharge.php" class="nav-link <?= $current_page === 'recharge.php' ? 'active' : '' ?>">
|
||||
<i class="fas fa-wallet"></i>
|
||||
<span>充值中心</span>
|
||||
</a>
|
||||
<a href="support.php" class="nav-link <?= $current_page === 'support.php' ? 'active' : '' ?>">
|
||||
<i class="fas fa-headset"></i>
|
||||
<span>联系客服</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="mt-auto">
|
||||
<?php if (isset($user['username'])): ?>
|
||||
<div class="p-3 bg-light rounded-4 mb-3 d-none d-lg-block">
|
||||
<div class="small text-muted fw-bold mb-1">当前用户</div>
|
||||
<div class="fw-bold text-truncate"><?= htmlspecialchars($user['username']) ?></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<a href="auth.php?action=logout" class="nav-link text-danger">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
487
index.php
487
index.php
@ -1,150 +1,351 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
session_start();
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<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);
|
||||
}
|
||||
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;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
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%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>全球接码 - 专业、安全、快速的验证码接收平台</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #ff4d94;
|
||||
--accent-color: #ff1a75;
|
||||
--bg-light: #fff5f7;
|
||||
--surface-light: #ffffff;
|
||||
--text-main: #2d1a1e;
|
||||
--text-muted: #8a6d71;
|
||||
--border-color: #ffd1dc;
|
||||
}
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-main);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.navbar {
|
||||
background-color: rgba(255, 245, 247, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 20px 0;
|
||||
}
|
||||
.hero-section {
|
||||
padding: 160px 0 100px;
|
||||
background: radial-gradient(circle at top right, rgba(255, 77, 148, 0.1), transparent 40%),
|
||||
radial-gradient(circle at bottom left, rgba(255, 209, 220, 0.2), transparent 40%);
|
||||
}
|
||||
.hero-title {
|
||||
font-size: 4rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 24px;
|
||||
color: var(--text-main);
|
||||
}
|
||||
.hero-title span {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.glass-card {
|
||||
background-color: var(--surface-light);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 32px;
|
||||
padding: 40px;
|
||||
box-shadow: 10px 10px 0px var(--border-color);
|
||||
}
|
||||
.feature-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-color: rgba(255, 77, 148, 0.1);
|
||||
color: var(--primary-color);
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border: none;
|
||||
padding: 16px 32px;
|
||||
border-radius: 16px;
|
||||
font-weight: 700;
|
||||
transition: all 0.3s ease;
|
||||
color: white;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--accent-color);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(255, 77, 148, 0.3);
|
||||
color: white;
|
||||
}
|
||||
.form-control {
|
||||
background-color: #fff;
|
||||
border: 2px solid var(--border-color);
|
||||
color: var(--text-main);
|
||||
border-radius: 16px;
|
||||
padding: 14px 20px;
|
||||
}
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
.footer {
|
||||
padding: 80px 0 40px;
|
||||
background-color: #ffffff;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
.step-number {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.hero-title { font-size: 2.5rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand fw-bold fs-3" href="#" style="color: var(--primary-color);"><i class="fas fa-globe me-2"></i>全球接码</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto gap-3">
|
||||
<li class="nav-item"><a class="nav-link" href="#features">服务特性</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#how-it-works">使用说明</a></li>
|
||||
<li class="nav-item"><a class="btn btn-outline-dark px-4 rounded-pill" href="#" data-bs-toggle="modal" data-bs-target="#loginModal">登录</a></li>
|
||||
<li class="nav-item"><a class="btn btn-primary px-4 rounded-pill" href="#" data-bs-toggle="modal" data-bs-target="#registerModal">开始使用</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</nav>
|
||||
|
||||
<section class="hero-section">
|
||||
<div class="container">
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-7">
|
||||
<h1 class="hero-title">隐私第一,<br><span>全球号码</span> 随心接</h1>
|
||||
<p class="fs-5 text-muted mb-5">我们提供稳定、高效的全球虚拟号码验证码接收服务。支持 Telegram, WhatsApp, Facebook 等数千个社交平台。全自动收码,仅支持 USDT 支付,让您的隐私得到全方位保护。</p>
|
||||
<div class="d-flex gap-3">
|
||||
<button class="btn btn-primary btn-lg px-5" data-bs-toggle="modal" data-bs-target="#registerModal">立即开启</button>
|
||||
<a href="#how-it-works" class="btn btn-outline-secondary btn-lg px-5">使用教程</a>
|
||||
</div>
|
||||
<div class="mt-5 d-flex gap-4">
|
||||
<div class="text-center">
|
||||
<h4 class="fw-bold mb-0">120+</h4>
|
||||
<p class="small text-muted">支持国家</p>
|
||||
</div>
|
||||
<div class="vr opacity-25"></div>
|
||||
<div class="text-center">
|
||||
<h4 class="fw-bold mb-0">2.5k+</h4>
|
||||
<p class="small text-muted">集成项目</p>
|
||||
</div>
|
||||
<div class="vr opacity-25"></div>
|
||||
<div class="text-center">
|
||||
<h4 class="fw-bold mb-0">秒级</h4>
|
||||
<p class="small text-muted">到码速度</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="glass-card">
|
||||
<h4 class="fw-bold mb-4 text-center">欢迎回来</h4>
|
||||
<form action="auth.php?action=login" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted">用户名</label>
|
||||
<input type="text" name="username" class="form-control" placeholder="输入您的用户名" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-muted">密码</label>
|
||||
<input type="password" name="password" class="form-control" placeholder="输入您的密码" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 py-3">进入工作台</button>
|
||||
</form>
|
||||
<p class="mt-4 text-center text-muted small">新用户? <a href="#" class="fw-bold" style="color: var(--primary-color);" data-bs-toggle="modal" data-bs-target="#registerModal">立即注册</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-5" id="how-it-works">
|
||||
<div class="container py-5">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold fs-1">四步开启高效收码</h2>
|
||||
<p class="text-muted">简单易用的流程,助您快速获取验证码</p>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="step-number mx-auto">1</div>
|
||||
<h5 class="fw-bold">注册账号</h5>
|
||||
<p class="small text-muted">创建一个属于您的匿名账号,仅需用户名和密码。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="step-number mx-auto">2</div>
|
||||
<h5 class="fw-bold">充值余额</h5>
|
||||
<p class="small text-muted">通过 USDT (TRC20/ERC20) 充值,金额实时到账。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="step-number mx-auto">3</div>
|
||||
<h5 class="fw-bold">选取项目</h5>
|
||||
<p class="small text-muted">在控制面板选择目标国家和社交软件项目。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="step-number mx-auto">4</div>
|
||||
<h5 class="fw-bold">接收短信</h5>
|
||||
<p class="small text-muted">获取号码后在对应 App 输入,网页自动刷新验证码。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-5 bg-white" id="features">
|
||||
<div class="container py-5">
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-6">
|
||||
<h2 class="fw-bold fs-1 mb-4">为什么选择我们的服务?</h2>
|
||||
<div class="d-flex gap-4 mb-4">
|
||||
<div class="feature-icon flex-shrink-0"><i class="fas fa-microchip"></i></div>
|
||||
<div>
|
||||
<h5 class="fw-bold">AI 智能路由</h5>
|
||||
<p class="text-muted">自动选择最稳定的线路,确保高频率注册也能顺畅进行。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-4 mb-4">
|
||||
<div class="feature-icon flex-shrink-0"><i class="fas fa-lock"></i></div>
|
||||
<div>
|
||||
<h5 class="fw-bold">端到端隐私保护</h5>
|
||||
<p class="text-muted">不记录任何用户敏感信息,支付痕迹不可追踪。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-4">
|
||||
<div class="feature-icon flex-shrink-0"><i class="fas fa-comments"></i></div>
|
||||
<div>
|
||||
<h5 class="fw-bold">专业中文客服</h5>
|
||||
<p class="text-muted">7x24 小时在线支持,解决您在取码过程中的任何疑难。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img src="https://images.pexels.com/photos/730547/pexels-photo-730547.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="Security" class="img-fluid rounded-5 shadow-lg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<h4 class="fw-bold mb-4" style="color: var(--primary-color);"><i class="fas fa-globe me-2"></i>全球接码</h4>
|
||||
<p class="text-muted">引领全球的隐私通讯解决方案。我们不仅提供号码,更提供安全感。</p>
|
||||
</div>
|
||||
<div class="col-lg-2 offset-lg-1">
|
||||
<h6 class="fw-bold mb-4">核心服务</h6>
|
||||
<ul class="list-unstyled d-flex flex-column gap-2">
|
||||
<li><a href="#" class="text-muted text-decoration-none">Telegram 取码</a></li>
|
||||
<li><a href="#" class="text-muted text-decoration-none">WhatsApp 注册</a></li>
|
||||
<li><a href="#" class="text-muted text-decoration-none">Facebook 验证</a></li>
|
||||
<li><a href="#" class="text-muted text-decoration-none">X (Twitter) 绑定</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<h6 class="fw-bold mb-4">快速导航</h6>
|
||||
<ul class="list-unstyled d-flex flex-column gap-2">
|
||||
<li><a href="#" class="text-muted text-decoration-none">常见问题</a></li>
|
||||
<li><a href="#" class="text-muted text-decoration-none">API 文档</a></li>
|
||||
<li><a href="#" class="text-muted text-decoration-none">服务条款</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<h6 class="fw-bold mb-4">关注动态</h6>
|
||||
<p class="text-muted small">加入我们的频道,获取最新号码上新通知。</p>
|
||||
<div class="d-flex gap-3 mt-3">
|
||||
<a href="#" class="text-muted fs-4"><i class="fab fa-telegram"></i></a>
|
||||
<a href="#" class="text-muted fs-4"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#" class="text-muted fs-4"><i class="fab fa-discord"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-5 opacity-10">
|
||||
<div class="text-center text-muted small">
|
||||
© 2026 全球接码平台. 版权所有。 保护您的隐私,是我们不变的使命。
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Modals -->
|
||||
<div class="modal fade" id="registerModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content bg-white border-0 rounded-4 shadow-lg">
|
||||
<div class="modal-body p-5">
|
||||
<h3 class="fw-bold mb-4 text-center">新用户注册</h3>
|
||||
<form action="auth.php?action=register" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted">设置用户名</label>
|
||||
<input type="text" name="username" class="form-control" placeholder="仅支持字母和数字" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted">设置登录密码</label>
|
||||
<input type="password" name="password" class="form-control" placeholder="至少 6 位字符" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-muted">再次确认密码</label>
|
||||
<input type="password" name="confirm_password" class="form-control" placeholder="请与上述密码一致" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 py-3">立即注册</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="loginModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content bg-white border-0 rounded-4 shadow-lg">
|
||||
<div class="modal-body p-5">
|
||||
<h3 class="fw-bold mb-4 text-center">登录账号</h3>
|
||||
<form action="auth.php?action=login" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted">用户名</label>
|
||||
<input type="text" name="username" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-muted">密码</label>
|
||||
<input type="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 py-3">立即登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
154
orders.php
Normal file
154
orders.php
Normal file
@ -0,0 +1,154 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT username, balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM sms_orders WHERE user_id = ? ORDER BY created_at DESC");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$orders = $stmt->fetchAll();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>接码记录 - 全球接码</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #ff4d94;
|
||||
--secondary-color: #ff1a75;
|
||||
--bg-light: #fff8f9;
|
||||
--surface-light: #ffffff;
|
||||
--text-main: #2d1a1e;
|
||||
--text-muted: #8a6d71;
|
||||
--border-color: #ffd1dc;
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-main);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem 3rem;
|
||||
}
|
||||
|
||||
.card-neo {
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 32px;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 8px 8px 0px var(--border-color);
|
||||
}
|
||||
|
||||
.table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 10px;
|
||||
}
|
||||
.table thead th {
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.table tbody tr {
|
||||
background-color: white;
|
||||
}
|
||||
.table tbody td {
|
||||
border: none;
|
||||
padding: 16px 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.table tbody td:first-child { border-top-left-radius: 16px; border-bottom-left-radius: 16px; border: 1px solid var(--border-color); border-right: none; }
|
||||
.table tbody td:last-child { border-top-right-radius: 16px; border-bottom-right-radius: 16px; border: 1px solid var(--border-color); border-left: none; }
|
||||
.table tbody td:not(:first-child):not(:last-child) { border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); }
|
||||
|
||||
.badge-custom { padding: 8px 16px; border-radius: 12px; font-weight: 600; font-size: 0.8rem; }
|
||||
.bg-received { background-color: rgba(69, 179, 107, 0.1); color: #28a745; }
|
||||
.bg-canceled { background-color: rgba(220, 53, 69, 0.1); color: #dc3545; }
|
||||
.bg-pending { background-color: rgba(255, 77, 148, 0.1); color: var(--primary-color); }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.main-content { margin-left: 80px; padding: 1.5rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include 'includes/sidebar.php'; ?>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="mb-5">
|
||||
<h2 class="fw-bold mb-1">接码记录清单</h2>
|
||||
<p class="text-muted">您可以查看所有历史获取过的号码及短信内容</p>
|
||||
</div>
|
||||
|
||||
<div class="card-neo">
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务时间</th>
|
||||
<th>项目名称</th>
|
||||
<th>临时号码</th>
|
||||
<th>短信内容</th>
|
||||
<th>执行状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<tr>
|
||||
<td class="small text-muted"><?= date('m-d H:i', strtotime($order['created_at'])) ?></td>
|
||||
<td class="fw-bold"><?= htmlspecialchars($order['service_name'] ?: '未知项目') ?></td>
|
||||
<td class="fw-bold text-primary"><?= $order['number'] ?></td>
|
||||
<td>
|
||||
<?php if ($order['sms_content']): ?>
|
||||
<span class="p-2 rounded px-3 fw-bold" style="background-color: var(--bg-light); color: var(--primary-color); border: 1px solid var(--border-color);"><?= htmlspecialchars($order['sms_content']) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted opacity-50">-</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($order['status'] === 'received'): ?>
|
||||
<span class="badge-custom bg-received">成功收码</span>
|
||||
<?php elseif ($order['status'] === 'canceled'): ?>
|
||||
<span class="badge-custom bg-canceled">已释放</span>
|
||||
<?php else: ?>
|
||||
<span class="badge-custom bg-pending">等待中</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($orders)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center py-5 text-muted">
|
||||
<i class="fas fa-history fa-3x mb-3 opacity-25"></i>
|
||||
<p>暂无任何接码记录</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
566
recharge.php
Normal file
566
recharge.php
Normal file
@ -0,0 +1,566 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT username, balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = 'usdt_trc20_address'");
|
||||
$stmt->execute();
|
||||
$trc20_address = $stmt->fetchColumn() ?: 'TEm1B...TRC20_ADDRESS_HERE';
|
||||
|
||||
$stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = 'usdt_erc20_address'");
|
||||
$stmt->execute();
|
||||
$erc20_address = $stmt->fetchColumn() ?: '0x71C...ERC20_ADDRESS_HERE';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>充值中心 - 全球接码 (Recharge)</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #ff4d94;
|
||||
--secondary-color: #ff1a75;
|
||||
--bg-light: #fff8f9;
|
||||
--surface-light: #ffffff;
|
||||
--text-main: #2d1a1e;
|
||||
--text-muted: #8a6d71;
|
||||
--border-color: #ffd1dc;
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-main);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem 3rem;
|
||||
}
|
||||
|
||||
.card-neo {
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 32px;
|
||||
padding: 40px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 8px 8px 0px var(--border-color);
|
||||
}
|
||||
|
||||
.network-btn {
|
||||
background-color: #fff;
|
||||
border: 2px solid var(--border-color);
|
||||
color: var(--text-muted);
|
||||
padding: 15px;
|
||||
border-radius: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
.network-btn.active {
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
background-color: rgba(255, 77, 148, 0.05);
|
||||
box-shadow: 0 4px 12px rgba(255, 77, 148, 0.1);
|
||||
}
|
||||
|
||||
.qr-container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 24px;
|
||||
display: inline-block;
|
||||
margin: 20px 0;
|
||||
border: 2px solid var(--border-color);
|
||||
box-shadow: 0 10px 30px rgba(255, 77, 148, 0.1);
|
||||
}
|
||||
|
||||
.address-container {
|
||||
position: relative;
|
||||
background-color: var(--bg-light);
|
||||
padding: 24px;
|
||||
border-radius: 20px;
|
||||
border: 2px dashed var(--border-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.address-box {
|
||||
word-break: break-all;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-color);
|
||||
font-weight: 800;
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.copy-btn:hover { background: var(--secondary-color); transform: translateY(-50%) scale(1.05); }
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border: none;
|
||||
padding: 18px 30px;
|
||||
border-radius: 20px;
|
||||
font-weight: 800;
|
||||
color: white;
|
||||
box-shadow: 0 8px 25px rgba(255, 77, 148, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.btn-primary:hover { transform: translateY(-3px); box-shadow: 0 12px 30px rgba(255, 77, 148, 0.4); }
|
||||
|
||||
.step-container { display: none; }
|
||||
.step-container.active { display: block; animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
|
||||
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
.status-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
backdrop-filter: blur(15px);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: all 0.6s ease;
|
||||
}
|
||||
.status-overlay.show { visibility: visible; opacity: 1; }
|
||||
|
||||
.instruction-item {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.instruction-item:last-child { margin-bottom: 0; }
|
||||
.instruction-icon {
|
||||
width: 32px; height: 32px;
|
||||
background: var(--bg-light);
|
||||
color: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
font-weight: 800;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.pulse-scanner {
|
||||
width: 100px; height: 100px;
|
||||
border: 4px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
animation: pulse 2s infinite;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 77, 148, 0.7); }
|
||||
70% { transform: scale(1); box-shadow: 0 0 0 20px rgba(255, 77, 148, 0); }
|
||||
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 77, 148, 0); }
|
||||
}
|
||||
|
||||
.notice-box {
|
||||
background: #fff5f8;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.accordion-button:not(.collapsed) {
|
||||
background-color: rgba(255, 77, 148, 0.05);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.main-content { margin-left: 80px; padding: 1.5rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include 'includes/sidebar.php'; ?>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="mb-5 d-flex justify-content-between align-items-end">
|
||||
<div>
|
||||
<h2 class="fw-bold mb-1"><i class="fas fa-wallet me-2 text-primary"></i> 资产充值 (Asset Recharge)</h2>
|
||||
<p class="text-muted mb-0">USDT 全自动到账,由区块链共识网络提供安全保障</p>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="small text-muted fw-bold d-block">当前可用余额</span>
|
||||
<span class="h4 fw-bold text-primary mb-0">$<?= number_format($user['balance'], 2) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-xl-8">
|
||||
<div class="card-neo">
|
||||
<!-- Step 1: Input Amount -->
|
||||
<div id="step1" class="step-container active">
|
||||
<h4 class="fw-bold mb-4">1. 设定充值金额 (Set Amount)</h4>
|
||||
<div class="notice-box">
|
||||
<p class="small mb-0">
|
||||
<i class="fas fa-info-circle me-1"></i> 为了识别您的充值订单,系统会自动为您的金额添加<strong>唯一的随机小数</strong>。请在支付时务必支付<strong>包含小数点的精确金额</strong>。
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-bold text-muted small">充值金额 (USDT)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 border-2" style="border-color: var(--border-color); color: var(--primary-color); font-weight: 800; padding: 0 25px;">$</span>
|
||||
<input type="number" id="inputAmount" class="form-control border-start-0 border-2" style="border-color: var(--border-color); font-size: 1.8rem; font-weight: 800;" placeholder="10.00" min="10" step="1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-5">
|
||||
<?php foreach([10, 50, 100, 200, 500, 1000] as $amt): ?>
|
||||
<div class="col-4 col-md-2">
|
||||
<button class="btn btn-outline-pink w-100 py-3 border-2 rounded-4" style="border-color: var(--border-color); color: var(--primary-color); font-weight: 800;" onclick="setAmount(<?= $amt ?>)"><?= $amt ?></button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100 py-4" onclick="confirmOrder()">
|
||||
<i class="fas fa-arrow-right me-2"></i> 生成专属支付订单
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Payment Details -->
|
||||
<div id="step2" class="step-container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-5">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">2. 完成链上支付</h4>
|
||||
<p class="text-muted small mb-0" id="orderIdDisplay">正在创建订单...</p>
|
||||
</div>
|
||||
<div class="text-end bg-light p-3 rounded-4 border">
|
||||
<div class="h2 fw-bold text-primary mb-0" id="displayAmount">0.00</div>
|
||||
<div class="small text-muted fw-bold">待支付精确金额 (USDT)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold text-muted mb-3">A. 选择支付网络</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-6">
|
||||
<div class="network-btn active" id="btnTRC" onclick="selectNetwork('TRC20')">
|
||||
<div class="fw-bold">TRC20</div>
|
||||
<div class="small opacity-50" style="font-size: 0.6rem;">波场/TRON</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="network-btn" id="btnERC" onclick="selectNetwork('ERC20')">
|
||||
<div class="fw-bold">ERC20</div>
|
||||
<div class="small opacity-50" style="font-size: 0.6rem;">以太坊/ETH</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 p-4 rounded-4" style="background: #fff9f0; border: 1px solid #ffeeba;">
|
||||
<p class="small mb-0 text-dark">
|
||||
<i class="fas fa-exclamation-triangle text-warning me-2"></i>
|
||||
<strong>必看操作指南:</strong> <br>
|
||||
1. 充值网络必须与您钱包发送的网络一致,否则资产将丢失。<br>
|
||||
2. 支付金额必须<strong>精确到小数点后两位</strong>,这是识别您身份的唯一凭据。<br>
|
||||
3. 支付完成后,请<strong>停留在本页面</strong>。系统检测到款项后会自动为您跳转。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<div class="qr-container" id="qrcode"></div>
|
||||
<div class="mt-2">
|
||||
<span class="badge bg-light text-primary p-2 px-4 rounded-pill fw-bold border">
|
||||
<i class="fas fa-hourglass-half me-2"></i> 订单监听剩余时间: <span id="countdown">60:00</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="text-muted small mb-2 d-block fw-bold">B. 复制专属收款地址</label>
|
||||
<div class="address-container">
|
||||
<div class="address-box" id="addressBox">正在获取地址...</div>
|
||||
<button class="copy-btn" onclick="copyAddress()" title="点击复制"><i class="fas fa-copy"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column align-items-center py-5 border-top">
|
||||
<div class="pulse-scanner">
|
||||
<i class="fas fa-broadcast-tower text-primary h3 mb-0"></i>
|
||||
</div>
|
||||
<h5 class="fw-bold mb-2">正在实时监控收款地址...</h5>
|
||||
<p class="text-muted small mb-4 text-center">
|
||||
我们正在 24/7 监听区块链节点数据。一旦检测到与您金额匹配的款项,将立即跳转。<br>
|
||||
<span class="text-primary fw-bold" id="pollingStatus">正在连接区块链节点...</span>
|
||||
</p>
|
||||
|
||||
<div class="d-flex gap-3 w-100">
|
||||
<button class="btn btn-light flex-grow-1 py-3 rounded-4 fw-bold border" onclick="goBackToStep1()">
|
||||
<i class="fas fa-edit me-1"></i> 修改金额
|
||||
</button>
|
||||
<button class="btn btn-primary flex-grow-1 py-3 rounded-4" onclick="checkStatusManual()">
|
||||
<i class="fas fa-sync-alt me-1"></i> 手动刷新到账状态
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4">
|
||||
<div class="card-neo">
|
||||
<h5 class="fw-bold mb-4"><i class="fas fa-question-circle me-2 text-primary"></i> 常见问题 (FAQ)</h5>
|
||||
|
||||
<div class="accordion accordion-flush" id="faqAccordion">
|
||||
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq1">
|
||||
为什么要支付包含随机小数的金额?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq1" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
|
||||
<div class="accordion-body small pt-0 opacity-75">
|
||||
为了实现无人值守的自动化到账,系统需要区分不同用户的充值。通过为每个订单分配独特的随机小数(如 100.42),我们可以在收款地址检测到对应的精确数值时,瞬间识别出是您的充值并自动入账,无需您手动上传截图。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq2">
|
||||
支付后多久可以到账?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
|
||||
<div class="accordion-body small pt-0 opacity-75">
|
||||
到账时间取决于区块链网络的确认速度。TRC20 网络通常在 1-2 分钟内完成确认,ERC20 网络视网络拥堵情况可能需要 5-10 分钟。一旦网络确认达到 1 个确认数,系统将立即为您增加余额。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq3">
|
||||
如果金额支付错误(未支付小数)怎么办?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
|
||||
<div class="accordion-body small pt-0 opacity-75">
|
||||
如果您支付的金额不包含指定的小数,系统将无法自动识别您的订单。这种情况下,请务必保留您的转账截图和 TXID,并联系在线客服进行人工手动审核和上分。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq4">
|
||||
支持哪些代币充值?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq4" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
|
||||
<div class="accordion-body small pt-0 opacity-75">
|
||||
目前系统仅支持 USDT (Tether) 充值。请确保您在转账时选择了正确的币种。请勿向收款地址发送非 USDT 资产,否则资产将永久丢失。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 p-4 bg-primary text-white rounded-4 shadow-sm">
|
||||
<h6 class="fw-bold mb-2"><i class="fas fa-headset me-2"></i> 充值遇到问题?</h6>
|
||||
<p class="small mb-3 opacity-75">我们提供 24/7 全天候技术支持。如果您的充值超过 30 分钟未到账,请通过工单联系我们。</p>
|
||||
<a href="support.php" class="btn btn-white btn-sm w-100 fw-bold">联系在线客服</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-neo mt-4 border-primary">
|
||||
<h6 class="fw-bold mb-3">区块链监控节点状态</h6>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="spinner-grow spinner-grow-sm text-success me-2" style="animation-duration: 3s;"></div>
|
||||
<span class="small text-muted">TRON (TRC20) 节点: <span class="text-success fw-bold">Active</span></span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="spinner-grow spinner-grow-sm text-success me-2" style="animation-duration: 2.5s;"></div>
|
||||
<span class="small text-muted">Ethereum (ERC20) 节点: <span class="text-success fw-bold">Active</span></span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="spinner-grow spinner-grow-sm text-success me-2" style="animation-duration: 4s;"></div>
|
||||
<span class="small text-muted">API 网关同步: <span class="text-success fw-bold">Synced</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-overlay" id="successOverlay">
|
||||
<div class="mb-4"><i class="fas fa-check-circle text-success" style="font-size: 8rem;"></i></div>
|
||||
<h1 class="fw-bold mb-2">支付已确认!</h1>
|
||||
<p class="text-muted h5">系统已识别您的充值并自动入账,正在跳转工作台...</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const addresses = { 'TRC20': '<?= $trc20_address ?>', 'ERC20': '<?= $erc20_address ?>' };
|
||||
let currentNetwork = 'TRC20';
|
||||
let timeLeft = 3600;
|
||||
let timerInterval, pollInterval, currentRechargeId = null;
|
||||
|
||||
function setAmount(val) { document.getElementById('inputAmount').value = val; }
|
||||
|
||||
async function confirmOrder() {
|
||||
const amt = document.getElementById('inputAmount').value;
|
||||
if (amt < 10) { alert('最低充值金额为 10 USDT'); return; }
|
||||
|
||||
const btn = event.currentTarget;
|
||||
const oldHtml = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>正在连接区块链支付网关...';
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('amount', amt);
|
||||
const res = await fetch('ajax_handler.php?action=create_recharge', { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
currentRechargeId = data.recharge_id;
|
||||
document.getElementById('orderIdDisplay').textContent = '支付流水号: #RE' + currentRechargeId;
|
||||
document.getElementById('displayAmount').textContent = parseFloat(data.amount).toFixed(2);
|
||||
document.getElementById('step1').classList.remove('active');
|
||||
document.getElementById('step2').classList.add('active');
|
||||
updateDisplay();
|
||||
startTimer();
|
||||
startPollingStatus();
|
||||
} else {
|
||||
alert(data.msg);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = oldHtml;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = oldHtml;
|
||||
}
|
||||
}
|
||||
|
||||
function startPollingStatus() {
|
||||
const statusEl = document.getElementById('pollingStatus');
|
||||
let dots = '';
|
||||
pollInterval = setInterval(async () => {
|
||||
dots = dots.length >= 3 ? '' : dots + '.';
|
||||
statusEl.textContent = '实时同步区块数据中' + dots;
|
||||
|
||||
try {
|
||||
const res = await fetch(`ajax_handler.php?action=check_recharge_status&recharge_id=${currentRechargeId}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0 && data.status === 'completed') {
|
||||
clearInterval(pollInterval);
|
||||
document.getElementById('successOverlay').classList.add('show');
|
||||
setTimeout(() => window.location.href = 'dashboard.php', 3000);
|
||||
}
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
async function checkStatusManual() {
|
||||
const btn = event.currentTarget;
|
||||
const oldHtml = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>正在查询链上记录...';
|
||||
|
||||
try {
|
||||
const res = await fetch(`ajax_handler.php?action=check_recharge_status&recharge_id=${currentRechargeId}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0 && data.status === 'completed') {
|
||||
document.getElementById('successOverlay').classList.add('show');
|
||||
setTimeout(() => window.location.href = 'dashboard.php', 3000);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = oldHtml;
|
||||
}, 1000);
|
||||
}
|
||||
} catch (e) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = oldHtml;
|
||||
}
|
||||
}
|
||||
|
||||
function selectNetwork(net) {
|
||||
currentNetwork = net;
|
||||
document.getElementById('btnTRC').classList.toggle('active', net === 'TRC20');
|
||||
document.getElementById('btnERC').classList.toggle('active', net === 'ERC20');
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function updateDisplay() {
|
||||
const addr = addresses[currentNetwork];
|
||||
document.getElementById('addressBox').textContent = addr;
|
||||
const qr = qrcode(0, 'M');
|
||||
qr.addData(addr);
|
||||
qr.make();
|
||||
document.getElementById('qrcode').innerHTML = qr.createImgTag(6, 0);
|
||||
}
|
||||
|
||||
function copyAddress() {
|
||||
const addr = document.getElementById('addressBox').textContent;
|
||||
navigator.clipboard.writeText(addr).then(() => {
|
||||
const btn = document.querySelector('.copy-btn');
|
||||
const oldHtml = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-check"></i>';
|
||||
setTimeout(() => btn.innerHTML = oldHtml, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
timerInterval = setInterval(() => {
|
||||
timeLeft--;
|
||||
const m = Math.floor(timeLeft / 60), s = timeLeft % 60;
|
||||
document.getElementById('countdown').textContent = `${m}:${s.toString().padStart(2, '0')}`;
|
||||
if (timeLeft <= 0) window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function goBackToStep1() {
|
||||
if (!confirm('离开此页面将关闭当前订单监听。确定返回修改金额吗?')) return;
|
||||
document.getElementById('step2').classList.remove('active');
|
||||
document.getElementById('step1').classList.add('active');
|
||||
clearInterval(timerInterval); clearInterval(pollInterval);
|
||||
const btn = document.querySelector('#step1 button.btn-primary');
|
||||
btn.disabled = false;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
136
support.php
Normal file
136
support.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT username, balance FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['message'])) {
|
||||
$msg = trim($_POST['message']);
|
||||
$stmt = $pdo->prepare("INSERT INTO support_messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$user_id, $msg]);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM support_messages WHERE user_id = ? ORDER BY created_at ASC");
|
||||
$stmt->execute([$user_id]);
|
||||
$messages = $stmt->fetchAll();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>客服中心 - 全球接码</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #ff4d94;
|
||||
--secondary-color: #ff1a75;
|
||||
--bg-light: #fff8f9;
|
||||
--surface-light: #ffffff;
|
||||
--text-main: #2d1a1e;
|
||||
--text-muted: #8a6d71;
|
||||
--border-color: #ffd1dc;
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-main);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem 3rem;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chat-card {
|
||||
background: white;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
box-shadow: 8px 8px 0px var(--border-color);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.chat-body {
|
||||
flex: 1;
|
||||
padding: 30px;
|
||||
overflow-y: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.chat-footer {
|
||||
padding: 24px;
|
||||
border-top: 2px solid var(--border-color);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.message { margin-bottom: 20px; max-width: 80%; display: flex; flex-direction: column; }
|
||||
.message-content { padding: 12px 20px; border-radius: 20px; font-size: 0.95rem; }
|
||||
.message-user { margin-left: auto; align-items: flex-end; }
|
||||
.message-user .message-content { background: var(--primary-color); color: white; border-bottom-right-radius: 4px; }
|
||||
.message-admin { align-items: flex-start; }
|
||||
.message-admin .message-content { background: var(--bg-light); border: 2px solid var(--border-color); border-bottom-left-radius: 4px; }
|
||||
|
||||
.btn-primary { background: var(--primary-color); border: none; border-radius: 16px; width: 50px; height: 50px; }
|
||||
.form-control { border: 2px solid var(--border-color); border-radius: 16px; padding: 12px 20px; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.main-content { margin-left: 80px; padding: 1.5rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include 'includes/sidebar.php'; ?>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="mb-4">
|
||||
<h2 class="fw-bold mb-1">在线技术支持</h2>
|
||||
<p class="text-muted">我们的专家团队全天候为您解决接码及充值问题</p>
|
||||
</div>
|
||||
|
||||
<div class="chat-card">
|
||||
<div class="chat-body" id="chatBody">
|
||||
<div class="message message-admin">
|
||||
<div class="message-content">您好!我是您的接码管家。如果您遇到任何问题,请在这里留言,我们会尽快回复。</div>
|
||||
</div>
|
||||
<?php foreach ($messages as $msg): ?>
|
||||
<div class="message <?= $msg['sender'] === 'user' ? 'message-user' : 'message-admin' ?>">
|
||||
<div class="message-content"><?= htmlspecialchars($msg['message']) ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="chat-footer">
|
||||
<form method="POST" class="d-flex gap-3">
|
||||
<input type="text" name="message" class="form-control" placeholder="输入您的问题..." required autocomplete="off">
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane text-white"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const chatBody = document.getElementById('chatBody');
|
||||
chatBody.scrollTop = chatBody.scrollHeight;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user