Files
2026-05-09 16:40:29 +08:00

158 lines
5.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { API_BASE } from "./config";
let isRefreshing = false;
let pendingQueue = [];
const isHtmlLike = (value) =>
typeof value === "string" && value.trim().startsWith("<");
export const extractList = (payload) => {
if (Array.isArray(payload?.items)) return payload.items;
if (Array.isArray(payload?.data)) return payload.data;
if (Array.isArray(payload)) return payload;
return [];
};
const doRequest = (options) => {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync("access_token");
uni.request({
url: API_BASE + options.url,
method: options.method || "GET",
data: options.data,
header: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
...options.header,
},
success: (res) => resolve(res),
fail: (err) => reject(err),
});
});
};
const tryRefreshToken = () => {
return new Promise((resolve, reject) => {
const rt = uni.getStorageSync("refresh_token");
if (!rt) return reject(new Error("no_refresh_token"));
uni.request({
url: API_BASE + "/auth/refresh",
method: "POST",
header: { "Content-Type": "application/json" },
data: { refresh_token: rt },
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
const { access_token, refresh_token } = res.data;
uni.setStorageSync("access_token", access_token);
if (refresh_token) uni.setStorageSync("refresh_token", refresh_token);
resolve();
} else {
reject(new Error("refresh_failed"));
}
},
fail: () => reject(new Error("refresh_network_error")),
});
});
};
const goLogin = () => {
uni.removeStorageSync("access_token");
uni.removeStorageSync("refresh_token");
const pages = getCurrentPages();
const current = pages[pages.length - 1];
const route = current ? `/${current.route}` : "";
if (route !== "/pages/login/index") {
uni.navigateTo({ url: "/pages/login/index" });
}
};
const handleError = (res) => {
const code = res.statusCode;
if (code === 403) return "没有权限执行此操作";
if (code === 404) return "请求的资源不存在";
if (code === 409) return "数据冲突,请刷新重试";
if (code === 422) {
const detail = res.data?.detail;
if (Array.isArray(detail) && detail.length > 0) {
return detail.map((d) => d.msg || d.message).join("");
}
}
if (code === 429) return "操作过于频繁,请稍后再试";
if (code >= 500) return "服务器繁忙,请稍后重试";
const raw = res.data?.message || res.data?.detail || "请求失败";
return typeof raw === "string" ? raw : JSON.stringify(raw);
};
const request = (options) => {
return new Promise((resolve, reject) => {
doRequest(options)
.then((res) => {
if (isHtmlLike(res.data)) {
const msg = "接口返回异常,请检查网络或服务状态";
uni.showToast({ title: msg, icon: "none" });
return reject(new Error(msg));
}
if (res.statusCode === 401) {
if (isRefreshing) {
pendingQueue.push({ options, resolve, reject });
return;
}
isRefreshing = true;
tryRefreshToken()
.then(() => {
isRefreshing = false;
doRequest(options).then((r2) => {
if (r2.statusCode >= 200 && r2.statusCode < 300) {
resolve(r2.data);
} else {
const msg = handleError(r2);
uni.showToast({ title: msg, icon: "none" });
reject(new Error(msg));
}
});
pendingQueue.forEach((p) => {
doRequest(p.options).then((r2) => {
if (r2.statusCode >= 200 && r2.statusCode < 300) {
p.resolve(r2.data);
} else {
p.reject(new Error(handleError(r2)));
}
});
});
pendingQueue = [];
})
.catch(() => {
isRefreshing = false;
pendingQueue.forEach((p) => p.reject(new Error("登录已过期")));
pendingQueue = [];
goLogin();
reject(new Error("登录已过期,请重新登录"));
});
return;
}
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data);
} else {
const msg = handleError(res);
uni.showToast({ title: msg, icon: "none" });
reject(new Error(msg));
}
})
.catch((err) => {
uni.showToast({ title: "网络连接失败,请检查网络", icon: "none" });
reject(err);
});
});
};
export const get = (url, data) => request({ url, method: "GET", data });
export const post = (url, data) => request({ url, method: "POST", data });
export const put = (url, data) => request({ url, method: "PUT", data });
export const del = (url, data) => request({ url, method: "DELETE", data });
export default request;