ph-utils 0.9.10 → 0.9.13

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/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
- }