ch-tgr/src/api/http.js
2026-05-23 08:36:36 +08:00

203 lines
5.5 KiB
JavaScript
Raw 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.

/**
* 通用网络请求封装 - 使用 fetch
*/
const BASE_URL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:3001';
// 默认超时时间 15 秒
const DEFAULT_TIMEOUT = 15000;
// 错误码定义
const ERR_CODE = {
SUCCESS: 200,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
SERVER_ERROR: 500,
};
function getHeaders(contentType = 'application/json') {
const headers = {
'Content-Type': contentType,
};
const token = localStorage.getItem('member_token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
function handleResponse(response) {
return response.text().then(text => {
let data;
try {
data = JSON.parse(text);
} catch (e) {
// JSON 解析失败,返回原始文本
return Promise.reject({
status: response.status,
message: '数据解析失败',
data: null
});
}
// 检查 HTTP 状态码
if (!response.ok) {
return Promise.reject({
status: response.status,
message: data.message || '请求失败',
data: null
});
}
// 检查业务状态码
if (data.status !== ERR_CODE.SUCCESS) {
return Promise.reject(data);
}
return data;
});
}
/**
* 处理 token 失效
*/
function handleUnauthorized() {
// 如果 localStorage 中本来就没有 token说明是未登录状态不做处理
if (!localStorage.getItem('member_token')) {
return;
}
// 清除登录状态
localStorage.removeItem('member_token');
localStorage.removeItem('member_username');
// 跳转到登录页
if (window.location.hash !== '#/Login') {
window.location.href = '#/Login';
}
}
/**
* 统一错误处理
*/
function handleError(err) {
// token 失效
if (err.status === ERR_CODE.UNAUTHORIZED || err.status === ERR_CODE.FORBIDDEN) {
handleUnauthorized();
}
return Promise.reject(err);
}
/**
* 带超时的 fetch
*/
function fetchWithTimeout(url, options, timeout = DEFAULT_TIMEOUT) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
clearTimeout(timer);
reject({ status: 0, message: '网络超时,请稍后重试', data: null });
}, timeout);
fetch(url, options)
.then(res => {
clearTimeout(timer);
resolve(res);
})
.catch(err => {
clearTimeout(timer);
reject({ status: 0, message: '网络连接失败', data: null });
});
});
}
/**
* 通用请求
* @param {string} url - 请求路径
* @param {object} data - 请求数据
* @param {string} method - 请求方法,默认 POST
* @param {number} timeout - 超时时间 ms
* @returns {Promise}
*/
export function request(url, data, method = 'POST', timeout = DEFAULT_TIMEOUT) {
return fetchWithTimeout(BASE_URL + url, {
method,
headers: getHeaders(),
body: JSON.stringify(data),
}, timeout).then(handleResponse).catch(handleError);
}
export const get = (url, params, timeout = DEFAULT_TIMEOUT) => {
let query = '';
if (params) {
query = '?' + new URLSearchParams(params).toString();
}
return fetchWithTimeout(BASE_URL + url + query, {
method: 'GET',
headers: getHeaders(),
}, timeout).then(handleResponse).catch(handleError);
};
export const post = (url, data, timeout = DEFAULT_TIMEOUT) => request(url, data, 'POST', timeout);
export const put = (url, data, timeout = DEFAULT_TIMEOUT) => request(url, data, 'PUT', timeout);
export const del = (url, data, timeout = DEFAULT_TIMEOUT) => request(url, data, 'DELETE', timeout);
/**
* POST FormData 请求
* @param {string} url - 请求路径
* @param {FormData} formData - FormData 对象
* @param {number} timeout - 超时时间 ms
* @returns {Promise}
*/
export function postForm(url, formData, timeout = DEFAULT_TIMEOUT) {
const headers = {};
const token = localStorage.getItem('member_token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return fetchWithTimeout(BASE_URL + url, {
method: 'POST',
headers,
body: formData,
}, timeout).then(handleResponse).catch(handleError);
}
/**
* 上传文件
* @param {string} url - 请求路径
* @param {File[]} files - 文件数组
* @param {number} timeout - 超时时间 ms默认 30 秒上传较长)
* @returns {Promise}
*/
export function upload(url, files, timeout = 30000) {
const formData = new FormData();
files.forEach(file => {
formData.append('file', file);
});
const headers = {};
const token = localStorage.getItem('member_token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return fetchWithTimeout(BASE_URL + url, {
method: 'POST',
headers,
body: formData,
}, timeout)
.then(res => res.text().then(text => {
try {
const data = JSON.parse(text);
if (data.status !== ERR_CODE.SUCCESS) {
return Promise.reject(data);
}
return data;
} catch (e) {
return Promise.reject({ status: 0, message: '数据解析失败', data: null });
}
}))
.catch(handleError);
}
export default { get, post, put, del, upload, postForm, request };