PHP 前后端一体:首页咨询师列表、预约表单、管理后台、二维码准入与邮件通知;敏感配置与提交数据通过 .gitignore 排除。 Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.7 KiB
JavaScript
121 lines
3.7 KiB
JavaScript
let isLoggedIn = false;
|
|
|
|
function formatTime(iso) {
|
|
if (!iso) return "-";
|
|
try {
|
|
const d = new Date(iso);
|
|
return d.toLocaleString("zh-CN", { hour12: false });
|
|
} catch {
|
|
return iso;
|
|
}
|
|
}
|
|
|
|
function renderSubmissionCard(item) {
|
|
const card = document.createElement("article");
|
|
card.className = "submission-card";
|
|
const doctorInfo = item.doctorName ? `${item.doctorName}` : "未指定咨询师";
|
|
card.innerHTML = `
|
|
<div class="submission-card__time">${formatTime(item.createdAt)}</div>
|
|
<div class="submission-card__row"><strong>昵称:</strong>${escapeHtml(item.nickname || "-")}</div>
|
|
<div class="submission-card__row"><strong>性别:</strong>${escapeHtml(item.gender || "-")}</div>
|
|
<div class="submission-card__row"><strong>联系电话:</strong>${escapeHtml(item.contact || "-")}</div>
|
|
<div class="submission-card__row"><strong>咨询师:</strong>${escapeHtml(doctorInfo)}</div>
|
|
<p class="submission-card__desc">${escapeHtml(item.description || "(无描述)")}</p>
|
|
`;
|
|
return card;
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
const div = document.createElement("div");
|
|
div.textContent = str;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function showLogin() {
|
|
document.getElementById("login-section").style.display = "block";
|
|
document.getElementById("dashboard-section").style.display = "none";
|
|
isLoggedIn = false;
|
|
}
|
|
|
|
function showDashboard() {
|
|
document.getElementById("login-section").style.display = "none";
|
|
document.getElementById("dashboard-section").style.display = "block";
|
|
isLoggedIn = true;
|
|
}
|
|
|
|
async function loadSubmissions() {
|
|
const listEl = document.getElementById("submission-list");
|
|
listEl.innerHTML = "<p>加载中…</p>";
|
|
|
|
try {
|
|
const res = await apiGet("submissions.php", true);
|
|
const items = res.data || [];
|
|
document.getElementById("submission-count").textContent = items.length;
|
|
|
|
if (items.length === 0) {
|
|
listEl.innerHTML = '<p class="admin-empty">暂无提交记录</p>';
|
|
return;
|
|
}
|
|
|
|
listEl.innerHTML = "";
|
|
items.forEach((item) => listEl.appendChild(renderSubmissionCard(item)));
|
|
} catch (e) {
|
|
if (e.message && e.message.includes("未登录")) {
|
|
showLogin();
|
|
return;
|
|
}
|
|
listEl.innerHTML = '<p class="admin-empty">加载失败</p>';
|
|
showToast(e.message || "加载失败");
|
|
}
|
|
}
|
|
|
|
async function tryAutoLogin() {
|
|
try {
|
|
const res = await apiGet("check.php", true);
|
|
if (res.loggedIn) {
|
|
showDashboard();
|
|
await loadSubmissions();
|
|
return true;
|
|
}
|
|
} catch (_) {}
|
|
return false;
|
|
}
|
|
|
|
function downloadExport(format) {
|
|
window.location.href = `${API_BASE}/export.php?format=${format}`;
|
|
}
|
|
|
|
async function initAdmin() {
|
|
document.getElementById("login-form").addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const user = document.getElementById("admin-user").value.trim();
|
|
const password = document.getElementById("admin-password").value;
|
|
|
|
try {
|
|
await apiPost("login.php", { user, password }, true);
|
|
showToast("登录成功");
|
|
showDashboard();
|
|
await loadSubmissions();
|
|
} catch (err) {
|
|
showToast(err.message || "登录失败");
|
|
}
|
|
});
|
|
|
|
document.getElementById("logout-btn").addEventListener("click", async () => {
|
|
try {
|
|
await apiPost("logout.php", {}, true);
|
|
} catch (_) {}
|
|
showLogin();
|
|
showToast("已退出");
|
|
});
|
|
|
|
document.getElementById("export-json").addEventListener("click", () => downloadExport("json"));
|
|
document.getElementById("export-csv").addEventListener("click", () => downloadExport("csv"));
|
|
document.getElementById("refresh-btn").addEventListener("click", loadSubmissions);
|
|
|
|
const ok = await tryAutoLogin();
|
|
if (!ok) showLogin();
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", initAdmin);
|