ph-utils 0.18.0 → 0.19.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/LICENSE +21 -0
- package/lib/dom.d.ts +114 -26
- package/lib/dom.js +21 -6
- package/lib/src/array.d.ts +79 -0
- package/lib/src/array.js +212 -0
- package/lib/src/color.d.ts +55 -0
- package/lib/src/color.js +294 -0
- package/lib/src/config.d.ts +33 -0
- package/lib/src/config.js +99 -0
- package/lib/src/copy.d.ts +11 -0
- package/lib/src/copy.js +101 -0
- package/lib/src/crypto.d.ts +74 -0
- package/lib/src/crypto.js +261 -0
- package/lib/src/crypto_node.d.ts +61 -0
- package/lib/src/crypto_node.js +133 -0
- package/lib/src/date.d.ts +66 -0
- package/lib/src/date.js +202 -0
- package/lib/src/dom.d.ts +265 -0
- package/lib/src/dom.js +635 -0
- package/lib/src/file.d.ts +29 -0
- package/lib/src/file.js +54 -0
- package/lib/src/id.d.ts +68 -0
- package/lib/src/id.js +170 -0
- package/lib/src/index.d.ts +154 -0
- package/lib/src/index.js +239 -0
- package/lib/src/logger.d.ts +62 -0
- package/lib/src/logger.js +122 -0
- package/lib/src/server.d.ts +33 -0
- package/lib/src/server.js +65 -0
- package/lib/src/storage.d.ts +51 -0
- package/lib/src/storage.js +73 -0
- package/lib/src/theme.d.ts +44 -0
- package/lib/src/theme.js +156 -0
- package/lib/src/validator.d.ts +71 -0
- package/lib/src/validator.js +238 -0
- package/lib/src/web.d.ts +30 -0
- package/lib/src/web.js +100 -0
- package/package.json +2 -2
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据验证器
|
|
3
|
+
*/
|
|
4
|
+
// 默认的错误提示信息
|
|
5
|
+
const defaultMsgs = {
|
|
6
|
+
mobile: "请输入正确的手机号",
|
|
7
|
+
same: "两次输入不一致",
|
|
8
|
+
required: "%s为必填字段",
|
|
9
|
+
};
|
|
10
|
+
const defaultMsg = "请输入正确的数据";
|
|
11
|
+
// 一些常用的验证正则
|
|
12
|
+
const ruleRegexs = {
|
|
13
|
+
/** 验证跟其余数据相等的正则,一般用于验证再次输入密码 */
|
|
14
|
+
same: /^same:(.+)$/i,
|
|
15
|
+
/** 验证手机号的正则表达式 */
|
|
16
|
+
mobile: /^1[3456789]\d{9}$/,
|
|
17
|
+
/** 非空验证的正则表达式 */
|
|
18
|
+
required: /^\S{1}.*/,
|
|
19
|
+
};
|
|
20
|
+
// 规则比对函数
|
|
21
|
+
const ruleFns = {
|
|
22
|
+
/** 验证相等 */
|
|
23
|
+
same(val1, val2) {
|
|
24
|
+
return val2 === val1;
|
|
25
|
+
},
|
|
26
|
+
/** 正则匹配 */
|
|
27
|
+
pattern(regex, val) {
|
|
28
|
+
if (val == null) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return regex.test(String(val));
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
class ValidateError extends Error {
|
|
35
|
+
constructor(detail, key, message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "ValidateError";
|
|
38
|
+
this.key = key;
|
|
39
|
+
this.detail = detail;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 数据验证器
|
|
44
|
+
*/
|
|
45
|
+
class Validator {
|
|
46
|
+
/**
|
|
47
|
+
* 构造数据验证转换器
|
|
48
|
+
*
|
|
49
|
+
* See {@link https://gitee.com/towardly/ph/wikis/utils/validator|Validator文档}.
|
|
50
|
+
*
|
|
51
|
+
* @param schemas 配置验证转换规则
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
*
|
|
55
|
+
* const validator = new Validator([
|
|
56
|
+
* { key: 'mobile', rules: ['required', 'mobile'] },
|
|
57
|
+
* { key: 'code': rules: /^\d{6}$/, message: '请输入正确的验证码' },
|
|
58
|
+
* { key: 'confirmPassword', rules: ['required', 'same:password'] }
|
|
59
|
+
* ])
|
|
60
|
+
* // 验证某一个字段
|
|
61
|
+
* validator.validateKey().then(res => {})
|
|
62
|
+
*/
|
|
63
|
+
constructor(schemas) {
|
|
64
|
+
this.rules = {};
|
|
65
|
+
this.addSchemas(schemas);
|
|
66
|
+
}
|
|
67
|
+
addSchemas(schemas) {
|
|
68
|
+
for (let schema of schemas) {
|
|
69
|
+
this.rules[schema.key] = this._parseSchemaRules(schema);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
addSchema(schema) {
|
|
73
|
+
this.rules[schema.key] = this._parseSchemaRules(schema);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 进行数据验证
|
|
77
|
+
* @param data 待验证的数据
|
|
78
|
+
* @param all 是否全部验证, false - 只要验证错误一个则停止验证
|
|
79
|
+
* @returns
|
|
80
|
+
*/
|
|
81
|
+
async validate(data, all = false) {
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const detail = {};
|
|
84
|
+
let currentKey = undefined;
|
|
85
|
+
let currentMessage = undefined;
|
|
86
|
+
for (let key in this.rules) {
|
|
87
|
+
let errMsg = this._validateRule(this.rules[key], data[key], data);
|
|
88
|
+
if (errMsg !== "") {
|
|
89
|
+
errMsg = errMsg.replace("%s", key);
|
|
90
|
+
detail[key] = errMsg;
|
|
91
|
+
currentKey = key;
|
|
92
|
+
currentMessage = errMsg;
|
|
93
|
+
if (all)
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (currentKey == null) {
|
|
98
|
+
resolve(true);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
reject(new ValidateError(detail, currentKey, currentMessage));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 只验证指定 key 的数据格式
|
|
107
|
+
* @param key 指定待验证的 key
|
|
108
|
+
* @param value 待验证的数据
|
|
109
|
+
* @param data 原始数据,当验证确认密码时需要使用
|
|
110
|
+
*/
|
|
111
|
+
async validateKey(key, value, data) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
let keyRules = this.rules[key];
|
|
114
|
+
if (keyRules == null) {
|
|
115
|
+
resolve({ key, value });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let errMsg = this._validateRule(keyRules, value, data);
|
|
119
|
+
if (errMsg !== "") {
|
|
120
|
+
errMsg = errMsg.replace("%s", key);
|
|
121
|
+
reject(new ValidateError({ [key]: errMsg }, key, errMsg));
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
resolve({ key, value });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
_validateRule(rules, value, data) {
|
|
129
|
+
let errMsg = "";
|
|
130
|
+
for (let rule of rules) {
|
|
131
|
+
if (!rule)
|
|
132
|
+
continue;
|
|
133
|
+
// 如果数据为空,则判断是否是必填
|
|
134
|
+
if (rule.rule === "required") {
|
|
135
|
+
if (value == null || !ruleFns.pattern(ruleRegexs.required, value)) {
|
|
136
|
+
errMsg = rule.message;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (typeof rule.rule === "function") {
|
|
140
|
+
if (!rule.rule(value)) {
|
|
141
|
+
errMsg = rule.message;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (rule.sameKey != null) {
|
|
145
|
+
if (data != null) {
|
|
146
|
+
if (!ruleFns.same(value, data[rule.sameKey])) {
|
|
147
|
+
errMsg = rule.message;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
if (rule && !ruleFns.pattern(rule.rule, value)) {
|
|
153
|
+
errMsg = rule.message;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (errMsg !== "") {
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return errMsg;
|
|
161
|
+
}
|
|
162
|
+
_parseSchemaRules(schema) {
|
|
163
|
+
// 解析规则
|
|
164
|
+
let rules = [];
|
|
165
|
+
let rule = schema.rules;
|
|
166
|
+
if (schema.required === true) {
|
|
167
|
+
rules.push(...this._parseStringRule("required", schema.message));
|
|
168
|
+
}
|
|
169
|
+
if (rule != null) {
|
|
170
|
+
if (typeof rule === "string") {
|
|
171
|
+
rules.push(...this._parseStringRule(rule, schema.message));
|
|
172
|
+
}
|
|
173
|
+
else if (rule instanceof Array) {
|
|
174
|
+
for (let ruleItem of rule) {
|
|
175
|
+
if (typeof ruleItem === "string") {
|
|
176
|
+
rules.push(...this._parseStringRule(ruleItem, schema.message));
|
|
177
|
+
}
|
|
178
|
+
else if (ruleItem instanceof RegExp ||
|
|
179
|
+
typeof ruleItem === "function") {
|
|
180
|
+
rules.push({
|
|
181
|
+
rule: ruleItem,
|
|
182
|
+
message: schema.message || defaultMsg,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const emessage = ruleItem.message || schema.message || defaultMsg;
|
|
187
|
+
if (typeof ruleItem.rule === "string") {
|
|
188
|
+
rules.push(...this._parseStringRule(ruleItem.rule, emessage));
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
rules.push({
|
|
192
|
+
rule: ruleItem.rule,
|
|
193
|
+
message: emessage,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
rules.push({ rule, message: defaultMsg });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return rules;
|
|
204
|
+
}
|
|
205
|
+
_parseStringRule(rule, ruleErrMsg) {
|
|
206
|
+
let rules = [];
|
|
207
|
+
let trule = rule.split("|");
|
|
208
|
+
for (let r of trule) {
|
|
209
|
+
let message = ruleErrMsg;
|
|
210
|
+
let rrule = null;
|
|
211
|
+
let sameKey;
|
|
212
|
+
if (rule === "required") {
|
|
213
|
+
rrule = "required";
|
|
214
|
+
message = message || ruleErrMsg || defaultMsgs.required;
|
|
215
|
+
}
|
|
216
|
+
else if (ruleRegexs.same.test(r)) {
|
|
217
|
+
let m = r.match(ruleRegexs.same);
|
|
218
|
+
if (m != null) {
|
|
219
|
+
rrule = ruleRegexs.same;
|
|
220
|
+
let m = r.match(ruleRegexs.same);
|
|
221
|
+
if (m != null) {
|
|
222
|
+
sameKey = m[1];
|
|
223
|
+
}
|
|
224
|
+
message = message || defaultMsgs["same"];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (Object.hasOwn(ruleRegexs, r)) {
|
|
228
|
+
rrule = ruleRegexs[r];
|
|
229
|
+
message = message || defaultMsgs[r];
|
|
230
|
+
}
|
|
231
|
+
if (rrule) {
|
|
232
|
+
rules.push({ rule: rrule, message: message, sameKey });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return rules;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
export default Validator;
|
package/lib/src/web.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 解析 Form 表单中的 input 元素的数据为 JSON 格式,key: input-name;value: input-value
|
|
3
|
+
* @param form {object} Form 节点对象
|
|
4
|
+
*/
|
|
5
|
+
export declare const formJson: <T>(form: HTMLFormElement) => T;
|
|
6
|
+
/**
|
|
7
|
+
* 获取 url query 参数 (get 请求的参数)
|
|
8
|
+
* @param search 如果是 React 应用就需要传递 useLocation().search
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
export declare function query(search?: string): {
|
|
12
|
+
[index: string]: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* 函数节流 - 每隔单位时间,只执行一次
|
|
16
|
+
* @param cb 待节流的函数
|
|
17
|
+
* @param wait 间隔时间
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
export declare function throttle<R extends any[], T>(fn: (...args: R) => T, wait?: number): (...args: R) => void;
|
|
21
|
+
/**
|
|
22
|
+
* 函数防抖 - 当重复触发某一个行为(事件时),只执行最后一次触发
|
|
23
|
+
* @param fn 防抖函数
|
|
24
|
+
* @param interval 间隔时间段
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
export declare function debounce<R extends any[], T>(fn: (...args: R) => T, interval?: number): {
|
|
28
|
+
(...args: R): void;
|
|
29
|
+
cancel(): void;
|
|
30
|
+
};
|
package/lib/src/web.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web(浏览器) 端工具类
|
|
3
|
+
*/
|
|
4
|
+
import { isBlank } from "./index.js";
|
|
5
|
+
/**
|
|
6
|
+
* 解析 Form 表单中的 input 元素的数据为 JSON 格式,key: input-name;value: input-value
|
|
7
|
+
* @param form {object} Form 节点对象
|
|
8
|
+
*/
|
|
9
|
+
export const formJson = function (form) {
|
|
10
|
+
let elems = form.elements;
|
|
11
|
+
let value = {};
|
|
12
|
+
for (let i = 0, len = elems.length; i < len; i++) {
|
|
13
|
+
let item = elems[i];
|
|
14
|
+
if (!isBlank(item.name)) {
|
|
15
|
+
if ((item.tagName === "INPUT" || item.tagName === "TEXTAREA") &&
|
|
16
|
+
!isBlank(item.value)) {
|
|
17
|
+
let dataType = item.getAttribute("data-type");
|
|
18
|
+
if (dataType === "number") {
|
|
19
|
+
value[item.name] = Number(item.value);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
value[item.name] = item.value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else if (item.tagName === "SELECT") {
|
|
26
|
+
value[item.name] = item.options[item.selectedIndex].value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* 获取 url query 参数 (get 请求的参数)
|
|
34
|
+
* @param search 如果是 React 应用就需要传递 useLocation().search
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
export function query(search) {
|
|
38
|
+
if (isBlank(search)) {
|
|
39
|
+
search = location.search;
|
|
40
|
+
}
|
|
41
|
+
const searchParams = new URLSearchParams(search);
|
|
42
|
+
let query = {};
|
|
43
|
+
for (const [key, value] of searchParams) {
|
|
44
|
+
let oldValue = query[key];
|
|
45
|
+
let newValue = value;
|
|
46
|
+
if (oldValue != null) {
|
|
47
|
+
if (oldValue instanceof Array) {
|
|
48
|
+
oldValue.push(value);
|
|
49
|
+
newValue = oldValue;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
newValue = [value, oldValue];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
query[key] = newValue;
|
|
56
|
+
}
|
|
57
|
+
return query;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 函数节流 - 每隔单位时间,只执行一次
|
|
61
|
+
* @param cb 待节流的函数
|
|
62
|
+
* @param wait 间隔时间
|
|
63
|
+
* @returns
|
|
64
|
+
*/
|
|
65
|
+
export function throttle(fn, wait = 500) {
|
|
66
|
+
// 上一次的请求时间
|
|
67
|
+
let last = 0;
|
|
68
|
+
return (...args) => {
|
|
69
|
+
// 当前时间戳
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
if (now - last > wait) {
|
|
72
|
+
fn(...args);
|
|
73
|
+
last = now;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 函数防抖 - 当重复触发某一个行为(事件时),只执行最后一次触发
|
|
79
|
+
* @param fn 防抖函数
|
|
80
|
+
* @param interval 间隔时间段
|
|
81
|
+
* @returns
|
|
82
|
+
*/
|
|
83
|
+
export function debounce(fn, interval = 500) {
|
|
84
|
+
let _t;
|
|
85
|
+
const handle = (...args) => {
|
|
86
|
+
if (_t)
|
|
87
|
+
clearTimeout(_t);
|
|
88
|
+
_t = setTimeout(() => {
|
|
89
|
+
//@ts-ignore
|
|
90
|
+
fn.apply(this, args);
|
|
91
|
+
}, interval);
|
|
92
|
+
};
|
|
93
|
+
handle.cancel = function () {
|
|
94
|
+
if (_t) {
|
|
95
|
+
clearTimeout(_t);
|
|
96
|
+
_t = null;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return handle;
|
|
100
|
+
}
|
package/package.json
CHANGED