Autosave: 20260221-022115
This commit is contained in:
parent
0b9cff662e
commit
2abf771e6c
@ -187,32 +187,37 @@ ob_start();
|
||||
<!-- Payment Info Modal -->
|
||||
<div class="modal fade" id="paymentModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">发送收款账号 (法币充值)</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
<div class="modal-content border-0 shadow-lg">
|
||||
<div class="modal-header bg-primary text-white border-0">
|
||||
<h5 class="modal-title fw-bold"><i class="bi bi-bank me-2"></i>匹配收款账户 (法币/USDT)</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">银行名称/支付方式</label>
|
||||
<input type="text" id="pay-bank" class="form-control" placeholder="例如: 建设银行, Alipay, etc.">
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-info small border-0 bg-light text-primary">
|
||||
<i class="bi bi-info-circle-fill me-2"></i>填写后点击发送,前端充值弹窗将自动切换并显示此账户。
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">收款人姓名</label>
|
||||
<input type="text" id="pay-name" class="form-control" placeholder="收款人姓名">
|
||||
<label class="form-label small fw-bold text-muted">银行名称 / 支付方式 (Bank Name)</label>
|
||||
<input type="text" id="pay-bank" class="form-control form-control-lg fs-6" placeholder="例如: 建设银行, Alipay, TRC20, etc.">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">收款账号</label>
|
||||
<input type="text" id="pay-account" class="form-control" placeholder="银行卡号或账号">
|
||||
<label class="form-label small fw-bold text-muted">收款人姓名 (Payee Name)</label>
|
||||
<input type="text" id="pay-name" class="form-control form-control-lg fs-6" placeholder="收款人姓名或账户别名">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">转账说明/备注</label>
|
||||
<textarea id="pay-note" class="form-control" rows="2" placeholder="告知用户转账时需要备注的内容"></textarea>
|
||||
<label class="form-label small fw-bold text-muted">收款账号 / 地址 (Account Number)</label>
|
||||
<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 class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="sendPaymentInfo()">确定发送</button>
|
||||
<div class="modal-footer border-0 p-4 pt-0">
|
||||
<button type="button" class="btn btn-light px-4 fw-bold" data-bs-dismiss="modal">取消</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>
|
||||
@ -244,51 +249,87 @@ let lastChatIds = new Set();
|
||||
let currentUserContext = '';
|
||||
|
||||
async function refreshUsers() {
|
||||
const r = await fetch('/api/chat.php?action=admin_get_all');
|
||||
const users = await r.json();
|
||||
const list = document.getElementById('user-list');
|
||||
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();
|
||||
try {
|
||||
const r = await fetch('/api/chat.php?action=admin_get_all');
|
||||
const users = await r.json();
|
||||
|
||||
if (search && !username.toLowerCase().includes(search) && !ip.includes(search) && !uid.toString().includes(search)) {
|
||||
if (users.error) {
|
||||
console.error('API Error:', users.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let lastMsgText = u.message;
|
||||
if (lastMsgText.startsWith('[PAYMENT_INFO]')) {
|
||||
lastMsgText = '[收款账号信息]';
|
||||
if (!Array.isArray(users)) {
|
||||
console.error('API response is not an array:', users);
|
||||
return;
|
||||
}
|
||||
|
||||
const isActive = (selectedIp === ip && selectedUser == u.user_id);
|
||||
if (isActive) {
|
||||
document.getElementById('info-user-time').innerText = userTime;
|
||||
}
|
||||
html += `
|
||||
<div class="user-card ${isActive ? 'active' : ''}" onclick="openChat('${u.user_id}', '${ip}', '${username}', '${uid}', '${remark.replace(/'/g, "\\'")}', '${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>
|
||||
${remark ? `<div class="small text-danger text-truncate mb-1" style="font-size: 11px;">[备注: ${remark}]</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>
|
||||
`;
|
||||
});
|
||||
list.innerHTML = html;
|
||||
const list = document.getElementById('user-list');
|
||||
const searchInput = document.getElementById('user-search');
|
||||
const search = searchInput ? searchInput.value.toLowerCase() : '';
|
||||
|
||||
let html = '';
|
||||
users.forEach(u => {
|
||||
try {
|
||||
const userId = u.user_id;
|
||||
const username = u.username || '匿名用户';
|
||||
const uid = u.uid || '---';
|
||||
const ip = u.ip_address || '---';
|
||||
const rawRemark = u.remark || '';
|
||||
const remark = rawRemark.toString().replace(/\n/g, " ");
|
||||
const userTime = u.user_time || '---';
|
||||
const lastTime = u.created_at ? new Date(u.created_at.replace(/-/g, "/")) : new Date();
|
||||
|
||||
let lastMsgText = u.message || '';
|
||||
if (lastMsgText.startsWith('[PAYMENT_INFO]')) {
|
||||
lastMsgText = '[收款账号信息]';
|
||||
}
|
||||
|
||||
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) {
|
||||
selectedUser = userId;
|
||||
selectedIp = ip;
|
||||
@ -573,8 +614,8 @@ document.getElementById('save-remark-btn').addEventListener('click', async () =>
|
||||
|
||||
document.getElementById('user-search').addEventListener('input', refreshUsers);
|
||||
|
||||
setInterval(refreshUsers, 300);
|
||||
setInterval(fetchMessages, 300);
|
||||
setInterval(refreshUsers, 2000);
|
||||
setInterval(fetchMessages, 2000);
|
||||
refreshUsers();
|
||||
</script>
|
||||
|
||||
|
||||
95
api/chat.php
95
api/chat.php
@ -85,7 +85,7 @@ if ($action === 'get_messages') {
|
||||
|
||||
if ($action === 'send_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);
|
||||
$ip = getRealIP();
|
||||
@ -93,16 +93,24 @@ if ($action === 'send_message') {
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, 'user', $message, $ip]);
|
||||
$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')]]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'admin_send') {
|
||||
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
|
||||
|
||||
$message = $_POST['message'] ?? '';
|
||||
$user_id = (int)($_POST['user_id'] ?? 0);
|
||||
$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;
|
||||
$sender = 'admin';
|
||||
@ -126,41 +134,58 @@ if ($action === 'ping') {
|
||||
}
|
||||
|
||||
if ($action === 'admin_get_all') {
|
||||
$stmt = db()->query("
|
||||
SELECT
|
||||
v.user_id,
|
||||
v.ip_address,
|
||||
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,
|
||||
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 (
|
||||
header('Content-Type: application/json');
|
||||
if (!isset($_SESSION['admin_id'])) {
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
try {
|
||||
// Robust query to get all active chat sessions
|
||||
$stmt = db()->query("
|
||||
SELECT
|
||||
user_id,
|
||||
MAX(ip_address) as ip_address,
|
||||
MAX(last_activity) as last_activity,
|
||||
MAX(user_time) as user_time
|
||||
v.user_id,
|
||||
v.ip_address,
|
||||
CASE
|
||||
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 (
|
||||
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
|
||||
UNION
|
||||
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
|
||||
) t1
|
||||
GROUP BY user_id, (CASE WHEN user_id = 0 THEN ip_address ELSE '0' END)
|
||||
) v
|
||||
LEFT JOIN (
|
||||
SELECT m1.* FROM messages m1
|
||||
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 48 HOUR)
|
||||
ORDER BY created_at DESC
|
||||
");
|
||||
echo json_encode($stmt->fetchAll());
|
||||
SELECT
|
||||
user_id,
|
||||
MAX(ip_address) as ip_address,
|
||||
MAX(last_activity) as last_activity,
|
||||
MAX(user_time) as user_time,
|
||||
MAX(has_recharge) as has_recharge
|
||||
FROM (
|
||||
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
|
||||
UNION ALL
|
||||
SELECT COALESCE(user_id, 0) as user_id, ip_address, last_ping as last_activity, user_time, 0 as has_recharge FROM chat_visitors
|
||||
UNION ALL
|
||||
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
|
||||
) t1
|
||||
GROUP BY user_id, (CASE WHEN user_id = 0 THEN ip_address ELSE '0' END)
|
||||
) v
|
||||
LEFT JOIN (
|
||||
SELECT m1.* FROM messages m1
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
if (session_status() === PHP_SESSION_NONE) session_start();
|
||||
require_once __DIR__ . '/../db/config.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="row g-0">
|
||||
<!-- 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="d-flex align-items-center gap-3">
|
||||
<div class="position-relative">
|
||||
@ -218,7 +218,7 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
||||
</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">
|
||||
<i class="bi bi-shield-lock-fill text-success me-2"></i><?= __('welcome_support') ?>
|
||||
</div>
|
||||
@ -241,62 +241,49 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
||||
</div>
|
||||
|
||||
<!-- Right Side: Account Matching -->
|
||||
<div class="col-lg-6 p-5 d-flex flex-column justify-content-center info-side position-relative overflow-hidden">
|
||||
<!-- Vibrant Background Overlay -->
|
||||
<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 & 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 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="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="mb-4">
|
||||
<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') ?>
|
||||
</div>
|
||||
<h2 class="display-6 fw-bold text-white mb-3 text-shadow-heavy"><?= __('matching_account') ?>...</h2>
|
||||
<p class="text-white fs-5 fw-bold opacity-100 text-shadow-medium"><?= __('matching_desc') ?></p>
|
||||
<h3 class="fw-bold text-white mb-2 text-shadow-ultra"><?= __('matching_account') ?>...</h3>
|
||||
<p class="text-white small fw-bold opacity-90 text-shadow-heavy"><?= __('matching_desc') ?></p>
|
||||
</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="col-sm-6 mb-3 mb-sm-0">
|
||||
<div class="text-white small mb-1 fw-bold"><?= __('remaining_time') ?></div>
|
||||
<div class="display-5 fw-bold text-warning tracking-wider text-shadow-glow" id="modal-countdown">10:00</div>
|
||||
<div class="col-6">
|
||||
<div class="text-white small mb-1 fw-bold text-shadow-medium"><?= __('remaining_time') ?></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 class="col-sm-6 border-start border-white border-opacity-20 ps-sm-4">
|
||||
<div class="text-white small mb-1 fw-bold"><?= __('security_level') ?></div>
|
||||
<div class="col-6 border-start border-white border-opacity-20 ps-4">
|
||||
<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="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></div>
|
||||
<div class="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></div>
|
||||
<div class="bg-warning rounded-pill shadow-glow-sm" style="width: 25px; height: 6px;"></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-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></div>
|
||||
<div class="bg-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></div>
|
||||
<div class="bg-success rounded-pill shadow-glow-green" style="width: 15px; height: 4px;"></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 class="space-y-4">
|
||||
<h6 class="text-white fw-bold mb-4 d-flex align-items-center gap-2 text-shadow-medium">
|
||||
<i class="bi bi-shield-check text-warning fs-5"></i> <?= __('security_instructions') ?>
|
||||
<div class="p-3 rounded-4 bg-black bg-opacity-30 border border-white border-opacity-10">
|
||||
<h6 class="text-white fw-bold mb-3 d-flex align-items-center gap-2" style="font-size: 14px;">
|
||||
<i class="bi bi-shield-lock-fill text-warning"></i> <?= __('security_instructions') ?>
|
||||
</h6>
|
||||
<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 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_1') ?></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 class="text-white opacity-90 small lh-base" style="font-size: 13px !important;">
|
||||
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>系统正在为您分配专属收款账户,请耐心等待</span></div>
|
||||
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>匹配期间请勿刷新页面或重复提交订单</span></div>
|
||||
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>若超过倒计时仍未匹配成功,请及时联系在线客服</span></div>
|
||||
<div class="mb-2 d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>收到充值账户,完成转账后,请将转账凭证提交给在线客服</span></div>
|
||||
<div class="d-flex gap-2"><i class="bi bi-dot text-primary"></i> <span>客服将在核实资金后为您确认到账</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -309,20 +296,51 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
||||
|
||||
<style>
|
||||
.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%;
|
||||
animation: gradientFlow 10s ease infinite;
|
||||
animation: gradientFlow 6s ease-in-out infinite;
|
||||
}
|
||||
@keyframes gradientFlow {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 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-medium { text-shadow: 1px 1px 4px rgba(0,0,0,0.6); }
|
||||
.text-shadow-glow { text-shadow: 0 0 15px rgba(255,255,255,0.6), 0 0 5px rgba(255,255,255,0.4); }
|
||||
.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-ultra {
|
||||
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);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.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 {
|
||||
width: 8px;
|
||||
@ -356,6 +374,12 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
||||
backdrop-filter: blur(5px);
|
||||
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>
|
||||
|
||||
<script>
|
||||
@ -420,7 +444,7 @@ function openRechargeModal(initialMessage) {
|
||||
modal.show();
|
||||
|
||||
// Start countdown
|
||||
let seconds = 600;
|
||||
let seconds = 1800;
|
||||
const display = document.getElementById('modal-countdown');
|
||||
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
|
||||
rechargeCountdownInterval = setInterval(() => {
|
||||
@ -430,14 +454,30 @@ function openRechargeModal(initialMessage) {
|
||||
if (--seconds < 0) clearInterval(rechargeCountdownInterval);
|
||||
}, 1000);
|
||||
|
||||
// Send initial message
|
||||
sendModalMessage(initialMessage);
|
||||
// Send initial message and show in chat
|
||||
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
|
||||
initModalChat();
|
||||
}
|
||||
|
||||
let modalChatPolling = false;
|
||||
|
||||
function initModalChat() {
|
||||
if (modalChatPolling) return; // Prevent multiple polling loops
|
||||
modalChatPolling = true;
|
||||
|
||||
const modalChatForm = document.getElementById('modal-chat-form');
|
||||
const modalChatInput = document.getElementById('modal-chat-input');
|
||||
const modalChatUpload = document.getElementById('modal-chat-upload');
|
||||
@ -510,6 +550,9 @@ function initModalChat() {
|
||||
const modalPoll = async () => {
|
||||
if (!document.getElementById('rechargeModal').classList.contains('show')) return;
|
||||
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 data = await resp.json();
|
||||
if (Array.isArray(data)) {
|
||||
@ -558,7 +601,17 @@ function appendModalMessage(m) {
|
||||
}
|
||||
|
||||
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 = `
|
||||
<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;">
|
||||
${text}
|
||||
</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>
|
||||
`;
|
||||
@ -582,70 +635,62 @@ function updateMatchingSide(info) {
|
||||
|
||||
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 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="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">
|
||||
<i class="bi bi-check-circle-fill"></i> <?= __('account_matched') ?>
|
||||
<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 text-success"></i> <?= __('account_matched') ?>
|
||||
</div>
|
||||
<h2 class="display-6 fw-bold text-white mb-3 text-shadow-heavy"><?= __('account_matched') ?></h2>
|
||||
<p class="text-white fs-5 fw-bold opacity-100 text-shadow-medium"><?= __('account_matched_desc') ?></p>
|
||||
<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-90 text-shadow-heavy"><?= __('account_matched_desc') ?></p>
|
||||
</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="payment-item">
|
||||
<div class="text-white small opacity-80 mb-1 fw-bold"><?= __('bank_name') ?></div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="h5 mb-0 fw-bold text-white text-shadow-medium">${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>
|
||||
<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 gap-3">
|
||||
<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 flex-shrink-0" onclick="copyText('${info.bank}')"><?= __('copy_info') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="payment-item border-top border-white border-opacity-10 pt-3">
|
||||
<div class="text-white small opacity-80 mb-1 fw-bold"><?= __('payee_name') ?></div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="h5 mb-0 fw-bold text-white text-shadow-medium">${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>
|
||||
<div class="payment-item border-top border-white border-opacity-20 pt-3">
|
||||
<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 gap-3">
|
||||
<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 flex-shrink-0" onclick="copyText('${info.name}')"><?= __('copy_info') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="payment-item border-top border-white border-opacity-10 pt-3">
|
||||
<div class="text-white small opacity-80 mb-1 fw-bold"><?= __('account_number') ?></div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="h4 mb-0 fw-bold text-warning tracking-wider text-shadow-glow">${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>
|
||||
<div class="payment-item border-top border-white border-opacity-20 pt-3">
|
||||
<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 gap-3">
|
||||
<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 flex-shrink-0" onclick="copyText('${info.account}')"><?= __('copy_info') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
${info.note ? `
|
||||
<div class="payment-item border-top border-white border-opacity-10 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="d-flex justify-content-between align-items-center">
|
||||
<div class="fw-bold text-info text-shadow-glow-sm">${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>
|
||||
<div class="payment-item border-top border-white border-opacity-20 pt-3">
|
||||
<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 gap-3">
|
||||
<div class="fw-bold text-warning text-shadow-glow-sm" style="word-break: break-all;">${info.note}</div>
|
||||
<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 class="space-y-4">
|
||||
<h6 class="text-white fw-bold mb-4 d-flex align-items-center gap-2 text-shadow-medium">
|
||||
<i class="bi bi-info-circle text-white fs-5"></i> <?= __('transfer_steps_title') ?>
|
||||
<h6 class="text-white fw-bold mb-4 d-flex align-items-center gap-2 text-shadow-heavy">
|
||||
<i class="bi bi-info-circle-fill text-white fs-5"></i> <?= __('transfer_steps_title') ?>
|
||||
</h6>
|
||||
<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="fw-bold text-warning fs-5 text-shadow-glow-sm">01</div>
|
||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('step_1') ?></div>
|
||||
<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" style="min-width: 25px;">01</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">02</div>
|
||||
<div class="text-white small lh-base fw-bold text-shadow-medium"><?= __('step_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 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 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" style="min-width: 25px;">02</div>
|
||||
<div class="text-white small lh-base fw-bold text-shadow-medium">客服将在核实资金后为您确认到账</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -692,6 +737,12 @@ function confirmFiatOrder() {
|
||||
|
||||
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();
|
||||
formData.append('action', 'recharge');
|
||||
formData.append('amount', estUsdt);
|
||||
@ -706,6 +757,8 @@ function confirmFiatOrder() {
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
if (data.success) {
|
||||
let message = `<?= __('recharge_msg_fiat') ?>`;
|
||||
const preciseRes = (amount / rate).toFixed(4);
|
||||
@ -718,18 +771,27 @@ function confirmFiatOrder() {
|
||||
} else {
|
||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
});
|
||||
}
|
||||
|
||||
function confirmCryptoOrder() {
|
||||
const amountInput = document.getElementById('cryptoAmount');
|
||||
const amount = parseFloat(amountInput.value);
|
||||
const btn = event.target;
|
||||
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
notify('warning', '<?= __("enter_amount") ?>');
|
||||
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();
|
||||
formData.append('action', 'recharge');
|
||||
formData.append('amount', amount);
|
||||
@ -741,17 +803,27 @@ function confirmCryptoOrder() {
|
||||
body: formData
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
.then(async data => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
if (data.success) {
|
||||
let message = `<?= __('recharge_msg_crypto') ?>`;
|
||||
message = message.replace('%uid%', userId)
|
||||
.replace('%amount%', amount)
|
||||
.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 = '';
|
||||
} else {
|
||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user