super-toolkit 1.0.2 → 1.0.5
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/.babelrc +8 -1
- package/README.md +50 -9
- package/index.d.ts +39 -28
- package/index.ts +68 -31
- package/lib/super-toolkit.min.js +47 -1
- package/package.json +6 -3
- package/src/array.ts +81 -56
- package/src/data.ts +175 -18
- package/src/element.ts +86 -5
- package/src/event.ts +101 -27
- package/src/file.ts +188 -96
- package/src/localStorage.ts +175 -17
- package/src/random.ts +161 -0
- package/src/reg.ts +164 -40
- package/src/time.ts +170 -85
- package/tsconfig.json +15 -8
- package/webpack.config.js +76 -4
- package/test.html +0 -20
package/src/file.ts
CHANGED
|
@@ -1,108 +1,200 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
/**
|
|
3
|
+
* 文件转 Base64
|
|
4
|
+
* @param file 文件或 Blob 对象
|
|
5
|
+
* @param callback 回调函数
|
|
6
|
+
*/
|
|
7
|
+
export function fileToBase64(file: File | Blob, callback: (error: Error | null, result: string | null) => void): void {
|
|
8
|
+
if (!file) {
|
|
9
|
+
callback(new Error('File is required'), null);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
var reader = new FileReader();
|
|
14
|
+
reader.readAsDataURL(file);
|
|
15
|
+
|
|
16
|
+
reader.onload = function (readRes: any) {
|
|
17
|
+
if (readRes.target && readRes.target.result) {
|
|
18
|
+
callback(null, readRes.target.result);
|
|
19
|
+
} else {
|
|
20
|
+
callback(new Error('Failed to convert file to base64'), null);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
reader.onerror = function () {
|
|
25
|
+
callback(new Error('Error reading file'), null);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Base64 转 Blob
|
|
31
|
+
* @param dataURL Base64 字符串
|
|
32
|
+
* @param mimeType MIME 类型
|
|
33
|
+
* @returns Blob 对象
|
|
34
|
+
*/
|
|
35
|
+
export function base64ToBlob(dataURL: string, mimeType: string = ""): Blob {
|
|
36
|
+
if (!dataURL || typeof dataURL !== 'string') {
|
|
37
|
+
throw new Error('Invalid dataURL');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var arr = dataURL.split(',');
|
|
41
|
+
if (arr.length < 2) {
|
|
42
|
+
throw new Error('Invalid dataURL format');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var match = arr[0].match(/:(.*?);/);
|
|
46
|
+
if (!match) {
|
|
47
|
+
throw new Error('Invalid dataURL format');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var defaultMimeType = match[1];
|
|
51
|
+
var bStr = atob(arr[1]);
|
|
52
|
+
var n = bStr.length;
|
|
53
|
+
var u8arr = new Uint8Array(n);
|
|
54
|
+
|
|
55
|
+
for (var i = 0; i < n; i++) {
|
|
56
|
+
u8arr[i] = bStr.charCodeAt(i);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new Blob([u8arr], { type: mimeType || defaultMimeType });
|
|
11
60
|
}
|
|
12
61
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Blob 转 File
|
|
64
|
+
* @param blob Blob 对象
|
|
65
|
+
* @param fileName 文件名
|
|
66
|
+
* @param mimeType MIME 类型
|
|
67
|
+
* @returns File 对象
|
|
68
|
+
*/
|
|
69
|
+
export function blobToFile(blob: Blob, fileName: string, mimeType: string = blob.type): File {
|
|
70
|
+
if (!blob || !fileName) {
|
|
71
|
+
throw new Error('Blob and fileName are required');
|
|
22
72
|
}
|
|
23
|
-
|
|
73
|
+
|
|
74
|
+
return new File([blob], fileName, { type: mimeType });
|
|
24
75
|
}
|
|
25
76
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Base64 转 File
|
|
79
|
+
* @param dataURL Base64 字符串
|
|
80
|
+
* @param fileName 文件名
|
|
81
|
+
* @param mimeType MIME 类型
|
|
82
|
+
* @returns File 对象
|
|
83
|
+
*/
|
|
84
|
+
export function base64ToFile(dataURL: string, fileName: string, mimeType: string = ""): File {
|
|
85
|
+
if (!dataURL || !fileName) {
|
|
86
|
+
throw new Error('dataURL and fileName are required');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
var blob = base64ToBlob(dataURL, mimeType);
|
|
90
|
+
return blobToFile(blob, fileName, mimeType || blob.type);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 缓存文件类型映射
|
|
94
|
+
var MIME_TYPE_MAP: Record<string, string> = {
|
|
95
|
+
'doc': 'application/msword',
|
|
96
|
+
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
97
|
+
'dot': 'application/msword',
|
|
98
|
+
'dotx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
|
99
|
+
'xls': 'application/vnd.ms-excel',
|
|
100
|
+
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
101
|
+
'ppt': 'application/vnd.ms-powerpoint',
|
|
102
|
+
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
103
|
+
'pdf': 'application/pdf',
|
|
104
|
+
'txt': 'text/plain',
|
|
105
|
+
'gif': 'image/gif',
|
|
106
|
+
'jpeg': 'image/jpeg',
|
|
107
|
+
'jpg': 'image/jpeg',
|
|
108
|
+
'png': 'image/png',
|
|
109
|
+
'css': 'text/css',
|
|
110
|
+
'html': 'text/html',
|
|
111
|
+
'htm': 'text/html',
|
|
112
|
+
'xsl': 'text/xml',
|
|
113
|
+
'xml': 'text/xml',
|
|
114
|
+
'mpeg': 'video/mpeg',
|
|
115
|
+
'mpg': 'video/mpeg',
|
|
116
|
+
'avi': 'video/x-msvideo',
|
|
117
|
+
'movie': 'video/x-sgi-movie',
|
|
118
|
+
'bin': 'application/octet-stream',
|
|
119
|
+
'exe': 'application/octet-stream',
|
|
120
|
+
'so': 'application/octet-stream',
|
|
121
|
+
'dll': 'application/octet-stream',
|
|
122
|
+
'ai': 'application/postscript',
|
|
123
|
+
'dir': 'application/x-director',
|
|
124
|
+
'js': 'application/x-javascript',
|
|
125
|
+
'swf': 'application/x-shockwave-flash',
|
|
126
|
+
'xhtml': 'application/xhtml+xml',
|
|
127
|
+
'xht': 'application/xhtml+xml',
|
|
128
|
+
'zip': 'application/zip',
|
|
129
|
+
'mid': 'audio/midi',
|
|
130
|
+
'midi': 'audio/midi',
|
|
131
|
+
'mp3': 'audio/mpeg',
|
|
132
|
+
'rm': 'audio/x-pn-realaudio',
|
|
133
|
+
'rpm': 'audio/x-pn-realaudio-plugin',
|
|
134
|
+
'wav': 'audio/x-wav',
|
|
135
|
+
'bmp': 'image/bmp'
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 获取指定 URL 文件的 MIME 类型
|
|
140
|
+
* @param url 文件 URL
|
|
141
|
+
* @returns MIME 类型字符串或 null
|
|
142
|
+
*/
|
|
143
|
+
export function getApplication(url: string): string | null {
|
|
144
|
+
if (!url || typeof url !== 'string') {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var parts = url.split('.');
|
|
149
|
+
var type = parts.length > 0 ? parts[parts.length - 1].toLowerCase() : null;
|
|
150
|
+
if (!type) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return MIME_TYPE_MAP[type] || null;
|
|
29
155
|
}
|
|
30
156
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
u8arr[n] = bStr.charCodeAt(n)
|
|
157
|
+
/**
|
|
158
|
+
* 下载文件
|
|
159
|
+
* @param data 文件数据(Blob 或 File)
|
|
160
|
+
* @param fileName 文件名
|
|
161
|
+
*/
|
|
162
|
+
export function downloadFile(data: Blob | File, fileName: string): void {
|
|
163
|
+
if (!data || !fileName) {
|
|
164
|
+
throw new Error('Data and fileName are required');
|
|
40
165
|
}
|
|
41
|
-
|
|
166
|
+
|
|
167
|
+
var url = URL.createObjectURL(data);
|
|
168
|
+
var link = document.createElement('a');
|
|
169
|
+
link.href = url;
|
|
170
|
+
link.download = fileName;
|
|
171
|
+
document.body.appendChild(link);
|
|
172
|
+
link.click();
|
|
173
|
+
document.body.removeChild(link);
|
|
174
|
+
URL.revokeObjectURL(url);
|
|
42
175
|
}
|
|
43
176
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
type: "pptx",
|
|
68
|
-
application:
|
|
69
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
70
|
-
},
|
|
71
|
-
{ type: "pdf", application: "application/pdf" },
|
|
72
|
-
{ type: "txt", application: "text/plain" },
|
|
73
|
-
{ type: "gif", application: "image/gif" },
|
|
74
|
-
{ type: "jpeg", application: "image/jpeg" },
|
|
75
|
-
{ type: "jpg", application: "image/jpeg" },
|
|
76
|
-
{ type: "png", application: "image/png" },
|
|
77
|
-
{ type: "css", application: "text/css" },
|
|
78
|
-
{ type: "html", application: "text/html" },
|
|
79
|
-
{ type: "htm", application: "text/html" },
|
|
80
|
-
{ type: "xsl", application: "text/xml" },
|
|
81
|
-
{ type: "xml", application: "text/xml" },
|
|
82
|
-
{ type: "mpeg", application: "video/mpeg" },
|
|
83
|
-
{ type: "mpg", application: "video/mpeg" },
|
|
84
|
-
{ type: "avi", application: "video/x-msvideo" },
|
|
85
|
-
{ type: "movie", application: "video/x-sgi-movie" },
|
|
86
|
-
{ type: "bin", application: "application/octet-stream" },
|
|
87
|
-
{ type: "exe", application: "application/octet-stream" },
|
|
88
|
-
{ type: "so", application: "application/octet-stream" },
|
|
89
|
-
{ type: "dll", application: "application/octet-stream" },
|
|
90
|
-
{ type: "ai", application: "application/postscript" },
|
|
91
|
-
{ type: "dir", application: "application/x-director" },
|
|
92
|
-
{ type: "js", application: "application/x-javascript" },
|
|
93
|
-
{ type: "swf", application: "application/x-shockwave-flash" },
|
|
94
|
-
{ type: "xhtml", application: "application/xhtml+xml" },
|
|
95
|
-
{ type: "xht", application: "application/xhtml+xml" },
|
|
96
|
-
{ type: "zip", application: "application/zip" },
|
|
97
|
-
{ type: "mid", application: "audio/midi" },
|
|
98
|
-
{ type: "midi", application: "audio/midi" },
|
|
99
|
-
{ type: "mp3", application: "audio/mpeg" },
|
|
100
|
-
{ type: "rm", application: "audio/x-pn-realaudio" },
|
|
101
|
-
{ type: "rpm", application: "audio/x-pn-realaudio-plugin" },
|
|
102
|
-
{ type: "wav", application: "audio/x-wav" },
|
|
103
|
-
{ type: "bmp", application: "image/bmp" }
|
|
104
|
-
];
|
|
105
|
-
let type = url.split('.').pop();
|
|
106
|
-
const row = applications.find(t => t.type == type)
|
|
107
|
-
return row?.type || null
|
|
177
|
+
/**
|
|
178
|
+
* 从 URL 获取文件
|
|
179
|
+
* @param url 文件 URL
|
|
180
|
+
* @param callback 回调函数
|
|
181
|
+
*/
|
|
182
|
+
export function getFileFromUrl(url: string, callback: (error: Error | null, result: Blob | null) => void): void {
|
|
183
|
+
var xhr = new XMLHttpRequest();
|
|
184
|
+
xhr.open('GET', url, true);
|
|
185
|
+
xhr.responseType = 'blob';
|
|
186
|
+
|
|
187
|
+
xhr.onload = function() {
|
|
188
|
+
if (xhr.status === 200) {
|
|
189
|
+
callback(null, xhr.response);
|
|
190
|
+
} else {
|
|
191
|
+
callback(new Error('HTTP error! status: ' + xhr.status), null);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
xhr.onerror = function() {
|
|
196
|
+
callback(new Error('Network error'), null);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
xhr.send();
|
|
108
200
|
}
|
package/src/localStorage.ts
CHANGED
|
@@ -1,24 +1,182 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 检查是否支持 localStorage
|
|
3
|
+
* @returns 是否支持 localStorage
|
|
4
|
+
*/
|
|
5
|
+
const isLocalStorageSupported = (): boolean => {
|
|
6
|
+
if (typeof window === 'undefined') {
|
|
7
|
+
return false;
|
|
4
8
|
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const testKey = '__localStorage_test__';
|
|
12
|
+
localStorage.setItem(testKey, testKey);
|
|
13
|
+
localStorage.removeItem(testKey);
|
|
14
|
+
return true;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 获取 localStorage 数据
|
|
22
|
+
* @param key 存储键名
|
|
23
|
+
* @param defaultValue 默认值
|
|
24
|
+
* @returns 存储的数据或默认值
|
|
25
|
+
*/
|
|
26
|
+
export function getLocalStorage<T>(key: string, defaultValue?: T): T | null {
|
|
27
|
+
if (!isLocalStorageSupported()) {
|
|
28
|
+
console.warn('localStorage is not supported in this environment');
|
|
29
|
+
return defaultValue || null;
|
|
30
|
+
}
|
|
31
|
+
|
|
5
32
|
if (!key) {
|
|
6
|
-
throw new Error('key
|
|
33
|
+
throw new Error('key cannot be empty');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const data = localStorage.getItem(key);
|
|
38
|
+
return data ? JSON.parse(data) : (defaultValue || null);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Error getting data from localStorage:', error);
|
|
41
|
+
return defaultValue || null;
|
|
7
42
|
}
|
|
8
|
-
const data = localStorage.getItem(key)
|
|
9
|
-
return data ? JSON.parse(data) : null
|
|
10
43
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 设置 localStorage 数据
|
|
47
|
+
* @param key 存储键名
|
|
48
|
+
* @param value 存储数据
|
|
49
|
+
* @param expire 过期时间(毫秒)
|
|
50
|
+
*/
|
|
51
|
+
export function setLocalStorage<T = any>(key: string, value: T, expire?: number): void {
|
|
52
|
+
if (!isLocalStorageSupported()) {
|
|
53
|
+
console.warn('localStorage is not supported in this environment');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!key) {
|
|
58
|
+
throw new Error('key cannot be empty');
|
|
14
59
|
}
|
|
15
|
-
|
|
16
|
-
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const data = {
|
|
63
|
+
value,
|
|
64
|
+
expire: expire ? Date.now() + expire : null
|
|
65
|
+
};
|
|
66
|
+
localStorage.setItem(key, JSON.stringify(data));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('Error setting data to localStorage:', error);
|
|
69
|
+
// 处理存储容量不足的情况
|
|
70
|
+
if ((error as Error).name === 'QuotaExceededError') {
|
|
71
|
+
console.warn('localStorage quota exceeded, trying to clear old data');
|
|
72
|
+
// 这里可以添加清理策略
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 移除 localStorage 数据
|
|
79
|
+
* @param key 存储键名
|
|
80
|
+
*/
|
|
81
|
+
export function removeLocalStorage(key: string): void {
|
|
82
|
+
if (!isLocalStorageSupported()) {
|
|
83
|
+
console.warn('localStorage is not supported in this environment');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!key) {
|
|
88
|
+
throw new Error('key cannot be empty');
|
|
17
89
|
}
|
|
18
|
-
|
|
19
|
-
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
localStorage.removeItem(key);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error removing data from localStorage:', error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 清空所有 localStorage 数据
|
|
100
|
+
*/
|
|
101
|
+
export function clearLocalStorage(): void {
|
|
102
|
+
if (!isLocalStorageSupported()) {
|
|
103
|
+
console.warn('localStorage is not supported in this environment');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
localStorage.clear();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('Error clearing localStorage:', error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 获取所有 localStorage 键名
|
|
116
|
+
* @returns 键名数组
|
|
117
|
+
*/
|
|
118
|
+
export function getLocalStorageKeys(): string[] {
|
|
119
|
+
if (!isLocalStorageSupported()) {
|
|
120
|
+
console.warn('localStorage is not supported in this environment');
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const keys: string[] = [];
|
|
126
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
127
|
+
const key = localStorage.key(i);
|
|
128
|
+
if (key) {
|
|
129
|
+
keys.push(key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return keys;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Error getting localStorage keys:', error);
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 创建带命名空间的 localStorage 操作
|
|
141
|
+
* @param namespace 命名空间
|
|
142
|
+
* @returns 命名空间化的 localStorage 操作方法
|
|
143
|
+
*/
|
|
144
|
+
export function createNamespacedStorage(namespace: string) {
|
|
145
|
+
const prefix = `${namespace}:`;
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
get: <T>(key: string, defaultValue?: T): T | null => {
|
|
149
|
+
return getLocalStorage(`${prefix}${key}`, defaultValue);
|
|
150
|
+
},
|
|
151
|
+
set: <T = any>(key: string, value: T, expire?: number): void => {
|
|
152
|
+
setLocalStorage(`${prefix}${key}`, value, expire);
|
|
153
|
+
},
|
|
154
|
+
remove: (key: string): void => {
|
|
155
|
+
removeLocalStorage(`${prefix}${key}`);
|
|
156
|
+
},
|
|
157
|
+
clear: (): void => {
|
|
158
|
+
const keys = getLocalStorageKeys();
|
|
159
|
+
keys.forEach(key => {
|
|
160
|
+
if (key.startsWith(prefix)) {
|
|
161
|
+
removeLocalStorage(key);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
keys: (): string[] => {
|
|
166
|
+
const keys = getLocalStorageKeys();
|
|
167
|
+
return keys
|
|
168
|
+
.filter(key => key.startsWith(prefix))
|
|
169
|
+
.map(key => key.replace(prefix, ''));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
20
172
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
173
|
+
|
|
174
|
+
// 导出默认方法
|
|
175
|
+
export default {
|
|
176
|
+
get: getLocalStorage,
|
|
177
|
+
set: setLocalStorage,
|
|
178
|
+
remove: removeLocalStorage,
|
|
179
|
+
clear: clearLocalStorage,
|
|
180
|
+
keys: getLocalStorageKeys,
|
|
181
|
+
createNamespaced: createNamespacedStorage
|
|
182
|
+
};
|
package/src/random.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 随机生成16进制颜色
|
|
3
|
+
* @returns 16进制颜色字符串
|
|
4
|
+
*/
|
|
5
|
+
export function getRandomColor(): string {
|
|
6
|
+
// 使用更兼容的实现方式
|
|
7
|
+
const color = Math.floor(Math.random() * 0xFFFFFF).toString(16);
|
|
8
|
+
// 手动补零,确保长度为6
|
|
9
|
+
const paddedColor = '000000'.substring(0, 6 - color.length) + color;
|
|
10
|
+
return '#' + paddedColor;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 随机生成指定长度的字符串
|
|
15
|
+
* @param length 字符串长度
|
|
16
|
+
* @param options 配置选项
|
|
17
|
+
* @returns 随机字符串
|
|
18
|
+
*/
|
|
19
|
+
export function getRandomString(
|
|
20
|
+
length: number,
|
|
21
|
+
options: {
|
|
22
|
+
includeLetters?: boolean;
|
|
23
|
+
includeNumbers?: boolean;
|
|
24
|
+
includeSymbols?: boolean;
|
|
25
|
+
caseSensitive?: boolean;
|
|
26
|
+
} = {}
|
|
27
|
+
): string {
|
|
28
|
+
// 参数验证
|
|
29
|
+
if (typeof length !== 'number' || length < 1) {
|
|
30
|
+
throw new Error('Length must be a positive number');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
includeLetters = true,
|
|
35
|
+
includeNumbers = true,
|
|
36
|
+
includeSymbols = false,
|
|
37
|
+
caseSensitive = true
|
|
38
|
+
} = options;
|
|
39
|
+
|
|
40
|
+
// 定义字符集
|
|
41
|
+
let charset = '';
|
|
42
|
+
|
|
43
|
+
if (includeLetters) {
|
|
44
|
+
charset += caseSensitive ? 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' : 'abcdefghijklmnopqrstuvwxyz';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (includeNumbers) {
|
|
48
|
+
charset += '0123456789';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (includeSymbols) {
|
|
52
|
+
charset += '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (charset.length === 0) {
|
|
56
|
+
throw new Error('At least one character set must be included');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 生成随机字符串
|
|
60
|
+
let result = '';
|
|
61
|
+
for (let i = 0; i < length; i++) {
|
|
62
|
+
const randomIndex = Math.floor(Math.random() * charset.length);
|
|
63
|
+
result += charset[randomIndex];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 生成指定范围内的随机整数
|
|
71
|
+
* @param min 最小值(包含)
|
|
72
|
+
* @param max 最大值(包含)
|
|
73
|
+
* @returns 随机整数
|
|
74
|
+
*/
|
|
75
|
+
export function getRandomInt(min: number, max: number): number {
|
|
76
|
+
// 参数验证
|
|
77
|
+
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
78
|
+
throw new Error('Min and max must be numbers');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (min > max) {
|
|
82
|
+
[min, max] = [max, min];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 生成指定范围内的随机浮点数
|
|
90
|
+
* @param min 最小值
|
|
91
|
+
* @param max 最大值
|
|
92
|
+
* @param decimals 小数位数
|
|
93
|
+
* @returns 随机浮点数
|
|
94
|
+
*/
|
|
95
|
+
export function getRandomFloat(min: number, max: number, decimals: number = 2): number {
|
|
96
|
+
// 参数验证
|
|
97
|
+
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
98
|
+
throw new Error('Min and max must be numbers');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (min > max) {
|
|
102
|
+
[min, max] = [max, min];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const random = Math.random() * (max - min) + min;
|
|
106
|
+
return parseFloat(random.toFixed(decimals));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 从数组中随机选择一个元素
|
|
111
|
+
* @param array 源数组
|
|
112
|
+
* @returns 随机选中的元素
|
|
113
|
+
*/
|
|
114
|
+
export function getRandomElement<T>(array: T[]): T | undefined {
|
|
115
|
+
// 参数验证
|
|
116
|
+
if (!Array.isArray(array) || array.length === 0) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const randomIndex = Math.floor(Math.random() * array.length);
|
|
121
|
+
return array[randomIndex];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 随机打乱数组
|
|
126
|
+
* @param array 源数组
|
|
127
|
+
* @returns 打乱后的新数组
|
|
128
|
+
*/
|
|
129
|
+
export function shuffleArray<T>(array: T[]): T[] {
|
|
130
|
+
// 参数验证
|
|
131
|
+
if (!Array.isArray(array)) {
|
|
132
|
+
throw new Error('Input must be an array');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 创建数组副本
|
|
136
|
+
const result = [...array];
|
|
137
|
+
|
|
138
|
+
// Fisher-Yates 洗牌算法
|
|
139
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
140
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
141
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 生成指定长度的随机ID
|
|
149
|
+
* @param prefix ID前缀
|
|
150
|
+
* @param length 随机部分长度
|
|
151
|
+
* @returns 随机ID
|
|
152
|
+
*/
|
|
153
|
+
export function generateRandomId(prefix: string = '', length: number = 8): string {
|
|
154
|
+
const randomPart = getRandomString(length, {
|
|
155
|
+
includeLetters: true,
|
|
156
|
+
includeNumbers: true,
|
|
157
|
+
caseSensitive: true
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return prefix ? `${prefix}_${randomPart}` : randomPart;
|
|
161
|
+
}
|