Autosave: 20260322-133028
@ -132,6 +132,14 @@ try {
|
||||
if ($recharge['status'] === 'completed') { echo json_encode(['code' => 0, 'status' => 'completed']); break; }
|
||||
echo json_encode(['code' => 0, 'status' => 'pending']);
|
||||
break;
|
||||
case "get_active_orders":
|
||||
$stmt = $pdo->prepare("SELECT * FROM sms_orders WHERE user_id = ? AND status != "canceled" ORDER BY created_at DESC");
|
||||
$stmt->execute([$_SESSION["user_id"]]);
|
||||
echo json_encode(["code" => 0, "data" => $stmt->fetchAll(PDO::FETCH_ASSOC)], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
$stmt->execute([$_SESSION["user_id"]]);
|
||||
echo json_encode(["code" => 0, "data" => $stmt->fetchAll(PDO::FETCH_ASSOC)], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
|
||||
case "upload_image":
|
||||
$file = $_FILES["image"] ?? null;
|
||||
|
||||
BIN
assets/pasted-20260322-124316-cd478cbe.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
assets/pasted-20260322-124734-96de97a5.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
assets/pasted-20260322-125217-e26b4cc2.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/pasted-20260322-130219-ba2ab35b.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/pasted-20260322-130624-689b1e4b.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/pasted-20260322-131318-4220415a.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/pasted-20260322-131949-2a620588.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/pasted-20260322-132511-b5d1f2dd.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
270
dashboard.php
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
date_default_timezone_set("Asia/Shanghai");
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
@ -237,13 +238,11 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* 提示框样式 */
|
||||
.toast-container { position: fixed; top: 20px; right: 20px; z-index: 2000; }
|
||||
.custom-toast { background: white; border-radius: 12px; box-shadow: var(--shadow-lg); padding: 16px 24px; border: 1px solid var(--border-color); margin-bottom: 10px; display: flex; align-items: center; gap: 12px; }
|
||||
.custom-toast.error { border-left: 4px solid #ef4444; }
|
||||
.custom-toast.success { border-left: 4px solid #22c55e; }
|
||||
|
||||
/* Modal Custom Style */
|
||||
.modal-custom {
|
||||
position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 3000;
|
||||
display: none; align-items: center; justify-content: center;
|
||||
@ -254,6 +253,14 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
box-shadow: var(--shadow-lg); text-align: center;
|
||||
}
|
||||
|
||||
.highlight-area {
|
||||
background: #f0f7ff;
|
||||
border: 2px dashed #3b82f6;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.main-content { margin-left: 0; padding: 1.5rem; }
|
||||
.search-grid { grid-template-columns: 1fr; gap: 1rem; }
|
||||
@ -267,7 +274,6 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
|
||||
<div class="toast-container" id="toastContainer"></div>
|
||||
|
||||
<!-- Custom Modal -->
|
||||
<div class="modal-custom" id="confirmModal">
|
||||
<div class="modal-content-custom">
|
||||
<h5 class="fw-bold mb-3" id="confirmTitle">确认操作</h5>
|
||||
@ -303,31 +309,45 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active Tasks -->
|
||||
<div class="active-tasks-area" id="activeTasksSection" style="display: none;">
|
||||
<div class="active-tasks-header">
|
||||
<span class="fw-bold text-primary"><i class="fas fa-satellite-dish me-2"></i> 活跃任务</span>
|
||||
<button class="btn btn-link btn-sm text-decoration-none fw-bold" onclick="loadActiveOrders()" style="color: #64748b;">
|
||||
<i class="fas fa-sync-alt me-1"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="text-muted small">
|
||||
<th class="ps-4">项目/地区</th>
|
||||
<th>号码</th>
|
||||
<th>短信内容</th>
|
||||
<th>剩余时间</th>
|
||||
<th class="text-end pe-4">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="activeTasksBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-card">
|
||||
<!-- 最新获取号码 -->
|
||||
<div class="highlight-area" id="latestOrderArea" style="display: none;">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="bg-success text-white rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 24px; height: 24px;">
|
||||
<i class="fas fa-check fa-xs"></i>
|
||||
</div>
|
||||
<h6 class="fw-bold mb-0 text-success">最新获取号码</h6>
|
||||
</div>
|
||||
<div id="latestOrderContent" class="row align-items-center text-center">
|
||||
<!-- Data populated here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active Tasks -->
|
||||
<div class="active-tasks-area" id="activeTasksSection" style="display: none;">
|
||||
<div class="active-tasks-header">
|
||||
<span class="fw-bold text-primary"><i class="fas fa-satellite-dish me-2"></i> 活跃任务</span>
|
||||
<button class="btn btn-link btn-sm text-decoration-none fw-bold" onclick="loadActiveOrders()" style="color: #64748b;">
|
||||
<i class="fas fa-sync-alt me-1"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead>
|
||||
<tr class="text-muted small">
|
||||
<th class="ps-4">项目/地区</th>
|
||||
<th>号码</th>
|
||||
<th>短信内容</th>
|
||||
<th>状态</th>
|
||||
<th>剩余时间</th>
|
||||
<th class="text-end pe-4">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="activeTasksBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-grid">
|
||||
<div class="custom-dropdown" id="countryContainer">
|
||||
<label class="form-label small fw-bold text-muted mb-2 px-1">第1步:选择国家/地区</label>
|
||||
@ -378,7 +398,6 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Success Modal -->
|
||||
<div class="modal fade" id="smsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 rounded-5 overflow-hidden">
|
||||
@ -405,14 +424,13 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
let currentCountry = null;
|
||||
let currentService = null;
|
||||
let activePolls = {};
|
||||
let activeTimers = {};
|
||||
let searchTimeout = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadCountries();
|
||||
renderServices(popularServices);
|
||||
loadActiveOrders();
|
||||
setInterval(loadActiveOrders, 30000);
|
||||
setInterval(loadActiveOrders, 10000);
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.custom-dropdown')) hideAllDropdowns();
|
||||
@ -445,14 +463,8 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
if (data.code === 0) {
|
||||
allCountries = Array.isArray(data.data) ? data.data : [];
|
||||
renderCountries();
|
||||
} else if (data.code === 401) {
|
||||
window.location.href = 'index.php';
|
||||
} else {
|
||||
listContainer.innerHTML = `<div class="p-4 text-center text-danger small">加载失败: ${data.msg || '未知API错误'}</div>`;
|
||||
}
|
||||
} catch (e) {
|
||||
listContainer.innerHTML = '<div class="p-4 text-center text-danger small">网络连接超时,请刷新页面</div>';
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function toggleDropdown(id, event) {
|
||||
@ -476,25 +488,15 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!Array.isArray(allCountries) || allCountries.length === 0) {
|
||||
container.innerHTML = '<div class="p-3 text-center text-muted small">暂无可用国家数据</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = filter ? allCountries.filter(c =>
|
||||
(c.name_zh && c.name_zh.includes(filter)) ||
|
||||
(c.name_en && c.name_en.toLowerCase().includes(filter.toLowerCase()))
|
||||
) : allCountries;
|
||||
|
||||
if (filtered.length === 0) {
|
||||
container.innerHTML = '<div class="p-3 text-center text-muted small">未找到匹配的国家</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
filtered.slice(0, 100).forEach(c => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'list-item';
|
||||
div.innerHTML = `<div><span class="fw-bold">${c.name_zh || '未知'}</span><span class="text-muted ms-2 small">${c.name_en || ''}</span></div><i class="fas fa-chevron-right small opacity-25"></i>`;
|
||||
div.innerHTML = `<div><span class="fw-bold">${c.name_zh || '未知'}</span></div><i class="fas fa-chevron-right small opacity-25"></i>`;
|
||||
div.onclick = (e) => { e.stopPropagation(); selectCountry(c); };
|
||||
container.appendChild(div);
|
||||
});
|
||||
@ -504,16 +506,10 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
const container = document.getElementById('servicesList');
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!Array.isArray(services) || services.length === 0) {
|
||||
container.innerHTML = '<div class="p-3 text-center text-muted small">暂无搜索结果</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
services.forEach(s => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'list-item';
|
||||
div.innerHTML = `<span class="fw-bold">${s.name}</span><i class="fas fa-star small text-warning opacity-75"></i>`;
|
||||
div.innerHTML = `<span class="fw-bold">${s.name}</span>`;
|
||||
div.onclick = (e) => { e.stopPropagation(); selectService(s); };
|
||||
container.appendChild(div);
|
||||
});
|
||||
@ -523,7 +519,6 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
|
||||
function handleServiceInput() {
|
||||
const q = document.getElementById('serviceSearch').value;
|
||||
const listContainer = document.getElementById('servicesList');
|
||||
if (searchTimeout) clearTimeout(searchTimeout);
|
||||
if (!q) { renderServices(popularServices); return; }
|
||||
|
||||
@ -534,20 +529,12 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
if (data.code === 0) {
|
||||
const unique = [];
|
||||
const map = new Map();
|
||||
const services = Array.isArray(data.data) ? data.data : [];
|
||||
services.forEach(i => {
|
||||
if(i.service_name && !map.has(i.service_name)){
|
||||
map.set(i.service_name, true);
|
||||
unique.push({name: i.service_name});
|
||||
}
|
||||
(data.data || []).forEach(i => {
|
||||
if(i.service_name && !map.has(i.service_name)){ map.set(i.service_name, true); unique.push({name: i.service_name}); }
|
||||
});
|
||||
renderServices(unique);
|
||||
} else if (data.code === 401) {
|
||||
window.location.href = 'index.php';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Search error", e);
|
||||
}
|
||||
} catch (e) {}
|
||||
}, 400);
|
||||
}
|
||||
|
||||
@ -555,8 +542,7 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
currentCountry = c;
|
||||
const l = document.getElementById('countryLabel');
|
||||
l.textContent = c.name_zh;
|
||||
l.classList.remove('placeholder');
|
||||
l.classList.add('val');
|
||||
l.classList.remove('placeholder'); l.classList.add('val');
|
||||
hideAllDropdowns();
|
||||
loadQuotation();
|
||||
}
|
||||
@ -565,15 +551,14 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
currentService = s;
|
||||
const l = document.getElementById('serviceLabel');
|
||||
l.textContent = s.name;
|
||||
l.classList.remove('placeholder');
|
||||
l.classList.add('val');
|
||||
l.classList.remove('placeholder'); l.classList.add('val');
|
||||
hideAllDropdowns();
|
||||
loadQuotation();
|
||||
}
|
||||
|
||||
async function loadQuotation() {
|
||||
const body = document.getElementById('quotationBody');
|
||||
body.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-primary" style="width: 2rem; height: 2rem;"></div><div class="mt-2 text-muted small">正在调取实时行情...</div></div>';
|
||||
body.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-primary"></div></div>';
|
||||
|
||||
const cP = currentCountry ? encodeURIComponent(currentCountry.name_zh) : '';
|
||||
const sP = currentService ? encodeURIComponent(currentService.name) : '';
|
||||
@ -583,66 +568,31 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
body.innerHTML = '';
|
||||
const services = Array.isArray(data.data) ? data.data : [];
|
||||
if (!services.length) {
|
||||
body.innerHTML = '<div class="p-5 text-center text-muted"><i class="fas fa-exclamation-circle fa-2x mb-3 opacity-25"></i><div>该地区暂无此服务,请尝试其他国家或项目</div></div>';
|
||||
return;
|
||||
}
|
||||
services.forEach(s => {
|
||||
(data.data || []).forEach(s => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'quotation-item';
|
||||
const isPop = popularServices.some(ps => ps.name === s.service_name);
|
||||
item.innerHTML = `
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-center gap-2 mb-1">
|
||||
<span class="fw-bold fs-5">${s.service_name}</span>
|
||||
${isPop ? '<span class="badge bg-primary bg-opacity-10 text-primary small" style="font-size: 10px; padding: 4px 8px;">热门</span>' : ''}
|
||||
</div>
|
||||
<div class="small text-muted"><i class="fas fa-globe-asia me-1 opacity-50"></i> ${s.country_name_zh || (currentCountry ? currentCountry.name_zh : '全球')}</div>
|
||||
</div>
|
||||
<div class="text-end me-5">
|
||||
<div class="small text-muted fw-bold" style="font-size: 10px; letter-spacing: 0.5px;">价格</div>
|
||||
<div class="fw-bold text-dark fs-5">$${s.cost}</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn-get" onclick="getNumber('${s.service_id}', '${s.service_name}', ${s.cost}, this)">获取号码</button>
|
||||
<div class="fw-bold fs-5">${s.service_name}</div>
|
||||
<div class="small text-muted">${s.country_name_zh}</div>
|
||||
</div>
|
||||
<div class="text-end me-5"><div class="fw-bold text-dark fs-5">$${s.cost}</div></div>
|
||||
<div><button class="btn-get" onclick="getNumber('${s.service_id}', '${s.service_name}', ${s.cost}, this)">获取号码</button></div>
|
||||
`;
|
||||
body.appendChild(item);
|
||||
});
|
||||
document.getElementById('lastUpdated').textContent = '更新时间: ' + new Date().toLocaleTimeString();
|
||||
} else if (data.code === 401) {
|
||||
window.location.href = 'index.php';
|
||||
} else {
|
||||
body.innerHTML = `<div class="p-5 text-center text-danger">加载行情失败: ${data.msg || '未知接口错误'}</div>`;
|
||||
}
|
||||
} catch (e) { body.innerHTML = '<div class="p-5 text-center text-danger">行情数据连接失败,请检查网络</div>'; }
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function getNumber(sid, sname, price, btn) {
|
||||
showConfirm('购买确认', `确认扣费 $${price} 购买 ${sname} 号码?`, async () => {
|
||||
const originalText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i>';
|
||||
|
||||
try {
|
||||
const cname = currentCountry ? currentCountry.name_zh : '全球';
|
||||
const res = await fetch(`${apiHandler}?action=get_number&service_id=${sid}&service_name=${encodeURIComponent(sname)}&country_name=${encodeURIComponent(cname)}&price=${price}`);
|
||||
const res = await fetch(`${apiHandler}?action=get_number&service_id=${sid}&service_name=${encodeURIComponent(sname)}&country_name=${encodeURIComponent(currentCountry.name_zh)}&price=${price}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) {
|
||||
showToast('号码获取成功!');
|
||||
loadActiveOrders(); updateBalance(); window.scrollTo({top: 0, behavior: 'smooth'});
|
||||
} else if (data.code === 401) {
|
||||
window.location.href = 'index.php';
|
||||
} else {
|
||||
showToast(data.msg || '库存不足或接口超时', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
showToast('获取号码失败,请重试', 'error');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
if (data.code === 0) { showToast('获取成功!'); loadActiveOrders(); updateBalance(); }
|
||||
else { showToast(data.msg || '获取失败', 'error'); }
|
||||
} catch (e) { showToast('接口异常', 'error'); }
|
||||
});
|
||||
}
|
||||
|
||||
@ -660,90 +610,68 @@ $notice_text = $settings['notice_text'] ?? '欢迎使用全球接码平台!';
|
||||
const data = await res.json();
|
||||
const body = document.getElementById('activeTasksBody');
|
||||
const section = document.getElementById('activeTasksSection');
|
||||
const latestArea = document.getElementById('latestOrderArea');
|
||||
const latestContent = document.getElementById('latestOrderContent');
|
||||
|
||||
Object.values(activeTimers).forEach(t => clearInterval(t));
|
||||
activeTimers = {};
|
||||
|
||||
if (data.code === 0 && Array.isArray(data.data) && data.data.length > 0) {
|
||||
section.style.display = 'block';
|
||||
body.innerHTML = '';
|
||||
|
||||
const receivedList = data.data.filter(o => o.status === 'received');
|
||||
if (receivedList.length > 0) {
|
||||
const latest = receivedList.sort((a,b) => new Date(b.created_at) - new Date(a.created_at))[0];
|
||||
const now = new Date();
|
||||
const rAt = new Date(latest.created_at.replace(/-/g, "/").replace(" ", "T") + "Z");
|
||||
if ((now - rAt) / 1000 < 300) {
|
||||
latestArea.style.display = 'block';
|
||||
latestContent.innerHTML = `
|
||||
<div class="col-md-3">项目: ${latest.service_name}</div>
|
||||
<div class="col-md-3">号码: <span class="fw-bold text-primary">${latest.number}</span></div>
|
||||
<div class="col-md-4">短信: <span class="text-dark fw-bold">${latest.sms_content}</span></div>
|
||||
<div class="col-md-2"><button class="btn btn-sm btn-danger" onclick="releaseNumber('${latest.request_id}')">取消</button></div>
|
||||
`;
|
||||
} else { latestArea.style.display = 'none'; }
|
||||
} else { latestArea.style.display = 'none'; }
|
||||
|
||||
data.data.forEach(o => {
|
||||
const exp = new Date(o.expire_at.replace(/-/g, "/")).getTime();
|
||||
let tl = Math.floor((exp - new Date().getTime())/1000);
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td class="ps-4 py-4">
|
||||
<div class="fw-bold text-dark">${o.service_name}</div>
|
||||
<div class="small text-muted">${o.country_name}</div>
|
||||
</td>
|
||||
<td class="fw-bold text-primary fs-5" style="letter-spacing: 1px;">${o.number}</td>
|
||||
<td id="sms-${o.request_id}">
|
||||
${o.status === 'received' ? `<span class="sms-badge">${o.sms_content}</span>` : `
|
||||
<div class="d-flex align-items-center gap-3 text-primary">
|
||||
<div class="spinner-grow spinner-grow-sm" style="animation-duration: 1.5s;"></div>
|
||||
<span class="fw-bold small" style="letter-spacing: 0.5px;">等待短信...</span>
|
||||
</div>`}
|
||||
</td>
|
||||
<td><span class="badge bg-light text-dark border p-2 px-3 fw-bold" id="timer-${o.request_id}">${formatTime(tl)}</span></td>
|
||||
<td class="text-end pe-4">
|
||||
<button class="btn btn-sm btn-outline-danger fw-bold px-3 py-2 rounded-3" onclick="releaseNumber('${o.request_id}')">取消/释放</button>
|
||||
</td>
|
||||
<td>${o.service_name} / ${o.country_name}</td>
|
||||
<td class="fw-bold">${o.number}</td>
|
||||
<td>${o.status === 'received' ? o.sms_content : '等待中...'}</td>
|
||||
<td>${o.status}</td>
|
||||
<td>${o.expire_at}</td>
|
||||
<td class="text-end"><button class="btn btn-sm btn-outline-danger" onclick="releaseNumber('${o.request_id}')">释放</button></td>
|
||||
`;
|
||||
body.appendChild(row);
|
||||
if (o.status !== 'received') { startPolling(o.request_id); startTimer(o.request_id, tl); }
|
||||
if (o.status !== 'received' && !activePolls[o.request_id]) startPolling(o.request_id);
|
||||
});
|
||||
} else { section.style.display = 'none'; }
|
||||
} else { section.style.display = 'none'; latestArea.style.display = 'none'; }
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function startPolling(rid) {
|
||||
if (activePolls[rid]) return;
|
||||
activePolls[rid] = setInterval(async () => {
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=check_sms&request_id=${rid}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0 && (data.msg === 'success' || data.sms_code)) {
|
||||
const el = document.getElementById(`sms-${rid}`);
|
||||
if (el) el.innerHTML = `<span class="sms-badge">${data.sms_code}</span>`;
|
||||
clearInterval(activePolls[rid]); delete activePolls[rid]; showSmsModal(data.sms_code);
|
||||
} else if (data.code === 400 || (data.code !== 0 && data.code !== 500)) {
|
||||
clearInterval(activePolls[rid]); delete activePolls[rid]; loadActiveOrders();
|
||||
clearInterval(activePolls[rid]); delete activePolls[rid]; showSmsModal(data.sms_code); loadActiveOrders();
|
||||
}
|
||||
} catch (e) {}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function startTimer(id, s) {
|
||||
activeTimers[id] = setInterval(() => {
|
||||
s--;
|
||||
const el = document.getElementById('timer-' + id);
|
||||
if (s <= 0) { clearInterval(activeTimers[id]); loadActiveOrders(); }
|
||||
else if (el) el.textContent = formatTime(s);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function releaseNumber(id) {
|
||||
showConfirm('释放确认', '确定取消并释放此号码吗?', async () => {
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=release_number&request_id=${id}`);
|
||||
const data = await res.json();
|
||||
if (data.code === 0) { showToast('号码已取消!'); loadActiveOrders(); updateBalance(); } else { showToast(data.msg, 'error'); }
|
||||
} catch (e) { showToast('连接服务器失败', 'error'); }
|
||||
});
|
||||
}
|
||||
|
||||
function formatTime(s) {
|
||||
if (s <= 0) return "00:00";
|
||||
const m = Math.floor(s/60), sec = s%60;
|
||||
return `${m.toString().padStart(2,'0')}:${sec.toString().padStart(2,'0')}`;
|
||||
try {
|
||||
const res = await fetch(`${apiHandler}?action=release_number&request_id=${id}`);
|
||||
loadActiveOrders(); updateBalance();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function showSmsModal(code) {
|
||||
document.getElementById('modalSmsCode').textContent = code;
|
||||
new bootstrap.Modal(document.getElementById('smsModal')).show();
|
||||
if(navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(code).catch(e => {});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
0
dashboard.php.new
Normal file
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
// Generated by setup_mariadb_project.sh — edit as needed.
|
||||
date_default_timezone_set("Asia/Shanghai");
|
||||
define('DB_HOST', '127.0.0.1');
|
||||
define('DB_NAME', 'app_38320');
|
||||
define('DB_USER', 'app_38320');
|
||||
@ -14,4 +15,4 @@ function db() {
|
||||
]);
|
||||
}
|
||||
return $pdo;
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
date_default_timezone_set("Asia/Shanghai");
|
||||
// Handle Cancel Request
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'cancel' && isset($_POST['order_id'])) {
|
||||
$order_id = $_POST['order_id'];
|
||||
|
||||
@ -256,7 +256,7 @@ $user = $stmt->fetch();
|
||||
const row = document.createElement("div");
|
||||
row.className = `message-row ${msg.sender === "user" ? "me" : "them"}`;
|
||||
|
||||
const time = new Date(msg.created_at.replace(" ", "T")).toLocaleTimeString([], {hour: "2-digit", minute:"2-digit"});
|
||||
const time = new Date(msg.created_at.replace(" ", "T") + "Z").toLocaleTimeString([], {hour: "2-digit", minute:"2-digit"});
|
||||
|
||||
row.innerHTML = `
|
||||
<div class="message-bubble">
|
||||
|
||||