etherreq 1.1.16 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -47
- package/dist/cache.js +281 -0
- package/dist/etherreq.js +84 -0
- package/dist/index.js +59 -0
- package/dist/request.js +96 -0
- package/dist/types/cache.d.ts +122 -0
- package/dist/types/cache.js +2 -0
- package/dist/types/config.js +58 -0
- package/dist/types/etherreq.d.ts +20 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/request.d.ts +10 -0
- package/dist/types/types/cache.d.ts +95 -0
- package/dist/types/types/config.d.ts +59 -0
- package/package.json +29 -19
- package/.github/workfiows/publish-npm.yml +0 -30
- package/.github/workflows/npm-publish.yml +0 -33
- package/src/cache.js +0 -58
- package/src/etherreq.js +0 -95
- package/src/index.js +0 -79
- package/src/request.js +0 -88
- package/src/types/etherreq.d.ts +0 -50
- package/src/upload.js +0 -88
package/src/etherreq.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// src/etherreq.js
|
|
2
|
-
|
|
3
|
-
export const create = (defaultConfig = {}) => {
|
|
4
|
-
const interceptors = {
|
|
5
|
-
request: [],
|
|
6
|
-
response: [],
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const use = (fulfilled, rejected, type = 'request') => {
|
|
10
|
-
interceptors[type].push({ fulfilled, rejected });
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// ✅ 使用 const 显式声明 dispatchRequest
|
|
14
|
-
// etherreq.js
|
|
15
|
-
const dispatchRequest = async (config) => {
|
|
16
|
-
// 请求拦截器执行
|
|
17
|
-
for (const interceptor of interceptors.request) {
|
|
18
|
-
const nextConfig = await interceptor.fulfilled(config);
|
|
19
|
-
// ✅ 拦截器可以返回一个 response 对象,表示中断请求
|
|
20
|
-
if (nextConfig && nextConfig.isFromCache) {
|
|
21
|
-
return nextConfig.data; // ✅ 直接返回缓存结果,不再往下执行
|
|
22
|
-
}
|
|
23
|
-
config = nextConfig;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let response;
|
|
27
|
-
const { url, method = 'GET', headers = {}, body } = config;
|
|
28
|
-
let text = '';
|
|
29
|
-
try {
|
|
30
|
-
const options = {
|
|
31
|
-
method,
|
|
32
|
-
headers,
|
|
33
|
-
body: method !== 'GET' ? JSON.stringify(body) : undefined,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const res = await fetch(url, options);
|
|
37
|
-
text = await res.text();
|
|
38
|
-
if (!res.ok) {
|
|
39
|
-
throw new Error(`HTTP 错误: ${res.status} - ${res.statusText}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (text.trim().startsWith('<')) {
|
|
43
|
-
throw new Error('服务器返回了 HTML 页面,预期为 JSON 格式');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const data = JSON.parse(text);
|
|
47
|
-
|
|
48
|
-
response = {
|
|
49
|
-
data,
|
|
50
|
-
status: res.status,
|
|
51
|
-
statusText: res.statusText,
|
|
52
|
-
headers: res.headers,
|
|
53
|
-
config,
|
|
54
|
-
};
|
|
55
|
-
} catch (error) {
|
|
56
|
-
error.message += `\n请求地址: ${url}`;
|
|
57
|
-
if (text) {
|
|
58
|
-
error.message += `\n响应内容: ${text.substring(0, 200)}...`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
for (const interceptor of interceptors.response) {
|
|
62
|
-
if (interceptor.rejected) {
|
|
63
|
-
return interceptor.rejected(error);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 响应拦截器执行
|
|
71
|
-
for (const interceptor of interceptors.response) {
|
|
72
|
-
response = await interceptor.fulfilled(response);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return response;
|
|
76
|
-
};
|
|
77
|
-
const instance = (config) => {
|
|
78
|
-
return dispatchRequest({
|
|
79
|
-
...defaultConfig,
|
|
80
|
-
...config,
|
|
81
|
-
});
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
instance.use = (fulfilled, rejected) => use(fulfilled, rejected, 'request');
|
|
85
|
-
instance.interceptors = {
|
|
86
|
-
request: {
|
|
87
|
-
use: (fulfilled, rejected) => use(fulfilled, rejected, 'request'),
|
|
88
|
-
},
|
|
89
|
-
response: {
|
|
90
|
-
use: (fulfilled, rejected) => use(fulfilled, rejected, 'response'),
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
return instance;
|
|
95
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// src/index.js
|
|
2
|
-
import { request, baseURL as _baseURL, setBaseURL } from './request.js';
|
|
3
|
-
// 示例封装方法
|
|
4
|
-
const createMethod = (method) => (url, data, callback) => {
|
|
5
|
-
let options;
|
|
6
|
-
if (
|
|
7
|
-
data &&
|
|
8
|
-
typeof data === 'object' &&
|
|
9
|
-
!Array.isArray(data) &&
|
|
10
|
-
!(data instanceof FormData) &&
|
|
11
|
-
!data.method &&
|
|
12
|
-
!data.headers &&
|
|
13
|
-
!data.params &&
|
|
14
|
-
!data.baseURL
|
|
15
|
-
) {
|
|
16
|
-
options = { body: data, method };
|
|
17
|
-
} else {
|
|
18
|
-
options = { ...data, method };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const promise = request(url, options);
|
|
22
|
-
|
|
23
|
-
if (typeof callback === 'function') {
|
|
24
|
-
promise.then(data => callback(null, data)).catch(err => callback(err, null));
|
|
25
|
-
} else {
|
|
26
|
-
return promise;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const etherreq = Object.assign(
|
|
31
|
-
(url, options, callback) => createMethod('GET')(url, options, callback),
|
|
32
|
-
{
|
|
33
|
-
get: createMethod('GET'),
|
|
34
|
-
post: createMethod('POST'),
|
|
35
|
-
put: createMethod('PUT'),
|
|
36
|
-
delete: createMethod('DELETE'),
|
|
37
|
-
del: createMethod('DELETE'),
|
|
38
|
-
login: (url, data, callback) => {
|
|
39
|
-
const loginPromise = createMethod('POST')(url, data);
|
|
40
|
-
|
|
41
|
-
loginPromise.then(response => {
|
|
42
|
-
const token = response.data?.token;
|
|
43
|
-
if (token) {
|
|
44
|
-
localStorage.setItem('token', token); // 保存 token 到 localStorage
|
|
45
|
-
}
|
|
46
|
-
return response;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (typeof callback === 'function') {
|
|
50
|
-
loginPromise.then(data => callback(null, data)).catch(err => callback(err, null));
|
|
51
|
-
} else {
|
|
52
|
-
return loginPromise;
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
therreq.install = (app, options = {}) => {
|
|
58
|
-
// 合并基础 URL 配置
|
|
59
|
-
if (options.baseURL) {
|
|
60
|
-
setBaseURL(options.baseURL);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// 兼容 Vue2 和 Vue3 的挂载方式
|
|
64
|
-
if (typeof app === 'function' && app.prototype) { // Vue2
|
|
65
|
-
app.prototype.$etherreq = etherreq;
|
|
66
|
-
} else if (app.config) { // Vue3
|
|
67
|
-
app.config.globalProperties.$etherreq = etherreq;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// 导出组合式函数(仅 Vue3 可用)
|
|
72
|
-
export const useEtherReq = () => {
|
|
73
|
-
const instance = getCurrentInstance();
|
|
74
|
-
if (!instance) {
|
|
75
|
-
throw new Error('useEtherReq 必须在 Vue 组件的 setup 函数中调用');
|
|
76
|
-
}
|
|
77
|
-
return instance.appContext.config.globalProperties.$etherreq;
|
|
78
|
-
};
|
|
79
|
-
export { setBaseURL, _baseURL as baseURL };
|
package/src/request.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// src/request.js
|
|
2
|
-
import { create } from './etherreq.js';
|
|
3
|
-
import { requestCache, getCacheKey } from './cache.js';
|
|
4
|
-
|
|
5
|
-
// 创建一个带有默认配置的请求实例
|
|
6
|
-
const instance = create({
|
|
7
|
-
baseURL: 'https://api.example.com', // 默认基础 URL
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
// 请求拦截器
|
|
11
|
-
instance.interceptors.request.use((config) => {
|
|
12
|
-
const token = localStorage.getItem('token');
|
|
13
|
-
|
|
14
|
-
const headers = {
|
|
15
|
-
...(config.headers || {}),
|
|
16
|
-
Authorization: token ? `Bearer ${token}` : undefined,
|
|
17
|
-
'Content-Type': 'application/json',
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// 新增:若存在幂等令牌,强制启用缓存校验(即使非GET请求)
|
|
21
|
-
const useCache = (config.method === 'GET' && !config.disableCache) || !!config.idempotencyKey;
|
|
22
|
-
if (useCache) {
|
|
23
|
-
const cacheKey = getCacheKey(config.url, config);
|
|
24
|
-
const cached = requestCache.get(cacheKey);
|
|
25
|
-
|
|
26
|
-
if (cached) {
|
|
27
|
-
return Promise.resolve({
|
|
28
|
-
data: cached,
|
|
29
|
-
status: 200,
|
|
30
|
-
statusText: 'OK',
|
|
31
|
-
config,
|
|
32
|
-
isFromCache: true,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
...config,
|
|
39
|
-
headers,
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// 响应拦截器
|
|
44
|
-
instance.interceptors.response.use(
|
|
45
|
-
(response) => {
|
|
46
|
-
if (response.isFromCache) {
|
|
47
|
-
return response.data;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const config = response.config;
|
|
51
|
-
// 新增:若存在幂等令牌或GET请求未禁用缓存,则缓存结果
|
|
52
|
-
const shouldCache = !!config.idempotencyKey || (config.method === 'GET' && !config.disableCache);
|
|
53
|
-
if (shouldCache) {
|
|
54
|
-
const cacheKey = getCacheKey(config.url, config);
|
|
55
|
-
requestCache.set(cacheKey, response.data);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return response.data;
|
|
59
|
-
},
|
|
60
|
-
(error) => {
|
|
61
|
-
console.error('请求异常:', error);
|
|
62
|
-
return Promise.reject(error);
|
|
63
|
-
}
|
|
64
|
-
);
|
|
65
|
-
let _baseURL = 'https://api..com'; // 内部变量用于保存 base URL
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 封装 request 函数,支持 baseURl 拼接等逻辑
|
|
69
|
-
* @param {string} url - 请求路径
|
|
70
|
-
* @param {Object} options - 请求配置
|
|
71
|
-
*/
|
|
72
|
-
export const request = (url, options = {}) => {
|
|
73
|
-
const baseURL = options.baseURL || _baseURL;
|
|
74
|
-
const finalURL = new URL(url, baseURL).toString();
|
|
75
|
-
|
|
76
|
-
return instance({
|
|
77
|
-
...options,
|
|
78
|
-
url: finalURL,
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// 导出可读写的 baseURL 变量
|
|
83
|
-
export { _baseURL as baseURL };
|
|
84
|
-
|
|
85
|
-
// 允许外部设置 baseURL
|
|
86
|
-
export const setBaseURL = (newBaseURL) => {
|
|
87
|
-
_baseURL = newBaseURL;
|
|
88
|
-
};
|
package/src/types/etherreq.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
interface EtherRequestOptions {
|
|
2
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
3
|
-
headers?: Record<string, string | number | boolean>;
|
|
4
|
-
body?: any;
|
|
5
|
-
params?: Record<string, any>;
|
|
6
|
-
baseURL?: string;
|
|
7
|
-
idempotencyKey?: string; // 新增:幂等请求令牌
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface EtherRequestResponse<T = any> {
|
|
11
|
-
data: T;
|
|
12
|
-
status: number;
|
|
13
|
-
statusText: string;
|
|
14
|
-
headers: Headers;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 请求方法类型
|
|
18
|
-
type EtherRequestMethod = <T = any>(
|
|
19
|
-
url: string,
|
|
20
|
-
options?: EtherRequestOptions
|
|
21
|
-
) => Promise<T>;
|
|
22
|
-
|
|
23
|
-
// 主函数类型(支持调用 + 属性方法)
|
|
24
|
-
// 导入 Vue 类型(需安装 @vue/runtime-core)
|
|
25
|
-
import type { App, ComponentInternalInstance } from 'vue';
|
|
26
|
-
|
|
27
|
-
// 扩展 EtherreqStatic 类型,添加 install 方法
|
|
28
|
-
interface EtherreqStatic extends EtherRequestMethod {
|
|
29
|
-
get: EtherRequestMethod;
|
|
30
|
-
post: EtherRequestMethod;
|
|
31
|
-
put: EtherRequestMethod;
|
|
32
|
-
delete: EtherRequestMethod;
|
|
33
|
-
del: EtherRequestMethod;
|
|
34
|
-
login: EtherRequestMethod;
|
|
35
|
-
install: (app: App | any, options?: { baseURL?: string }) => void; // 兼容 Vue2/Vue3
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 声明组合式函数类型
|
|
39
|
-
export function useEtherReq(): EtherreqStatic;
|
|
40
|
-
|
|
41
|
-
// 导出对象
|
|
42
|
-
declare const etherreq: EtherreqStatic;
|
|
43
|
-
|
|
44
|
-
declare module 'etherreq' {
|
|
45
|
-
export const etherreq: EtherreqStatic;
|
|
46
|
-
export let baseURL: string;
|
|
47
|
-
export function setBaseURL(url: string): void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export { etherreq };
|
package/src/upload.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// src/upload.js
|
|
2
|
-
import { etherreq } from './index.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 分片上传配置项
|
|
6
|
-
* @typedef {Object} ChunkedUploadOptions
|
|
7
|
-
* @property {number} chunkSize 分片大小,默认 5MB
|
|
8
|
-
* @property {function(number): void} onProgress 进度回调
|
|
9
|
-
* @property {boolean} resume 是否启用断点续传
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 大文件分片上传
|
|
14
|
-
* @param {string} url 接口地址
|
|
15
|
-
* @param {File|Blob} file 文件对象
|
|
16
|
-
* @param {ChunkedUploadOptions} [options] 配置项
|
|
17
|
-
*/
|
|
18
|
-
export async function uploadLargeFile(url, file, options = {}) {
|
|
19
|
-
const { chunkSize = 5 * 1024 * 1024, onProgress = () => {}, resume = true } = options;
|
|
20
|
-
|
|
21
|
-
const totalChunks = Math.ceil(file.size / chunkSize);
|
|
22
|
-
const uploadedChunks = await getUploadedChunks(url, file, totalChunks, resume);
|
|
23
|
-
|
|
24
|
-
let current = 0;
|
|
25
|
-
|
|
26
|
-
for (let i = 0; i < totalChunks; i++) {
|
|
27
|
-
if (uploadedChunks.includes(i)) {
|
|
28
|
-
current++;
|
|
29
|
-
onProgress(Math.round((current / totalChunks) * 100));
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const start = i * chunkSize;
|
|
34
|
-
const end = Math.min(start + chunkSize, file.size);
|
|
35
|
-
const chunk = file.slice(start, end);
|
|
36
|
-
|
|
37
|
-
const formData = new FormData();
|
|
38
|
-
formData.append('file', chunk);
|
|
39
|
-
formData.append('chunkIndex', i);
|
|
40
|
-
formData.append('totalChunks', totalChunks);
|
|
41
|
-
formData.append('fileName', file.name);
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
await etherreq.post(url, formData, {
|
|
45
|
-
headers: {
|
|
46
|
-
'Content-Type': 'multipart/form-data',
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
current++;
|
|
51
|
-
onProgress(Math.round((current / totalChunks) * 100));
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.error(`上传分片 ${i} 失败`, error);
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// 合并请求
|
|
59
|
-
await etherreq.post(`${url}/complete`, {
|
|
60
|
-
fileName: file.name,
|
|
61
|
-
totalChunks,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
fileName: file.name,
|
|
66
|
-
size: file.size,
|
|
67
|
-
status: 'completed',
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 获取已上传的分片索引(用于断点续传)
|
|
73
|
-
* @param {string} uploadUrl
|
|
74
|
-
* @param {File} file
|
|
75
|
-
* @param {number} totalChunks
|
|
76
|
-
* @param {boolean} enableResume
|
|
77
|
-
* @returns {Promise<number[]>}
|
|
78
|
-
*/
|
|
79
|
-
async function getUploadedChunks(uploadUrl, file, totalChunks, enableResume) {
|
|
80
|
-
if (!enableResume) return [];
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const res = await etherreq.get(`${uploadUrl}?check=1&fileName=${encodeURIComponent(file.name)}&totalChunks=${totalChunks}`);
|
|
84
|
-
return res.data.uploadedChunks || [];
|
|
85
|
-
} catch (e) {
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
}
|