@hzab/utils 0.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # @hzab/utils@0.0.1
2
+ 组件初始化
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @hzab/utils
2
+
3
+ 组件模板
4
+
5
+ - node@16.16.0
6
+
7
+ - 注意:首次克隆先执行 npm run prepare 命令
8
+
9
+ # 组件
10
+
11
+ ## ossUpload 示例
12
+
13
+ ```jsx
14
+ import OssUpload from "@hzab/utils/src/ossUpload";
15
+
16
+ /**
17
+ * 批量文件上传
18
+ * @param {Array} files
19
+ * @param {Object} opt
20
+ * @returns
21
+ */
22
+ export async function handleOssUpload(files, opt) {
23
+ const _files = files;
24
+ const { ossUrl, signatureParams, ossParams, axiosConf, useHashName } = opt || {};
25
+ const ossUpload = new OssUpload({
26
+ axios: opt.axios,
27
+ axiosConf: axiosConf,
28
+ serverUrl: ossUrl || "/api/v1/user/oss/getWebOssConfig",
29
+ });
30
+
31
+ const promise = [];
32
+ _files?.forEach((file) => {
33
+ if (!file) {
34
+ return;
35
+ }
36
+ // 数据已经是 url 的情况
37
+ if (typeof file === "string" || file.ossUrl || file.url) {
38
+ promise.push(Promise.resolve(file));
39
+ } else {
40
+ promise.push(
41
+ ossUpload
42
+ .upload(file, {
43
+ signatureParams: {
44
+ isPublic: 1,
45
+ ...(signatureParams || {}),
46
+ },
47
+ ossParams,
48
+ axiosConf,
49
+ useHashName,
50
+ })
51
+ .then((res) => {
52
+ return Promise.resolve(res?.data?.data?.fileUrl);
53
+ }),
54
+ );
55
+ }
56
+ });
57
+
58
+ return Promise.all(promise).then((filePromises) => {
59
+ filePromises?.forEach((fileUrl, idx) => {
60
+ if (typeof fileUrl === "string") {
61
+ _files[idx].ossUrl = fileUrl;
62
+ _files[idx].url = fileUrl;
63
+ } else if (_files[idx].url) {
64
+ _files[idx].ossUrl = _files[idx].url;
65
+ }
66
+ });
67
+ return Promise.resolve(_files);
68
+ });
69
+ }
70
+ ```
71
+
72
+ ## API
73
+
74
+ ### InfoPanel Attributes
75
+
76
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
77
+ | ------ | ------ | ---- | ------ | ----------------- |
78
+ | schema | Object | 是 | - | 数据信息的 schema |
79
+
80
+ # 组件开发流程
81
+
82
+ - 在 config/webpack.config.js 中按需修改 library 配置的文件名
83
+ - 在 config/webpack.config.js 中按需修改 alias 配置的包名,便于本地调试
84
+ - 在 tsconfig.json 中按需修改 paths 配置的包名,解决 ts 报错问题
85
+ - npm run dev
86
+
87
+ ## 文件目录
88
+
89
+ - example 本地开发测试代码
90
+ - src 组件源码
91
+
92
+ ## 命令
93
+
94
+ - Mac 执行该命令,设置 pre-commit 为可执行文件
95
+
96
+ - npm run mac-chmod
97
+ - chmod +x .husky && chmod +x .husky/pre-commit
98
+
99
+ - 生成文档:npm run docs
100
+ - 本地运行:npm run dev
101
+ - 打包编译:npm run build
102
+
103
+ ## 发布
104
+
105
+ - 注意:示例代码生效,但发布之后未生效。确认是否执行了编译!!!
106
+
107
+ - 编译组件:npm run build
108
+ - 命令:npm publish --access public
109
+ - 发布目录:
110
+ - src
111
+
112
+ ## 配置
113
+
114
+ ### 配置文件
115
+
116
+ - 本地配置文件:config/config.js
117
+
118
+ ### webpack 配置文件
119
+
120
+ - config/webpack.config.js
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@hzab/utils",
3
+ "version": "0.0.1",
4
+ "description": "utils",
5
+ "main": "src",
6
+ "scripts": {
7
+ "dev": "webpack serve -c ./config/webpack.config.js --env local",
8
+ "build": "webpack -c ./config/webpack.config.js --env production",
9
+ "publish-beta": "npm publish --beta",
10
+ "publish-patch": "npm version patch && npm publish --access public",
11
+ "publish-minor": "npm version minor && npm publish --access public",
12
+ "publish-major": "npm version major && npm publish --access public",
13
+ "prepare": "husky install",
14
+ "mac-chmod": "chmod +x .husky && chmod +x .husky/pre-commit",
15
+ "docs": "typedoc"
16
+ },
17
+ "files": [
18
+ "CHANGELOG.md",
19
+ "src"
20
+ ],
21
+ "keywords": [],
22
+ "author": "CaiYansong",
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "@hzab/data-model": "^1.8.1",
26
+ "@hzab/permissions": "^1.0.0",
27
+ "@hzab/webpack-config": "^1.1.2",
28
+ "@types/react": "^17.0.62",
29
+ "@types/react-dom": "^17.0.20",
30
+ "antd": "^4.24.16",
31
+ "axios": "^1.4.0",
32
+ "eslint": "^8.30.0",
33
+ "less": "^4.1.3",
34
+ "mobx": "^6.7.0",
35
+ "mobx-react": "^7.6.0",
36
+ "nanoid": "^3.3.7",
37
+ "react": "^17.0.2",
38
+ "react-dom": "^17.0.2",
39
+ "react-router-dom": "^6.14.1",
40
+ "typedoc": "^0.24.8",
41
+ "typescript": "^4.9.4"
42
+ },
43
+ "directories": {
44
+ "src": "src"
45
+ },
46
+ "peerDependencies": {
47
+ "@hzab/data-model": ">=1.2.0",
48
+ "antd": "4.x",
49
+ "axios": ">=1.6.2",
50
+ "react": ">=16.8.0",
51
+ "react-dom": ">=16.8.0"
52
+ },
53
+ "lint-staged": {
54
+ "**.{js,jsx,ts,tsx,css,scss,less,json,html}": [
55
+ "prettier --write"
56
+ ]
57
+ }
58
+ }
package/src/array.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 获取数组格式数据
3
+ * @param value
4
+ * @returns
5
+ */
6
+ export function getArr(value) {
7
+ return Array.isArray(value) ? value : (value && [value]) || [];
8
+ }
package/src/color.ts ADDED
@@ -0,0 +1,302 @@
1
+ /**
2
+ * 计算颜色的亮度
3
+ * @param r
4
+ * @param g
5
+ * @param b
6
+ * @returns
7
+ */
8
+ export function luminance(r, g, b) {
9
+ const a = [r, g, b].map((v) => {
10
+ v /= 255;
11
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
12
+ });
13
+ return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
14
+ }
15
+
16
+ /**
17
+ * 计算两个颜色之间的对比度
18
+ * @param l1
19
+ * @param l2
20
+ * @returns
21
+ */
22
+ export function contrast(l1, l2) {
23
+ const brightest = Math.max(l1, l2);
24
+ const darkest = Math.min(l1, l2);
25
+ return (brightest + 0.05) / (darkest + 0.05);
26
+ }
27
+
28
+ /**
29
+ * 计算对比度
30
+ * @param luminance1
31
+ * @param luminance2
32
+ * @returns
33
+ */
34
+ export function contrastRatio(luminance1, luminance2) {
35
+ return (Math.max(luminance1, luminance2) + 0.05) / (Math.min(luminance1, luminance2) + 0.05);
36
+ }
37
+
38
+ /**
39
+ * 十六进制颜色转 RGB
40
+ * @param hex
41
+ * @returns
42
+ */
43
+ export function hexToRGB(hex) {
44
+ hex = hex.replace("#", "");
45
+ if (hex.length === 3) {
46
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
47
+ }
48
+ const r = parseInt(hex.substring(0, 2), 16);
49
+ const g = parseInt(hex.substring(2, 4), 16);
50
+ const b = parseInt(hex.substring(4, 6), 16);
51
+ return [r, g, b];
52
+ }
53
+
54
+ /**
55
+ * rgb 数值限制
56
+ * @param val
57
+ * @returns
58
+ */
59
+ export function rgbNumLimit(val) {
60
+ if (val > 250) {
61
+ return 250;
62
+ }
63
+ if (val < 0) {
64
+ return 0;
65
+ }
66
+ return val;
67
+ }
68
+
69
+ /**
70
+ * RGB 转十六进制颜色
71
+ * @param r
72
+ * @param g
73
+ * @param b
74
+ * @returns
75
+ */
76
+ export function rgbToHex(r, g, b) {
77
+ return (
78
+ "#" +
79
+ [r, g, b]
80
+ .map((x) => {
81
+ const hex = x.toString(16);
82
+ return hex.length === 1 ? "0" + hex : hex;
83
+ })
84
+ .join("")
85
+ );
86
+ }
87
+
88
+ /**
89
+ * RGB 转 HSL
90
+ * @param r
91
+ * @param g
92
+ * @param b
93
+ * @returns
94
+ */
95
+ export function rgbToHsl(r, g, b) {
96
+ r /= 255;
97
+ g /= 255;
98
+ b /= 255;
99
+ const max = Math.max(r, g, b);
100
+ const min = Math.min(r, g, b);
101
+ let h,
102
+ s,
103
+ l = (max + min) / 2;
104
+
105
+ if (max === min) {
106
+ h = s = 0;
107
+ } else {
108
+ const d = max - min;
109
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
110
+ switch (max) {
111
+ case r:
112
+ h = (g - b) / d + (g < b ? 6 : 0);
113
+ break;
114
+ case g:
115
+ h = (b - r) / d + 2;
116
+ break;
117
+ case b:
118
+ h = (r - g) / d + 4;
119
+ break;
120
+ }
121
+ h /= 6;
122
+ }
123
+ return [h * 360, s * 100, l * 100];
124
+ }
125
+
126
+ /**
127
+ * HSL 转 RGB
128
+ * @param h
129
+ * @param s
130
+ * @param l
131
+ * @returns
132
+ */
133
+ export function hslToRgb(h, s, l) {
134
+ h /= 360;
135
+ s /= 100;
136
+ l /= 100;
137
+ let r, g, b;
138
+
139
+ if (s === 0) {
140
+ r = g = b = l;
141
+ } else {
142
+ const hue2rgb = (p, q, t) => {
143
+ if (t < 0) t += 1;
144
+ if (t > 1) t -= 1;
145
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
146
+ if (t < 1 / 2) return q;
147
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
148
+ return p;
149
+ };
150
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
151
+ const p = 2 * l - q;
152
+ r = hue2rgb(p, q, h + 1 / 3);
153
+ g = hue2rgb(p, q, h);
154
+ b = hue2rgb(p, q, h - 1 / 3);
155
+ }
156
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
157
+ }
158
+
159
+ /**
160
+ * 判断是否为灰色背景
161
+ * @param r
162
+ * @param g
163
+ * @param b
164
+ * @returns
165
+ */
166
+ export function isGray(r, g, b, opt = { tolerance: 10 }) {
167
+ const {
168
+ /**
169
+ * 允许的误差范围
170
+ */
171
+ tolerance = 10,
172
+ } = opt || {}; //
173
+ return Math.abs(r - g) <= tolerance && Math.abs(g - b) <= tolerance && Math.abs(r - b) <= tolerance;
174
+ }
175
+
176
+ /**
177
+ * 根据背景颜色选择合适的文本颜色(十六进制格式)
178
+ * @param backgroundColor
179
+ * @returns
180
+ */
181
+ export function getTextColor(backgroundColor) {
182
+ let [r, g, b] = [0, 0, 0];
183
+ if (backgroundColor.startsWith("#")) {
184
+ [r, g, b] = hexToRGB(backgroundColor);
185
+ } else {
186
+ const div = document.createElement("div");
187
+ div.style.color = backgroundColor;
188
+ document.body.appendChild(div);
189
+ const cs = getComputedStyle(div);
190
+ const rgb = cs.color.match(/\d+/g).map(Number);
191
+ document.body.removeChild(div);
192
+ [r, g, b] = rgb;
193
+ }
194
+ const bgLuminance = luminance(r, g, b);
195
+ const whiteLuminance = luminance(255, 255, 255);
196
+ const blackLuminance = luminance(0, 0, 0);
197
+ const whiteContrast = contrast(bgLuminance, whiteLuminance);
198
+ const blackContrast = contrast(bgLuminance, blackLuminance);
199
+ return whiteContrast > blackContrast ? "#ffffff" : "#000000";
200
+ }
201
+
202
+ /**
203
+ * 根据背景亮度调整文字颜色深浅
204
+ * @param bgR
205
+ * @param bgG
206
+ * @param bgB
207
+ * @returns
208
+ */
209
+ export function adjustTextColor(hexColor) {
210
+ const [bgR, bgG, bgB] = hexToRGB(hexColor);
211
+ const bgLuminance = luminance(bgR, bgG, bgB);
212
+ let textR, textG, textB;
213
+ const targetContrast = 4.5; // WCAG AA 标准
214
+
215
+ if (bgR === 0 && bgG === 0 && bgB === 0) {
216
+ // 黑色背景
217
+ let textR = 255,
218
+ textG = 255,
219
+ textB = 255;
220
+ let textLuminance = luminance(textR, textG, textB);
221
+ let ratio = contrastRatio(bgLuminance, textLuminance);
222
+ if (ratio < targetContrast) {
223
+ textR = 230;
224
+ textG = 230;
225
+ textB = 230;
226
+ }
227
+ return rgbToHex(textR, textG, textB);
228
+ } else if (bgR === 255 && bgG === 255 && bgB === 255) {
229
+ // 白色背景
230
+ let textR = 0,
231
+ textG = 0,
232
+ textB = 0;
233
+ let textLuminance = luminance(textR, textG, textB);
234
+ let ratio = contrastRatio(bgLuminance, textLuminance);
235
+ if (ratio < targetContrast) {
236
+ textR = 20;
237
+ textG = 20;
238
+ textB = 20;
239
+ }
240
+ return rgbToHex(textR, textG, textB);
241
+ } else if (isGray(bgR, bgG, bgB)) {
242
+ // 灰色背景
243
+ if (bgLuminance < 0.4) {
244
+ // 深色灰色背景,使用白色文字
245
+ let textR = 255,
246
+ textG = 255,
247
+ textB = 255;
248
+ let textLuminance = luminance(textR, textG, textB);
249
+ let ratio = contrastRatio(bgLuminance, textLuminance);
250
+ if (ratio < targetContrast) {
251
+ textR = 230;
252
+ textG = 230;
253
+ textB = 230;
254
+ }
255
+ return rgbToHex(textR, textG, textB);
256
+ } else {
257
+ // 浅色灰色背景,使用黑色文字
258
+ let textR = 0,
259
+ textG = 0,
260
+ textB = 0;
261
+ let textLuminance = luminance(textR, textG, textB);
262
+ let ratio = contrastRatio(bgLuminance, textLuminance);
263
+ if (ratio < targetContrast) {
264
+ textR = 20;
265
+ textG = 20;
266
+ textB = 20;
267
+ }
268
+ return rgbToHex(textR, textG, textB);
269
+ }
270
+ }
271
+
272
+ // 非黑白灰背景,使用差值颜色
273
+ const adjustNum = 150;
274
+ if (bgLuminance > 0.5) {
275
+ // 背景较亮,文字颜色调深
276
+ textR = Math.max(0, bgR - adjustNum);
277
+ textG = Math.max(0, bgG - adjustNum);
278
+ textB = Math.max(0, bgB - adjustNum);
279
+ } else {
280
+ // 背景较暗,文字颜色调亮
281
+ textR = Math.min(255, bgR + adjustNum);
282
+ textG = Math.min(255, bgG + adjustNum);
283
+ textB = Math.min(255, bgB + adjustNum);
284
+ }
285
+ return rgbToHex(textR, textG, textB);
286
+ }
287
+
288
+ /**
289
+ * 调整颜色深浅
290
+ * @param hexColor
291
+ * @param amount
292
+ * @returns
293
+ */
294
+ export function adjustColor(hexColor, amount) {
295
+ const [r, g, b] = hexToRGB(hexColor);
296
+
297
+ const newR = rgbNumLimit(r + amount);
298
+ const newG = rgbNumLimit(g + amount);
299
+ const newB = rgbNumLimit(b + amount);
300
+
301
+ return rgbToHex(newR, newG, newB);
302
+ }
@@ -0,0 +1,37 @@
1
+ export interface IFile extends File {
2
+ file?: IFile;
3
+ url?: string;
4
+ }
5
+
6
+ /**
7
+ * 建立一个可以存取该 file 的 url
8
+ * @param {Object} file 文件
9
+ * @returns {string} url
10
+ * blob:http://localhost:8000/c9950644-5118-4231-9be7-8183bde1fdc7
11
+ */
12
+ export function getFileURL(file: IFile) {
13
+ const _file: IFile = file?.file || file;
14
+ if (!(_file instanceof File)) {
15
+ return;
16
+ }
17
+
18
+ let url = _file.url || null;
19
+
20
+ try {
21
+ // 下面函数执行的效果是一样的,只是需要针对不同的浏览器执行不同的 js 函数而已
22
+ if (window.createObjectURL != undefined) {
23
+ // basic
24
+ return window.createObjectURL(_file);
25
+ } else if (window.URL != undefined) {
26
+ // mozilla(firefox)
27
+ return window.URL.createObjectURL(_file);
28
+ } else if (window.webkitURL != undefined) {
29
+ // webkit or chrome
30
+ return window.webkitURL.createObjectURL(_file);
31
+ }
32
+ } catch (error) {
33
+ console.warn("getFileURL Error: ", error);
34
+ }
35
+
36
+ return url;
37
+ }
@@ -0,0 +1,75 @@
1
+ import { nanoidNumALetters } from "../nanoid";
2
+
3
+ /**
4
+ * 获取文件后缀
5
+ * @param fileUrl
6
+ * @returns
7
+ */
8
+ export function getFileExt(fileUrl) {
9
+ if (typeof fileUrl !== "string") {
10
+ return;
11
+ }
12
+ const res = fileUrl?.match(/([^\/]+?)\.([^\/\.]+)$/);
13
+ if (res && res.length > 2) {
14
+ return res[2];
15
+ }
16
+ return fileUrl;
17
+ }
18
+
19
+ /**
20
+ * 从 url 获取文件名称,不包含后缀
21
+ * @param {string} fileName
22
+ * @param {string} str
23
+ * @returns
24
+ */
25
+ export function getFileName(fileUrl) {
26
+ if (typeof fileUrl !== "string") {
27
+ return;
28
+ }
29
+ const res = fileUrl?.match(/([^\/]+?)\.[^\/\.]+$/);
30
+ if (res && res.length > 0) {
31
+ return res?.[1] || res?.[0];
32
+ }
33
+ return fileUrl;
34
+ }
35
+
36
+ /**
37
+ * 从 url 获取文件名称,包含后缀
38
+ * @param {string} fileName
39
+ * @param {string} str
40
+ * @returns
41
+ */
42
+ export function getFullFileName(fileUrl) {
43
+ if (typeof fileUrl !== "string") {
44
+ return;
45
+ }
46
+ const res = fileUrl?.match(/[^\/]+?\.[^\/\.]+$/);
47
+ if (res && res.length > 0) {
48
+ return res[0];
49
+ }
50
+ return fileUrl;
51
+ }
52
+
53
+ /**
54
+ * 合并文件名称,自动处理后缀
55
+ * @param {string} fileName
56
+ * @param {string} str
57
+ * @returns
58
+ */
59
+ export const mergeFileName = function (fileName, str = "") {
60
+ if (typeof fileName !== "string") {
61
+ return "";
62
+ }
63
+ const name = fileName?.match(/([^\/]+?)\.[^\/]+$/)?.[1] || "";
64
+ const suffix = fileName?.match(/[^\/]+?\.([^\/]+)$/)?.[1] || "";
65
+ return `${name}${str ?? ""}.${suffix}`;
66
+ };
67
+
68
+ /**
69
+ * 获取唯一的文件名
70
+ * @param {string} fileName
71
+ * @returns
72
+ */
73
+ export const getUFileName = function (fileName) {
74
+ return mergeFileName(getFileName(fileName) || "rc-upload", "-" + `~kid-${nanoidNumALetters()}~` + `${Date.now()}`);
75
+ };