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/src/file.ts CHANGED
@@ -1,108 +1,200 @@
1
1
 
2
- /* file转base64 */
3
- export function fileToBase64(file: File | Blob): Promise<string> {
4
- return new Promise<string>((resolve, reject) => {
5
- const reader = new FileReader();
6
- reader.readAsDataURL(file); //发起异步请求
7
- reader.onload = function (readRes) {
8
- resolve(readRes.target?.result as string)
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
- /* base64转blob */
14
- export function base64ToBlob(dataURL: string, mimeType = "") {
15
- let arr: any = dataURL.split(','),
16
- defaultMimeType = arr[0].match(/:(.*?);/)[1],
17
- bStr = atob(arr[1]),
18
- n = bStr.length,
19
- u8arr = new Uint8Array(n)
20
- while (n--) {
21
- u8arr[n] = bStr.charCodeAt(n)
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
- return new Blob([u8arr], { type: mimeType || defaultMimeType })
73
+
74
+ return new File([blob], fileName, { type: mimeType });
24
75
  }
25
76
 
26
- /* blob转file */
27
- export function blobToFile(blob: Blob, fileName: string, mimeType: string) {
28
- return new File([blob], fileName, { type: mimeType })
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
- /* base64转file */
32
- export function base64ToFile(dataURL: string, fileName: string, mimeType = "") {
33
- let arr: any = dataURL.split(','),
34
- defaultMimeType = arr[0].match(/:(.*?);/)[1],
35
- bStr = atob(arr[1]),
36
- n = bStr.length,
37
- u8arr = new Uint8Array(n)
38
- while (n--) {
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
- return new File([u8arr], fileName, { type: mimeType || defaultMimeType })
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
- /** 获取指定url文件的application */
45
- export function getApplication(url: string) {
46
- let applications = [
47
- { type: "doc", application: "application/msword" },
48
- {
49
- type: "docx",
50
- application:
51
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
52
- },
53
- { type: "dot", application: "application/msword" },
54
- {
55
- type: "dotx",
56
- application:
57
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template"
58
- },
59
- { type: "xls", application: "application/vnd.ms-excel" },
60
- {
61
- type: "xlsx",
62
- application:
63
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
64
- },
65
- { type: "ppt", application: "application/vnd.ms-powerpoint" },
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
  }
@@ -1,24 +1,182 @@
1
- const getLocalStorage = <T>(key: string): T | null => {
2
- if (!window) {
3
- throw new Error('非web环境禁止使用localStorage')
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
- const setLocalStorage = <T = any>(key: string, value: T): void => {
12
- if (!window) {
13
- throw new Error('非web环境禁止使用localStorage')
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
- if (!(key && value)) {
16
- throw new Error('key or value的值不能为空')
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
- const data = JSON.stringify(value)
19
- localStorage.setItem(key, data)
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
- export {
22
- getLocalStorage,
23
- setLocalStorage
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
+ }