@zl-ui-kit/utils 0.0.0-stage → 0.3.0
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/LICENSE +21 -0
- package/README.md +26 -2
- package/dist/index.d.ts +222 -0
- package/dist/index.js +432 -0
- package/package.json +61 -3
- package/vendor/mtoken.js +4164 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vik05
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zl-ui-kit/utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ZL UI 通用工具函数库(TypeScript):数组/对象/DOM/函数、AES 加解密(crypto-js)、文件工具、axios 请求封装(`createRequest`:防重复、qs 序列化、`{cipher}` 响应解密)、微前端 URL 工具、分页/格式化等。
|
|
4
|
+
|
|
5
|
+
## 快速上手
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createRequest, convertArrayToTree, paginate, aesEncrypt } from '@zl-ui-kit/utils'
|
|
9
|
+
|
|
10
|
+
const request = createRequest({ baseURL: '/api', onError(code, err) { /* ... */ } })
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
注入式设计(决策 §12-Q3):`baseURL`、调试开关、微前端 dev 源映射均由参数注入,**不读取构建环境变量**,Vite/webpack 消费方均可使用。
|
|
14
|
+
|
|
15
|
+
## 模块说明
|
|
16
|
+
|
|
17
|
+
- 不迁移(业务/循环依赖,见需求 §5.1):原 `policy.js`、`list-hook.js`
|
|
18
|
+
- `echarts` 图表工具并入 `@zl-ui-kit/components`(随 JcChart/ZlChart 批次)
|
|
19
|
+
- UKey SDK 以 `vendor/mtoken.js` 原样附带(`@zl-ui-kit/utils/vendor/mtoken.js`,未纳入类型与主入口)
|
|
20
|
+
|
|
21
|
+
## 本地调试
|
|
22
|
+
|
|
23
|
+
源码即入口(playground alias 直指 `src/`);`pnpm build`(tsup 产 `dist/index.mjs` + d.ts);`pnpm test:unit`(vitest,纯函数用例)。详见 `docs/调试指南.md`。
|
|
24
|
+
|
|
25
|
+
## 发布
|
|
26
|
+
|
|
27
|
+
见根目录 `docs/发布流程.md`(publint/attw/脱敏扫描为硬门槛)。
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
|
|
2
|
+
export { saveAs as fileSave } from 'file-saver';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 数组工具
|
|
6
|
+
*/
|
|
7
|
+
interface ConvertArrayToTreeOptions {
|
|
8
|
+
/** 主键字段名,默认 'id' */
|
|
9
|
+
id?: string;
|
|
10
|
+
/** 父级字段名,默认 'pid' */
|
|
11
|
+
pid?: string;
|
|
12
|
+
/** 子级字段名,默认 'children' */
|
|
13
|
+
children?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 扁平数组转树结构(基于 id/pid 关联,支持乱序输入)
|
|
17
|
+
*/
|
|
18
|
+
declare function convertArrayToTree<T extends Record<string, any>>(list: T[], options?: ConvertArrayToTreeOptions): T[];
|
|
19
|
+
/**
|
|
20
|
+
* 保证返回数组:非数组值包装为单元素数组
|
|
21
|
+
*/
|
|
22
|
+
declare function ensureArray<T>(val: T | T[]): T[];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* AES 加解密工具(crypto-js,CBC/PKCS7)
|
|
26
|
+
* 密钥由使用方传入或使用默认值
|
|
27
|
+
*/
|
|
28
|
+
declare const DEFAULT_AES_KEY = "d9e6b141f8c26d74";
|
|
29
|
+
declare const DEFAULT_AES_IV_KEY = "7b51fd7053196308";
|
|
30
|
+
declare const DATABASE_AES_KEY = "b6fa92796c6431c5";
|
|
31
|
+
declare const DATABASE_AES_IV_KEY = "7b51fd7053196308";
|
|
32
|
+
declare function aesEncrypt(password: string, aesKey?: string, aesIvKey?: string): string;
|
|
33
|
+
declare function aesDecrypt(encrypted: string, aesKey?: string, aesIvKey?: string): string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* DOM 工具
|
|
37
|
+
*/
|
|
38
|
+
interface RelativeRect {
|
|
39
|
+
top: number;
|
|
40
|
+
bottom: number;
|
|
41
|
+
left: number;
|
|
42
|
+
right: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 计算元素相对于指定参照元素的矩形位置
|
|
48
|
+
*/
|
|
49
|
+
declare function getRelativeRect(target: HTMLElement, relative: HTMLElement): RelativeRect;
|
|
50
|
+
/**
|
|
51
|
+
* 读取元素样式(含计算样式回退)
|
|
52
|
+
* @param prop 样式属性名(小驼峰或 float)
|
|
53
|
+
*/
|
|
54
|
+
declare function getStyle(element: HTMLElement | null, prop: string): string | null;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 文件工具(下载/预览类型判断/大小格式化)
|
|
58
|
+
* 注入式改造(需求 §12-Q3):文件前缀由调用方传入,不再读取构建环境变量
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 拼接文件服务前缀路径
|
|
63
|
+
* @param path 文件路径(带不带开头 / 均可)
|
|
64
|
+
* @param prefix 文件服务前缀,默认 '/_file'(原实现取自环境变量,现由使用方注入)
|
|
65
|
+
*/
|
|
66
|
+
declare function joinFilePrefix(path: string, prefix?: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* 通过创建临时 <a> 标签触发浏览器下载
|
|
69
|
+
*/
|
|
70
|
+
declare function downloadFileByUrl(url: string, prefix?: string): void;
|
|
71
|
+
type FilePreviewType = 'image' | 'pdf' | 'word' | 'ppt' | 'excel';
|
|
72
|
+
/**
|
|
73
|
+
* 根据扩展名判断文件预览类型
|
|
74
|
+
*/
|
|
75
|
+
declare function fileUrlPreviewType(path: string): FilePreviewType;
|
|
76
|
+
interface FormatFilesizeOptions {
|
|
77
|
+
/** 小数位数,默认 2 */
|
|
78
|
+
precision?: number;
|
|
79
|
+
/** 单位列表 */
|
|
80
|
+
units?: string[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 格式化文件大小(B/KB/MB/GB)
|
|
84
|
+
*/
|
|
85
|
+
declare function formatFilesize(size: number, options?: FormatFilesizeOptions): string;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 函数工具
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* 防抖:延迟 delay 毫秒后执行,期间重复触发会重置计时
|
|
92
|
+
*/
|
|
93
|
+
declare function debounce<Args extends any[]>(callback: (...args: Args) => void, delay?: number): (...args: Args) => void;
|
|
94
|
+
/**
|
|
95
|
+
* 深度遍历树结构数组
|
|
96
|
+
*/
|
|
97
|
+
declare function traverseTree<T extends Record<string, any>>(tree: T[], callback: (item: T) => void, fieldNames?: {
|
|
98
|
+
children?: string;
|
|
99
|
+
}): void;
|
|
100
|
+
/**
|
|
101
|
+
* 创建带 resolve/reject 句柄的 Promise(Promise.withResolvers 的兼容实现)
|
|
102
|
+
*/
|
|
103
|
+
declare function withResolvers<T = unknown>(): {
|
|
104
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
105
|
+
reject: (reason?: unknown) => void;
|
|
106
|
+
promise: Promise<T>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 类型判断工具
|
|
111
|
+
*/
|
|
112
|
+
declare function isObject(val: unknown): val is Record<string, any>;
|
|
113
|
+
declare function isPlainObject(val: unknown): val is Record<string, any>;
|
|
114
|
+
declare function isArray(val: unknown): val is any[];
|
|
115
|
+
declare function isFunction(val: unknown): val is (...args: any[]) => any;
|
|
116
|
+
declare function isPromise(val: unknown): val is Promise<unknown>;
|
|
117
|
+
declare function isBoolean(val: unknown): val is boolean;
|
|
118
|
+
declare function isNumber(val: unknown): val is number;
|
|
119
|
+
declare function isDefined<T>(val: T | undefined): val is T;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 微前端 URL 工具(micro-app 约定)
|
|
123
|
+
* 注入式改造(需求 §12-Q3):开发态源地址映射由调用方传入,不再读取构建环境变量
|
|
124
|
+
*/
|
|
125
|
+
interface FormatMicroUrlOptions {
|
|
126
|
+
/** 是否处于开发环境(替换微应用源为本机 dev server) */
|
|
127
|
+
dev?: boolean;
|
|
128
|
+
/** 微应用标识 → 开发态源地址映射,如 { dsc: 'http://localhost:5204' } */
|
|
129
|
+
origins?: Record<string, string>;
|
|
130
|
+
}
|
|
131
|
+
interface FormatMicroUrlResult {
|
|
132
|
+
/** 从 URL 第一段路径提取的微应用标识 */
|
|
133
|
+
micro: string;
|
|
134
|
+
URL: URL;
|
|
135
|
+
url: string;
|
|
136
|
+
fullpath: string;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 格式化微前端 URL 地址
|
|
140
|
+
*/
|
|
141
|
+
declare function formatMicroUrl(url: string, options?: FormatMicroUrlOptions | boolean): FormatMicroUrlResult;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 对象工具
|
|
145
|
+
*/
|
|
146
|
+
declare function shallowCopy<T>(obj: T): T;
|
|
147
|
+
declare function hasOwn(obj: object, key: string): boolean;
|
|
148
|
+
declare function deepCopy<T>(obj: T): T;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* axios 请求封装工厂
|
|
152
|
+
* 注入式改造(需求 §12-Q3):baseURL/调试开关均由 options 注入,不读取构建环境变量,兼容任意构建链消费方
|
|
153
|
+
*/
|
|
154
|
+
|
|
155
|
+
/** 业务响应为 AES 加密数据时的密文前缀标识 */
|
|
156
|
+
declare const CRYPTO_CIPHER_CODE = "{cipher}{1}";
|
|
157
|
+
/** 扩展的请求配置 */
|
|
158
|
+
interface RequestOptions extends AxiosRequestConfig {
|
|
159
|
+
/** 业务成功码(默认 200,可按后端契约覆盖) */
|
|
160
|
+
successCode?: number;
|
|
161
|
+
/** 是否启用 `{cipher}` AES 密文响应解密(默认 true,可关闭) */
|
|
162
|
+
enableCipher?: boolean;
|
|
163
|
+
/** 重复请求时是否直接抛错(默认 true),为 false 时仅回调 onRepeat */
|
|
164
|
+
throwOnRepeat?: boolean;
|
|
165
|
+
/** 重复请求回调 */
|
|
166
|
+
onRepeat?: (config: AxiosRequestConfig) => void;
|
|
167
|
+
/** 业务错误/网络错误统一回调 */
|
|
168
|
+
onError?: (code: number, source: AxiosResponse | Error, extra?: string) => void;
|
|
169
|
+
/** 响应数据统一回调(解密后、业务码判断前) */
|
|
170
|
+
onResponse?: (data: any, res: AxiosResponse) => void;
|
|
171
|
+
/** 是否打印解密日志(原实现绑定构建环境变量,现由使用方注入) */
|
|
172
|
+
logDecrypt?: boolean;
|
|
173
|
+
}
|
|
174
|
+
/** 业务错误对象:code 为业务码(200 成功;95xx 网络/HTTP 错误码) */
|
|
175
|
+
declare class RequestError extends Error {
|
|
176
|
+
name: string;
|
|
177
|
+
code: number;
|
|
178
|
+
extra?: string;
|
|
179
|
+
constructor(code: number, message?: string, extra?: string);
|
|
180
|
+
}
|
|
181
|
+
declare module 'axios' {
|
|
182
|
+
interface AxiosRequestConfig {
|
|
183
|
+
/** 跳过重复请求检测 */
|
|
184
|
+
ignoreRepeat?: boolean;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 创建请求实例:防重复请求、qs 序列化、{cipher} 响应 AES 解密、业务码统一处理
|
|
189
|
+
*/
|
|
190
|
+
declare function createRequest(options?: RequestOptions): AxiosInstance;
|
|
191
|
+
/**
|
|
192
|
+
* 为已有请求实例追加统一拦截(供子应用/宿主扩展)
|
|
193
|
+
*/
|
|
194
|
+
declare function setupRequest(request: AxiosInstance, onRequest?: (config: AxiosRequestConfig) => void, onError?: (error: unknown) => void): void;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* ============================================================
|
|
198
|
+
* @zl-ui-kit/utils 入口
|
|
199
|
+
* 通用工具函数库(详见 README.md)
|
|
200
|
+
* 不迁移说明(需求文档 §5.1):policy.js / list-hook.js 为业务代码不迁移;
|
|
201
|
+
* mToken UKey SDK 以 vendor 形式保留(@zl-ui-kit/utils/vendor/mtoken.js),暂不纳入类型与主入口
|
|
202
|
+
* ============================================================
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
/** 生成 UUID v4 */
|
|
206
|
+
declare function generateUUID(): string;
|
|
207
|
+
/** 生成 [min, max] 区间随机整数 */
|
|
208
|
+
declare function getRandomInt(min: number, max: number): number;
|
|
209
|
+
/** 静态分页结果 */
|
|
210
|
+
interface PaginateResult<T> {
|
|
211
|
+
rows: T[];
|
|
212
|
+
pageNum: number;
|
|
213
|
+
total: number;
|
|
214
|
+
}
|
|
215
|
+
/** 静态分页(对内存数组按页截取) */
|
|
216
|
+
declare function paginate<T>(data: T[], pageSize: number, pageNum: number): PaginateResult<T>;
|
|
217
|
+
/** 千分位格式化数字(保留小数) */
|
|
218
|
+
declare function formatNumber(number: number | string): string;
|
|
219
|
+
/** 计算表格行序号(当前页第 rowIndex 行的全局序号,从 1 开始) */
|
|
220
|
+
declare function getIndex(rowIndex: number, pageNum: number, pageSize: number): number;
|
|
221
|
+
|
|
222
|
+
export { CRYPTO_CIPHER_CODE, type ConvertArrayToTreeOptions, DATABASE_AES_IV_KEY, DATABASE_AES_KEY, DEFAULT_AES_IV_KEY, DEFAULT_AES_KEY, type FilePreviewType, type FormatFilesizeOptions, type FormatMicroUrlOptions, type FormatMicroUrlResult, type PaginateResult, type RelativeRect, RequestError, type RequestOptions, aesDecrypt, aesEncrypt, convertArrayToTree, createRequest, debounce, deepCopy, downloadFileByUrl, ensureArray, fileUrlPreviewType, formatFilesize, formatMicroUrl, formatNumber, generateUUID, getIndex, getRandomInt, getRelativeRect, getStyle, hasOwn, isArray, isBoolean, isDefined, isFunction, isNumber, isObject, isPlainObject, isPromise, joinFilePrefix, paginate, createRequest as request, setupRequest, shallowCopy, traverseTree, withResolvers };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// src/array.ts
|
|
2
|
+
function convertArrayToTree(list, options = {}) {
|
|
3
|
+
var _a, _b;
|
|
4
|
+
const { id = "id", pid = "pid", children = "children" } = options;
|
|
5
|
+
const map = {};
|
|
6
|
+
const parentChain = {};
|
|
7
|
+
for (const item of list) {
|
|
8
|
+
map[_a = item[pid]] ?? (map[_a] = { [children]: [] });
|
|
9
|
+
parentChain[_b = item[pid]] ?? (parentChain[_b] = []);
|
|
10
|
+
map[item[pid]].children.push(item);
|
|
11
|
+
parentChain[item[id]] = [];
|
|
12
|
+
parentChain[item[pid]].push(item[id]);
|
|
13
|
+
if (map[item[id]]) {
|
|
14
|
+
const existedChildren = map[item[id]].children;
|
|
15
|
+
Object.assign(map[item[id]], item, { children: existedChildren });
|
|
16
|
+
} else {
|
|
17
|
+
;
|
|
18
|
+
item.children ?? (item.children = []);
|
|
19
|
+
map[item[id]] = item;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
let index = 0;
|
|
23
|
+
while (true) {
|
|
24
|
+
const item = Object.entries(parentChain)[index];
|
|
25
|
+
index += 1;
|
|
26
|
+
if (!item)
|
|
27
|
+
break;
|
|
28
|
+
const childrenIds = item[1].slice();
|
|
29
|
+
for (const child of childrenIds) {
|
|
30
|
+
childrenIds.push(...parentChain[child] ?? []);
|
|
31
|
+
}
|
|
32
|
+
childrenIds.forEach((child) => {
|
|
33
|
+
delete parentChain[child];
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return Object.keys(parentChain).flatMap((id2) => map[id2].children);
|
|
37
|
+
}
|
|
38
|
+
function ensureArray(val) {
|
|
39
|
+
return Array.isArray(val) ? val : [val];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/crypto.ts
|
|
43
|
+
import CryptoJS from "crypto-js";
|
|
44
|
+
var DEFAULT_AES_KEY = "d9e6b141f8c26d74";
|
|
45
|
+
var DEFAULT_AES_IV_KEY = "7b51fd7053196308";
|
|
46
|
+
var DATABASE_AES_KEY = "b6fa92796c6431c5";
|
|
47
|
+
var DATABASE_AES_IV_KEY = "7b51fd7053196308";
|
|
48
|
+
function aesEncrypt(password, aesKey = DEFAULT_AES_KEY, aesIvKey = DEFAULT_AES_IV_KEY) {
|
|
49
|
+
const ivs = CryptoJS.enc.Utf8.parse(aesIvKey);
|
|
50
|
+
const encrypted = CryptoJS.AES.encrypt(
|
|
51
|
+
password,
|
|
52
|
+
CryptoJS.enc.Utf8.parse(aesKey),
|
|
53
|
+
{
|
|
54
|
+
iv: ivs,
|
|
55
|
+
padding: CryptoJS.pad.Pkcs7,
|
|
56
|
+
mode: CryptoJS.mode.CBC
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
return encrypted.toString();
|
|
60
|
+
}
|
|
61
|
+
function aesDecrypt(encrypted, aesKey = DEFAULT_AES_KEY, aesIvKey = DEFAULT_AES_IV_KEY) {
|
|
62
|
+
const ivs = CryptoJS.enc.Utf8.parse(aesIvKey);
|
|
63
|
+
const decrypted = CryptoJS.AES.decrypt(
|
|
64
|
+
encrypted,
|
|
65
|
+
CryptoJS.enc.Utf8.parse(aesKey),
|
|
66
|
+
{
|
|
67
|
+
iv: ivs,
|
|
68
|
+
padding: CryptoJS.pad.Pkcs7,
|
|
69
|
+
mode: CryptoJS.mode.CBC
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
return decrypted.toString(CryptoJS.enc.Utf8);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/dom.ts
|
|
76
|
+
function getRelativeRect(target2, relative) {
|
|
77
|
+
const targetRect = target2.getBoundingClientRect();
|
|
78
|
+
const relativeRect = relative.getBoundingClientRect();
|
|
79
|
+
return {
|
|
80
|
+
top: targetRect.top - relativeRect.top,
|
|
81
|
+
bottom: relativeRect.bottom - targetRect.bottom,
|
|
82
|
+
left: targetRect.left - relativeRect.left,
|
|
83
|
+
right: relativeRect.right - targetRect.right,
|
|
84
|
+
width: targetRect.width,
|
|
85
|
+
height: targetRect.height
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function getStyle(element, prop) {
|
|
89
|
+
if (!element || !prop)
|
|
90
|
+
return null;
|
|
91
|
+
let styleName = prop;
|
|
92
|
+
if (prop === "float") {
|
|
93
|
+
styleName = "cssFloat";
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
if (document.defaultView) {
|
|
97
|
+
const computed = document.defaultView.getComputedStyle(element, "");
|
|
98
|
+
return element.style[styleName] || computed ? computed[styleName] : "";
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
return element.style[styleName];
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/file.ts
|
|
107
|
+
import { saveAs } from "file-saver";
|
|
108
|
+
function joinFilePrefix(path, prefix = "/_file") {
|
|
109
|
+
return `${prefix}${(path || "").indexOf("/") === 0 ? path : `/${path}`}`;
|
|
110
|
+
}
|
|
111
|
+
function downloadFileByUrl(url, prefix) {
|
|
112
|
+
if (!url) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const link = document.createElement("a");
|
|
116
|
+
link.href = joinFilePrefix(url, prefix);
|
|
117
|
+
document.body.appendChild(link);
|
|
118
|
+
link.click();
|
|
119
|
+
document.body.removeChild(link);
|
|
120
|
+
}
|
|
121
|
+
function fileUrlPreviewType(path) {
|
|
122
|
+
const arr = (path || "").split(".");
|
|
123
|
+
const type = arr[arr.length - 1] ?? "";
|
|
124
|
+
if (["jpg", "png", "gif", "svg"].includes(type)) {
|
|
125
|
+
return "image";
|
|
126
|
+
}
|
|
127
|
+
if (type === "pdf") {
|
|
128
|
+
return "pdf";
|
|
129
|
+
}
|
|
130
|
+
if (["docx", "doc", "dotx", "docm", "dotm"].includes(type)) {
|
|
131
|
+
return "word";
|
|
132
|
+
}
|
|
133
|
+
if (["ppt", "pptx", "pps", "ppsx", "ppa", "ppam", "pot", "potx"].includes(type)) {
|
|
134
|
+
return "ppt";
|
|
135
|
+
}
|
|
136
|
+
if (["xls", "xlsx", "xlsm", "xlsb", "csv", "et", "ods"].includes(type)) {
|
|
137
|
+
return "excel";
|
|
138
|
+
}
|
|
139
|
+
return "word";
|
|
140
|
+
}
|
|
141
|
+
function formatFilesize(size, options = {}) {
|
|
142
|
+
const { precision = 2, units = ["B", "KB", "MB", "GB"] } = options;
|
|
143
|
+
const index = Math.max(0, Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024))));
|
|
144
|
+
return `${(size / 1024 ** index).toFixed(precision)}${units[index]}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/is.ts
|
|
148
|
+
var ops = Object.prototype.toString;
|
|
149
|
+
function isObject(val) {
|
|
150
|
+
return val !== null && typeof val === "object";
|
|
151
|
+
}
|
|
152
|
+
function isPlainObject(val) {
|
|
153
|
+
return ops.call(val) === "[object Object]";
|
|
154
|
+
}
|
|
155
|
+
function isArray(val) {
|
|
156
|
+
return Array.isArray(val);
|
|
157
|
+
}
|
|
158
|
+
function isFunction(val) {
|
|
159
|
+
return typeof val === "function";
|
|
160
|
+
}
|
|
161
|
+
function isPromise(val) {
|
|
162
|
+
return ops.call(val) === "[object Promise]";
|
|
163
|
+
}
|
|
164
|
+
function isBoolean(val) {
|
|
165
|
+
return typeof val === "boolean";
|
|
166
|
+
}
|
|
167
|
+
function isNumber(val) {
|
|
168
|
+
return typeof val === "number";
|
|
169
|
+
}
|
|
170
|
+
function isDefined(val) {
|
|
171
|
+
return typeof val !== "undefined";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/function.ts
|
|
175
|
+
var target = typeof window === "undefined" ? globalThis : window;
|
|
176
|
+
function debounce(callback, delay = 300) {
|
|
177
|
+
let timer;
|
|
178
|
+
return (...args) => {
|
|
179
|
+
if (timer) {
|
|
180
|
+
target.clearTimeout(timer);
|
|
181
|
+
}
|
|
182
|
+
timer = target.setTimeout(() => {
|
|
183
|
+
timer = void 0;
|
|
184
|
+
callback(...args);
|
|
185
|
+
}, delay);
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function traverseTree(tree, callback, fieldNames = {}) {
|
|
189
|
+
const { children = "children" } = fieldNames;
|
|
190
|
+
for (let i = 0; i < tree.length; i++) {
|
|
191
|
+
const item = tree[i];
|
|
192
|
+
callback(item);
|
|
193
|
+
if (isArray(item[children])) {
|
|
194
|
+
traverseTree(item[children], callback, fieldNames);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function withResolvers() {
|
|
199
|
+
let resolve;
|
|
200
|
+
let reject;
|
|
201
|
+
const promise = new Promise((res, rej) => {
|
|
202
|
+
resolve = res;
|
|
203
|
+
reject = rej;
|
|
204
|
+
});
|
|
205
|
+
return { resolve, reject, promise };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/micro.ts
|
|
209
|
+
function formatMicroUrl(url, options = {}) {
|
|
210
|
+
const { dev = false, origins = {} } = typeof options === "boolean" ? { dev: options } : options;
|
|
211
|
+
let _url = new URL(url, window.location.origin);
|
|
212
|
+
const micro = _url.pathname.split("/")[1] ?? "";
|
|
213
|
+
if (dev && origins[micro]) {
|
|
214
|
+
_url = new URL(_url.pathname + _url.search, origins[micro]);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
micro,
|
|
218
|
+
URL: _url,
|
|
219
|
+
url: _url.href,
|
|
220
|
+
fullpath: _url.pathname + _url.search
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/object.ts
|
|
225
|
+
function shallowCopy(obj) {
|
|
226
|
+
if (isObject(obj)) {
|
|
227
|
+
return Object.assign(isArray(obj) ? [] : {}, obj);
|
|
228
|
+
}
|
|
229
|
+
return obj;
|
|
230
|
+
}
|
|
231
|
+
function hasOwn(obj, key) {
|
|
232
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
233
|
+
}
|
|
234
|
+
function deepCopy(obj) {
|
|
235
|
+
if (isObject(obj)) {
|
|
236
|
+
const newObj = isArray(obj) ? [] : {};
|
|
237
|
+
for (const key in obj) {
|
|
238
|
+
if (hasOwn(obj, key)) {
|
|
239
|
+
newObj[key] = deepCopy(obj[key]);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return newObj;
|
|
243
|
+
}
|
|
244
|
+
return obj;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/request.ts
|
|
248
|
+
import axios, { isCancel } from "axios";
|
|
249
|
+
import qs from "qs";
|
|
250
|
+
var CRYPTO_CIPHER_CODE = "{cipher}{1}";
|
|
251
|
+
var RequestError = class extends Error {
|
|
252
|
+
constructor(code, message, extra) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = "RequestError";
|
|
255
|
+
this.code = code;
|
|
256
|
+
this.extra = extra;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var requestMap = /* @__PURE__ */ new Set();
|
|
260
|
+
function generateRequestKey(config) {
|
|
261
|
+
return `${config.url}:${config.method}:${JSON.stringify(config.params)}:${JSON.stringify(config.data)}`;
|
|
262
|
+
}
|
|
263
|
+
function createRequest(options = {}) {
|
|
264
|
+
const {
|
|
265
|
+
successCode = 200,
|
|
266
|
+
enableCipher = true,
|
|
267
|
+
throwOnRepeat = true,
|
|
268
|
+
onRepeat,
|
|
269
|
+
onError,
|
|
270
|
+
onResponse,
|
|
271
|
+
logDecrypt = false,
|
|
272
|
+
...axiosOptions
|
|
273
|
+
} = options;
|
|
274
|
+
const request = axios.create({
|
|
275
|
+
paramsSerializer: {
|
|
276
|
+
serialize: (params) => {
|
|
277
|
+
return qs.stringify(params);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
...axiosOptions
|
|
281
|
+
});
|
|
282
|
+
request.interceptors.request.use((config) => {
|
|
283
|
+
if (!config.ignoreRepeat) {
|
|
284
|
+
const key = generateRequestKey(config);
|
|
285
|
+
if (requestMap.has(key)) {
|
|
286
|
+
onRepeat?.(config);
|
|
287
|
+
if (throwOnRepeat) {
|
|
288
|
+
throw new RequestError(-1, "\u8BF7\u6C42\u5DF2\u53D1\u51FA\uFF0C\u8BF7\u52FF\u91CD\u590D\u8BF7\u6C42");
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
requestMap.add(key);
|
|
292
|
+
setTimeout(() => requestMap.delete(key), 200);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return config;
|
|
296
|
+
});
|
|
297
|
+
request.interceptors.response.use((res) => {
|
|
298
|
+
const key = generateRequestKey(res.config);
|
|
299
|
+
requestMap.delete(key);
|
|
300
|
+
let data = res.data;
|
|
301
|
+
if (res.request.responseType === "blob" || res.request.responseType === "arraybuffer" || res.headers?.["content-type"] === "text/csv") {
|
|
302
|
+
return res.data;
|
|
303
|
+
} else if (enableCipher && typeof data === "string" && data?.startsWith?.(CRYPTO_CIPHER_CODE)) {
|
|
304
|
+
try {
|
|
305
|
+
const dataStr = aesDecrypt(data.slice(CRYPTO_CIPHER_CODE.length));
|
|
306
|
+
data = JSON.parse(dataStr);
|
|
307
|
+
if (logDecrypt) {
|
|
308
|
+
console.log("decrypt data: ", res.config.url, "\n", dataStr);
|
|
309
|
+
}
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
onResponse?.(data, res);
|
|
314
|
+
const code = Number(data?.code);
|
|
315
|
+
if (code === successCode) {
|
|
316
|
+
return data;
|
|
317
|
+
}
|
|
318
|
+
onError?.(code, res);
|
|
319
|
+
return Promise.reject(new RequestError(code, data?.msg));
|
|
320
|
+
}, (error) => {
|
|
321
|
+
if (isCancel(error)) {
|
|
322
|
+
return Promise.resolve();
|
|
323
|
+
}
|
|
324
|
+
let code = 9e3;
|
|
325
|
+
let errorExtraInfo;
|
|
326
|
+
const message = error.message ?? "";
|
|
327
|
+
if (message.includes("Network Error")) {
|
|
328
|
+
code = 9501;
|
|
329
|
+
} else if (message.includes("timeout")) {
|
|
330
|
+
code = 9502;
|
|
331
|
+
} else if (message.includes("Request failed with status code")) {
|
|
332
|
+
code = 9503;
|
|
333
|
+
errorExtraInfo = message.slice(message.length - 3);
|
|
334
|
+
}
|
|
335
|
+
onError?.(code, error, errorExtraInfo);
|
|
336
|
+
return Promise.reject(new RequestError(code, message, errorExtraInfo));
|
|
337
|
+
});
|
|
338
|
+
return request;
|
|
339
|
+
}
|
|
340
|
+
function setupRequest(request, onRequest, onError) {
|
|
341
|
+
request.interceptors.request.use((config) => {
|
|
342
|
+
onRequest?.(config);
|
|
343
|
+
return config;
|
|
344
|
+
});
|
|
345
|
+
request.interceptors.response.use(void 0, (error) => {
|
|
346
|
+
onError?.(error);
|
|
347
|
+
return Promise.reject(error);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
var request_default = createRequest;
|
|
351
|
+
|
|
352
|
+
// src/index.ts
|
|
353
|
+
function generateUUID() {
|
|
354
|
+
return ([1e7].toString() + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (Number(c) ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> Number(c) / 4).toString(16));
|
|
355
|
+
}
|
|
356
|
+
function getRandomInt(min, max) {
|
|
357
|
+
min = Math.ceil(min);
|
|
358
|
+
max = Math.floor(max);
|
|
359
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
360
|
+
}
|
|
361
|
+
function paginate(data, pageSize, pageNum) {
|
|
362
|
+
const total = data.length;
|
|
363
|
+
if (pageNum < 1 || pageNum > total) {
|
|
364
|
+
return {
|
|
365
|
+
rows: [],
|
|
366
|
+
pageNum: 0,
|
|
367
|
+
total: 0
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const start = (pageNum - 1) * pageSize;
|
|
371
|
+
const end = start + pageSize;
|
|
372
|
+
return {
|
|
373
|
+
rows: data.slice(start, end),
|
|
374
|
+
pageNum,
|
|
375
|
+
total
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function formatNumber(number) {
|
|
379
|
+
const numStr = (number || 0).toString();
|
|
380
|
+
const [integerPart, decimalPart] = numStr.split(".");
|
|
381
|
+
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
382
|
+
if (decimalPart) {
|
|
383
|
+
return `${formattedInteger}.${decimalPart}`;
|
|
384
|
+
}
|
|
385
|
+
return formattedInteger;
|
|
386
|
+
}
|
|
387
|
+
function getIndex(rowIndex, pageNum, pageSize) {
|
|
388
|
+
return (pageNum - 1) * pageSize + rowIndex + 1;
|
|
389
|
+
}
|
|
390
|
+
export {
|
|
391
|
+
CRYPTO_CIPHER_CODE,
|
|
392
|
+
DATABASE_AES_IV_KEY,
|
|
393
|
+
DATABASE_AES_KEY,
|
|
394
|
+
DEFAULT_AES_IV_KEY,
|
|
395
|
+
DEFAULT_AES_KEY,
|
|
396
|
+
RequestError,
|
|
397
|
+
aesDecrypt,
|
|
398
|
+
aesEncrypt,
|
|
399
|
+
convertArrayToTree,
|
|
400
|
+
createRequest,
|
|
401
|
+
debounce,
|
|
402
|
+
deepCopy,
|
|
403
|
+
downloadFileByUrl,
|
|
404
|
+
ensureArray,
|
|
405
|
+
saveAs as fileSave,
|
|
406
|
+
fileUrlPreviewType,
|
|
407
|
+
formatFilesize,
|
|
408
|
+
formatMicroUrl,
|
|
409
|
+
formatNumber,
|
|
410
|
+
generateUUID,
|
|
411
|
+
getIndex,
|
|
412
|
+
getRandomInt,
|
|
413
|
+
getRelativeRect,
|
|
414
|
+
getStyle,
|
|
415
|
+
hasOwn,
|
|
416
|
+
isArray,
|
|
417
|
+
isBoolean,
|
|
418
|
+
isDefined,
|
|
419
|
+
isFunction,
|
|
420
|
+
isNumber,
|
|
421
|
+
isObject,
|
|
422
|
+
isPlainObject,
|
|
423
|
+
isPromise,
|
|
424
|
+
joinFilePrefix,
|
|
425
|
+
paginate,
|
|
426
|
+
request_default as request,
|
|
427
|
+
setupRequest,
|
|
428
|
+
shallowCopy,
|
|
429
|
+
traverseTree,
|
|
430
|
+
withResolvers
|
|
431
|
+
};
|
|
432
|
+
//# sourceMappingURL=index.js.map
|