Autosave: 20260220-153724

This commit is contained in:
Flatlogic Bot 2026-02-20 15:37:24 +00:00
parent 10e38bc178
commit 0b9cff662e
5 changed files with 582 additions and 21 deletions

View File

@ -175,12 +175,49 @@ ob_start();
<button type="button" class="btn btn-outline-secondary btn-sm rounded-circle" id="plus-btn" style="width: 32px; height: 32px; flex-shrink: 0;">
<i class="bi bi-plus-lg"></i>
</button>
<button type="button" class="btn btn-outline-primary btn-sm rounded-circle" id="payment-btn" style="width: 32px; height: 32px; flex-shrink: 0;" title="发送收款账号">
<i class="bi bi-bank"></i>
</button>
<input type="text" id="msg-input" class="form-control" placeholder="输入回复内容..." autocomplete="off">
<button type="submit" class="btn btn-primary btn-sm px-3">发送</button>
</form>
</div>
</div>
<!-- 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>
<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>
<div class="mb-3">
<label class="form-label small">收款人姓名</label>
<input type="text" id="pay-name" class="form-control" placeholder="收款人姓名">
</div>
<div class="mb-3">
<label class="form-label small">收款账号</label>
<input type="text" id="pay-account" class="form-control" placeholder="银行卡号或账号">
</div>
<div class="mb-3">
<label class="form-label small">转账说明/备注</label>
<textarea id="pay-note" class="form-control" rows="2" placeholder="告知用户转账时需要备注的内容"></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>
</div>
</div>
</div>
<!-- Remark Area -->
<div class="remark-area" id="remark-area" style="display: none;">
<h6 class="fw-bold mb-3 mt-1"><i class="bi bi-pencil-square me-1"></i> 用户备注</h6>
@ -225,6 +262,11 @@ async function refreshUsers() {
return;
}
let lastMsgText = u.message;
if (lastMsgText.startsWith('[PAYMENT_INFO]')) {
lastMsgText = '[收款账号信息]';
}
const isActive = (selectedIp === ip && selectedUser == u.user_id);
if (isActive) {
document.getElementById('info-user-time').innerText = userTime;
@ -236,7 +278,7 @@ async function refreshUsers() {
<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;">${u.message}</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>
@ -351,11 +393,23 @@ function appendMessageHTML(m) {
const timeStr = msgDate.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit', second: '2-digit'});
const recallHtml = m.sender === 'admin' ? `<span class="recall-btn text-white-50 ms-2" style="cursor:pointer; text-decoration:underline;" onclick="recallMessage(${m.id})">撤回</span>` : '';
const isImage = typeof m.message === 'string' && m.message.indexOf('<img') !== -1;
const isPaymentInfo = typeof m.message === 'string' && m.message.startsWith('[PAYMENT_INFO]');
const div = document.createElement('div');
div.className = `msg ${m.sender === 'admin' ? 'msg-admin' : 'msg-user'}`;
div.setAttribute('data-id', m.id);
let displayMsg = m.message;
if (isPaymentInfo) {
try {
const info = JSON.parse(m.message.replace('[PAYMENT_INFO]', ''));
displayMsg = `<div class="p-2 border border-white border-opacity-20 rounded bg-white bg-opacity-10 small">
<div class="fw-bold"><i class="bi bi-bank me-1"></i>已发送收款账号</div>
<div class="mt-1 opacity-75">${info.bank} - ${info.name}</div>
</div>`;
} catch(e) { displayMsg = '[支付信息错误]'; }
}
if (isImage) {
div.style.padding = '5px';
div.style.background = m.sender === 'admin' ? '#007bff' : '#f0f0f0';
@ -363,7 +417,7 @@ function appendMessageHTML(m) {
}
div.innerHTML = `
<div class="msg-content">${m.message}</div>
<div class="msg-content">${displayMsg}</div>
<div class="msg-time" style="${isImage ? 'position: absolute; bottom: 8px; right: 10px; background: rgba(0,0,0,0.4); color: #fff; padding: 0 6px; border-radius: 4px; font-size: 9px; line-height: 1.5;' : ''}">${timeStr} ${recallHtml}</div>
`;
@ -374,6 +428,45 @@ document.getElementById('plus-btn').addEventListener('click', () => {
document.getElementById('image-input').click();
});
const paymentModal = new bootstrap.Modal(document.getElementById('paymentModal'));
document.getElementById('payment-btn').addEventListener('click', () => {
paymentModal.show();
});
async function sendPaymentInfo() {
const bank = document.getElementById('pay-bank').value.trim();
const name = document.getElementById('pay-name').value.trim();
const account = document.getElementById('pay-account').value.trim();
const note = document.getElementById('pay-note').value.trim();
if (!bank || !name || !account) {
alert('请完整填写收款信息');
return;
}
const info = { bank, name, account, note };
const msg = `[PAYMENT_INFO]${JSON.stringify(info)}`;
const fd = new URLSearchParams();
fd.append('message', msg);
fd.append('user_id', selectedUser);
fd.append('ip_address', selectedIp);
try {
const r = await fetch('/api/chat.php?action=admin_send', { method: 'POST', body: fd });
const res = await r.json();
if (res.success) {
paymentModal.hide();
// Clear inputs
document.getElementById('pay-bank').value = '';
document.getElementById('pay-name').value = '';
document.getElementById('pay-account').value = '';
document.getElementById('pay-note').value = '';
fetchMessages();
}
} catch(err) {}
}
document.getElementById('image-input').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1 +1 @@
{"USD":1,"AED":3.67,"AFN":62.82,"ALL":81.38,"AMD":377.18,"ANG":1.79,"AOA":921.13,"ARS":1452.25,"AUD":1.42,"AWG":1.79,"AZN":1.7,"BAM":1.66,"BBD":2,"BDT":122.19,"BGN":1.6,"BHD":0.376,"BIF":2967.31,"BMD":1,"BND":1.27,"BOB":6.92,"BRL":5.22,"BSD":1,"BTN":90.77,"BWP":13.29,"BYN":2.84,"BZD":2,"CAD":1.37,"CDF":2281.91,"CHF":0.772,"CLF":0.0219,"CLP":866.65,"CNH":6.9,"CNY":6.91,"COP":3648.29,"CRC":481.3,"CUP":24,"CVE":93.37,"CZK":20.52,"DJF":177.72,"DKK":6.31,"DOP":61.55,"DZD":129.72,"EGP":46.95,"ERN":15,"ETB":154.44,"EUR":0.847,"FJD":2.2,"FKP":0.739,"FOK":6.31,"GBP":0.739,"GEL":2.68,"GGP":0.739,"GHS":11.02,"GIP":0.739,"GMD":74.1,"GNF":8762.47,"GTQ":7.67,"GYD":209.26,"HKD":7.82,"HNL":26.44,"HRK":6.38,"HTG":131.36,"HUF":320.31,"IDR":16895.02,"ILS":3.11,"IMP":0.739,"INR":90.77,"IQD":1310.4,"IRR":1281616.32,"ISK":122.37,"JEP":0.739,"JMD":155.71,"JOD":0.709,"JPY":154.42,"KES":129,"KGS":87.48,"KHR":4017.09,"KID":1.42,"KMF":416.61,"KRW":1447.31,"KWD":0.306,"KYD":0.833,"KZT":489.38,"LAK":21513.29,"LBP":89500,"LKR":309.13,"LRD":185.55,"LSL":16.06,"LYD":6.3,"MAD":9.14,"MDL":17.01,"MGA":4331.69,"MKD":52.14,"MMK":2100.51,"MNT":3550.21,"MOP":8.05,"MRU":39.97,"MUR":45.96,"MVR":15.45,"MWK":1739.99,"MXN":17.18,"MYR":3.9,"MZN":63.72,"NAD":16.06,"NGN":1338.38,"NIO":36.78,"NOK":9.52,"NPR":145.24,"NZD":1.67,"OMR":0.384,"PAB":1,"PEN":3.35,"PGK":4.33,"PHP":57.94,"PKR":279.71,"PLN":3.57,"PYG":6522.64,"QAR":3.64,"RON":4.3,"RSD":99.15,"RUB":76.67,"RWF":1460.55,"SAR":3.75,"SBD":8.02,"SCR":14.01,"SDG":543.58,"SEK":9.02,"SGD":1.27,"SHP":0.739,"SLE":24.46,"SLL":24455.37,"SOS":570.65,"SRD":37.71,"SSP":4568.21,"STN":20.75,"SYP":113.49,"SZL":16.06,"THB":31.26,"TJS":9.4,"TMT":3.5,"TND":2.86,"TOP":2.36,"TRY":43.77,"TTD":6.75,"TVD":1.42,"TWD":31.47,"TZS":2574.76,"UAH":43.3,"UGX":3532.39,"UYU":38.82,"UZS":12156.76,"VES":398.75,"VND":25919.9,"VUV":118.59,"WST":2.68,"XAF":555.48,"XCD":2.7,"XCG":1.79,"XDR":0.726,"XOF":555.48,"XPF":101.05,"YER":238.28,"ZAR":16.06,"ZMW":18.61,"ZWG":25.6,"ZWL":25.6}
{"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}

View File

@ -528,6 +528,29 @@ $translations = [
'swap_success_desc' => '您的兑换已成功完成!',
'unknown_error' => '发生未知错误',
'rate_fetch_failed' => '获取汇率失败(服务商问题)',
'matching_account' => '正在匹配充值账户',
'matching_desc' => '系统正在为您匹配最合适的充值账户,请稍后...',
'security_instructions' => '安全说明',
'security_tip_1' => '请在倒计时结束前完成充值',
'security_tip_2' => '转账时请务必备注您的用户ID',
'security_tip_3' => '如有任何疑问请及时联系客服',
'safe_payment' => '安全支付',
'instant_confirmation' => '即时确认',
'remaining_time' => '剩余时间',
'account_matched' => '已成功匹配收款账户',
'account_matched_desc' => '请按照以下信息完成转账汇款。',
'bank_name' => '银行/支付方式',
'payee_name' => '收款人姓名',
'account_number' => '收款账号',
'transfer_note' => '转账备注',
'copy_info' => '复制',
'transfer_steps_title' => '转账步骤说明',
'step_1' => '打开您的银行 APP 或支付应用',
'step_2' => '按照匹配金额准确转账至上方账户',
'step_3' => '务必填写转账备注(如有)并保留凭证',
'step_4' => '转账完成后在聊天框告知客服或等待系统同步',
'recharge_success_title' => '申请已提交',
'recharge_success_text' => '您的充值申请已收到,请等待审核通过。',
],
'en' => [
'home' => 'Home',
@ -1035,6 +1058,29 @@ $translations = [
'swap_success_desc' => 'Your exchange has been completed successfully!',
'unknown_error' => 'Unknown error occurred',
'rate_fetch_failed' => 'Failed to fetch exchange rate (Provider issue)',
'matching_account' => 'Matching Account',
'matching_desc' => 'System is matching the most suitable recharge account for you, please wait...',
'security_instructions' => 'Security Instructions',
'security_tip_1' => 'Please complete the recharge before the countdown ends',
'security_tip_2' => 'Be sure to note your User ID when transferring',
'security_tip_3' => 'Contact customer service if you have any questions',
'safe_payment' => 'Safe Payment',
'instant_confirmation' => 'Instant Confirmation',
'remaining_time' => 'Remaining Time',
'account_matched' => 'Account Matched Successfully',
'account_matched_desc' => 'Please complete the transfer according to the following info.',
'bank_name' => 'Bank Name',
'payee_name' => 'Payee Name',
'account_number' => 'Account Number',
'transfer_note' => 'Transfer Note',
'copy_info' => 'Copy',
'transfer_steps_title' => 'Transfer Steps',
'step_1' => 'Open your banking app or wallet',
'step_2' => 'Transfer exact amount to the matched account',
'step_3' => 'Keep your transfer receipt for verification',
'step_4' => 'Click "I have paid" in chat or wait for system sync',
'recharge_success_title' => 'Request Submitted',
'recharge_success_text' => 'Your recharge request has been received. Please wait for approval.',
],
];

View File

@ -190,6 +190,174 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
}
</style>
<!-- Recharge Confirmation Modal -->
<div class="modal fade" id="rechargeModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content border-0 shadow-lg overflow-hidden" style="border-radius: 24px; background: #161a1e;">
<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="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">
<div class="bg-primary rounded-circle d-flex align-items-center justify-content-center shadow-lg" style="width: 48px; height: 48px; border: 2px solid rgba(255,255,255,0.1);">
<i class="bi bi-headset text-white fs-4"></i>
</div>
<div class="position-absolute bottom-0 end-0 bg-success border border-2 border-dark rounded-circle" style="width: 14px; height: 14px;"></div>
</div>
<div class="flex-grow-1">
<h6 class="mb-0 fw-bold text-white fs-5" style="letter-spacing: 0.5px;"><?= __('online_support') ?></h6>
<div class="d-flex align-items-center gap-2 mt-1">
<span class="badge bg-primary bg-opacity-20 text-primary border border-primary border-opacity-30 small px-2 py-1" style="font-size: 10px;">ONLINE</span>
<span class="text-white opacity-50 small">IP:</span>
<span class="text-info small fw-bold fw-mono" style="text-shadow: 0 0 5px rgba(0,210,255,0.3);"><?= getRealIP() ?></span>
</div>
</div>
<button type="button" class="btn-close btn-close-white ms-auto shadow-none" data-bs-dismiss="modal"></button>
</div>
</div>
<div id="modal-chat-messages" class="flex-grow-1 p-4 overflow-y-auto" style="scrollbar-width: thin; background: #161a1e;">
<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>
</div>
<div class="p-4 bg-black bg-opacity-40 border-top border-secondary border-opacity-20 shadow-lg">
<form id="modal-chat-form" class="d-flex gap-2 align-items-center">
<input type="file" id="modal-chat-file" class="d-none" accept="image/*">
<button type="button" id="modal-chat-upload" class="btn btn-dark border-secondary border-opacity-50 rounded-circle d-flex align-items-center justify-content-center hover-scale transition-all" style="width: 42px; height: 42px; min-width: 42px; background: #2a2f35;">
<i class="bi bi-plus-lg text-white fs-5"></i>
</button>
<div class="flex-grow-1 position-relative">
<input type="text" id="modal-chat-input" class="form-control bg-dark border-0 text-white py-2 ps-3 rounded-pill shadow-none" placeholder="<?= __('type_message') ?>" style="height: 42px; background: #2a2f35 !important; border: 1px solid rgba(255,255,255,0.05) !important;">
</div>
<button type="submit" class="btn btn-primary rounded-circle d-flex align-items-center justify-content-center shadow-lg hover-scale transition-all" style="width: 42px; height: 42px; min-width: 42px;">
<i class="bi bi-send-fill text-white"></i>
</button>
</form>
</div>
</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="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="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">
<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>
</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="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>
<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="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>
<div class="text-warning small fw-bold text-shadow-glow-sm"><?= __('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') ?>
</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>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.vibrancy-bg {
background: linear-gradient(135deg, #ff9a00 0%, #ff5200 50%, #ff0055 100%);
background-size: 400% 400%;
animation: gradientFlow 10s ease 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); }
.pulse-dot-white {
width: 8px;
height: 8px;
background-color: #fff;
border-radius: 50%;
display: inline-block;
animation: dot-pulse-white 1.5s infinite;
box-shadow: 0 0 8px rgba(255,255,255,0.8);
}
@keyframes dot-pulse-white {
0% { transform: scale(0.95); opacity: 0.6; }
50% { transform: scale(1.3); opacity: 1; }
100% { transform: scale(0.95); opacity: 0.6; }
}
#modal-chat-messages::-webkit-scrollbar {
width: 6px;
}
#modal-chat-messages::-webkit-scrollbar-track {
background: transparent;
}
#modal-chat-messages::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.1);
border-radius: 10px;
}
#modal-chat-messages::-webkit-scrollbar-thumb:hover {
background: rgba(255,255,255,0.2);
}
.modal-msg .msg-bubble {
backdrop-filter: blur(5px);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
</style>
<script>
let currentNetwork = 'TRC20';
let currentAddress = '<?= $trc20_addr ?>';
@ -244,6 +412,271 @@ function selectNetwork(net, addr) {
}
const userId = '<?= $user['uid'] ?? $user['id'] ?>';
let rechargeCountdownInterval;
let modalChatLastIds = new Set();
function openRechargeModal(initialMessage) {
const modal = new bootstrap.Modal(document.getElementById('rechargeModal'));
modal.show();
// Start countdown
let seconds = 600;
const display = document.getElementById('modal-countdown');
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
rechargeCountdownInterval = setInterval(() => {
let mins = Math.floor(seconds / 60);
let secs = seconds % 60;
display.innerText = `${mins}:${secs < 10 ? '0' : ''}${secs}`;
if (--seconds < 0) clearInterval(rechargeCountdownInterval);
}, 1000);
// Send initial message
sendModalMessage(initialMessage);
// Start polling for modal
initModalChat();
}
function initModalChat() {
const modalChatForm = document.getElementById('modal-chat-form');
const modalChatInput = document.getElementById('modal-chat-input');
const modalChatUpload = document.getElementById('modal-chat-upload');
const modalChatFile = document.getElementById('modal-chat-file');
modalChatUpload.onclick = () => modalChatFile.click();
modalChatFile.onchange = async () => {
if (!modalChatFile.files[0]) return;
const file = modalChatFile.files[0];
const tempId = 'modal_temp_img_' + Date.now();
const localUrl = URL.createObjectURL(file);
appendModalMessage({
id: tempId,
sender: 'user',
message: `<img src="${localUrl}" class="img-fluid rounded" style="max-width: 100%; max-height: 250px; opacity: 0.6;">`,
created_at: new Date().toISOString()
});
scrollModalToBottom();
const formData = new FormData();
formData.append('file', file);
formData.append('action', 'upload_image');
try {
const resp = await fetch('/api/chat.php', { method: 'POST', body: formData });
const data = await resp.json();
document.querySelector(`[data-modal-id="${tempId}"]`)?.remove();
if (data.success) {
appendModalMessage(data.message);
scrollModalToBottom();
}
} catch (err) { console.error(err); }
modalChatFile.value = '';
setTimeout(() => URL.revokeObjectURL(localUrl), 5000);
};
modalChatForm.onsubmit = async (e) => {
e.preventDefault();
const msg = modalChatInput.value.trim();
if (!msg) return;
modalChatInput.value = '';
const tempId = 'modal_temp_msg_' + Date.now();
appendModalMessage({
id: tempId,
sender: 'user',
message: msg,
created_at: new Date().toISOString()
});
scrollModalToBottom();
try {
const resp = await fetch('/api/chat.php?action=send_message', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `message=${encodeURIComponent(msg)}`
});
const data = await resp.json();
document.querySelector(`[data-modal-id="${tempId}"]`)?.remove();
if (data.success) {
appendModalMessage(data.message);
scrollModalToBottom();
}
} catch (err) { console.error(err); }
};
// Modal Polling
const modalPoll = async () => {
if (!document.getElementById('rechargeModal').classList.contains('show')) return;
try {
const resp = await fetch('/api/chat.php?action=get_messages');
const data = await resp.json();
if (Array.isArray(data)) {
data.forEach(m => {
if (!modalChatLastIds.has(m.id)) {
appendModalMessage(m);
modalChatLastIds.add(m.id);
scrollModalToBottom();
}
});
}
} catch (err) {}
setTimeout(modalPoll, 1000);
};
modalPoll();
}
async function sendModalMessage(msg) {
try {
await fetch('/api/chat.php?action=send_message', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `message=${encodeURIComponent(msg)}`
});
} catch (err) {}
}
function appendModalMessage(m) {
const container = document.getElementById('modal-chat-messages');
if (!container || document.querySelector(`[data-modal-id="${m.id}"]`)) return;
const sender = m.sender;
const text = m.message;
// Check for payment info
if (sender === 'admin' && text.startsWith('[PAYMENT_INFO]')) {
try {
const info = JSON.parse(text.replace('[PAYMENT_INFO]', ''));
updateMatchingSide(info);
// Don't show the raw JSON in chat
modalChatLastIds.add(m.id);
return;
} catch (e) {
console.error('Failed to parse payment info', e);
}
}
const isImage = text.indexOf('<img') !== -1;
const time = new Date(m.created_at.replace(/-/g, "/")).toLocaleTimeString('zh-CN', {hour:'2-digit', minute:'2-digit'});
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="msg-bubble p-2 px-3 rounded-4 small ${sender === 'user' ? 'bg-primary text-white' : 'bg-dark text-white border border-secondary border-opacity-30'}" style="max-width: 85%; position: relative; ${isImage ? 'padding: 5px !important; line-height: 0;' : 'padding-bottom: 22px !important;'}">
<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>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
modalChatLastIds.add(m.id);
}
function updateMatchingSide(info) {
const side = document.querySelector('.info-side');
if (!side) return;
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
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="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>
<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>
</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="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>
</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>
</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>
</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>
</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>
<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>
<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>
</div>
</div>
</div>
`;
}
function copyText(text) {
const el = document.createElement('textarea');
el.value = text;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
Swal.fire({
icon: 'success',
title: '<?= __("copy_success") ?>',
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 2000,
background: '#1e2329',
color: '#fff'
});
}
function scrollModalToBottom() {
const container = document.getElementById('modal-chat-messages');
if (container) container.scrollTop = container.scrollHeight;
}
function confirmFiatOrder() {
const amountInput = document.getElementById('fiatAmount');
@ -281,7 +714,7 @@ function confirmFiatOrder() {
.replace('%currency%', currency)
.replace('%rate%', rate)
.replace('%res%', preciseRes);
sendToCS(message);
openRechargeModal(message);
} else {
notify('error', data.error || '<?= __('request_failed') ?>');
}
@ -312,9 +745,10 @@ function confirmCryptoOrder() {
if (data.success) {
let message = `<?= __('recharge_msg_crypto') ?>`;
message = message.replace('%uid%', userId)
.replace('%network%', currentNetwork)
.replace('%amount%', amount);
sendToCS(message);
.replace('%amount%', amount)
.replace('%network%', currentNetwork);
openRechargeModal(message);
amountInput.value = '';
} else {
notify('error', data.error || '<?= __('request_failed') ?>');
}
@ -322,20 +756,8 @@ function confirmCryptoOrder() {
}
function sendToCS(message) {
const csBox = document.getElementById('cs-box');
if (csBox && csBox.classList.contains('d-none')) {
const toggle = document.getElementById('cs-toggle');
if (toggle) toggle.click();
}
const csInput = document.getElementById('cs-input');
if (csInput) {
csInput.value = message;
document.getElementById('cs-form').dispatchEvent(new Event('submit'));
notify('success', '<?= __("request_sent") ?>');
} else {
notify('error', '<?= __("cs_connect_fail") ?>');
}
// Legacy support or fallback
openRechargeModal(message);
}
function copyAddress() {