203 lines
5.5 KiB
JavaScript
203 lines
5.5 KiB
JavaScript
/**
|
||
* 通用网络请求封装 - 使用 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 }; |