ph-utils 0.4.7 → 0.5.0
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 +7 -7
- package/lib/crypto.d.ts +1 -1
- package/lib/crypto.js +47 -45
- package/lib/file.d.ts +6 -8
- package/lib/file.js +12 -44
- package/lib/validator.d.ts +6 -3
- package/lib/validator.js +53 -54
- package/package.json +1 -1
- package/lib/clipboard.d.ts +0 -11
- package/lib/clipboard.js +0 -101
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/crypto.d.ts
CHANGED
@@ -14,7 +14,7 @@ export declare function bufferToHex(bf: ArrayBuffer | Uint8Array, upper?: boolea
|
|
14
14
|
*/
|
15
15
|
export declare function sha(message: string, upper?: boolean, algorithm?: string): Promise<string>;
|
16
16
|
/** 返回结果类似 */
|
17
|
-
type AlgorithmResType =
|
17
|
+
type AlgorithmResType = "hex" | "hexUpper" | "base64" | "raw";
|
18
18
|
/**
|
19
19
|
* AES 加密
|
20
20
|
* @param message 待加密的数据
|
package/lib/crypto.js
CHANGED
@@ -10,10 +10,10 @@ export function bufferToHex(bf, upper = false) {
|
|
10
10
|
const hashArray = Array.from(u8Array);
|
11
11
|
return hashArray
|
12
12
|
.map((b) => {
|
13
|
-
let hx = b.toString(16).padStart(2,
|
13
|
+
let hx = b.toString(16).padStart(2, "0");
|
14
14
|
return upper === true ? hx.toUpperCase() : hx;
|
15
15
|
})
|
16
|
-
.join(
|
16
|
+
.join("");
|
17
17
|
}
|
18
18
|
/**
|
19
19
|
* 将16进制的数据转换为 UInt8Array
|
@@ -38,7 +38,7 @@ function hexToBuffer(data) {
|
|
38
38
|
function bufferToBase64(bf) {
|
39
39
|
const u8Array = bf instanceof Uint8Array ? bf : new Uint8Array(bf);
|
40
40
|
const hashArray = Array.from(u8Array);
|
41
|
-
return
|
41
|
+
return globalThis.btoa(String.fromCharCode.apply(null, hashArray));
|
42
42
|
}
|
43
43
|
/**
|
44
44
|
* 将 Base64 转换为 UInt8Array 数据
|
@@ -46,9 +46,9 @@ function bufferToBase64(bf) {
|
|
46
46
|
* @returns
|
47
47
|
*/
|
48
48
|
function base64ToBuffer(data) {
|
49
|
-
return new Uint8Array(
|
49
|
+
return new Uint8Array(globalThis
|
50
50
|
.atob(data)
|
51
|
-
.split(
|
51
|
+
.split("")
|
52
52
|
.map((char) => char.charCodeAt(0)));
|
53
53
|
}
|
54
54
|
/**
|
@@ -58,14 +58,14 @@ function base64ToBuffer(data) {
|
|
58
58
|
* @param algorithm hash算法, 支持: SHA-1、SHA-256、SHA-384、SHA-512; 默认为: SHA-256
|
59
59
|
* @returns
|
60
60
|
*/
|
61
|
-
export async function sha(message, upper = false, algorithm =
|
61
|
+
export async function sha(message, upper = false, algorithm = "SHA-256") {
|
62
62
|
const msgUint8 = new TextEncoder().encode(message);
|
63
|
-
const hashBuffer = await
|
63
|
+
const hashBuffer = await globalThis.crypto.subtle.digest(algorithm || "SHA-256", msgUint8);
|
64
64
|
return bufferToHex(hashBuffer, upper);
|
65
65
|
}
|
66
66
|
function parseRsaKey(pem) {
|
67
|
-
const pemHeader =
|
68
|
-
const pemFooter =
|
67
|
+
const pemHeader = "-----BEGIN PUBLIC KEY-----";
|
68
|
+
const pemFooter = "-----END PUBLIC KEY-----";
|
69
69
|
if (pem.indexOf(pemHeader) !== -1 && pem.indexOf(pemFooter) !== -1) {
|
70
70
|
pem = pem.substring(pemHeader.length, pem.indexOf(pemFooter)).trim();
|
71
71
|
}
|
@@ -79,34 +79,34 @@ function parseRsaKey(pem) {
|
|
79
79
|
* @returns
|
80
80
|
*/
|
81
81
|
// eslint-disable-next-line no-undef
|
82
|
-
async function importKey(key, algorithmName, usages, encoding =
|
83
|
-
let name =
|
82
|
+
async function importKey(key, algorithmName, usages, encoding = "hex") {
|
83
|
+
let name = "AES-CBC";
|
84
84
|
if (algorithmName == null) {
|
85
|
-
name =
|
85
|
+
name = "AES-CBC";
|
86
86
|
}
|
87
87
|
else {
|
88
|
-
if (algorithmName.toUpperCase() ===
|
89
|
-
name =
|
88
|
+
if (algorithmName.toUpperCase() === "AES") {
|
89
|
+
name = "AES-CBC";
|
90
90
|
}
|
91
|
-
else if (algorithmName.toUpperCase() ===
|
92
|
-
name =
|
91
|
+
else if (algorithmName.toUpperCase() === "RSA") {
|
92
|
+
name = "RSA-OAEP";
|
93
93
|
}
|
94
94
|
}
|
95
95
|
name = name.toUpperCase();
|
96
96
|
if (usages == null || usages.length === 0) {
|
97
|
-
usages = [
|
97
|
+
usages = ["encrypt"];
|
98
98
|
}
|
99
|
-
let format =
|
99
|
+
let format = "raw";
|
100
100
|
let algorithm = { name };
|
101
|
-
if (name.includes(
|
102
|
-
format =
|
103
|
-
algorithm.hash = { name:
|
101
|
+
if (name.includes("RSA")) {
|
102
|
+
format = "spki";
|
103
|
+
algorithm.hash = { name: "SHA-256" };
|
104
104
|
key = parseRsaKey(key);
|
105
105
|
}
|
106
|
-
const keyBuf = encoding ===
|
106
|
+
const keyBuf = encoding === "base64" ? base64ToBuffer(key) : hexToBuffer(key);
|
107
107
|
// importKey时传 false 表明该密钥不能被导出
|
108
108
|
return Promise.all([
|
109
|
-
|
109
|
+
globalThis.crypto.subtle.importKey(format, keyBuf, algorithm, false, usages),
|
110
110
|
Promise.resolve({ name }),
|
111
111
|
]);
|
112
112
|
}
|
@@ -118,18 +118,18 @@ async function importKey(key, algorithmName, usages, encoding = 'hex') {
|
|
118
118
|
* @param encode 解密后返回数据格式
|
119
119
|
* @returns
|
120
120
|
*/
|
121
|
-
async function encrypt(algorithm, key, message, encode =
|
122
|
-
if (typeof message ===
|
121
|
+
async function encrypt(algorithm, key, message, encode = "hex") {
|
122
|
+
if (typeof message === "string") {
|
123
123
|
message = new TextEncoder().encode(message);
|
124
124
|
}
|
125
|
-
const encrypted = await
|
126
|
-
if (encode ===
|
125
|
+
const encrypted = await globalThis.crypto.subtle.encrypt(algorithm, key, message);
|
126
|
+
if (encode === "hex") {
|
127
127
|
return bufferToHex(encrypted);
|
128
128
|
}
|
129
|
-
else if (encode ===
|
129
|
+
else if (encode === "hexUpper") {
|
130
130
|
return bufferToHex(encrypted, true);
|
131
131
|
}
|
132
|
-
else if (encode ===
|
132
|
+
else if (encode === "base64") {
|
133
133
|
return bufferToBase64(encrypted);
|
134
134
|
}
|
135
135
|
else {
|
@@ -144,8 +144,8 @@ async function encrypt(algorithm, key, message, encode = 'hex') {
|
|
144
144
|
* @returns
|
145
145
|
*/
|
146
146
|
async function decrypt(algorithm, key, message) {
|
147
|
-
const decrypted = await
|
148
|
-
return new TextDecoder(
|
147
|
+
const decrypted = await globalThis.crypto.subtle.decrypt(algorithm, key, message);
|
148
|
+
return new TextDecoder("utf-8").decode(decrypted);
|
149
149
|
}
|
150
150
|
/**
|
151
151
|
* AES 加密
|
@@ -155,15 +155,15 @@ async function decrypt(algorithm, key, message) {
|
|
155
155
|
* @param iv 加解密向量
|
156
156
|
* @returns [加密数据,向量]
|
157
157
|
*/
|
158
|
-
export async function aesEncrypt(message, key, encode =
|
159
|
-
let ciphertext =
|
160
|
-
let resIv =
|
158
|
+
export async function aesEncrypt(message, key, encode = "hex", iv = null) {
|
159
|
+
let ciphertext = "";
|
160
|
+
let resIv = "";
|
161
161
|
// 导入密钥
|
162
|
-
const [cryptoKey, algorithm] = await importKey(key,
|
162
|
+
const [cryptoKey, algorithm] = await importKey(key, "aes", ["encrypt"]);
|
163
163
|
if (iv == null) {
|
164
|
-
iv =
|
164
|
+
iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
165
165
|
}
|
166
|
-
else if (typeof iv ===
|
166
|
+
else if (typeof iv === "string") {
|
167
167
|
iv = hexToBuffer(iv);
|
168
168
|
}
|
169
169
|
const encodeData = await encrypt({ ...algorithm, iv }, cryptoKey, message, encode);
|
@@ -179,8 +179,8 @@ export async function aesEncrypt(message, key, encode = 'hex', iv = null) {
|
|
179
179
|
*/
|
180
180
|
function parseEncryptData(message, type) {
|
181
181
|
let input;
|
182
|
-
if (typeof message ===
|
183
|
-
if (type ===
|
182
|
+
if (typeof message === "string") {
|
183
|
+
if (type === "hex" || type === "hexUpper") {
|
184
184
|
input = hexToBuffer(message);
|
185
185
|
}
|
186
186
|
else {
|
@@ -200,9 +200,9 @@ function parseEncryptData(message, type) {
|
|
200
200
|
* @param encode 加密后数据的形式: hex | base64
|
201
201
|
* @returns
|
202
202
|
*/
|
203
|
-
export async function aesDecrypt(message, key, iv, encode =
|
203
|
+
export async function aesDecrypt(message, key, iv, encode = "hex") {
|
204
204
|
// 导入密钥
|
205
|
-
const [cryptoKey, algorithm] = await importKey(key,
|
205
|
+
const [cryptoKey, algorithm] = await importKey(key, "aes", ["decrypt"]);
|
206
206
|
const input = parseEncryptData(message, encode);
|
207
207
|
return await decrypt({ ...algorithm, iv: hexToBuffer(iv) }, cryptoKey, input);
|
208
208
|
}
|
@@ -213,9 +213,9 @@ export async function aesDecrypt(message, key, iv, encode = 'hex') {
|
|
213
213
|
* @param encode 返回类型
|
214
214
|
* @returns
|
215
215
|
*/
|
216
|
-
export async function rsaEncrypt(message, publicKey, encode =
|
216
|
+
export async function rsaEncrypt(message, publicKey, encode = "hex") {
|
217
217
|
// 导入密钥
|
218
|
-
const [cryptoKey, algorithm] = await importKey(publicKey,
|
218
|
+
const [cryptoKey, algorithm] = await importKey(publicKey, "rsa", ["encrypt"], "base64");
|
219
219
|
return await encrypt(algorithm, cryptoKey, message, encode);
|
220
220
|
}
|
221
221
|
/**
|
@@ -225,8 +225,10 @@ export async function rsaEncrypt(message, publicKey, encode = 'hex') {
|
|
225
225
|
* @param encode 加密后的数据形式
|
226
226
|
* @returns
|
227
227
|
*/
|
228
|
-
export async function rsaDecrypt(privateKey, message, encode =
|
228
|
+
export async function rsaDecrypt(privateKey, message, encode = "hex") {
|
229
229
|
// 导入密钥
|
230
|
-
const [cryptoKey, algorithm] = await importKey(privateKey,
|
230
|
+
const [cryptoKey, algorithm] = await importKey(privateKey, "rsa", [
|
231
|
+
"decrypt",
|
232
|
+
]);
|
231
233
|
return await decrypt({ ...algorithm }, cryptoKey, parseEncryptData(message, encode));
|
232
234
|
}
|
package/lib/file.d.ts
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
/**
|
2
2
|
* 读取文件内容为JSON格式
|
3
3
|
* @param filepath 读取的文件路径
|
4
|
+
* @param errIsNull 读取失败时,是否返回 null,默认为 false[抛出异常]
|
5
|
+
*
|
6
|
+
* @example <caption>1. 文件不存在时, 返回null,而不是 catch</caption>
|
7
|
+
* await readJSON("./not-exists.json", true);
|
8
|
+
*
|
4
9
|
* @returns Promise<unknown>
|
5
10
|
*/
|
6
|
-
export declare function readJSON<T>(filepath: string): Promise<T>;
|
11
|
+
export declare function readJSON<T>(filepath: string, errIsNull?: boolean): Promise<T>;
|
7
12
|
/**
|
8
13
|
* 写入 JSON 格式的数据到文件
|
9
14
|
* @param file 待写入的文件
|
@@ -16,13 +21,6 @@ export declare function write(file: string, data: any, opts?: {
|
|
16
21
|
json: boolean;
|
17
22
|
format: boolean;
|
18
23
|
}): Promise<unknown>;
|
19
|
-
/**
|
20
|
-
* 遍历文件夹
|
21
|
-
* @param dir 待遍历的目录
|
22
|
-
* @param callback 遍历到文件后的回调
|
23
|
-
* @param done 遍历完成后的回调
|
24
|
-
*/
|
25
|
-
export declare function traverseDir(dir: string, callback?: (filename: string) => void, done?: () => void): void;
|
26
24
|
/**
|
27
25
|
* 根据文件的 stat 获取文件的 etag
|
28
26
|
* @param filePath 文件地址
|
package/lib/file.js
CHANGED
@@ -4,13 +4,23 @@ import fs from "node:fs";
|
|
4
4
|
/**
|
5
5
|
* 读取文件内容为JSON格式
|
6
6
|
* @param filepath 读取的文件路径
|
7
|
+
* @param errIsNull 读取失败时,是否返回 null,默认为 false[抛出异常]
|
8
|
+
*
|
9
|
+
* @example <caption>1. 文件不存在时, 返回null,而不是 catch</caption>
|
10
|
+
* await readJSON("./not-exists.json", true);
|
11
|
+
*
|
7
12
|
* @returns Promise<unknown>
|
8
13
|
*/
|
9
|
-
export function readJSON(filepath) {
|
14
|
+
export function readJSON(filepath, errIsNull = false) {
|
10
15
|
return new Promise((resolve, reject) => {
|
11
16
|
fs.readFile(path.resolve(filepath), "utf-8", (err, data) => {
|
12
17
|
if (err) {
|
13
|
-
|
18
|
+
if (errIsNull) {
|
19
|
+
resolve(null);
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
reject(err);
|
23
|
+
}
|
14
24
|
}
|
15
25
|
else {
|
16
26
|
resolve(JSON.parse(data));
|
@@ -43,48 +53,6 @@ export function write(file, data, opts) {
|
|
43
53
|
});
|
44
54
|
});
|
45
55
|
}
|
46
|
-
/**
|
47
|
-
* 遍历文件夹
|
48
|
-
* @param dir 待遍历的目录
|
49
|
-
* @param callback 遍历到文件后的回调
|
50
|
-
* @param done 遍历完成后的回调
|
51
|
-
*/
|
52
|
-
export function traverseDir(dir, callback, done) {
|
53
|
-
let t = -1; // 定时任务,简单延迟作为遍历完成计算
|
54
|
-
function list(dr, cb, d) {
|
55
|
-
fs.readdir(path.resolve(dr), { withFileTypes: true }, (err, files) => {
|
56
|
-
if (err && err.errno === -4052) {
|
57
|
-
// 本身就是文件
|
58
|
-
if (typeof cb === "function")
|
59
|
-
cb(dr);
|
60
|
-
if (typeof d === "function")
|
61
|
-
d(); // 遍历完成
|
62
|
-
}
|
63
|
-
else {
|
64
|
-
for (let i = 0, len = files.length; i < len; i++) {
|
65
|
-
const file = files[i];
|
66
|
-
if (file.isFile()) {
|
67
|
-
if (typeof cb === "function")
|
68
|
-
cb(path.join(dr, file.name));
|
69
|
-
clearTimeout(t);
|
70
|
-
t = setTimeout(() => {
|
71
|
-
setImmediate(() => {
|
72
|
-
if (typeof done === "function") {
|
73
|
-
done();
|
74
|
-
}
|
75
|
-
});
|
76
|
-
}, 10);
|
77
|
-
}
|
78
|
-
else {
|
79
|
-
// 文件夹
|
80
|
-
list(path.join(dr, file.name), cb, d);
|
81
|
-
}
|
82
|
-
}
|
83
|
-
}
|
84
|
-
});
|
85
|
-
}
|
86
|
-
list(dir, callback, done);
|
87
|
-
}
|
88
56
|
/**
|
89
57
|
* 根据文件的 stat 获取文件的 etag
|
90
58
|
* @param filePath 文件地址
|
package/lib/validator.d.ts
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
* 数据验证器
|
3
3
|
*/
|
4
4
|
interface RuleItem {
|
5
|
-
rule: RegExp | ((v: any) => boolean) |
|
5
|
+
rule: RegExp | ((v: any) => boolean) | "required";
|
6
6
|
message: string;
|
7
7
|
sameKey?: string;
|
8
8
|
}
|
@@ -18,7 +18,7 @@ export interface SchemaType {
|
|
18
18
|
message?: string;
|
19
19
|
}
|
20
20
|
/**
|
21
|
-
*
|
21
|
+
* 数据验证器
|
22
22
|
*/
|
23
23
|
declare class Validator {
|
24
24
|
rules: {
|
@@ -40,7 +40,10 @@ declare class Validator {
|
|
40
40
|
* @param key 指定待验证的 key
|
41
41
|
* @param value 待验证的数据
|
42
42
|
*/
|
43
|
-
validateKey(key: string, value: any, data?: any): Promise<
|
43
|
+
validateKey(key: string, value: any, data?: any): Promise<{
|
44
|
+
key: string;
|
45
|
+
value: any;
|
46
|
+
}>;
|
44
47
|
private _validateRule;
|
45
48
|
private _parseStringRule;
|
46
49
|
}
|
package/lib/validator.js
CHANGED
@@ -3,17 +3,17 @@
|
|
3
3
|
*/
|
4
4
|
// 默认的错误提示信息
|
5
5
|
const defaultMsgs = {
|
6
|
-
mobile:
|
7
|
-
same:
|
8
|
-
required:
|
6
|
+
mobile: "请输入正确的手机号",
|
7
|
+
same: "两次输入不一致",
|
8
|
+
required: "%s为必填字段",
|
9
9
|
};
|
10
|
-
const defaultMsg =
|
10
|
+
const defaultMsg = "请输入正确的数据";
|
11
11
|
// 一些常用的验证正则
|
12
12
|
const ruleRegexs = {
|
13
13
|
/** 验证跟其余数据相等的正则,一般用于验证再次输入密码 */
|
14
14
|
same: /^same:(.+)$/i,
|
15
15
|
/** 验证手机号的正则表达式 */
|
16
|
-
mobile: /^1[
|
16
|
+
mobile: /^1[345678]\d{9}$/,
|
17
17
|
/** 非空验证的正则表达式 */
|
18
18
|
required: /^\S{1}.*/,
|
19
19
|
};
|
@@ -36,12 +36,12 @@ class ValidateError extends Error {
|
|
36
36
|
key;
|
37
37
|
constructor(key, msg) {
|
38
38
|
super(msg);
|
39
|
-
this.name =
|
39
|
+
this.name = "ValidateError";
|
40
40
|
this.key = key;
|
41
41
|
}
|
42
42
|
}
|
43
43
|
/**
|
44
|
-
*
|
44
|
+
* 数据验证器
|
45
45
|
*/
|
46
46
|
class Validator {
|
47
47
|
rules;
|
@@ -56,22 +56,23 @@ class Validator {
|
|
56
56
|
let rules = [];
|
57
57
|
let rule = schema.rules;
|
58
58
|
if (rule != null) {
|
59
|
-
if (typeof rule ===
|
59
|
+
if (typeof rule === "string") {
|
60
60
|
rules = rules.concat(this._parseStringRule(rule, schema.message));
|
61
61
|
}
|
62
62
|
else if (rule instanceof Array) {
|
63
63
|
for (let ruleItem of rule) {
|
64
|
-
if (typeof ruleItem ===
|
64
|
+
if (typeof ruleItem === "string") {
|
65
65
|
rules.push(...this._parseStringRule(ruleItem, schema.message));
|
66
66
|
}
|
67
|
-
else if (ruleItem instanceof RegExp ||
|
67
|
+
else if (ruleItem instanceof RegExp ||
|
68
|
+
typeof ruleItem === "function") {
|
68
69
|
rules.push({
|
69
70
|
rule: ruleItem,
|
70
71
|
message: schema.message || defaultMsg,
|
71
72
|
});
|
72
73
|
}
|
73
74
|
else {
|
74
|
-
if (typeof ruleItem.rule ===
|
75
|
+
if (typeof ruleItem.rule === "string") {
|
75
76
|
rules.push(...this._parseStringRule(ruleItem.rule, ruleItem.message));
|
76
77
|
}
|
77
78
|
else {
|
@@ -87,8 +88,9 @@ class Validator {
|
|
87
88
|
rules.push({ rule, message: defaultMsg });
|
88
89
|
}
|
89
90
|
}
|
90
|
-
if (schema.required === true &&
|
91
|
-
rules.
|
91
|
+
if (schema.required === true &&
|
92
|
+
(rules == null || rules.findIndex((r) => r.rule === "required") === -1)) {
|
93
|
+
rules.push(...this._parseStringRule("required", schema.message));
|
92
94
|
}
|
93
95
|
parsedRules[schema.key] = rules;
|
94
96
|
}
|
@@ -99,57 +101,54 @@ class Validator {
|
|
99
101
|
* @param data 待验证的数据
|
100
102
|
* @returns
|
101
103
|
*/
|
102
|
-
validate(data) {
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
break;
|
114
|
-
}
|
104
|
+
async validate(data) {
|
105
|
+
let errMsg = "";
|
106
|
+
let errKey = "";
|
107
|
+
for (let key in this.rules) {
|
108
|
+
// eslint-disable-next-line no-prototype-builtins
|
109
|
+
if (this.rules.hasOwnProperty(key)) {
|
110
|
+
errMsg = this._validateRule(this.rules[key], data[key], data);
|
111
|
+
if (errMsg !== "") {
|
112
|
+
errKey = key;
|
113
|
+
errMsg = errMsg.replace("%s", key);
|
114
|
+
break;
|
115
115
|
}
|
116
116
|
}
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
}
|
117
|
+
}
|
118
|
+
if (errMsg === "") {
|
119
|
+
return true;
|
120
|
+
}
|
121
|
+
else {
|
122
|
+
throw new ValidateError(errKey, errMsg);
|
123
|
+
}
|
124
124
|
}
|
125
125
|
/**
|
126
126
|
* 只验证指定 key 的数据格式
|
127
127
|
* @param key 指定待验证的 key
|
128
128
|
* @param value 待验证的数据
|
129
129
|
*/
|
130
|
-
validateKey(key, value, data) {
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
}
|
141
|
-
});
|
130
|
+
async validateKey(key, value, data) {
|
131
|
+
let keyRules = this.rules[key];
|
132
|
+
let errMsg = this._validateRule(keyRules, value, data);
|
133
|
+
if (errMsg !== "") {
|
134
|
+
errMsg = errMsg.replace("%s", key);
|
135
|
+
throw new ValidateError(key, errMsg);
|
136
|
+
}
|
137
|
+
else {
|
138
|
+
return { key, value };
|
139
|
+
}
|
142
140
|
}
|
143
141
|
_validateRule(rules, value, data) {
|
144
|
-
|
142
|
+
this.validateKey("", 1).catch((err) => { });
|
143
|
+
let errMsg = "";
|
145
144
|
for (let rule of rules) {
|
146
145
|
// 如果数据为空,则判断是否是必填
|
147
|
-
if (rule.rule ===
|
146
|
+
if (rule.rule === "required") {
|
148
147
|
if (value == null || !ruleFns.pattern(ruleRegexs.required, value)) {
|
149
148
|
errMsg = rule.message;
|
150
149
|
}
|
151
150
|
}
|
152
|
-
if (typeof rule.rule ===
|
151
|
+
if (typeof rule.rule === "function") {
|
153
152
|
if (!rule.rule(value)) {
|
154
153
|
errMsg = rule.message;
|
155
154
|
}
|
@@ -161,7 +160,7 @@ class Validator {
|
|
161
160
|
}
|
162
161
|
}
|
163
162
|
}
|
164
|
-
else if (rule.rule ===
|
163
|
+
else if (rule.rule === "required") {
|
165
164
|
if (!ruleFns.pattern(ruleRegexs.required, String(value))) {
|
166
165
|
errMsg = rule.message;
|
167
166
|
}
|
@@ -171,7 +170,7 @@ class Validator {
|
|
171
170
|
errMsg = rule.message;
|
172
171
|
}
|
173
172
|
}
|
174
|
-
if (errMsg !==
|
173
|
+
if (errMsg !== "") {
|
175
174
|
break;
|
176
175
|
}
|
177
176
|
}
|
@@ -179,7 +178,7 @@ class Validator {
|
|
179
178
|
}
|
180
179
|
_parseStringRule(rule, ruleErrMsg) {
|
181
180
|
let rules = [];
|
182
|
-
let trule = rule.split(
|
181
|
+
let trule = rule.split("|");
|
183
182
|
for (let r of trule) {
|
184
183
|
let message = ruleErrMsg;
|
185
184
|
let rrule = null;
|
@@ -192,11 +191,11 @@ class Validator {
|
|
192
191
|
if (m != null) {
|
193
192
|
sameKey = m[1];
|
194
193
|
}
|
195
|
-
message = message || defaultMsgs[
|
194
|
+
message = message || defaultMsgs["same"];
|
196
195
|
}
|
197
196
|
}
|
198
|
-
else if (rule ===
|
199
|
-
rrule =
|
197
|
+
else if (rule === "required") {
|
198
|
+
rrule = "required";
|
200
199
|
message = message || ruleErrMsg || defaultMsgs.required;
|
201
200
|
// eslint-disable-next-line no-prototype-builtins
|
202
201
|
}
|
package/package.json
CHANGED
package/lib/clipboard.d.ts
DELETED
@@ -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
|
-
}
|