63 lines
2.7 KiB
JavaScript
63 lines
2.7 KiB
JavaScript
/* =====================================================================
|
||
* 网络层(api.js)
|
||
* 统一调用 /api/* 接口;localStorage 只保存 token 与 username(绝不保存密码)。
|
||
* ===================================================================== */
|
||
|
||
export class ApiError extends Error {
|
||
constructor(message, status) {
|
||
super(message);
|
||
this.status = status;
|
||
}
|
||
}
|
||
|
||
export async function api(path, { method = "GET", body } = {}) {
|
||
const headers = { "Content-Type": "application/json" };
|
||
const token = localStorage.getItem("token");
|
||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||
let res;
|
||
try {
|
||
res = await fetch(path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
|
||
} catch (err) {
|
||
throw new ApiError("网络请求失败,请稍后再试", 0);
|
||
}
|
||
let json = null;
|
||
try { json = await res.json(); } catch (err) { /* 忽略非 JSON 响应 */ }
|
||
if (!json || typeof json.success === "undefined") {
|
||
throw new ApiError(`服务器响应异常(${res.status})`, res.status);
|
||
}
|
||
if (!json.success) throw new ApiError(json.message || "请求失败", res.status);
|
||
return json.data;
|
||
}
|
||
|
||
// multipart 表单提交(普通字段 + 单个文件),用于项目管理等
|
||
export async function uploadForm(path, fields = {}, file = null, method = "POST") {
|
||
const form = new FormData();
|
||
for (const [key, value] of Object.entries(fields)) {
|
||
if (value !== undefined && value !== null && value !== "") form.append(key, String(value));
|
||
}
|
||
if (file) form.append("file", file);
|
||
const headers = {};
|
||
const token = localStorage.getItem("token");
|
||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||
const res = await fetch(path, { method, headers, body: form });
|
||
let json = null;
|
||
try { json = await res.json(); } catch (err) { /* 忽略非 JSON 响应 */ }
|
||
if (!json || !json.success) throw new ApiError((json && json.message) || "操作失败", res.status);
|
||
return json.data;
|
||
}
|
||
|
||
// multipart 上传(头像/图片/项目文件),返回 {url, filename, size}
|
||
export async function uploadFile(path, file, extra = {}) {
|
||
const form = new FormData();
|
||
form.append("file", file);
|
||
// extra:附加字段(如上传文档时携带文章分区 category,供后端校验)
|
||
for (const [key, value] of Object.entries(extra)) form.append(key, value);
|
||
const headers = {};
|
||
const token = localStorage.getItem("token");
|
||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||
const res = await fetch(path, { method: "POST", headers, body: form });
|
||
let json = null;
|
||
try { json = await res.json(); } catch (err) { /* 忽略非 JSON 响应 */ }
|
||
if (!json || !json.success) throw new ApiError((json && json.message) || "上传失败", res.status);
|
||
return json.data;
|
||
} |