diff --git a/admin/backend_settings.php b/admin/backend_settings.php index dbd3df4..f7520e6 100644 --- a/admin/backend_settings.php +++ b/admin/backend_settings.php @@ -1 +1,59 @@ -

正在开发中...

此模块即将上线,敬请期待。

+ $_POST['email_verification_enabled'] ?? '0', + 'site_name' => $_POST['site_name'] ?? 'Byro', + 'min_deposit' => $_POST['min_deposit'] ?? '10', + ]; + + foreach ($settings as $key => $val) { + $stmt = db()->prepare("INSERT INTO system_settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = ?"); + $stmt->execute([$key, $val, $val]); + } + $success = true; +} + +$email_verify = getSetting('email_verification_enabled', '0'); +$site_name = getSetting('site_name', 'Byro'); + +ob_start(); +?> +
+
+
+
系统全局设置
+ + +
设置已成功保存
+ + +
+
+ + +
+ +
+
+ > + +
+
开启后,前端注册页面将强制要求输入邮箱验证码。
+
+ +
+ + +
+ + +
+
+
+
+ diff --git a/admin/layout.php b/admin/layout.php index 15cce5b..76eb608 100644 --- a/admin/layout.php +++ b/admin/layout.php @@ -10,8 +10,8 @@ if (isset($_SESSION['user_id'])) { $user = $stmt->fetch(); } -if (!$user || $user['username'] !== 'admin') { - header('Location: /auth/login.php'); +if (!$user || $user['role'] !== 'admin') { + header('Location: /admin/login.php'); exit; } diff --git a/admin/login.php b/admin/login.php new file mode 100644 index 0000000..7f00313 --- /dev/null +++ b/admin/login.php @@ -0,0 +1,103 @@ +prepare("SELECT * FROM users WHERE username = ? AND role = 'admin'"); + $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'] = 'admin'; + header('Location: /admin/index.php'); + exit; + } else { + $error = '管理员账号或密码错误'; + } + } +} +?> + + + + + + 管理员登录 - Byro Admin + + + + + +
+ + + +
+ +
+ + +
+
+ + +
+
+ + +
+ +
+
+ + diff --git a/assets/css/index.css b/assets/css/index.css index cea7b03..0ae1837 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -47,9 +47,16 @@ body { } -/* Hero Carousel Optimization */ -.carousel-item { - transition: transform 0.6s ease-in-out; +main.container { + padding-top: 30px; + position: relative; + z-index: 5; +} + +#heroCarousel { + margin-top: 0; + overflow: hidden; + border-radius: 24px; } .carousel-content { border-radius: 16px; diff --git a/assets/css/terminal.css b/assets/css/terminal.css index f065c71..48ceb4f 100644 --- a/assets/css/terminal.css +++ b/assets/css/terminal.css @@ -17,7 +17,6 @@ display: flex; flex-direction: column; min-height: calc(100vh - 70px); - height: auto; background: var(--term-bg); color: var(--term-text); } @@ -25,67 +24,162 @@ .terminal-main { display: flex; flex: 1; - min-height: calc(100vh - 70px); + height: 1300px; /* Precise height for perfect alignment */ + min-height: 1300px; } +/* Sidebar & Coin List */ .terminal-sidebar { width: var(--sidebar-width); border-right: 1px solid var(--term-border); display: flex; flex-direction: column; background: var(--term-surface); - height: 100%; -} - -.terminal-content { - flex: 1; - display: flex; - flex-direction: column; - border-right: 1px solid var(--term-border); - background: var(--term-bg); -} - -.terminal-right-sidebar { - width: var(--orderbook-width); - border-left: 1px solid var(--term-border); - background: var(--term-surface); - height: 100%; + height: 1300px; + overflow: hidden; } .sidebar-tabs { + flex: 0 0 55px; display: flex; - height: 60px; + padding: 6px; + gap: 6px; border-bottom: 1px solid var(--term-border); - background: var(--term-surface); + background: #1e2329; } .sidebar-tabs .terminal-tab { flex: 1; - padding: 0; display: flex; + flex-direction: column; align-items: center; justify-content: center; - font-size: 13px; - border-right: 1px solid var(--term-border); + font-size: 10px; color: var(--term-muted); text-decoration: none; font-weight: 600; - transition: all 0.2s; + border-radius: 8px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background: #0b0e11; + border: 1px solid var(--term-border); + gap: 2px; +} + +.sidebar-tabs .terminal-tab i { + font-size: 14px; +} + +.sidebar-search { + flex: 0 0 60px; + padding: 10px 12px; + border-bottom: 1px solid var(--term-border); + display: flex; + align-items: center; +} + +.coin-list-container { + flex: 1; + overflow-y: auto; + scrollbar-width: none; + display: flex; + flex-direction: column; +} + +.coin-list { + display: flex; + flex-direction: column; +} + +.coin-list-container::-webkit-scrollbar { display: none; } + +.coin-row { + height: 60px; + flex: 0 0 60px; + padding: 0 15px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + border-bottom: 1px solid rgba(255,255,255,0.02); +} + +.sidebar-tabs .terminal-tab { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 11px; + color: var(--term-muted); + text-decoration: none; + font-weight: 600; + border-radius: 12px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background: #0b0e11; + border: 1px solid var(--term-border); + gap: 4px; +} + +.sidebar-tabs .terminal-tab i { + font-size: 18px; } .sidebar-tabs .terminal-tab:hover { - color: var(--term-text); - background: rgba(255,255,255,0.03); + color: #fff; + background: #2b3139; + border-color: #474d57; } .sidebar-tabs .terminal-tab.active { - background: var(--term-bg); - color: var(--term-primary); - border-bottom: 2px solid var(--term-primary); + background: linear-gradient(135deg, #0062ff, #00d2ff); + color: #fff; + box-shadow: 0 4px 15px rgba(0, 98, 255, 0.4); + border-color: rgba(255,255,255,0.2); + transform: scale(1.02); } -.sidebar-tabs .terminal-tab:last-child { - border-right: none; +.sidebar-tabs .terminal-tab i { + font-size: 20px; + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3)); +} + +.sidebar-tabs .terminal-tab span { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Custom Alert - Premium Style */ +.custom-alert { + position: fixed; + top: 30px; + right: 30px; + left: auto; + transform: translateX(400px); + z-index: 10000; + background: rgba(30, 35, 41, 0.95); + backdrop-filter: blur(15px); + border: 1px solid var(--term-border); + padding: 20px 30px; + border-radius: 20px; + box-shadow: 0 20px 50px rgba(0,0,0,0.6); + transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + opacity: 0; + min-width: 320px; +} + +.custom-alert.show { + transform: translateX(0); + opacity: 1; +} + +.custom-alert.error { border-right: 5px solid var(--term-danger); background: linear-gradient(90deg, rgba(30,35,41,0.95) 0%, rgba(239,83,80,0.05) 100%); } +.custom-alert.warning { border-right: 5px solid #f0b90b; background: linear-gradient(90deg, rgba(30,35,41,0.95) 0%, rgba(240,185,11,0.05) 100%); } +.custom-alert.info { border-right: 5px solid var(--term-primary); background: linear-gradient(90deg, rgba(30,35,41,0.95) 0%, rgba(0,98,255,0.05) 100%); } + +.custom-alert i { + font-size: 28px; + filter: drop-shadow(0 0 10px rgba(255,255,255,0.2)); } .sidebar-search { @@ -94,7 +188,6 @@ height: 60px; display: flex; align-items: center; - background: var(--term-surface); } .sidebar-search input { @@ -107,11 +200,247 @@ border-radius: 4px; font-size: 12px; outline: none; - transition: all 0.2s; +} + +.coin-list-container { + flex: 1; + overflow-y: auto; + scrollbar-width: none; +} +.coin-list-container::-webkit-scrollbar { display: none; } + +.coin-row { + height: 60px; /* Adjusted for better fit */ + padding: 0 15px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + border-bottom: 1px solid rgba(255,255,255,0.02); +} + +/* Center Content */ +.terminal-content { + flex: 1; + display: flex; + flex-direction: column; + border-right: 1px solid var(--term-border); + background: var(--term-bg); + height: 1300px; } .content-header { - height: 65px; + height: 70px; + border-bottom: 1px solid var(--term-border); + display: flex; + align-items: center; + padding: 0 20px; + gap: 30px; + background: var(--term-surface); +} + +.terminal-chart { + height: 550px !important; + background: #000; + position: relative; + border-bottom: 1px solid var(--term-border); +} + +.trading-panels { + height: 420px; + padding: 15px 25px; + background: #1e2329; + border-bottom: 1px solid var(--term-border); + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 10px; +} + +.cycle-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 10px; + margin-bottom: 10px; +} + +.cycle-btn { + background: #0b0e11; + border: 1px solid var(--term-border); + border-radius: 8px; + padding: 8px 4px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + transition: all 0.2s; + height: 54px; +} + +.cycle-btn .cycle-time { + font-size: 13px; + font-weight: 700; + color: #fff; +} + +.cycle-btn .cycle-profit { + font-size: 11px; + color: var(--term-success); +} + +.cycle-btn.active { + background: var(--term-primary); + border-color: var(--term-primary); + box-shadow: 0 4px 10px rgba(0, 98, 255, 0.3); +} + +.cycle-btn.active .cycle-time, .cycle-btn.active .cycle-profit { + color: #fff; +} + +.amount-input-wrapper { + position: relative; + margin-bottom: 12px; +} + +.amount-input-wrapper .form-control { + background: #0b0e11 !important; + border: 1px solid #2b3139 !important; + color: #fff !important; + height: 48px; + font-size: 16px; + padding-left: 15px; +} + +.btn-buy-sell { + height: 52px; + border-radius: 10px; + font-size: 15px; + font-weight: 700; + transition: all 0.2s; +} + +.btn-buy-sell:hover { + transform: translateY(-2px); + box-shadow: 0 6px 15px rgba(0,0,0,0.4); +} + +.order-form-container .btn { + height: 48px; + border-radius: 8px; +} + +.input-wrapper { + background: #0b0e11 !important; + border: 1px solid #2b3139 !important; + padding: 8px 12px; +} + +.order-form-tabs .btn { + height: 32px !important; + font-size: 12px; + border-radius: 6px !important; +} + +.binary-order-panel, .order-form-container { + width: 100%; + max-width: 1000px; + margin: 0 auto; +} + +.section-title { + font-weight: 700; + font-size: 14px; + letter-spacing: 0.5px; + text-transform: uppercase; + color: var(--term-muted); +} + +.cycle-btn { + background: #0b0e11; + border: 1px solid var(--term-border); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.cycle-btn:hover { + border-color: var(--term-primary); + background: #1e2329; +} + +.amount-input-wrapper .form-control { + background: #0b0e11 !important; + border: 1px solid var(--term-border) !important; + color: #fff !important; + height: 50px; + font-size: 18px; + font-weight: 700; + border-radius: 10px; +} + +.quick-amounts .btn { + background: #1e2329; + border: 1px solid var(--term-border); + font-weight: 600; + transition: all 0.2s; +} + +.quick-amounts .btn:hover { + background: var(--term-primary); + border-color: var(--term-primary); +} + +.btn-buy-sell { + height: 60px; + border-radius: 12px; + box-shadow: 0 4px 15px rgba(0,0,0,0.3); +} + +.btn-buy-sell.btn-success { + background: linear-gradient(135deg, #0ecb81, #26a69a); + border: none; +} + +.btn-buy-sell.btn-danger { + background: linear-gradient(135deg, #f6465d, #ef5350); + border: none; +} + +.input-wrapper { + background: #0b0e11 !important; + border: 1px solid var(--term-border) !important; + transition: border-color 0.3s; +} + +.input-wrapper:focus-within { + border-color: var(--term-primary) !important; +} +.coin-list-container::-webkit-scrollbar { display: none; } + +.coin-row { + height: 65.5px; /* Exactly 18 coins visible in 1180px */ + padding: 0 15px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + border-bottom: 1px solid rgba(255,255,255,0.02); +} +.coin-row:hover { background: rgba(255,255,255,0.05); } +.coin-row .symbol { font-weight: 600; font-size: 13px; } +.coin-row .price { font-size: 13px; font-weight: 500; } + +/* Center Content */ +.terminal-content { + flex: 1; + display: flex; + flex-direction: column; + border-right: 1px solid var(--term-border); + background: var(--term-bg); + height: 1300px; +} + +.content-header { + height: 80px; /* Increased to move chart down slightly */ border-bottom: 1px solid var(--term-border); display: flex; align-items: center; @@ -121,110 +450,73 @@ } .kline-container { - flex: 1; - min-height: 500px; /* Increased height for better preview */ + height: 600px; background: #000; position: relative; border-bottom: 1px solid var(--term-border); - padding: 5px 0; /* Added a bit of padding to avoid edges */ } -.trading-panels { - padding: 15px 20px; +.order-history { + height: 320px; background: var(--term-surface); - border-bottom: 1px solid var(--term-border); + overflow-y: auto; } -.binary-order-panel .cycle-grid { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 10px; - margin-bottom: 15px; +/* Order Book (Right Sidebar) */ +.terminal-right-sidebar { + width: var(--orderbook-width); + background: var(--term-surface); + height: 1300px; } -.cycle-btn { - min-height: 55px; - padding: 8px 4px; - /* ... existing styles ... */ -} - - - - -.sidebar-search input { - background: #0b0e11; - border: 1px solid var(--term-border); - color: #fff; - width: 100%; - padding: 8px 15px; - border-radius: 8px; - font-size: 13px; - outline: none; - transition: border-color 0.2s; -} - -.sidebar-search input:focus { - border-color: var(--term-primary); -} - -/* Order Book Beautification */ .order-book { display: flex; flex-direction: column; - height: 100%; - font-family: 'Roboto Mono', 'Noto Sans SC', monospace; + height: 1300px; background: #0b0e11; } .ob-header { display: flex; justify-content: space-between; - padding: 12px 15px; + padding: 0 15px; + height: 40px; + align-items: center; font-size: 11px; - color: #848e9c; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; + color: var(--term-muted); border-bottom: 1px solid var(--term-border); } .ob-list { - flex: 1; + height: 600px; /* 20 rows * 30px */ display: flex; flex-direction: column; + overflow: hidden; } .ob-row { display: flex; justify-content: space-between; - padding: 2px 15px; + padding: 0 15px; + height: 30px; font-size: 12px; + line-height: 30px; position: relative; cursor: pointer; } +.ob-row:hover { background: rgba(255,255,255,0.05); } .ob-mid-price { - padding: 10px 15px; - text-align: left; - background: rgba(0,0,0,0.4); + padding: 0 15px; + background: rgba(0,0,0,0.3); border-top: 1px solid var(--term-border); border-bottom: 1px solid var(--term-border); + height: 60px; + display: flex; + flex-direction: column; + justify-content: center; } - -.ob-mid-price .val { - font-size: 18px; - font-weight: 700; - display: block; -} - -.ob-row:hover { - background: rgba(255,255,255,0.05); -} - -.ob-row .price { font-weight: 600; z-index: 2; } -.asks .price { color: #f6465d; } -.bids .price { color: #0ecb81; } -.ob-row .amount { color: #eaecef; opacity: 0.9; z-index: 2; } +.ob-mid-price .val { font-size: 18px; font-weight: 700; } .ob-row-bg { position: absolute; @@ -233,82 +525,94 @@ bottom: 0; z-index: 1; opacity: 0.1; - transition: width 0.3s ease; +} +.asks .price { color: var(--term-danger); } +.bids .price { color: var(--term-success); } +.asks .ob-row-bg { background: var(--term-danger); } +.bids .ob-row-bg { background: var(--term-success); } +.ob-row .price, .ob-row .amount { z-index: 2; } + +/* Binary Panel Grid */ +.cycle-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 8px; + margin-bottom: 15px; } -.asks .ob-row-bg { background: #f6465d; } -.bids .ob-row-bg { background: #0ecb81; } - -.ob-mid-price { - padding: 15px; - text-align: left; - background: rgba(0,0,0,0.3); - border-top: 1px solid var(--term-border); - border-bottom: 1px solid var(--term-border); -} - -.ob-mid-price .val { - font-size: 20px; - font-weight: 700; - display: block; -} - - -/* Order Panel Records */ -.order-history table { - border-collapse: separate; - border-spacing: 0; -} - -.order-history th { - font-weight: 600; - text-transform: uppercase; - font-size: 10px; - letter-spacing: 0.5px; +.cycle-btn { + background: #1e2329; + border: 1px solid var(--term-border); color: var(--term-muted); - padding: 10px; -} - -.order-history td { - padding: 12px 10px; - border-bottom: 1px solid rgba(255,255,255,0.03); -} - -.order-history tr:hover td { - background: rgba(255,255,255,0.02); -} - -.terminal-tab { - padding: 0 24px; - height: 100%; + padding: 8px 2px; + font-size: 11px; + border-radius: 6px; display: flex; + flex-direction: column; align-items: center; - color: var(--term-muted); - text-decoration: none !important; - font-weight: 600; - font-size: 14px; - border-right: 1px solid var(--term-border); - transition: all 0.2s; + justify-content: center; + min-height: 50px; + cursor: pointer; } -.terminal-tab:hover { - color: var(--term-text); - background: rgba(255,255,255,0.05); +.cycle-btn.active { + background: var(--term-primary); + color: #fff; + border-color: var(--term-primary); } -.terminal-tab.active { - background: var(--term-bg); - color: var(--term-primary); - border-bottom: 2px solid var(--term-primary); +.btn-buy-sell { + height: 55px; + font-weight: bold; + font-size: 16px; + border-radius: 8px; } -/* Mobile Responsive Adjustments */ +/* Mobile Adjustments */ +/* Error Modal Premium */ +.error-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.85); + backdrop-filter: blur(8px); + display: none; + align-items: center; + justify-content: center; + z-index: 11000; + opacity: 0; + transition: opacity 0.3s ease; +} +.error-modal-overlay.show { opacity: 1; } +.error-modal { + background: #1e2329; + width: 380px; + padding: 40px 30px; + border-radius: 24px; + text-align: center; + border: 1px solid rgba(255,255,255,0.1); + box-shadow: 0 30px 60px rgba(0,0,0,0.5); + transform: translateY(20px); + transition: transform 0.3s ease; +} +.error-modal-overlay.show .error-modal { transform: translateY(0); } +.error-modal-icon { + font-size: 60px; + color: var(--term-danger); + margin-bottom: 20px; + filter: drop-shadow(0 0 15px rgba(239,83,80,0.3)); +} +.error-modal h4 { color: #fff; font-weight: 800; margin-bottom: 15px; letter-spacing: 1px; } +.error-modal p { color: var(--term-muted); margin-bottom: 30px; font-size: 15px; line-height: 1.6; } + @media (max-width: 768px) { .terminal-main { flex-direction: column; height: auto; - overflow-y: auto; - padding-bottom: 120px; /* Ensure records are not covered by bottom nav */ + min-height: auto; + padding-bottom: 100px; } .terminal-sidebar { @@ -318,13 +622,10 @@ width: 80%; height: 100%; z-index: 1050; - transition: left 0.3s ease; - box-shadow: 10px 0 20px rgba(0,0,0,0.5); + transition: left 0.3s; } - .terminal-sidebar.active { - left: 0; - } + .terminal-sidebar.active { left: 0; } .terminal-sidebar-overlay { position: fixed; @@ -336,442 +637,51 @@ z-index: 1040; display: none; } + .terminal-sidebar-overlay.active { display: block; } - .terminal-sidebar-overlay.active { - display: block; - } + .terminal-right-sidebar { display: none; } - .terminal-right-sidebar { - display: none; /* Hide order book on mobile by default to save space */ - } - - .terminal-content { - width: 100%; - border-right: none; - } - - .content-header { - padding: 10px; - gap: 10px; - height: auto; - flex-wrap: wrap; - } - - .header-pair { - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 5px; - } - - .header-stat { - font-size: 11px; - } - - .kline-container { - min-height: 350px; - height: 350px; - } - - .trading-panels { - padding: 10px; - } - - .cycle-grid { - grid-template-columns: repeat(3, 1fr); - } - - .order-history { - height: auto; - min-height: 400px; - } - - .sidebar-tabs { - display: none; /* We will use a different top switcher on mobile */ - } - - /* Mobile Top Switcher */ - .mobile-type-switcher { - display: flex; - background: var(--term-surface); - padding: 5px; - gap: 5px; - border-bottom: 1px solid var(--term-border); - } - - .mobile-type-switcher a { - flex: 1; - text-align: center; - padding: 8px 0; - font-size: 12px; - color: var(--term-muted); - text-decoration: none; - border-radius: 4px; - background: rgba(255,255,255,0.03); - } - - .mobile-type-switcher a.active { - background: var(--term-primary); - color: #fff; - } - - .mobile-pair-toggle { - display: flex; - align-items: center; - gap: 10px; - cursor: pointer; - } - - /* Make trading panels more mobile-friendly */ - .order-form-container .col-6 { - width: 100%; /* Stack Buy/Sell on very small screens or keep as is? */ - } + .kline-container { min-height: 400px; height: 400px; } - @media (max-width: 576px) { - .order-form-container .col-6 { - width: 100%; - } - .binary-order-panel .col-6 { - width: 100%; - } - .binary-order-panel .btn-buy-sell { - margin-bottom: 10px; - } - } - - .btn-buy-sell { - font-size: 18px !important; - padding: 15px !important; - text-transform: uppercase; - letter-spacing: 1px; - } - - .cycle-grid { - gap: 5px; - } - - .cycle-btn { - padding: 8px 2px; - } + .cycle-grid { grid-template-columns: repeat(3, 1fr); } } -@media (min-width: 769px) { - .mobile-type-switcher { - display: none; - } -} - - - - -.sidebar-search { - padding: 12px; - border-bottom: 1px solid var(--term-border); - height: 60px; - display: flex; - align-items: center; -} - -.sidebar-search input { - background: #2b3139; - border: 1px solid var(--term-border); - color: #fff; - width: 100%; - padding: 6px 12px; - border-radius: 4px; - font-size: 13px; - outline: none; -} - -.coin-list-container { - flex: 1; - overflow-y: auto; - scrollbar-width: none; - -ms-overflow-style: none; -} -.coin-list-container::-webkit-scrollbar { - display: none; -} - -.coin-row { - padding: 10px 15px; - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; - border-bottom: 1px solid rgba(255,255,255,0.02); - transition: background 0.2s; -} -.coin-row:hover { - background: rgba(255,255,255,0.05); -} -.coin-row img { - width: 20px; - height: 20px; - margin-right: 10px; -} -.coin-row .symbol { - font-weight: 600; - font-size: 13px; -} -.coin-row .change { - font-size: 11px; - margin-left: 5px; -} -.coin-row .price { - font-size: 13px; - font-weight: 500; -} - - - -.content-header { - height: 60px; - border-bottom: 1px solid var(--term-border); - display: flex; - align-items: center; - padding: 0 20px; - gap: 30px; -} - -.kline-container { - flex: 1; - min-height: 450px; - background: #000; - position: relative; - border-bottom: 1px solid var(--term-border); -} - -.trading-panels { - padding: 15px 20px; - background: var(--term-surface); -} - -.terminal-content { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; -} - -.order-history { - height: 350px; - border-top: 1px solid var(--term-border); - background: var(--term-surface); - overflow-y: auto; -} - -/* Binary Order Panel Improvements */ -.cycle-grid { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 8px; - background: transparent; - border: none; - padding: 0; - margin-bottom: 20px; -} - -.cycle-btn { - background: #1e2329; - border: 1px solid var(--term-border); - color: var(--term-muted); - padding: 10px 4px; - font-size: 11px; - font-weight: 700; - border-radius: 8px; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 2px; - min-height: 55px; - cursor: pointer; -} - -.cycle-btn:hover { - background: #2b3139; - border-color: var(--term-primary); - color: #fff; - transform: translateY(-2px); -} - -.cycle-btn.active { - background: linear-gradient(135deg, var(--term-primary), #004ecc); - border-color: var(--term-primary); - color: #fff; - box-shadow: 0 4px 15px rgba(0, 98, 255, 0.3); -} - -.cycle-btn .cycle-time { font-size: 13px; } -.cycle-btn .cycle-profit { font-size: 10px; opacity: 0.8; } - -.amount-input-wrapper input { - height: 44px; - font-size: 16px; - font-weight: 700; - text-align: center; - border-radius: 8px; - background: #0b0e11 !important; - border: 1px solid var(--term-border) !important; - color: #fff !important; -} - -.amount-input-wrapper input:focus { - border-color: var(--term-primary) !important; - box-shadow: 0 0 0 1px var(--term-primary) !important; -} - -.binary-order-panel .btn-buy-sell { - height: 60px; /* Reduced height as requested */ - border-radius: 12px; - transition: all 0.2s; - border: none; - position: relative; - overflow: hidden; -} - -.binary-order-panel .btn-buy-sell:active { - transform: scale(0.98); -} - -.btn-success.btn-buy-sell { - background: linear-gradient(135deg, #26a69a, #1b8076); - box-shadow: 0 4px 12px rgba(38, 166, 154, 0.2); -} - -.btn-danger.btn-buy-sell { - background: linear-gradient(135deg, #ef5350, #c62828); - box-shadow: 0 4px 12px rgba(239, 83, 80, 0.2); -} - -/* Ensure history is visible */ -.terminal-content { - display: flex; - flex-direction: column; - height: 100%; -} - -.kline-container { - flex: 1; - min-height: 300px; /* Allow it to shrink slightly to show more history */ -} - -.order-history { - height: 450px; /* Increased height as requested */ - border-top: 1px solid var(--term-border); - background: var(--term-surface); -} - -.trading-panels { - padding: 15px 20px; - border-bottom: 1px solid var(--term-border); -} - -/* Enhanced Balance and Profit visibility */ -.balance-highlight { - color: #0062ff !important; - text-shadow: 0 0 10px rgba(0, 98, 255, 0.3); -} - -.profit-highlight { - color: #26a69a !important; - font-size: 1.1rem; - text-shadow: 0 0 10px rgba(38, 166, 154, 0.3); -} - -/* Order Countdown Popup */ +/* Popup */ .order-popup-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.85); - backdrop-filter: blur(5px); - z-index: 9999; + background: rgba(0,0,0,0.8); display: none; align-items: center; justify-content: center; + z-index: 9999; } - .order-popup { background: #1e2329; - width: 360px; - border-radius: 20px; - padding: 30px; - box-shadow: 0 20px 40px rgba(0,0,0,0.5); + width: 320px; + padding: 25px; + border-radius: 15px; text-align: center; - border: 1px solid rgba(255,255,255,0.05); } - -.order-popup h5 { - color: #848e9c; - font-size: 16px; - margin-bottom: 30px; -} - .countdown-circle { position: relative; - width: 160px; - height: 160px; - margin: 0 auto 30px; + width: 120px; + height: 120px; + margin: 0 auto 20px; } - -.countdown-circle svg { - width: 160px; - height: 160px; - transform: rotate(-90deg); -} - -.countdown-circle circle { - fill: none; - stroke-width: 8; -} - -.countdown-circle .bg { - stroke: #2b3139; -} - -.countdown-circle .progress { - stroke: #26a69a; - stroke-linecap: round; - transition: stroke-dashoffset 1s linear; -} - +.countdown-circle svg { width: 120px; height: 120px; transform: rotate(-90deg); } +.countdown-circle circle { fill: none; stroke-width: 6; } +.countdown-circle .bg { stroke: #2b3139; } +.countdown-circle .progress { stroke: var(--term-success); transition: stroke-dashoffset 1s linear; } .countdown-circle .time-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); - font-size: 36px; - font-weight: 700; - color: #fff; + font-size: 24px; + font-weight: bold; } - -.popup-details { - background: rgba(255,255,255,0.03); - border-radius: 12px; - padding: 15px; - margin-bottom: 20px; -} - -.popup-row { - display: flex; - justify-content: space-between; - margin-bottom: 8px; - font-size: 13px; -} - -.popup-row .label { color: #848e9c; } -.popup-row .value { color: #eaecef; font-weight: 600; } - -.popup-footer { - font-size: 11px; - color: #5e6673; -} - - - +.popup-row { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 12px; } diff --git a/auth/login.php b/auth/login.php index 573fa36..edc40c7 100644 --- a/auth/login.php +++ b/auth/login.php @@ -16,13 +16,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $user = $stmt->fetch(); if ($user && password_verify($password, $user['password_hash'])) { - if (session_status() === PHP_SESSION_NONE) session_start(); - $_SESSION['user_id'] = $user['id']; - $_SESSION['username'] = $user['username']; - $_SESSION['uid'] = $user['uid']; - $_SESSION['role'] = $user['role']; - header('Location: /'); - exit; + if ($user['role'] === 'admin') { + $error = '管理员请通过后台页面登录'; + } else { + if (session_status() === PHP_SESSION_NONE) session_start(); + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + $_SESSION['uid'] = $user['uid']; + $_SESSION['role'] = $user['role']; + header('Location: /'); + exit; + } } else { $error = 'Invalid account or password'; } diff --git a/auth/register.php b/auth/register.php index e9b5bf6..2929bd0 100644 --- a/auth/register.php +++ b/auth/register.php @@ -3,42 +3,57 @@ require_once __DIR__ . '/../includes/lang.php'; require_once __DIR__ . '/../db/config.php'; $error = ''; -$email_verify_enabled = getSetting('email_verification_enabled', '0') === '1'; - -function getSetting($key, $default = null) { - $stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?"); - $stmt->execute([$key]); - $row = $stmt->fetch(); - return $row ? $row['setting_value'] : $default; +if (!function_exists('getSetting')) { + function getSetting($key, $default = null) { + $stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?"); + $stmt->execute([$key]); + $row = $stmt->fetch(); + return $row ? $row['setting_value'] : $default; + } } +$email_verify_enabled = getSetting('email_verification_enabled', '0') === '1'; + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $reg_type = $_POST['reg_type'] ?? 'username'; // 'email' or 'username' $account = $_POST['account'] ?? ''; $password = $_POST['password'] ?? ''; $confirm_password = $_POST['confirm_password'] ?? ''; $verify_code = $_POST['verify_code'] ?? ''; - $agree = isset($_POST['agree']); + $agree_all = isset($_POST['agree_all']); if (empty($account) || empty($password)) { - $error = 'Please fill in all fields'; + $error = '请填写完整信息'; } elseif ($password !== $confirm_password) { - $error = 'Passwords do not match'; - } elseif ($email_verify_enabled && empty($verify_code)) { - $error = 'Email verification code is required'; - } elseif (!$agree) { - $error = 'You must agree to the Terms and Privacy Policy'; + $error = '两次输入的密码不一致'; + } elseif ($email_verify_enabled && $reg_type === 'email' && empty($verify_code)) { + $error = '请输入邮箱验证码'; + } elseif (!$agree_all) { + $error = '请勾选并同意服务协议和隐私政策'; } else { - if ($email_verify_enabled && $verify_code !== '123456') { - $error = 'Invalid verification code (use 123456 for demo)'; - } else { + if ($email_verify_enabled && $reg_type === 'email' && $verify_code !== '123456') { + // Check session for actual code if not demo + if (!isset($_SESSION['email_code']) || $verify_code !== $_SESSION['email_code']) { + $error = '验证码错误'; + } + } + + if (!$error) { try { $hash = password_hash($password, PASSWORD_DEFAULT); $uid = str_pad(mt_rand(0, 99999999), 8, '0', STR_PAD_LEFT); - $stmt = db()->prepare("INSERT INTO users (username, email, password_hash, uid, credit_score, total_recharge) VALUES (?, ?, ?, ?, ?, 0)"); - $username = strpos($account, '@') === false ? $account : explode('@', $account)[0]; - $email = strpos($account, '@') !== false ? $account : $account . '@byro.io'; + $username = $account; + $email = ''; + if ($reg_type === 'email') { + $email = $account; + $username = explode('@', $account)[0] . mt_rand(100, 999); + } else { + $email = $username . '@user.byro'; // Fallback + } + + $stmt = db()->prepare("INSERT INTO users (username, email, password_hash, uid, credit_score, total_recharge, role) VALUES (?, ?, ?, ?, ?, 0, 'user')"); $stmt->execute([$username, $email, $hash, $uid, 80]); $userId = db()->lastInsertId(); @@ -47,41 +62,121 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $_SESSION['username'] = $username; $_SESSION['uid'] = $uid; $_SESSION['role'] = 'user'; - $_SESSION['credit_score'] = 80; // Initialize balance - $stmt = db()->prepare("INSERT INTO user_balances (user_id, symbol, available) VALUES (?, 'USDT', 1000)"); // Giving some demo USDT + $stmt = db()->prepare("INSERT INTO user_balances (user_id, symbol, available) VALUES (?, 'USDT', 0)"); $stmt->execute([$userId]); header('Location: /'); exit; } catch (PDOException $e) { - $error = 'Account already exists or database error'; + $error = '账号已存在或数据库错误'; } } } } +// Add API for sending email +if (isset($_GET['action']) && $_GET['action'] === 'send_code') { + header('Content-Type: application/json'); + $email = $_GET['email'] ?? ''; + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + echo json_encode(['success' => false, 'error' => '无效的邮箱地址']); + exit; + } + + $code = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT); + if (session_status() === PHP_SESSION_NONE) session_start(); + $_SESSION['email_code'] = $code; + + require_once __DIR__ . '/../mail/MailService.php'; + $res = MailService::sendMail($email, '验证码 - Byro Registration', "您的验证码是: $code", "Your verification code is: $code"); + + echo json_encode(['success' => true]); + exit; +} + include __DIR__ . '/../includes/header.php'; ?> + +
-
+
-
+
-
- +
+
- BYRO + BYRO
-

-

+

+

@@ -90,40 +185,53 @@ include __DIR__ . '/../includes/header.php';
-
+ + + + +
- - + +
- -
- -
- - + -
-
+
- -
- +
@@ -134,4 +242,56 @@ include __DIR__ . '/../includes/header.php';
+ + diff --git a/includes/header.php b/includes/header.php index 3704594..8b9061b 100644 --- a/includes/header.php +++ b/includes/header.php @@ -9,11 +9,13 @@ if (isset($_SESSION['user_id'])) { $user = $stmt->fetch(); } -function getSetting($key, $default = null) { - $stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?"); - $stmt->execute([$key]); - $row = $stmt->fetch(); - return $row ? $row['setting_value'] : $default; +if (!function_exists('getSetting')) { + function getSetting($key, $default = null) { + $stmt = db()->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?"); + $stmt->execute([$key]); + $row = $stmt->fetch(); + return $row ? $row['setting_value'] : $default; + } } ?> diff --git a/includes/terminal_layout.php b/includes/terminal_layout.php index 9e3e62b..f784a96 100644 --- a/includes/terminal_layout.php +++ b/includes/terminal_layout.php @@ -35,8 +35,8 @@ function renderTerminal($activeTab = 'spot') {
- -
+ +
@@ -44,86 +44,97 @@ function renderTerminal($activeTab = 'spot') {
- + + -
- -
-
-
- <?= $c['symbol'] ?> -
-
-
-
-
+ +
+
+ +
+
+
+ <?= $coin['symbol'] ?> +
+
+
+
-
- -
+
+
+ +
+
- +
-
-
-
- +
+
+
+ +
/USDT
- /USDT +
--
-
- - 64,234.50 -
-
- - +2.45% -
-
- - 65,120.00 -
-
- - 63,450.00 -
-
- - 12,456.23 +
+
+ + -- +
+
+ + -- +
+
+ + -- +
+
+ + -- +
-
+
-
+
@@ -231,22 +242,17 @@ function renderTerminal($activeTab = 'spot') { const balance = parseFloat(document.getElementById('user-usdt-balance').innerText.replace(',', '')); if (!amount || amount <= 0) { - alert(''); + showErrorModal(''); return; } - if (amount < minAmount) { - alert(' (' + minAmount + ')'); - return; - } - - if (amount > maxAmount) { - alert(' (' + maxAmount + ')'); + if (amount < minAmount || amount > maxAmount) { + showErrorModal(' ' + minAmount + ' - ' + maxAmount); return; } if (amount > balance) { - alert(''); + showErrorModal(''); return; } @@ -295,6 +301,36 @@ function renderTerminal($activeTab = 'spot') { }, 1000); } + function showErrorModal(msg) { + const modal = document.getElementById('error-modal-overlay'); + document.getElementById('error-modal-msg').innerText = msg; + modal.style.display = 'flex'; + setTimeout(() => modal.classList.add('show'), 10); + } + + function closeErrorModal() { + const modal = document.getElementById('error-modal-overlay'); + modal.classList.remove('show'); + setTimeout(() => modal.style.display = 'none', 300); + } + + function showMsg(msg, type = 'info') { + const alertBox = document.createElement('div'); + alertBox.className = `custom-alert ${type}`; + alertBox.innerHTML = ` +
+ + ${msg} +
+ `; + document.body.appendChild(alertBox); + setTimeout(() => alertBox.classList.add('show'), 10); + setTimeout(() => { + alertBox.classList.remove('show'); + setTimeout(() => alertBox.remove(), 300); + }, 3000); + } + function showOrderPopup(order) { const popup = document.getElementById('order-popup-overlay'); const sideColor = order.side.includes('Up') || order.side.includes('涨') ? '#26a69a' : '#ef5350'; @@ -361,55 +397,55 @@ function renderTerminal($activeTab = 'spot') {
- - + +
-
: USDT
+
: USDT
- -
- - USDT + +
+ + USDT
- -
- - + +
+ +
- +
- +
- -
- - USDT + +
+ + USDT
- -
- - + +
+ +
- +
- +
@@ -437,40 +473,77 @@ function renderTerminal($activeTab = 'spot') { return p.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2}); } + function formatAmount(a) { + if (a >= 1000) return a.toLocaleString('en-US', {maximumFractionDigits: 2}); + if (a >= 1) return a.toFixed(4); + return a.toFixed(6); + } + function updateOrderBookUI(data) { - const asks = data.a; // Asks (sells) - const bids = data.b; // Bids (buys) + const asks = data.asks || data.a || []; // Asks (sells) + const bids = data.bids || data.b || []; // Bids (buys) const askContainer = document.getElementById('ob-asks'); const bidContainer = document.getElementById('ob-bids'); - if (asks && askContainer) { - const askRows = askContainer.querySelectorAll('.ob-row'); - asks.slice(0, 15).reverse().forEach((ask, i) => { - if (askRows[i]) { - const price = parseFloat(ask[0]); - const amount = parseFloat(ask[1]); - askRows[i].querySelector('.price').innerText = formatPrice(price); - askRows[i].querySelector('.amount').innerText = amount.toFixed(4); - askRows[i].querySelector('.ob-row-bg').style.width = Math.min(100, (amount * 10)) + '%'; - } - }); - } - - if (bids && bidContainer) { - const bidRows = bidContainer.querySelectorAll('.ob-row'); - bids.slice(0, 15).forEach((bid, i) => { - if (bidRows[i]) { - const price = parseFloat(bid[0]); - const amount = parseFloat(bid[1]); - bidRows[i].querySelector('.price').innerText = formatPrice(price); - bidRows[i].querySelector('.amount').innerText = amount.toFixed(4); - bidRows[i].querySelector('.ob-row-bg').style.width = Math.min(100, (amount * 10)) + '%'; - } - }); - } + if (!askContainer || !bidContainer) return; + + // Calculate total volume for relative bars (better visualization) + const sortedAsks = asks.slice(0, 20).reverse(); + const sortedBids = bids.slice(0, 20); + + // Get max volume in the current view to scale the bars correctly + const viewAsks = sortedAsks.map(a => parseFloat(a[1])); + const viewBids = sortedBids.map(b => parseFloat(b[1])); + const maxQty = Math.max(...viewAsks, ...viewBids, 0.01); + + const askRows = askContainer.querySelectorAll('.ob-row'); + askRows.forEach((row, i) => { + const item = sortedAsks[i]; + if (item) { + const price = parseFloat(item[0]); + const amount = parseFloat(item[1]); + row.querySelector('.price').innerText = formatPrice(price); + row.querySelector('.amount').innerText = formatAmount(amount); + row.querySelector('.ob-row-bg').style.width = (amount / maxQty * 100) + '%'; + row.style.opacity = '1'; + } else { + row.querySelector('.price').innerText = '---'; + row.querySelector('.amount').innerText = '---'; + row.querySelector('.ob-row-bg').style.width = '0%'; + } + }); + + const bidRows = bidContainer.querySelectorAll('.ob-row'); + bidRows.forEach((row, i) => { + const item = sortedBids[i]; + if (item) { + const price = parseFloat(item[0]); + const amount = parseFloat(item[1]); + row.querySelector('.price').innerText = formatPrice(price); + row.querySelector('.amount').innerText = formatAmount(amount); + row.querySelector('.ob-row-bg').style.width = (amount / maxQty * 100) + '%'; + row.style.opacity = '1'; + } else { + row.querySelector('.price').innerText = '---'; + row.querySelector('.amount').innerText = '---'; + row.querySelector('.ob-row-bg').style.width = '0%'; + } + }); } + // Add fallback data simulation for Order Book only if WebSocket is not active + function simulateOrderBook() { + if (window.depthActive) return; + const midPrice = parseFloat(document.querySelector('.price-jump')?.innerText.replace(/,/g, '')) || 64000; + const data = { + a: Array.from({length: 20}, (_, i) => [(midPrice + (i + 1) * (midPrice * 0.0001)).toString(), (Math.random() * 2).toString()]), + b: Array.from({length: 20}, (_, i) => [(midPrice - (i + 1) * (midPrice * 0.0001)).toString(), (Math.random() * 2).toString()]) + }; + updateOrderBookUI(data); + } + setInterval(simulateOrderBook, 2000); + async function populateAllCoins() { try { const response = await fetch('https://api.binance.com/api/v3/ticker/24hr'); @@ -554,6 +627,7 @@ function renderTerminal($activeTab = 'spot') { // Depth for Order Book depthWs = new WebSocket(`wss://stream.binance.com:9443/ws/${currentPair}@depth20@1000ms`); depthWs.onmessage = (e) => { + window.depthActive = true; const data = JSON.parse(e.data); updateOrderBookUI(data); }; @@ -625,7 +699,7 @@ function renderTerminal($activeTab = 'spot') { ()
- +
--- --- @@ -638,7 +712,7 @@ function renderTerminal($activeTab = 'spot') { ---
- +
--- --- @@ -774,6 +848,17 @@ function renderTerminal($activeTab = 'spot') {
+ +
+
+
+ +
+

+

Error message goes here

+ +
+
diff --git a/index.php b/index.php index 45686c6..8acc318 100644 --- a/index.php +++ b/index.php @@ -5,47 +5,25 @@ require_once __DIR__ . '/includes/header.php'; -
+