const API_BASE = `${location.protocol}//${location.hostname}${location.port ? `:${location.port}` : ""}/api`; function getToken() { return localStorage.getItem("token") || ""; } function setToken(token) { localStorage.setItem("token", token || ""); } function clearAuth() { localStorage.removeItem("token"); localStorage.removeItem("me"); } async function getMe(forceRefresh = false) { const cached = localStorage.getItem("me"); if (!forceRefresh && cached) { try { return JSON.parse(cached); } catch (_) {} } const me = await api("/me"); localStorage.setItem("me", JSON.stringify(me)); return me; } async function api(path, options = {}) { const headers = { "Content-Type": "application/json", ...(options.headers || {}) }; const token = getToken(); if (token) headers.Authorization = `Bearer ${token}`; let resp; try { resp = await fetch(`${API_BASE}${path}`, { ...options, headers }); } catch (networkError) { console.error("Network error:", networkError); throw new Error("网络请求失败,请检查网络连接"); } let data = {}; try { data = await resp.json(); } catch (_) {} if (!resp.ok) { if (resp.status === 413) { throw new Error("照片过大,请降低清晰度后重试"); } if (resp.status === 401) { clearAuth(); const isAdminPath = window.location.pathname.includes("admin"); const loginPage = isAdminPath ? "/admin.html" : "/"; if (window.location.pathname !== loginPage) { window.location.href = loginPage; } throw new Error(data.detail || "登录已过期,请重新登录"); } const error = new Error(data.detail || "请求失败"); error.status = resp.status; error.data = data; throw error; } return data; } function colorToLeaflet(color) { if (color === "green") return "#188038"; if (color === "yellow") return "#fbbc04"; if (color === "half-yellow-half-green") return "#0b57d0"; return "#d93025"; }