158 lines
5.1 KiB
JavaScript
158 lines
5.1 KiB
JavaScript
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;
|