Autosave: 20260220-153724
This commit is contained in:
parent
10e38bc178
commit
0b9cff662e
@ -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;
|
||||
|
||||
BIN
assets/pasted-20260220-151143-1fc00a8c.png
Normal file
BIN
assets/pasted-20260220-151143-1fc00a8c.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@ -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}
|
||||
@ -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.',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
458
recharge.php
458
recharge.php
@ -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() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user