Autosave: 20260221-022115
This commit is contained in:
parent
0b9cff662e
commit
2abf771e6c
@ -187,32 +187,37 @@ ob_start();
|
|||||||
<!-- Payment Info Modal -->
|
<!-- Payment Info Modal -->
|
||||||
<div class="modal fade" id="paymentModal" tabindex="-1">
|
<div class="modal fade" id="paymentModal" tabindex="-1">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content border-0 shadow-lg">
|
||||||
<div class="modal-header">
|
<div class="modal-header bg-primary text-white border-0">
|
||||||
<h5 class="modal-title">发送收款账号 (法币充值)</h5>
|
<h5 class="modal-title fw-bold"><i class="bi bi-bank me-2"></i>匹配收款账户 (法币/USDT)</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body p-4">
|
||||||
<div class="mb-3">
|
<div class="alert alert-info small border-0 bg-light text-primary">
|
||||||
<label class="form-label small">银行名称/支付方式</label>
|
<i class="bi bi-info-circle-fill me-2"></i>填写后点击发送,前端充值弹窗将自动切换并显示此账户。
|
||||||
<input type="text" id="pay-bank" class="form-control" placeholder="例如: 建设银行, Alipay, etc.">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label small">收款人姓名</label>
|
<label class="form-label small fw-bold text-muted">银行名称 / 支付方式 (Bank Name)</label>
|
||||||
<input type="text" id="pay-name" class="form-control" placeholder="收款人姓名">
|
<input type="text" id="pay-bank" class="form-control form-control-lg fs-6" placeholder="例如: 建设银行, Alipay, TRC20, etc.">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label small">收款账号</label>
|
<label class="form-label small fw-bold text-muted">收款人姓名 (Payee Name)</label>
|
||||||
<input type="text" id="pay-account" class="form-control" placeholder="银行卡号或账号">
|
<input type="text" id="pay-name" class="form-control form-control-lg fs-6" placeholder="收款人姓名或账户别名">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label small">转账说明/备注</label>
|
<label class="form-label small fw-bold text-muted">收款账号 / 地址 (Account Number)</label>
|
||||||
<textarea id="pay-note" class="form-control" rows="2" placeholder="告知用户转账时需要备注的内容"></textarea>
|
<input type="text" id="pay-account" class="form-control form-control-lg fs-6 fw-bold text-primary" placeholder="银行卡号或钱包地址">
|
||||||
|
</div>
|
||||||
|
<div class="mb-0">
|
||||||
|
<label class="form-label small fw-bold text-muted">转账说明 / 备注 (Instructions)</label>
|
||||||
|
<textarea id="pay-note" class="form-control" rows="3" placeholder="告知用户转账时需要注意的事项,例如:务必备注UID"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer border-0 p-4 pt-0">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
<button type="button" class="btn btn-light px-4 fw-bold" data-bs-dismiss="modal">取消</button>
|
||||||
<button type="button" class="btn btn-primary" onclick="sendPaymentInfo()">确定发送</button>
|
<button type="button" class="btn btn-primary px-5 fw-bold shadow" onclick="sendPaymentInfo()">
|
||||||
|
<i class="bi bi-send-fill me-2"></i>立即匹配并发送
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -244,51 +249,87 @@ let lastChatIds = new Set();
|
|||||||
let currentUserContext = '';
|
let currentUserContext = '';
|
||||||
|
|
||||||
async function refreshUsers() {
|
async function refreshUsers() {
|
||||||
const r = await fetch('/api/chat.php?action=admin_get_all');
|
try {
|
||||||
const users = await r.json();
|
const r = await fetch('/api/chat.php?action=admin_get_all');
|
||||||
const list = document.getElementById('user-list');
|
const users = await r.json();
|
||||||
const search = document.getElementById('user-search').value.toLowerCase();
|
|
||||||
|
|
||||||
let html = '';
|
|
||||||
users.forEach(u => {
|
|
||||||
const username = u.username || '匿名用户';
|
|
||||||
const uid = u.uid || '---';
|
|
||||||
const ip = u.ip_address || '---';
|
|
||||||
const remark = u.remark || '';
|
|
||||||
const userTime = u.user_time || '---';
|
|
||||||
const lastTime = u.created_at ? new Date(u.created_at.replace(/-/g, "/")) : new Date();
|
|
||||||
|
|
||||||
if (search && !username.toLowerCase().includes(search) && !ip.includes(search) && !uid.toString().includes(search)) {
|
if (users.error) {
|
||||||
|
console.error('API Error:', users.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastMsgText = u.message;
|
if (!Array.isArray(users)) {
|
||||||
if (lastMsgText.startsWith('[PAYMENT_INFO]')) {
|
console.error('API response is not an array:', users);
|
||||||
lastMsgText = '[收款账号信息]';
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isActive = (selectedIp === ip && selectedUser == u.user_id);
|
const list = document.getElementById('user-list');
|
||||||
if (isActive) {
|
const searchInput = document.getElementById('user-search');
|
||||||
document.getElementById('info-user-time').innerText = userTime;
|
const search = searchInput ? searchInput.value.toLowerCase() : '';
|
||||||
}
|
|
||||||
html += `
|
let html = '';
|
||||||
<div class="user-card ${isActive ? 'active' : ''}" onclick="openChat('${u.user_id}', '${ip}', '${username}', '${uid}', '${remark.replace(/'/g, "\\'")}', '${userTime}')">
|
users.forEach(u => {
|
||||||
<div class="d-flex justify-content-between mb-1">
|
try {
|
||||||
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span>
|
const userId = u.user_id;
|
||||||
<span class="text-muted" style="font-size: 10px;">${lastTime.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'})}</span>
|
const username = u.username || '匿名用户';
|
||||||
</div>
|
const uid = u.uid || '---';
|
||||||
${remark ? `<div class="small text-danger text-truncate mb-1" style="font-size: 11px;">[备注: ${remark}]</div>` : ''}
|
const ip = u.ip_address || '---';
|
||||||
<div class="small text-truncate text-muted mb-1" style="font-size: 12px;">${lastMsgText}</div>
|
const rawRemark = u.remark || '';
|
||||||
<div class="d-flex justify-content-between align-items-center" style="font-size: 10px;">
|
const remark = rawRemark.toString().replace(/\n/g, " ");
|
||||||
<span class="text-secondary">UID: ${uid}</span>
|
const userTime = u.user_time || '---';
|
||||||
<span class="text-primary fw-bold">${ip}</span>
|
const lastTime = u.created_at ? new Date(u.created_at.replace(/-/g, "/")) : new Date();
|
||||||
</div>
|
|
||||||
</div>
|
let lastMsgText = u.message || '';
|
||||||
`;
|
if (lastMsgText.startsWith('[PAYMENT_INFO]')) {
|
||||||
});
|
lastMsgText = '[收款账号信息]';
|
||||||
list.innerHTML = html;
|
}
|
||||||
|
|
||||||
|
const isActive = (selectedIp === ip && selectedUser == userId);
|
||||||
|
if (isActive) {
|
||||||
|
const infoUserTime = document.getElementById('info-user-time');
|
||||||
|
if (infoUserTime) infoUserTime.innerText = userTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using data attributes to avoid escaping issues in onclick
|
||||||
|
html += `
|
||||||
|
<div class="user-card ${isActive ? 'active' : ''}"
|
||||||
|
data-user-id="${userId}"
|
||||||
|
data-ip="${ip}"
|
||||||
|
data-name="${username.replace(/"/g, '"')}"
|
||||||
|
data-uid="${uid}"
|
||||||
|
data-remark="${remark.replace(/"/g, '"')}"
|
||||||
|
data-user-time="${userTime}">
|
||||||
|
<div class="d-flex justify-content-between mb-1">
|
||||||
|
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span>
|
||||||
|
<span class="text-muted" style="font-size: 10px;">${lastTime.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'})}</span>
|
||||||
|
</div>
|
||||||
|
${rawRemark ? `<div class="small text-danger text-truncate mb-1" style="font-size: 11px;">[备注: ${rawRemark}]</div>` : ''}
|
||||||
|
<div class="small text-truncate text-muted mb-1" style="font-size: 12px;">${lastMsgText}</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-center" style="font-size: 10px;">
|
||||||
|
<span class="text-secondary">UID: ${uid}</span>
|
||||||
|
<span class="text-primary fw-bold">${ip}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error rendering user card:', e, u);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
list.innerHTML = html || '<div class="p-4 text-center text-muted small">暂无活跃会话</div>';
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Refresh users failed:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle clicks on user cards using event delegation
|
||||||
|
document.getElementById('user-list').addEventListener('click', (e) => {
|
||||||
|
const card = e.target.closest('.user-card');
|
||||||
|
if (card) {
|
||||||
|
const d = card.dataset;
|
||||||
|
openChat(d.userId, d.ip, d.name, d.uid, d.remark, d.userTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function openChat(userId, ip, name, uid, remark, userTime) {
|
function openChat(userId, ip, name, uid, remark, userTime) {
|
||||||
selectedUser = userId;
|
selectedUser = userId;
|
||||||
selectedIp = ip;
|
selectedIp = ip;
|
||||||
@ -573,8 +614,8 @@ document.getElementById('save-remark-btn').addEventListener('click', async () =>
|
|||||||
|
|
||||||
document.getElementById('user-search').addEventListener('input', refreshUsers);
|
document.getElementById('user-search').addEventListener('input', refreshUsers);
|
||||||
|
|
||||||
setInterval(refreshUsers, 300);
|
setInterval(refreshUsers, 2000);
|
||||||
setInterval(fetchMessages, 300);
|
setInterval(fetchMessages, 2000);
|
||||||
refreshUsers();
|
refreshUsers();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
95
api/chat.php
95
api/chat.php
@ -85,7 +85,7 @@ if ($action === 'get_messages') {
|
|||||||
|
|
||||||
if ($action === 'send_message') {
|
if ($action === 'send_message') {
|
||||||
$message = $_POST['message'] ?? '';
|
$message = $_POST['message'] ?? '';
|
||||||
if (!$message) exit(json_encode(['success' => false]));
|
if (!$message) exit(json_encode(['success' => false, 'error' => 'Empty message']));
|
||||||
|
|
||||||
$user_id = (int)($_SESSION['user_id'] ?? 0);
|
$user_id = (int)($_SESSION['user_id'] ?? 0);
|
||||||
$ip = getRealIP();
|
$ip = getRealIP();
|
||||||
@ -93,16 +93,24 @@ if ($action === 'send_message') {
|
|||||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||||
$stmt->execute([$user_id, 'user', $message, $ip]);
|
$stmt->execute([$user_id, 'user', $message, $ip]);
|
||||||
$newId = db()->lastInsertId();
|
$newId = db()->lastInsertId();
|
||||||
|
|
||||||
|
// Also update visitors table to ensure immediate visibility
|
||||||
|
$user_time = date('H:i:s');
|
||||||
|
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, user_time) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP");
|
||||||
|
$stmt->execute([$user_id, $ip, $user_time]);
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
|
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($action === 'admin_send') {
|
if ($action === 'admin_send') {
|
||||||
|
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
|
||||||
|
|
||||||
$message = $_POST['message'] ?? '';
|
$message = $_POST['message'] ?? '';
|
||||||
$user_id = (int)($_POST['user_id'] ?? 0);
|
$user_id = (int)($_POST['user_id'] ?? 0);
|
||||||
$target_ip = $_POST['ip_address'] ?? '';
|
$target_ip = $_POST['ip_address'] ?? '';
|
||||||
|
|
||||||
if (!$message) exit(json_encode(['success' => false]));
|
if (!$message) exit(json_encode(['success' => false, 'error' => 'Empty message']));
|
||||||
|
|
||||||
$admin_id = $_SESSION['admin_id'] ?? 1;
|
$admin_id = $_SESSION['admin_id'] ?? 1;
|
||||||
$sender = 'admin';
|
$sender = 'admin';
|
||||||
@ -126,41 +134,58 @@ if ($action === 'ping') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($action === 'admin_get_all') {
|
if ($action === 'admin_get_all') {
|
||||||
$stmt = db()->query("
|
header('Content-Type: application/json');
|
||||||
SELECT
|
if (!isset($_SESSION['admin_id'])) {
|
||||||
v.user_id,
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
v.ip_address,
|
exit;
|
||||||
CASE WHEN m.message LIKE '<img%' THEN '[Image]' ELSE COALESCE(m.message, 'User joined') END as message,
|
}
|
||||||
COALESCE(m.created_at, v.last_activity) as created_at,
|
try {
|
||||||
CASE WHEN u.email LIKE '%@user.byro' THEN u.username ELSE COALESCE(u.email, u.username) END as username,
|
// Robust query to get all active chat sessions
|
||||||
u.uid,
|
$stmt = db()->query("
|
||||||
r.remark,
|
|
||||||
v.user_time
|
|
||||||
FROM (
|
|
||||||
SELECT
|
SELECT
|
||||||
user_id,
|
v.user_id,
|
||||||
MAX(ip_address) as ip_address,
|
v.ip_address,
|
||||||
MAX(last_activity) as last_activity,
|
CASE
|
||||||
MAX(user_time) as user_time
|
WHEN m.message LIKE '<img%' THEN '[图片消息]'
|
||||||
|
WHEN m.message IS NULL AND v.has_recharge = 1 THEN '[充值申请]'
|
||||||
|
ELSE COALESCE(m.message, '新会话')
|
||||||
|
END as message,
|
||||||
|
COALESCE(m.created_at, v.last_activity) as created_at,
|
||||||
|
CASE WHEN u.email LIKE '%@user.byro' THEN u.username ELSE COALESCE(u.email, u.username) END as username,
|
||||||
|
u.uid,
|
||||||
|
r.remark,
|
||||||
|
v.user_time
|
||||||
FROM (
|
FROM (
|
||||||
SELECT COALESCE(user_id, 0) as user_id, ip_address, MAX(created_at) as last_activity, NULL as user_time FROM messages GROUP BY COALESCE(user_id, 0), ip_address
|
SELECT
|
||||||
UNION
|
user_id,
|
||||||
SELECT COALESCE(user_id, 0) as user_id, ip_address, MAX(last_ping) as last_activity, MAX(user_time) as user_time FROM chat_visitors GROUP BY COALESCE(user_id, 0), ip_address
|
MAX(ip_address) as ip_address,
|
||||||
) t1
|
MAX(last_activity) as last_activity,
|
||||||
GROUP BY user_id, (CASE WHEN user_id = 0 THEN ip_address ELSE '0' END)
|
MAX(user_time) as user_time,
|
||||||
) v
|
MAX(has_recharge) as has_recharge
|
||||||
LEFT JOIN (
|
FROM (
|
||||||
SELECT m1.* FROM messages m1
|
SELECT COALESCE(user_id, 0) as user_id, ip_address, created_at as last_activity, NULL as user_time, 0 as has_recharge FROM messages
|
||||||
INNER JOIN (
|
UNION ALL
|
||||||
SELECT MAX(id) as max_id FROM messages GROUP BY COALESCE(user_id, 0), (CASE WHEN COALESCE(user_id, 0) = 0 THEN ip_address ELSE '0' END)
|
SELECT COALESCE(user_id, 0) as user_id, ip_address, last_ping as last_activity, user_time, 0 as has_recharge FROM chat_visitors
|
||||||
) m2 ON m1.id = m2.max_id
|
UNION ALL
|
||||||
) m ON (v.user_id = COALESCE(m.user_id, 0) AND (v.user_id != 0 OR v.ip_address = m.ip_address))
|
SELECT COALESCE(user_id, 0) as user_id, ip_address, created_at as last_activity, NULL as user_time, 1 as has_recharge FROM finance_requests
|
||||||
LEFT JOIN users u ON (v.user_id = u.id AND v.user_id != 0)
|
) t1
|
||||||
LEFT JOIN chat_remarks r ON (v.user_id = COALESCE(r.user_id, 0) AND (v.user_id != 0 OR v.ip_address = r.ip_address))
|
GROUP BY user_id, (CASE WHEN user_id = 0 THEN ip_address ELSE '0' END)
|
||||||
WHERE v.last_activity > DATE_SUB(NOW(), INTERVAL 48 HOUR)
|
) v
|
||||||
ORDER BY created_at DESC
|
LEFT JOIN (
|
||||||
");
|
SELECT m1.* FROM messages m1
|
||||||
echo json_encode($stmt->fetchAll());
|
INNER JOIN (
|
||||||
|
SELECT MAX(id) as max_id FROM messages GROUP BY COALESCE(user_id, 0), (CASE WHEN COALESCE(user_id, 0) = 0 THEN ip_address ELSE '0' END)
|
||||||
|
) m2 ON m1.id = m2.max_id
|
||||||
|
) m ON (v.user_id = COALESCE(m.user_id, 0) AND (v.user_id != 0 OR v.ip_address = m.ip_address))
|
||||||
|
LEFT JOIN users u ON (v.user_id = u.id AND v.user_id != 0)
|
||||||
|
LEFT JOIN chat_remarks r ON (v.user_id = COALESCE(r.user_id, 0) AND (v.user_id != 0 OR v.ip_address = r.ip_address))
|
||||||
|
WHERE v.last_activity > DATE_SUB(NOW(), INTERVAL 72 HOUR)
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
");
|
||||||
|
echo json_encode($stmt->fetchAll());
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
assets/pasted-20260220-154227-bba38fff.png
Normal file
BIN
assets/pasted-20260220-154227-bba38fff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
BIN
assets/pasted-20260220-154843-2ebbe68f.png
Normal file
BIN
assets/pasted-20260220-154843-2ebbe68f.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
BIN
assets/pasted-20260221-012422-d1a25d7e.png
Normal file
BIN
assets/pasted-20260221-012422-d1a25d7e.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/pasted-20260221-014154-7c28d6d8.png
Normal file
BIN
assets/pasted-20260221-014154-7c28d6d8.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/pasted-20260221-015413-636e7149.png
Normal file
BIN
assets/pasted-20260221-015413-636e7149.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/pasted-20260221-021224-61e15e8e.png
Normal file
BIN
assets/pasted-20260221-021224-61e15e8e.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@ -1 +1 @@
|
|||||||
{"USD":1,"AED":3.67,"AFN":62.82,"ALL":81.84,"AMD":377.04,"ANG":1.79,"AOA":921.61,"ARS":1452.25,"AUD":1.42,"AWG":1.79,"AZN":1.7,"BAM":1.66,"BBD":2,"BDT":122.38,"BGN":1.61,"BHD":0.376,"BIF":2970.24,"BMD":1,"BND":1.27,"BOB":6.95,"BRL":5.23,"BSD":1,"BTN":91.07,"BWP":13.49,"BYN":2.85,"BZD":2,"CAD":1.37,"CDF":2274.65,"CHF":0.775,"CLF":0.0218,"CLP":862.61,"CNH":6.9,"CNY":6.92,"COP":3681.92,"CRC":482.89,"CUP":24,"CVE":93.67,"CZK":20.6,"DJF":177.72,"DKK":6.34,"DOP":61.77,"DZD":130.18,"EGP":47.56,"ERN":15,"ETB":155,"EUR":0.85,"FJD":2.2,"FKP":0.743,"FOK":6.34,"GBP":0.743,"GEL":2.68,"GGP":0.743,"GHS":11.02,"GIP":0.743,"GMD":74.11,"GNF":8759.48,"GTQ":7.69,"GYD":209.23,"HKD":7.81,"HNL":26.53,"HRK":6.4,"HTG":131.33,"HUF":322.26,"IDR":16900.93,"ILS":3.14,"IMP":0.743,"INR":91.08,"IQD":1310.74,"IRR":1286967.92,"ISK":123.16,"JEP":0.743,"JMD":156.2,"JOD":0.709,"JPY":155.01,"KES":128.98,"KGS":87.45,"KHR":4018.24,"KID":1.42,"KMF":417.93,"KRW":1449.41,"KWD":0.307,"KYD":0.833,"KZT":492.36,"LAK":21626,"LBP":89500,"LKR":309.19,"LRD":186.21,"LSL":16.17,"LYD":6.31,"MAD":9.16,"MDL":17.07,"MGA":4341.94,"MKD":52.1,"MMK":2106.54,"MNT":3543.97,"MOP":8.05,"MRU":40,"MUR":46.19,"MVR":15.47,"MWK":1741.99,"MXN":17.26,"MYR":3.91,"MZN":63.61,"NAD":16.17,"NGN":1345.77,"NIO":36.91,"NOK":9.56,"NPR":145.72,"NZD":1.67,"OMR":0.384,"PAB":1,"PEN":3.36,"PGK":4.34,"PHP":58.05,"PKR":280,"PLN":3.59,"PYG":6549.98,"QAR":3.64,"RON":4.33,"RSD":99.77,"RUB":76.79,"RWF":1460.66,"SAR":3.75,"SBD":7.96,"SCR":14.14,"SDG":511.55,"SEK":9.07,"SGD":1.27,"SHP":0.743,"SLE":24.46,"SLL":24455.37,"SOS":570.82,"SRD":37.72,"SSP":4576.76,"STN":20.81,"SYP":113.2,"SZL":16.17,"THB":31.2,"TJS":9.4,"TMT":3.5,"TND":2.87,"TOP":2.37,"TRY":43.81,"TTD":6.77,"TVD":1.42,"TWD":31.59,"TZS":2582.55,"UAH":43.32,"UGX":3547.23,"UYU":38.91,"UZS":12177.35,"VES":402.33,"VND":25905.86,"VUV":118.62,"WST":2.68,"XAF":557.24,"XCD":2.7,"XCG":1.79,"XDR":0.728,"XOF":557.24,"XPF":101.37,"YER":238.87,"ZAR":16.17,"ZMW":18.72,"ZWG":25.57,"ZWL":25.57}
|
{"USD":1,"AED":3.67,"AFN":62.91,"ALL":81.77,"AMD":376.96,"ANG":1.79,"AOA":921.54,"ARS":1452.25,"AUD":1.41,"AWG":1.79,"AZN":1.7,"BAM":1.66,"BBD":2,"BDT":122.24,"BGN":1.61,"BHD":0.376,"BIF":2972.8,"BMD":1,"BND":1.27,"BOB":6.94,"BRL":5.21,"BSD":1,"BTN":90.95,"BWP":13.63,"BYN":2.86,"BZD":2,"CAD":1.37,"CDF":2280.68,"CHF":0.776,"CLF":0.0219,"CLP":864.48,"CNH":6.9,"CNY":6.92,"COP":3676.38,"CRC":482.12,"CUP":24,"CVE":93.62,"CZK":20.58,"DJF":177.72,"DKK":6.34,"DOP":61.68,"DZD":130.05,"EGP":47.54,"ERN":15,"ETB":154.9,"EUR":0.849,"FJD":2.2,"FKP":0.742,"FOK":6.34,"GBP":0.742,"GEL":2.68,"GGP":0.742,"GHS":10.97,"GIP":0.742,"GMD":74.12,"GNF":8765.19,"GTQ":7.68,"GYD":209.23,"HKD":7.81,"HNL":26.5,"HRK":6.4,"HTG":131.33,"HUF":322.52,"IDR":16879.29,"ILS":3.12,"IMP":0.742,"INR":90.96,"IQD":1310.75,"IRR":1284718.39,"ISK":123.11,"JEP":0.742,"JMD":156.06,"JOD":0.709,"JPY":155.09,"KES":128.97,"KGS":87.44,"KHR":4018.25,"KID":1.41,"KMF":417.69,"KRW":1447.74,"KWD":0.307,"KYD":0.833,"KZT":492.41,"LAK":21645.16,"LBP":89500,"LKR":309.31,"LRD":185.93,"LSL":16.07,"LYD":6.31,"MAD":9.17,"MDL":17.09,"MGA":4323.12,"MKD":52.41,"MMK":2105.62,"MNT":3537.8,"MOP":8.05,"MRU":40.01,"MUR":46.31,"MVR":15.46,"MWK":1745.39,"MXN":17.17,"MYR":3.9,"MZN":63.57,"NAD":16.07,"NGN":1346.36,"NIO":36.87,"NOK":9.54,"NPR":145.52,"NZD":1.67,"OMR":0.384,"PAB":1,"PEN":3.36,"PGK":4.33,"PHP":58.08,"PKR":279.85,"PLN":3.58,"PYG":6535.86,"QAR":3.64,"RON":4.33,"RSD":99.71,"RUB":76.83,"RWF":1462.18,"SAR":3.75,"SBD":7.96,"SCR":13.84,"SDG":510.48,"SEK":9.06,"SGD":1.27,"SHP":0.742,"SLE":24.46,"SLL":24455.37,"SOS":570.82,"SRD":37.72,"SSP":4583.75,"STN":20.8,"SYP":114.12,"SZL":16.07,"THB":31.18,"TJS":9.39,"TMT":3.5,"TND":2.87,"TOP":2.36,"TRY":43.85,"TTD":6.74,"TVD":1.41,"TWD":31.54,"TZS":2579.03,"UAH":43.3,"UGX":3568.92,"UYU":38.81,"UZS":12205.78,"VES":405.35,"VND":25927.65,"VUV":118.68,"WST":2.69,"XAF":556.92,"XCD":2.7,"XCG":1.79,"XDR":0.727,"XOF":556.92,"XPF":101.32,"YER":238.65,"ZAR":16.07,"ZMW":18.82,"ZWG":25.54,"ZWL":25.54}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
require_once __DIR__ . '/lang.php';
|
require_once __DIR__ . '/lang.php';
|
||||||
|
|
||||||
|
|||||||
266
recharge.php
266
recharge.php
@ -197,7 +197,7 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
|||||||
<div class="modal-body p-0">
|
<div class="modal-body p-0">
|
||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<!-- Left Side: Online Service -->
|
<!-- Left Side: Online Service -->
|
||||||
<div class="col-lg-6 d-flex flex-column border-end border-secondary border-opacity-20" style="height: 650px; background: #1c2127;">
|
<div class="col-lg-6 d-flex flex-column border-end border-secondary border-opacity-20 order-2 order-lg-1 chat-column" style="background: #1c2127;">
|
||||||
<div class="p-4 border-bottom border-secondary border-opacity-20 bg-black bg-opacity-40">
|
<div class="p-4 border-bottom border-secondary border-opacity-20 bg-black bg-opacity-40">
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
@ -218,7 +218,7 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="modal-chat-messages" class="flex-grow-1 p-4 overflow-y-auto" style="scrollbar-width: thin; background: #161a1e;">
|
<div id="modal-chat-messages" class="flex-grow-1 p-4 overflow-y-auto" style="scrollbar-width: thin; background: #161a1e; min-height: 300px;">
|
||||||
<div class="text-center text-muted small mb-4 py-3 bg-white bg-opacity-5 rounded-3 border border-white border-opacity-5">
|
<div class="text-center text-muted small mb-4 py-3 bg-white bg-opacity-5 rounded-3 border border-white border-opacity-5">
|
||||||
<i class="bi bi-shield-lock-fill text-success me-2"></i><?= __('welcome_support') ?>
|
<i class="bi bi-shield-lock-fill text-success me-2"></i><?= __('welcome_support') ?>
|
||||||
</div>
|
</div>
|
||||||
@ -241,62 +241,49 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right Side: Account Matching -->
|
<!-- Right Side: Account Matching -->
|
||||||
<div class="col-lg-6 p-5 d-flex flex-column justify-content-center info-side position-relative overflow-hidden">
|
<div class="col-lg-6 p-4 p-lg-5 d-flex flex-column justify-content-center info-side position-relative overflow-hidden order-1 order-lg-2">
|
||||||
<!-- Vibrant Background Overlay -->
|
<!-- Vibrant & Coordinated Background -->
|
||||||
<div class="position-absolute top-0 start-0 w-100 h-100 vibrancy-bg"></div>
|
<div class="position-absolute top-0 start-0 w-100 h-100 vibrancy-bg"></div>
|
||||||
<div class="position-absolute top-0 start-0 w-100 h-100 bg-black bg-opacity-30" style="z-index: 1;"></div>
|
<div class="position-absolute top-0 start-0 w-100 h-100 bg-black bg-opacity-40" style="z-index: 1; backdrop-filter: blur(2px);"></div>
|
||||||
|
|
||||||
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
|
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
|
||||||
<div class="mb-5">
|
<div class="mb-4">
|
||||||
<div class="d-inline-flex align-items-center gap-2 px-3 py-1 rounded-pill bg-white bg-opacity-20 text-white small fw-bold mb-3 border border-white border-opacity-30 shadow-sm">
|
<div class="d-inline-flex align-items-center gap-2 px-3 py-1 rounded-pill bg-white bg-opacity-10 text-white small fw-bold mb-3 border border-white border-opacity-20 shadow-sm" style="backdrop-filter: blur(10px);">
|
||||||
<span class="pulse-dot-white"></span> <?= __('executing') ?>
|
<span class="pulse-dot-white"></span> <?= __('executing') ?>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="display-6 fw-bold text-white mb-3 text-shadow-heavy"><?= __('matching_account') ?>...</h2>
|
<h3 class="fw-bold text-white mb-2 text-shadow-ultra"><?= __('matching_account') ?>...</h3>
|
||||||
<p class="text-white fs-5 fw-bold opacity-100 text-shadow-medium"><?= __('matching_desc') ?></p>
|
<p class="text-white small fw-bold opacity-90 text-shadow-heavy"><?= __('matching_desc') ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-5 py-4 px-4 rounded-4 shadow-lg border border-white border-opacity-30" style="background: rgba(0,0,0,0.4); backdrop-filter: blur(20px);">
|
<div class="mb-4 py-3 px-4 rounded-4 shadow-2xl border border-white border-opacity-30" style="background: rgba(255,255,255,0.1); backdrop-filter: blur(40px);">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col-sm-6 mb-3 mb-sm-0">
|
<div class="col-6">
|
||||||
<div class="text-white small mb-1 fw-bold"><?= __('remaining_time') ?></div>
|
<div class="text-white small mb-1 fw-bold text-shadow-medium"><?= __('remaining_time') ?></div>
|
||||||
<div class="display-5 fw-bold text-warning tracking-wider text-shadow-glow" id="modal-countdown">10:00</div>
|
<div class="h3 fw-bold text-warning tracking-wider text-shadow-glow mb-0" id="modal-countdown" style="font-family: 'Courier New', monospace;">30:00</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 border-start border-white border-opacity-20 ps-sm-4">
|
<div class="col-6 border-start border-white border-opacity-20 ps-4">
|
||||||
<div class="text-white small mb-1 fw-bold"><?= __('security_level') ?></div>
|
<div class="text-white small mb-1 fw-bold text-shadow-medium"><?= __('security_level') ?></div>
|
||||||
<div class="d-flex gap-1 mb-1">
|
<div class="d-flex gap-1 mb-1">
|
||||||
<div class="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></div>
|
<div class="bg-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></div>
|
||||||
<div class="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></div>
|
<div class="bg-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></div>
|
||||||
<div class="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></div>
|
<div class="bg-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></div>
|
||||||
<div class="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></div>
|
<div class="bg-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-warning small fw-bold text-shadow-glow-sm"><?= __('high') ?></div>
|
<div class="text-white fw-bold text-shadow-glow-sm" style="color: #4ade80 !important; font-size: 10px;"><?= __('high') ?></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="p-3 rounded-4 bg-black bg-opacity-30 border border-white border-opacity-10">
|
||||||
<h6 class="text-white fw-bold mb-4 d-flex align-items-center gap-2 text-shadow-medium">
|
<h6 class="text-white fw-bold mb-3 d-flex align-items-center gap-2" style="font-size: 14px;">
|
||||||
<i class="bi bi-shield-check text-warning fs-5"></i> <?= __('security_instructions') ?>
|
<i class="bi bi-shield-lock-fill text-warning"></i> <?= __('security_instructions') ?>
|
||||||
</h6>
|
</h6>
|
||||||
<div class="d-flex flex-column gap-3">
|
<div class="text-white opacity-90 small lh-base" style="font-size: 13px !important;">
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 transition-all hover-bg-opacity-20">
|
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>系统正在为您分配专属收款账户,请耐心等待</span></div>
|
||||||
<div class="p-2 rounded-circle bg-white bg-opacity-20 text-white shadow-sm">
|
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>匹配期间请勿刷新页面或重复提交订单</span></div>
|
||||||
<i class="bi bi-check2"></i>
|
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>若超过倒计时仍未匹配成功,请及时联系在线客服</span></div>
|
||||||
</div>
|
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>收到充值账户,完成转账后,请将转账凭证提交给在线客服</span></div>
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('security_tip_1') ?></div>
|
<div class="d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>客服将在核实资金后为您确认到账</span></div>
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 transition-all hover-bg-opacity-20">
|
|
||||||
<div class="p-2 rounded-circle bg-white bg-opacity-20 text-white shadow-sm">
|
|
||||||
<i class="bi bi-check2"></i>
|
|
||||||
</div>
|
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('security_tip_2') ?></div>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 transition-all hover-bg-opacity-20">
|
|
||||||
<div class="p-2 rounded-circle bg-white bg-opacity-20 text-white shadow-sm">
|
|
||||||
<i class="bi bi-check2"></i>
|
|
||||||
</div>
|
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('security_tip_3') ?></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -309,20 +296,51 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.vibrancy-bg {
|
.vibrancy-bg {
|
||||||
background: linear-gradient(135deg, #ff9a00 0%, #ff5200 50%, #ff0055 100%);
|
background: linear-gradient(135deg, #0f172a 0%, #1e40af 25%, #0369a1 50%, #0d9488 75%, #059669 100%);
|
||||||
background-size: 400% 400%;
|
background-size: 400% 400%;
|
||||||
animation: gradientFlow 10s ease infinite;
|
animation: gradientFlow 6s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
@keyframes gradientFlow {
|
@keyframes gradientFlow {
|
||||||
0% { background-position: 0% 50%; }
|
0% { background-position: 0% 50%; }
|
||||||
50% { background-position: 100% 50%; }
|
50% { background-position: 100% 50%; }
|
||||||
100% { background-position: 0% 50%; }
|
100% { background-position: 0% 50%; }
|
||||||
}
|
}
|
||||||
.text-shadow-heavy { text-shadow: 2px 2px 8px rgba(0,0,0,0.8), 0 0 20px rgba(0,0,0,0.5); }
|
.text-shadow-ultra {
|
||||||
.text-shadow-medium { text-shadow: 1px 1px 4px rgba(0,0,0,0.6); }
|
text-shadow: 0 0 10px rgba(0,0,0,0.8), 0 0 20px rgba(0,0,0,0.5), 0 4px 12px rgba(0,0,0,0.9);
|
||||||
.text-shadow-glow { text-shadow: 0 0 15px rgba(255,255,255,0.6), 0 0 5px rgba(255,255,255,0.4); }
|
letter-spacing: 0.5px;
|
||||||
.text-shadow-glow-sm { text-shadow: 0 0 10px rgba(255,255,255,0.4); }
|
}
|
||||||
.shadow-glow-sm { box-shadow: 0 0 10px rgba(255,193,7,0.5); }
|
.text-shadow-heavy {
|
||||||
|
text-shadow: 2px 2px 10px rgba(0,0,0,0.9), 0 0 5px rgba(0,0,0,0.7);
|
||||||
|
font-weight: 800 !important;
|
||||||
|
}
|
||||||
|
.text-shadow-medium {
|
||||||
|
text-shadow: 1px 1px 5px rgba(0,0,0,0.8);
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
.text-shadow-glow {
|
||||||
|
text-shadow: 0 0 20px rgba(255,193,7,0.7), 0 0 10px rgba(255,193,7,0.4), 2px 2px 4px rgba(0,0,0,0.9);
|
||||||
|
}
|
||||||
|
.text-shadow-glow-sm {
|
||||||
|
text-shadow: 0 0 10px rgba(40,167,69,0.7), 1px 1px 3px rgba(0,0,0,0.9);
|
||||||
|
}
|
||||||
|
.shadow-glow-green { box-shadow: 0 0 25px rgba(40,167,69,0.5); }
|
||||||
|
.shadow-2xl { box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.8); }
|
||||||
|
|
||||||
|
.payment-item .h5, .payment-item .h4 {
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-item .btn-light {
|
||||||
|
background: rgba(255,255,255,0.9);
|
||||||
|
border: none;
|
||||||
|
color: #1e3a8a;
|
||||||
|
}
|
||||||
|
.payment-item .btn-light:hover {
|
||||||
|
background: #ffffff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-scale-sm:hover { transform: translateX(5px); background: rgba(0,0,0,0.4) !important; }
|
||||||
|
|
||||||
.pulse-dot-white {
|
.pulse-dot-white {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@ -356,6 +374,12 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
|||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
}
|
}
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.chat-column { height: 450px !important; }
|
||||||
|
.modal-xl { margin: 10px; max-width: calc(100% - 20px); }
|
||||||
|
.modal-dialog-centered { align-items: flex-start; }
|
||||||
|
.info-side { padding: 1.5rem !important; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -420,7 +444,7 @@ function openRechargeModal(initialMessage) {
|
|||||||
modal.show();
|
modal.show();
|
||||||
|
|
||||||
// Start countdown
|
// Start countdown
|
||||||
let seconds = 600;
|
let seconds = 1800;
|
||||||
const display = document.getElementById('modal-countdown');
|
const display = document.getElementById('modal-countdown');
|
||||||
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
|
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
|
||||||
rechargeCountdownInterval = setInterval(() => {
|
rechargeCountdownInterval = setInterval(() => {
|
||||||
@ -430,14 +454,30 @@ function openRechargeModal(initialMessage) {
|
|||||||
if (--seconds < 0) clearInterval(rechargeCountdownInterval);
|
if (--seconds < 0) clearInterval(rechargeCountdownInterval);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
// Send initial message
|
// Send initial message and show in chat
|
||||||
sendModalMessage(initialMessage);
|
const tempId = 'modal_temp_' + Date.now();
|
||||||
|
appendModalMessage({
|
||||||
|
id: tempId,
|
||||||
|
sender: 'user',
|
||||||
|
message: initialMessage,
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ping first to register visitor, then send message
|
||||||
|
fetch(`/api/chat.php?action=ping&user_time=${encodeURIComponent(new Date().toLocaleString())}`)
|
||||||
|
.then(() => sendModalMessage(initialMessage))
|
||||||
|
.catch(err => console.error('Ping failed:', err));
|
||||||
|
|
||||||
// Start polling for modal
|
// Start polling for modal
|
||||||
initModalChat();
|
initModalChat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let modalChatPolling = false;
|
||||||
|
|
||||||
function initModalChat() {
|
function initModalChat() {
|
||||||
|
if (modalChatPolling) return; // Prevent multiple polling loops
|
||||||
|
modalChatPolling = true;
|
||||||
|
|
||||||
const modalChatForm = document.getElementById('modal-chat-form');
|
const modalChatForm = document.getElementById('modal-chat-form');
|
||||||
const modalChatInput = document.getElementById('modal-chat-input');
|
const modalChatInput = document.getElementById('modal-chat-input');
|
||||||
const modalChatUpload = document.getElementById('modal-chat-upload');
|
const modalChatUpload = document.getElementById('modal-chat-upload');
|
||||||
@ -510,6 +550,9 @@ function initModalChat() {
|
|||||||
const modalPoll = async () => {
|
const modalPoll = async () => {
|
||||||
if (!document.getElementById('rechargeModal').classList.contains('show')) return;
|
if (!document.getElementById('rechargeModal').classList.contains('show')) return;
|
||||||
try {
|
try {
|
||||||
|
// Periodic ping to keep session alive and visitor status active
|
||||||
|
fetch(`/api/chat.php?action=ping&user_time=${encodeURIComponent(new Date().toLocaleString())}`);
|
||||||
|
|
||||||
const resp = await fetch('/api/chat.php?action=get_messages');
|
const resp = await fetch('/api/chat.php?action=get_messages');
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
@ -558,7 +601,17 @@ function appendModalMessage(m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isImage = text.indexOf('<img') !== -1;
|
const isImage = text.indexOf('<img') !== -1;
|
||||||
const time = new Date(m.created_at.replace(/-/g, "/")).toLocaleTimeString('zh-CN', {hour:'2-digit', minute:'2-digit'});
|
let timeStr = '';
|
||||||
|
try {
|
||||||
|
if (m.created_at) {
|
||||||
|
const dateObj = m.created_at.includes('-') ? new Date(m.created_at.replace(/-/g, "/")) : new Date(m.created_at);
|
||||||
|
timeStr = dateObj.toLocaleTimeString('zh-CN', {hour:'2-digit', minute:'2-digit'});
|
||||||
|
} else {
|
||||||
|
timeStr = new Date().toLocaleTimeString('zh-CN', {hour:'2-digit', minute:'2-digit'});
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
timeStr = '--:--';
|
||||||
|
}
|
||||||
|
|
||||||
const html = `
|
const html = `
|
||||||
<div class="mb-3 d-flex flex-column ${sender === 'user' ? 'align-items-end' : 'align-items-start'} modal-msg" data-modal-id="${m.id}">
|
<div class="mb-3 d-flex flex-column ${sender === 'user' ? 'align-items-end' : 'align-items-start'} modal-msg" data-modal-id="${m.id}">
|
||||||
@ -566,7 +619,7 @@ function appendModalMessage(m) {
|
|||||||
<div class="message-content" style="text-shadow: 0 1px 2px rgba(0,0,0,0.2); font-size: 14px; line-height: 1.5;">
|
<div class="message-content" style="text-shadow: 0 1px 2px rgba(0,0,0,0.2); font-size: 14px; line-height: 1.5;">
|
||||||
${text}
|
${text}
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 9px; opacity: 0.6; position: absolute; bottom: 4px; ${sender === 'user' ? 'right: 12px;' : 'left: 12px;'} ${isImage ? 'background: rgba(0,0,0,0.5); padding: 2px 6px; border-radius: 4px; bottom: 8px;' : ''}">${time}</div>
|
<div style="font-size: 9px; opacity: 0.6; position: absolute; bottom: 4px; ${sender === 'user' ? 'right: 12px;' : 'left: 12px;'} ${isImage ? 'background: rgba(0,0,0,0.5); padding: 2px 6px; border-radius: 4px; bottom: 8px;' : ''}">${timeStr}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -582,70 +635,62 @@ function updateMatchingSide(info) {
|
|||||||
|
|
||||||
side.innerHTML = `
|
side.innerHTML = `
|
||||||
<div class="position-absolute top-0 start-0 w-100 h-100 vibrancy-bg"></div>
|
<div class="position-absolute top-0 start-0 w-100 h-100 vibrancy-bg"></div>
|
||||||
<div class="position-absolute top-0 start-0 w-100 h-100 bg-black bg-opacity-30" style="z-index: 1;"></div>
|
<div class="position-absolute top-0 start-0 w-100 h-100 bg-black bg-opacity-40" style="z-index: 1; backdrop-filter: blur(2px);"></div>
|
||||||
<div class="text-center text-lg-start fade-in position-relative" style="z-index: 2;">
|
<div class="text-center text-lg-start fade-in position-relative" style="z-index: 2;">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<div class="d-inline-flex align-items-center gap-2 px-3 py-1 rounded-pill bg-white bg-opacity-20 text-white small fw-bold mb-3 border border-white border-opacity-30 shadow-sm">
|
<div class="d-inline-flex align-items-center gap-2 px-3 py-1 rounded-pill bg-white bg-opacity-20 text-white small fw-bold mb-3 border border-white border-opacity-30 shadow-sm" style="backdrop-filter: blur(10px);">
|
||||||
<i class="bi bi-check-circle-fill"></i> <?= __('account_matched') ?>
|
<i class="bi bi-check-circle-fill text-success"></i> <?= __('account_matched') ?>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="display-6 fw-bold text-white mb-3 text-shadow-heavy"><?= __('account_matched') ?></h2>
|
<h2 class="display-6 fw-bold text-white mb-3 text-shadow-ultra"><?= __('account_matched') ?></h2>
|
||||||
<p class="text-white fs-5 fw-bold opacity-100 text-shadow-medium"><?= __('account_matched_desc') ?></p>
|
<p class="text-white fs-5 fw-bold opacity-90 text-shadow-heavy"><?= __('account_matched_desc') ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-5 p-4 rounded-4 shadow-lg border border-white border-opacity-30" style="background: rgba(0,0,0,0.5); backdrop-filter: blur(25px);">
|
<div class="mb-5 p-4 rounded-4 shadow-2xl border border-white border-opacity-30" style="background: rgba(255,255,255,0.1); backdrop-filter: blur(40px);">
|
||||||
<div class="d-flex flex-column gap-4">
|
<div class="d-flex flex-column gap-4">
|
||||||
<div class="payment-item">
|
<div class="payment-item">
|
||||||
<div class="text-white small opacity-80 mb-1 fw-bold"><?= __('bank_name') ?></div>
|
<div class="text-white small mb-1 fw-bold text-shadow-medium"><?= __('bank_name') ?></div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center gap-3">
|
||||||
<div class="h5 mb-0 fw-bold text-white text-shadow-medium">${info.bank}</div>
|
<div class="h5 mb-0 fw-bold text-white text-shadow-heavy" style="word-break: break-all;">${info.bank}</div>
|
||||||
<button class="btn btn-sm btn-light rounded-pill px-3 fw-bold shadow-sm" onclick="copyText('${info.bank}')"><?= __('copy_info') ?></button>
|
<button class="btn btn-sm btn-light rounded-pill px-3 fw-bold shadow-sm flex-shrink-0" onclick="copyText('${info.bank}')"><?= __('copy_info') ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payment-item border-top border-white border-opacity-10 pt-3">
|
<div class="payment-item border-top border-white border-opacity-20 pt-3">
|
||||||
<div class="text-white small opacity-80 mb-1 fw-bold"><?= __('payee_name') ?></div>
|
<div class="text-white small mb-1 fw-bold text-shadow-medium"><?= __('payee_name') ?></div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center gap-3">
|
||||||
<div class="h5 mb-0 fw-bold text-white text-shadow-medium">${info.name}</div>
|
<div class="h5 mb-0 fw-bold text-white text-shadow-heavy" style="word-break: break-all;">${info.name}</div>
|
||||||
<button class="btn btn-sm btn-light rounded-pill px-3 fw-bold shadow-sm" onclick="copyText('${info.name}')"><?= __('copy_info') ?></button>
|
<button class="btn btn-sm btn-light rounded-pill px-3 fw-bold shadow-sm flex-shrink-0" onclick="copyText('${info.name}')"><?= __('copy_info') ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payment-item border-top border-white border-opacity-10 pt-3">
|
<div class="payment-item border-top border-white border-opacity-20 pt-3">
|
||||||
<div class="text-white small opacity-80 mb-1 fw-bold"><?= __('account_number') ?></div>
|
<div class="text-white small mb-1 fw-bold text-shadow-medium"><?= __('account_number') ?></div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center gap-3">
|
||||||
<div class="h4 mb-0 fw-bold text-warning tracking-wider text-shadow-glow">${info.account}</div>
|
<div class="h4 mb-0 fw-bold text-warning tracking-wider text-shadow-glow" style="word-break: break-all; font-family: 'Courier New', monospace;">${info.account}</div>
|
||||||
<button class="btn btn-sm btn-warning rounded-pill px-3 shadow-lg fw-bold" onclick="copyText('${info.account}')"><?= __('copy_info') ?></button>
|
<button class="btn btn-sm btn-warning rounded-pill px-3 shadow-lg fw-bold flex-shrink-0" onclick="copyText('${info.account}')"><?= __('copy_info') ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${info.note ? `
|
${info.note ? `
|
||||||
<div class="payment-item border-top border-white border-opacity-10 pt-3">
|
<div class="payment-item border-top border-white border-opacity-20 pt-3">
|
||||||
<div class="text-info small mb-1 fw-bold"><i class="bi bi-exclamation-circle me-1"></i><?= __('transfer_note') ?></div>
|
<div class="text-warning small mb-1 fw-bold"><i class="bi bi-exclamation-circle me-1"></i><?= __('transfer_note') ?></div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center gap-3">
|
||||||
<div class="fw-bold text-info text-shadow-glow-sm">${info.note}</div>
|
<div class="fw-bold text-warning text-shadow-glow-sm" style="word-break: break-all;">${info.note}</div>
|
||||||
<button class="btn btn-sm btn-info text-white rounded-pill px-3 fw-bold shadow-sm" onclick="copyText('${info.note}')"><?= __('copy_info') ?></button>
|
<button class="btn btn-sm btn-warning text-dark rounded-pill px-3 fw-bold shadow-sm flex-shrink-0" onclick="copyText('${info.note}')"><?= __('copy_info') ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<h6 class="text-white fw-bold mb-4 d-flex align-items-center gap-2 text-shadow-medium">
|
<h6 class="text-white fw-bold mb-4 d-flex align-items-center gap-2 text-shadow-heavy">
|
||||||
<i class="bi bi-info-circle text-white fs-5"></i> <?= __('transfer_steps_title') ?>
|
<i class="bi bi-info-circle-fill text-white fs-5"></i> <?= __('transfer_steps_title') ?>
|
||||||
</h6>
|
</h6>
|
||||||
<div class="d-flex flex-column gap-3">
|
<div class="d-flex flex-column gap-3">
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 shadow-sm">
|
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-20 shadow-sm hover-scale-sm" style="backdrop-filter: blur(10px);">
|
||||||
<div class="fw-bold text-warning fs-5 text-shadow-glow-sm">01</div>
|
<div class="fw-bold text-warning fs-5 text-shadow-glow-sm" style="min-width: 25px;">01</div>
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('step_1') ?></div>
|
<div class="text-white small lh-base fw-bold text-shadow-medium">完成转账后,请将转账凭证提交给在线客服</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 shadow-sm">
|
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-20 shadow-sm hover-scale-sm" style="backdrop-filter: blur(10px);">
|
||||||
<div class="fw-bold text-warning fs-5 text-shadow-glow-sm">02</div>
|
<div class="fw-bold text-warning fs-5 text-shadow-glow-sm" style="min-width: 25px;">02</div>
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('step_2') ?></div>
|
<div class="text-white small lh-base fw-bold text-shadow-medium">客服将在核实资金后为您确认到账</div>
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 shadow-sm">
|
|
||||||
<div class="fw-bold text-warning fs-5 text-shadow-glow-sm">03</div>
|
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('step_3') ?></div>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-3 align-items-start p-3 rounded-3 bg-white bg-opacity-10 border border-white border-opacity-10 shadow-sm">
|
|
||||||
<div class="fw-bold text-warning fs-5 text-shadow-glow-sm">04</div>
|
|
||||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('step_4') ?></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -692,6 +737,12 @@ function confirmFiatOrder() {
|
|||||||
|
|
||||||
const estUsdt = amount / rate;
|
const estUsdt = amount / rate;
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
const btn = event.target;
|
||||||
|
const originalText = btn.innerHTML;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerHTML = `<span class="spinner-border spinner-border-sm me-2"></span>${originalText}`;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'recharge');
|
formData.append('action', 'recharge');
|
||||||
formData.append('amount', estUsdt);
|
formData.append('amount', estUsdt);
|
||||||
@ -706,6 +757,8 @@ function confirmFiatOrder() {
|
|||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = originalText;
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
let message = `<?= __('recharge_msg_fiat') ?>`;
|
let message = `<?= __('recharge_msg_fiat') ?>`;
|
||||||
const preciseRes = (amount / rate).toFixed(4);
|
const preciseRes = (amount / rate).toFixed(4);
|
||||||
@ -718,18 +771,27 @@ function confirmFiatOrder() {
|
|||||||
} else {
|
} else {
|
||||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = originalText;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmCryptoOrder() {
|
function confirmCryptoOrder() {
|
||||||
const amountInput = document.getElementById('cryptoAmount');
|
const amountInput = document.getElementById('cryptoAmount');
|
||||||
const amount = parseFloat(amountInput.value);
|
const amount = parseFloat(amountInput.value);
|
||||||
|
const btn = event.target;
|
||||||
|
|
||||||
if (isNaN(amount) || amount <= 0) {
|
if (isNaN(amount) || amount <= 0) {
|
||||||
notify('warning', '<?= __("enter_amount") ?>');
|
notify('warning', '<?= __("enter_amount") ?>');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const originalText = btn.innerHTML;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerHTML = `<span class="spinner-border spinner-border-sm me-2"></span>${originalText}`;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'recharge');
|
formData.append('action', 'recharge');
|
||||||
formData.append('amount', amount);
|
formData.append('amount', amount);
|
||||||
@ -741,17 +803,27 @@ function confirmCryptoOrder() {
|
|||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(async data => {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = originalText;
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
let message = `<?= __('recharge_msg_crypto') ?>`;
|
let message = `<?= __('recharge_msg_crypto') ?>`;
|
||||||
message = message.replace('%uid%', userId)
|
message = message.replace('%uid%', userId)
|
||||||
.replace('%amount%', amount)
|
.replace('%amount%', amount)
|
||||||
.replace('%network%', currentNetwork);
|
.replace('%network%', currentNetwork);
|
||||||
openRechargeModal(message);
|
|
||||||
|
// Send message to CS quietly for USDT
|
||||||
|
await sendModalMessage(message);
|
||||||
|
|
||||||
|
notify('success', '<?= __("recharge_success_title") ?>', '<?= __("recharge_success_text") ?>');
|
||||||
amountInput.value = '';
|
amountInput.value = '';
|
||||||
} else {
|
} else {
|
||||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = originalText;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user