ph-utils 0.9.10 → 0.9.13

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
- ## ph-utils
2
-
3
- 整理了 js 前后端开发(web + nodejs)时常用的一些工具;[详细文档](https://gitee.com/towardly/ph/wikis/Home?sort_id=4035190)
4
-
5
- ### 包含如下工具文件
6
-
7
- `index` 基础工具类、`date` 跟日期相关的工具类、`file` 文件操作相关工具类[**服务端**]、`server` 服务端工具类、`validator` 数据验证、`dom` 浏览器节点操作相关[**前端**]、`web` 一些只适用于前端相关的工具、`color` 颜色相关工具
1
+ ## ph-utils
2
+
3
+ 整理了 js 前后端开发(web + nodejs)时常用的一些工具;[详细文档](https://gitee.com/towardly/ph/wikis/Home?sort_id=4035190)
4
+
5
+ ### 包含如下工具文件
6
+
7
+ `index` 基础工具类、`date` 跟日期相关的工具类、`file` 文件操作相关工具类[**服务端**]、`server` 服务端工具类、`validator` 数据验证、`dom` 浏览器节点操作相关[**前端**]、`web` 一些只适用于前端相关的工具、`color` 颜色相关工具
package/lib/index.d.ts CHANGED
@@ -126,4 +126,29 @@ export declare function round(num: number, precision?: number, roundType?: 0 | 1
126
126
  * 反转字符串
127
127
  */
128
128
  export declare function reverseStr(str: string): string;
129
+ /** 数据格式化配置 */
130
+ interface FormDataConfig<T> {
131
+ /** 配置需要转换为数字的字段 */
132
+ numberFields?: (keyof T)[];
133
+ /** 配置需要转换为字符串的字段 */
134
+ stringFields?: (keyof T)[];
135
+ /** 自定义的格式化 */
136
+ formatter?: {
137
+ [K in keyof T]?: "number" | "string" | ((value: any) => number | string);
138
+ };
139
+ }
140
+ /**
141
+ * 嵌套的 json 指定 key 数据
142
+ * @param data JSON格式数据
143
+ * @param keys 待获取的数据 key, 可以通过 [.] 获取嵌套数据, 例如: a.b.c
144
+ * @returns
145
+ */
146
+ export declare function getJSONValue(data: Record<string, any>, keystr: string): Record<string, any> | null;
147
+ /**
148
+ * 数据格式化主要用于数据类型转换
149
+ * @param data 待转换数据类型的数据
150
+ * @param config 转换配置
151
+ * @returns
152
+ */
153
+ export declare function formatData<T extends Record<string, any>>(data: T, config?: FormDataConfig<T>): T;
129
154
  export {};
package/lib/index.js CHANGED
@@ -173,3 +173,67 @@ export function round(num, precision = 2, roundType = 0) {
173
173
  export function reverseStr(str) {
174
174
  return str.split("").reverse().join("");
175
175
  }
176
+ function getObjKeyValue(data, key) {
177
+ if (data == null)
178
+ return null;
179
+ return data[key];
180
+ }
181
+ /**
182
+ * 嵌套的 json 指定 key 数据
183
+ * @param data JSON格式数据
184
+ * @param keys 待获取的数据 key, 可以通过 [.] 获取嵌套数据, 例如: a.b.c
185
+ * @returns
186
+ */
187
+ export function getJSONValue(data, keystr) {
188
+ if (data == null)
189
+ return null;
190
+ const keys = keystr.split(".");
191
+ let res = data;
192
+ for (const key of keys) {
193
+ res = getObjKeyValue(res, key);
194
+ if (res == null)
195
+ break;
196
+ }
197
+ return res;
198
+ }
199
+ /**
200
+ * 数据格式化主要用于数据类型转换
201
+ * @param data 待转换数据类型的数据
202
+ * @param config 转换配置
203
+ * @returns
204
+ */
205
+ export function formatData(data, config) {
206
+ const cfg = {
207
+ numberFields: [],
208
+ stringFields: [],
209
+ formatter: {},
210
+ ...config,
211
+ };
212
+ const res = {};
213
+ for (const key in data) {
214
+ let value = getJSONValue(data, key);
215
+ let formater;
216
+ if (key in cfg.formatter) {
217
+ formater = cfg.formatter[key];
218
+ }
219
+ if (cfg.numberFields.includes(key)) {
220
+ formater = "number";
221
+ }
222
+ if (cfg.stringFields.includes(key)) {
223
+ formater = "string";
224
+ }
225
+ if (formater != null) {
226
+ if (typeof formater == "function") {
227
+ value = formater(value);
228
+ }
229
+ else if (formater == "number") {
230
+ value = Number(value);
231
+ }
232
+ else if (formater == "string") {
233
+ value = String(value);
234
+ }
235
+ }
236
+ res[key] = value;
237
+ }
238
+ return res;
239
+ }
package/lib/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { execFile } from "node:child_process";
2
2
  import { parseEnv, parseArgs } from "node:util";
3
3
  import { readFileSync } from "node:fs";
4
4
  /**
@@ -7,47 +7,37 @@ import { readFileSync } from "node:fs";
7
7
  * @returns
8
8
  */
9
9
  export function exec(command, ...params) {
10
- return new Promise((resolve, reject) => {
11
- let argvs = [];
12
- const commandItems = command.split(" ");
13
- const cmd = commandItems.shift();
14
- if (commandItems.length > 0) {
15
- argvs = commandItems;
16
- }
17
- let opts = undefined;
18
- if (params[0] != null) {
19
- if (params[0] instanceof Array) {
20
- argvs.push(...params[0]);
21
- if (params[1] != null) {
22
- opts = params[1];
23
- }
24
- }
25
- else {
26
- opts = params[0];
10
+ let argvs = [];
11
+ const commandItems = command.split(" ");
12
+ const cmd = commandItems.shift();
13
+ if (commandItems.length > 0) {
14
+ argvs = commandItems;
15
+ }
16
+ let opts = { shell: true };
17
+ if (params[0] != null) {
18
+ if (params[0] instanceof Array) {
19
+ argvs.push(...params[0]);
20
+ if (params[1] != null) {
21
+ opts = params[1];
27
22
  }
28
23
  }
29
- const prs = spawn(cmd, argvs, opts);
30
- let msg = [];
31
- let error;
32
- prs.stderr.on("data", (chunk) => {
33
- msg.push(chunk.toString("utf-8"));
34
- });
35
- prs.stdout.on("data", (chunk) => {
36
- msg.push(chunk.toString("utf-8"));
37
- });
38
- prs.on("error", (err) => {
39
- error = err;
40
- });
41
- prs.on("close", (code, _signal) => {
42
- if (code === 0) {
43
- resolve(msg.join("\n"));
24
+ else {
25
+ opts = params[0];
26
+ }
27
+ }
28
+ return new Promise((resolve, reject) => {
29
+ execFile(cmd, argvs, opts, (err, stdout, stderr) => {
30
+ if (err) {
31
+ reject(err);
44
32
  }
45
33
  else {
46
- if (error == null) {
47
- error = new Error(msg[msg.length - 1]);
34
+ let msg = stdout || stderr;
35
+ if (msg.includes("error") || msg.includes("Error")) {
36
+ const error = new Error(msg);
37
+ error.type = "StdErr";
38
+ reject(error);
48
39
  }
49
- error.errno = code;
50
- reject(error);
40
+ resolve(msg);
51
41
  }
52
42
  });
53
43
  });
package/lib/theme.js CHANGED
@@ -19,10 +19,10 @@ export async function initTheme() {
19
19
  if ($themeStyle == null) {
20
20
  $themeStyle = document.createElement("style");
21
21
  $themeStyle.id = "theme-style";
22
- $themeStyle.innerHTML = `
23
- :root{color-scheme:light dark;}
24
- html.light{color-scheme: light;}
25
- html.dark {color-scheme: dark;}
22
+ $themeStyle.innerHTML = `
23
+ :root{color-scheme:light dark;}
24
+ html.light{color-scheme: light;}
25
+ html.dark {color-scheme: dark;}
26
26
  `;
27
27
  document.head.appendChild($themeStyle);
28
28
  }
@@ -52,6 +52,7 @@ declare class Validator {
52
52
  * 只验证指定 key 的数据格式
53
53
  * @param key 指定待验证的 key
54
54
  * @param value 待验证的数据
55
+ * @param data 原始数据,当验证确认密码时需要使用
55
56
  */
56
57
  validateKey(key: string, value: any, data?: any): Promise<{
57
58
  key: string;
package/lib/validator.js CHANGED
@@ -136,6 +136,7 @@ class Validator {
136
136
  * 只验证指定 key 的数据格式
137
137
  * @param key 指定待验证的 key
138
138
  * @param value 待验证的数据
139
+ * @param data 原始数据,当验证确认密码时需要使用
139
140
  */
140
141
  async validateKey(key, value, data) {
141
142
  let keyRules = this.rules[key];
package/package.json CHANGED
@@ -68,7 +68,7 @@
68
68
  },
69
69
  "./*": "./lib/*"
70
70
  },
71
- "version": "0.9.10",
71
+ "version": "0.9.13",
72
72
  "type": "module",
73
73
  "repository": {
74
74
  "type": "git",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "homepage": "https://gitee.com/towardly/ph/tree/master/packages/utils",
84
84
  "devDependencies": {
85
- "@types/node": "^20.12.12",
86
- "typescript": "^5.4.5"
85
+ "@types/node": "^22.7.9",
86
+ "typescript": "^5.6.3"
87
87
  },
88
88
  "files": [
89
89
  "lib"
@@ -1,11 +0,0 @@
1
- /**
2
- * 复制数据, 可以从多种类型的数据
3
- * 1. 直接复制文本: await copy("待复制的文本")
4
- * 2. 复制节点上的 data-copy-text:
5
- * <button data-copy-text="这是待复制的文本">复制</button>
6
- * await copy(e.target) // or await copy("#a") or await copy(document.querySelector('#a'))
7
- * 3. 直接复制节点本身数据: await copy('#a')
8
- * @param {string | HTMLElement} source 复制源, 从中解析待复制的数据
9
- * @returns {Promise<boolean>} 是否复制成功
10
- */
11
- export declare function copy(source: string | HTMLElement): Promise<boolean>;
package/lib/clipboard.js DELETED
@@ -1,101 +0,0 @@
1
- /**
2
- * 创建一个临时节点缓存待复制数据
3
- * @param {String} value - 待复制文本
4
- * @return {HTMLElement}
5
- */
6
- function createFakeElement(value) {
7
- const fakeElement = document.createElement("textarea");
8
- fakeElement.style.border = "0";
9
- fakeElement.style.padding = "0";
10
- fakeElement.style.margin = "0";
11
- fakeElement.style.position = "absolute";
12
- fakeElement.style.left = "-9999px";
13
- fakeElement.style.top = "-9999";
14
- fakeElement.setAttribute("readonly", "");
15
- fakeElement.value = value;
16
- return fakeElement;
17
- }
18
- /** 通过执行 execCommand 来执行复制 */
19
- function copyFromCommand(text) {
20
- // 添加节点
21
- const fakeEl = createFakeElement(text);
22
- document.body.append(fakeEl);
23
- fakeEl.focus();
24
- fakeEl.select();
25
- // 执行复制
26
- const res = document.execCommand("copy");
27
- fakeEl.remove(); // 删除节点
28
- return Promise.resolve(res);
29
- }
30
- /** 使用 navigator.clipboard 复制 */
31
- function copyFromClipboard(text) {
32
- const theClipboard = navigator.clipboard;
33
- if (theClipboard != null) {
34
- return theClipboard
35
- .writeText(text)
36
- .then(() => {
37
- Promise.resolve(true);
38
- })
39
- .catch(() => Promise.resolve(false));
40
- }
41
- return Promise.resolve(false);
42
- }
43
- /** 解析待复制的文本 */
44
- function parseCopyText(source) {
45
- let copyText = null; // 待复制文本
46
- let sourceEl = null;
47
- // 获取待复制数据
48
- if (typeof source === "string") {
49
- // 从节点拿数据
50
- if (source.startsWith("#") || source.startsWith(".")) {
51
- sourceEl = document.querySelector(source);
52
- if (sourceEl == null) {
53
- copyText = source;
54
- }
55
- }
56
- else {
57
- copyText = source;
58
- }
59
- }
60
- if (source instanceof HTMLElement) {
61
- sourceEl = source;
62
- }
63
- // 从节点获取待复制数据
64
- if (sourceEl != null) {
65
- if (sourceEl.hasAttribute("data-copy-text")) {
66
- copyText = sourceEl.getAttribute("data-copy-text");
67
- }
68
- else {
69
- const tagName = sourceEl.tagName;
70
- if (tagName === "INPUT" || tagName === "TEXTAREA") {
71
- copyText = sourceEl.value;
72
- }
73
- else {
74
- copyText = sourceEl.textContent;
75
- }
76
- }
77
- }
78
- return copyText;
79
- }
80
- /**
81
- * 复制数据, 可以从多种类型的数据
82
- * 1. 直接复制文本: await copy("待复制的文本")
83
- * 2. 复制节点上的 data-copy-text:
84
- * <button data-copy-text="这是待复制的文本">复制</button>
85
- * await copy(e.target) // or await copy("#a") or await copy(document.querySelector('#a'))
86
- * 3. 直接复制节点本身数据: await copy('#a')
87
- * @param {string | HTMLElement} source 复制源, 从中解析待复制的数据
88
- * @returns {Promise<boolean>} 是否复制成功
89
- */
90
- export async function copy(source) {
91
- // 待复制文本
92
- const copyText = parseCopyText(source);
93
- if (copyText == null) {
94
- return Promise.resolve(false);
95
- }
96
- const v = await copyFromClipboard(copyText);
97
- if (v === false) {
98
- return copyFromCommand(copyText);
99
- }
100
- return Promise.resolve(true);
101
- }