Initial commit: 心理咨询预约系统
PHP 前后端一体:首页咨询师列表、预约表单、管理后台、二维码准入与邮件通知;敏感配置与提交数据通过 .gitignore 排除。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
85
js/api.js
Normal file
85
js/api.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/** 基于当前页面 URL 解析 api 路径,适配根目录或子目录部署 */
|
||||
function apiUrl(endpoint) {
|
||||
return new URL(`api/${endpoint}`, window.location.href).href;
|
||||
}
|
||||
|
||||
/** 静态资源追加版本号,避免手机浏览器缓存旧 CSS/JS/图片 */
|
||||
function assetUrl(path) {
|
||||
if (!path || /^https?:\/\//i.test(path)) return path;
|
||||
const v = window.__ASSET_V__ || "";
|
||||
if (!v) return path;
|
||||
const sep = path.includes("?") ? "&" : "?";
|
||||
return `${path}${sep}v=${encodeURIComponent(v)}`;
|
||||
}
|
||||
|
||||
async function parseResponse(res) {
|
||||
const text = await res.text();
|
||||
if (!text) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
const hint = res.status === 404
|
||||
? "接口不存在,请确认 api 目录已上传"
|
||||
: res.status >= 500
|
||||
? "服务器错误,请检查 PHP 与 data 目录写权限"
|
||||
: "服务器返回异常,请确认 PHP 已启用";
|
||||
throw new Error(`${hint}(HTTP ${res.status})`);
|
||||
}
|
||||
}
|
||||
|
||||
async function apiGet(endpoint, useCredentials = false) {
|
||||
const res = await fetch(apiUrl(endpoint), {
|
||||
credentials: useCredentials ? "include" : "same-origin",
|
||||
});
|
||||
const data = await parseResponse(res);
|
||||
if (!res.ok) {
|
||||
throw new Error(data.message || `请求失败(HTTP ${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async function apiPost(endpoint, body, useCredentials = false, timeoutMs = 30000) {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(apiUrl(endpoint), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
credentials: useCredentials ? "include" : "same-origin",
|
||||
signal: controller.signal,
|
||||
});
|
||||
} catch (e) {
|
||||
clearTimeout(timer);
|
||||
if (e.name === "AbortError") {
|
||||
throw new Error("请求超时,数据可能已保存,请稍后刷新或联系管理员");
|
||||
}
|
||||
throw new Error("网络请求失败,请检查网络或域名是否正确");
|
||||
}
|
||||
clearTimeout(timer);
|
||||
const data = await parseResponse(res);
|
||||
if (!res.ok) {
|
||||
throw new Error(data.message || `提交失败(HTTP ${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function showToast(message, duration = 2500) {
|
||||
const existing = document.querySelector(".toast");
|
||||
if (existing) existing.remove();
|
||||
const el = document.createElement("div");
|
||||
el.className = "toast";
|
||||
el.textContent = message;
|
||||
document.body.appendChild(el);
|
||||
setTimeout(() => el.remove(), duration);
|
||||
}
|
||||
|
||||
/** 供管理端下载使用 */
|
||||
const API_BASE = (() => {
|
||||
const u = new URL("api/", window.location.href);
|
||||
return u.pathname.replace(/\/$/, "");
|
||||
})();
|
||||
Reference in New Issue
Block a user