function adminApiBase(): string { const env = import.meta.env.VITE_API_BASE; const envBase = typeof env === "string" ? env.trim().replace(/\/+$/, "") : ""; return envBase; } /** * 管理后台下载/水印打包用:经后端 admin/v1/uploads/raw 拉取二进制,避免前端页与静态资源跨域时 fetch 被 CORS 拦截。 * 展示用仍用 {@link resolveAdminMediaUrl}(img 标签不受同源策略限制)。 */ export function resolveAdminUploadBlobFetchUrl(displayUrl: string): string { const raw = typeof displayUrl === "string" ? displayUrl.trim() : ""; if (!raw) return ""; let path = ""; try { if (raw.startsWith("http://") || raw.startsWith("https://")) { path = new URL(raw).pathname; } else if (raw.startsWith("/uploads/")) { path = raw.split("?")[0] ?? raw; } } catch { return raw; } if (!path.startsWith("/uploads/")) { return raw; } const base = adminApiBase(); if (!base) { return raw; } return `${base}/admin/v1/uploads/raw?path=${encodeURIComponent(path)}`; } /** * 管理后台展示用:将 /uploads/... 转为浏览器可请求的绝对地址。 * 未配置 VITE_API_BASE 时使用当前页面 origin(开发环境由 Vite 代理 /uploads)。 */ export function resolveAdminMediaUrl(path: string | null | undefined): string { const raw = typeof path === "string" ? path.trim() : ""; if (!raw) return ""; if (raw.startsWith("blob:") || raw.startsWith("data:")) { return ""; } if (raw.startsWith("http://") || raw.startsWith("https://")) { return raw; } const uploads = raw.startsWith("/uploads/") ? raw : raw.startsWith("uploads/") ? `/${raw}` : ""; if (uploads) { const envBase = adminApiBase(); const base = envBase || (typeof window !== "undefined" && window.location?.origin ? window.location.origin.replace(/\/+$/, "") : ""); return base ? `${base}${uploads}` : uploads; } if (raw.startsWith("/") && typeof window !== "undefined" && window.location?.origin) { return `${window.location.origin.replace(/\/+$/, "")}${raw}`; } return raw; } /** 持久化字段:去掉浏览器临时地址,避免写入数据库 */ export function sanitizeAvatarForSave(path: string): string { const t = path.trim(); if (!t || t.startsWith("blob:") || t.startsWith("data:")) { return ""; } return t; }