@wwog/react 1.3.12 → 1.3.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 +78 -0
- package/dist/index.d.mts +101 -10
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/algorithm/date/zellersKongruenz.ts +9 -14
- package/src/algorithm/date.ts +2 -2
- package/src/components/Layout/index.ts +2 -2
- package/src/components/ProcessControl/If.tsx +2 -2
- package/src/components/Sundry/Boundary.tsx +67 -0
- package/src/components/Sundry/Portal.tsx +59 -0
- package/src/components/Sundry/Repeat.tsx +34 -0
- package/src/components/Sundry/index.ts +8 -5
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useScreen.ts +54 -65
- package/src/index.ts +5 -5
- package/src/utils/constants.ts +7 -16
- package/src/utils/createExternalState.ts +71 -84
- package/src/utils/index.ts +7 -7
- package/src/utils/promise.ts +18 -18
- package/src/utils/ruleChecker.test.ts +336 -349
- package/src/utils/ruleChecker.ts +98 -181
- package/src/utils/sundry.ts +66 -67
package/src/utils/ruleChecker.ts
CHANGED
|
@@ -1,51 +1,49 @@
|
|
|
1
1
|
// ==== 基础规则类型 ====
|
|
2
2
|
export type BaseRule<TAll, TValue> = {
|
|
3
|
-
required?: boolean
|
|
4
|
-
message?: string
|
|
5
|
-
validator?: (value: TValue, data: Partial<TAll>) => boolean | string
|
|
6
|
-
dependsOn?: (data: Partial<TAll>) => boolean | string
|
|
7
|
-
}
|
|
3
|
+
required?: boolean
|
|
4
|
+
message?: string
|
|
5
|
+
validator?: (value: TValue, data: Partial<TAll>) => boolean | string
|
|
6
|
+
dependsOn?: (data: Partial<TAll>) => boolean | string // 支持依赖校验
|
|
7
|
+
}
|
|
8
8
|
|
|
9
9
|
// ==== 长度相关规则(字符串和数组共用) ====
|
|
10
10
|
export type LengthRuleProps = {
|
|
11
|
-
min?: number
|
|
12
|
-
max?: number
|
|
13
|
-
len?: number
|
|
14
|
-
}
|
|
11
|
+
min?: number
|
|
12
|
+
max?: number
|
|
13
|
+
len?: number
|
|
14
|
+
}
|
|
15
15
|
|
|
16
16
|
// ==== 数字范围规则 ====
|
|
17
17
|
export type NumberRangeProps = {
|
|
18
|
-
min?: number
|
|
19
|
-
max?: number
|
|
20
|
-
}
|
|
18
|
+
min?: number
|
|
19
|
+
max?: number
|
|
20
|
+
}
|
|
21
21
|
|
|
22
22
|
// ==== 字符串特有规则 ====
|
|
23
23
|
export type StringSpecificProps = {
|
|
24
|
-
regex?: RegExp
|
|
25
|
-
email?: boolean
|
|
26
|
-
url?: boolean
|
|
27
|
-
phone?: boolean
|
|
28
|
-
}
|
|
24
|
+
regex?: RegExp
|
|
25
|
+
email?: boolean
|
|
26
|
+
url?: boolean
|
|
27
|
+
phone?: boolean
|
|
28
|
+
}
|
|
29
29
|
|
|
30
30
|
// ==== 数组特有规则 ====
|
|
31
31
|
export type ArraySpecificProps = {
|
|
32
|
-
unique?: boolean
|
|
33
|
-
}
|
|
32
|
+
unique?: boolean
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
// ==== 各类型规则 ====
|
|
36
|
-
export type NumberRule<TAll> = BaseRule<TAll, number> & NumberRangeProps
|
|
36
|
+
export type NumberRule<TAll> = BaseRule<TAll, number> & NumberRangeProps
|
|
37
37
|
|
|
38
|
-
export type StringRule<TAll> = BaseRule<TAll, string> &
|
|
39
|
-
LengthRuleProps &
|
|
40
|
-
StringSpecificProps;
|
|
38
|
+
export type StringRule<TAll> = BaseRule<TAll, string> & LengthRuleProps & StringSpecificProps
|
|
41
39
|
|
|
42
|
-
export type BooleanRule<TAll> = BaseRule<TAll, boolean
|
|
40
|
+
export type BooleanRule<TAll> = BaseRule<TAll, boolean>
|
|
43
41
|
|
|
44
42
|
export type ArrayRule<TAll, U> = BaseRule<TAll, U[]> &
|
|
45
43
|
LengthRuleProps &
|
|
46
44
|
ArraySpecificProps & {
|
|
47
|
-
elementRule?: FieldRule<U, TAll
|
|
48
|
-
}
|
|
45
|
+
elementRule?: FieldRule<U, TAll> // 元素级规则
|
|
46
|
+
}
|
|
49
47
|
|
|
50
48
|
// ==== 泛型推导:字段类型 → 规则类型 ====
|
|
51
49
|
export type FieldRule<TValue, TAll> = TValue extends string
|
|
@@ -56,49 +54,46 @@ export type FieldRule<TValue, TAll> = TValue extends string
|
|
|
56
54
|
? BooleanRule<TAll>
|
|
57
55
|
: TValue extends (infer U)[]
|
|
58
56
|
? ArrayRule<TAll, U>
|
|
59
|
-
: BaseRule<TAll, TValue
|
|
57
|
+
: BaseRule<TAll, TValue>
|
|
60
58
|
|
|
61
59
|
// ==== 描述对象 ====
|
|
62
60
|
export type RuleDescription<T extends Record<string, unknown>> = {
|
|
63
|
-
[K in keyof T]?: FieldRule<T[K], T> | FieldRule<T[K], T>[]
|
|
64
|
-
}
|
|
61
|
+
[K in keyof T]?: FieldRule<T[K], T> | FieldRule<T[K], T>[]
|
|
62
|
+
}
|
|
65
63
|
|
|
66
64
|
// ==== Required 判断 ====
|
|
67
|
-
type IsRequired<R> = R extends {
|
|
65
|
+
type IsRequired<R> = R extends {required: true} ? true : false
|
|
68
66
|
|
|
69
|
-
export type ApplyRules<
|
|
70
|
-
T extends
|
|
71
|
-
|
|
72
|
-
> = {
|
|
73
|
-
[K in keyof T]: IsRequired<R[K]> extends true ? T[K] : T[K] | undefined;
|
|
74
|
-
};
|
|
67
|
+
export type ApplyRules<T extends Record<string, unknown>, R extends RuleDescription<T>> = {
|
|
68
|
+
[K in keyof T]: IsRequired<R[K]> extends true ? T[K] : T[K] | undefined
|
|
69
|
+
}
|
|
75
70
|
|
|
76
71
|
// ==== 错误收集 ====
|
|
77
|
-
type FieldErrors<T> = Partial<Record<keyof T, string[]
|
|
72
|
+
type FieldErrors<T> = Partial<Record<keyof T, string[]>>
|
|
78
73
|
|
|
79
74
|
function pushError<T extends Record<string, unknown>>(
|
|
80
75
|
fieldErrors: FieldErrors<T>,
|
|
81
76
|
field: keyof T,
|
|
82
|
-
msg: string
|
|
77
|
+
msg: string,
|
|
83
78
|
) {
|
|
84
|
-
if (!msg) return
|
|
85
|
-
if (!fieldErrors[field]) fieldErrors[field] = []
|
|
86
|
-
(fieldErrors[field] as string[]).push(msg)
|
|
79
|
+
if (!msg) return
|
|
80
|
+
if (!fieldErrors[field]) fieldErrors[field] = []
|
|
81
|
+
;(fieldErrors[field] as string[]).push(msg)
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
function isPresent(value: unknown): boolean {
|
|
90
|
-
if (value === null || value === undefined) return false
|
|
91
|
-
if (typeof value === 'string') return value.trim().length > 0
|
|
92
|
-
if (Array.isArray(value)) return value.length > 0
|
|
93
|
-
return true
|
|
85
|
+
if (value === null || value === undefined) return false
|
|
86
|
+
if (typeof value === 'string') return value.trim().length > 0
|
|
87
|
+
if (Array.isArray(value)) return value.length > 0
|
|
88
|
+
return true
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
function isValueProvided(value: unknown): boolean {
|
|
97
|
-
return value !== null && value !== undefined
|
|
92
|
+
return value !== null && value !== undefined
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
function defaultMsg(field: string | number | symbol, reason: string) {
|
|
101
|
-
return `${String(field)} ${reason}
|
|
96
|
+
return `${String(field)} ${reason}`
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
// ==== 内置校验器 ====
|
|
@@ -106,7 +101,7 @@ const builtins = {
|
|
|
106
101
|
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
107
102
|
url: /^(https?:\/\/)?([\w.-]+)\.([a-z]{2,6})([/\w .-]*)*\/?$/,
|
|
108
103
|
phone: /^1[3-9]\d{9}$/, // 中国手机号
|
|
109
|
-
}
|
|
104
|
+
}
|
|
110
105
|
|
|
111
106
|
// ==== 单字段校验 ====
|
|
112
107
|
function validateRule<T extends Record<string, unknown>, V>(
|
|
@@ -114,227 +109,149 @@ function validateRule<T extends Record<string, unknown>, V>(
|
|
|
114
109
|
value: V,
|
|
115
110
|
rule: FieldRule<V, T>,
|
|
116
111
|
data: Partial<T>,
|
|
117
|
-
fieldErrors: FieldErrors<T
|
|
112
|
+
fieldErrors: FieldErrors<T>,
|
|
118
113
|
) {
|
|
119
|
-
const present = isPresent(value)
|
|
120
|
-
const valueProvided = isValueProvided(value)
|
|
114
|
+
const present = isPresent(value)
|
|
115
|
+
const valueProvided = isValueProvided(value)
|
|
121
116
|
|
|
122
117
|
// required
|
|
123
118
|
if (rule.required && !present) {
|
|
124
|
-
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '为必填项'))
|
|
125
|
-
return
|
|
119
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '为必填项'))
|
|
120
|
+
return
|
|
126
121
|
}
|
|
127
122
|
// 如果值没有提供且不是必填,跳过验证
|
|
128
|
-
if (!valueProvided && !rule.required) return
|
|
123
|
+
if (!valueProvided && !rule.required) return
|
|
129
124
|
|
|
130
125
|
// dependsOn
|
|
131
126
|
if (rule.dependsOn) {
|
|
132
|
-
const res = rule.dependsOn(data)
|
|
127
|
+
const res = rule.dependsOn(data)
|
|
133
128
|
if (res === false)
|
|
134
|
-
pushError(
|
|
135
|
-
|
|
136
|
-
key,
|
|
137
|
-
rule.message ?? defaultMsg(key, '依赖条件未满足')
|
|
138
|
-
);
|
|
139
|
-
else if (typeof res === 'string') pushError(fieldErrors, key, res);
|
|
129
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '依赖条件未满足'))
|
|
130
|
+
else if (typeof res === 'string') pushError(fieldErrors, key, res)
|
|
140
131
|
}
|
|
141
132
|
|
|
142
133
|
// string 相关校验
|
|
143
134
|
if (typeof value === 'string') {
|
|
144
|
-
const stringRule = rule as StringRule<T
|
|
145
|
-
const {
|
|
135
|
+
const stringRule = rule as StringRule<T>
|
|
136
|
+
const {len, min, max, regex, email, url, phone} = stringRule
|
|
146
137
|
|
|
147
138
|
if (typeof len === 'number' && value.length !== len) {
|
|
148
|
-
pushError(
|
|
149
|
-
fieldErrors,
|
|
150
|
-
key,
|
|
151
|
-
rule.message ?? defaultMsg(key, `长度必须为 ${len}`)
|
|
152
|
-
);
|
|
139
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `长度必须为 ${len}`))
|
|
153
140
|
}
|
|
154
141
|
if (typeof min === 'number' && value.length < min) {
|
|
155
|
-
pushError(
|
|
156
|
-
fieldErrors,
|
|
157
|
-
key,
|
|
158
|
-
rule.message ?? defaultMsg(key, `长度不能少于 ${min}`)
|
|
159
|
-
);
|
|
142
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `长度不能少于 ${min}`))
|
|
160
143
|
}
|
|
161
144
|
if (typeof max === 'number' && value.length > max) {
|
|
162
|
-
pushError(
|
|
163
|
-
fieldErrors,
|
|
164
|
-
key,
|
|
165
|
-
rule.message ?? defaultMsg(key, `长度不能超过 ${max}`)
|
|
166
|
-
);
|
|
145
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `长度不能超过 ${max}`))
|
|
167
146
|
}
|
|
168
147
|
if (regex && !regex.test(value)) {
|
|
169
|
-
pushError(
|
|
170
|
-
fieldErrors,
|
|
171
|
-
key,
|
|
172
|
-
rule.message ?? defaultMsg(key, '格式不正确')
|
|
173
|
-
);
|
|
148
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '格式不正确'))
|
|
174
149
|
}
|
|
175
150
|
if (email && !builtins.email.test(value)) {
|
|
176
|
-
pushError(
|
|
177
|
-
fieldErrors,
|
|
178
|
-
key,
|
|
179
|
-
rule.message ?? defaultMsg(key, '不是有效的邮箱')
|
|
180
|
-
);
|
|
151
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '不是有效的邮箱'))
|
|
181
152
|
}
|
|
182
153
|
if (url && !builtins.url.test(value)) {
|
|
183
|
-
pushError(
|
|
184
|
-
fieldErrors,
|
|
185
|
-
key,
|
|
186
|
-
rule.message ?? defaultMsg(key, '不是有效的URL')
|
|
187
|
-
);
|
|
154
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '不是有效的URL'))
|
|
188
155
|
}
|
|
189
156
|
if (phone && !builtins.phone.test(value)) {
|
|
190
|
-
pushError(
|
|
191
|
-
fieldErrors,
|
|
192
|
-
key,
|
|
193
|
-
rule.message ?? defaultMsg(key, '不是有效的手机号')
|
|
194
|
-
);
|
|
157
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '不是有效的手机号'))
|
|
195
158
|
}
|
|
196
159
|
}
|
|
197
160
|
|
|
198
161
|
// number 相关校验
|
|
199
162
|
if (typeof value === 'number') {
|
|
200
|
-
const numberRule = rule as NumberRule<T
|
|
201
|
-
const {
|
|
163
|
+
const numberRule = rule as NumberRule<T>
|
|
164
|
+
const {min, max} = numberRule
|
|
202
165
|
if (typeof min === 'number' && value < min) {
|
|
203
|
-
pushError(
|
|
204
|
-
fieldErrors,
|
|
205
|
-
key,
|
|
206
|
-
rule.message ?? defaultMsg(key, `不能小于 ${min}`)
|
|
207
|
-
);
|
|
166
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `不能小于 ${min}`))
|
|
208
167
|
}
|
|
209
168
|
if (typeof max === 'number' && value > max) {
|
|
210
|
-
pushError(
|
|
211
|
-
fieldErrors,
|
|
212
|
-
key,
|
|
213
|
-
rule.message ?? defaultMsg(key, `不能大于 ${max}`)
|
|
214
|
-
);
|
|
169
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `不能大于 ${max}`))
|
|
215
170
|
}
|
|
216
171
|
}
|
|
217
172
|
|
|
218
173
|
// array 相关校验
|
|
219
174
|
if (Array.isArray(value)) {
|
|
220
|
-
const arrayRule = rule as ArrayRule<T, unknown
|
|
221
|
-
const {
|
|
175
|
+
const arrayRule = rule as ArrayRule<T, unknown>
|
|
176
|
+
const {len, min, max, unique, elementRule} = arrayRule
|
|
222
177
|
|
|
223
178
|
if (typeof len === 'number' && value.length !== len) {
|
|
224
|
-
pushError(
|
|
225
|
-
fieldErrors,
|
|
226
|
-
key,
|
|
227
|
-
rule.message ?? defaultMsg(key, `长度必须为 ${len}`)
|
|
228
|
-
);
|
|
179
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `长度必须为 ${len}`))
|
|
229
180
|
}
|
|
230
181
|
if (typeof min === 'number' && value.length < min) {
|
|
231
|
-
pushError(
|
|
232
|
-
fieldErrors,
|
|
233
|
-
key,
|
|
234
|
-
rule.message ?? defaultMsg(key, `长度不能小于 ${min}`)
|
|
235
|
-
);
|
|
182
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `长度不能小于 ${min}`))
|
|
236
183
|
}
|
|
237
184
|
if (typeof max === 'number' && value.length > max) {
|
|
238
|
-
pushError(
|
|
239
|
-
fieldErrors,
|
|
240
|
-
key,
|
|
241
|
-
rule.message ?? defaultMsg(key, `长度不能大于 ${max}`)
|
|
242
|
-
);
|
|
185
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, `长度不能大于 ${max}`))
|
|
243
186
|
}
|
|
244
187
|
if (unique && new Set(value).size !== value.length) {
|
|
245
|
-
pushError(
|
|
246
|
-
fieldErrors,
|
|
247
|
-
key,
|
|
248
|
-
rule.message ?? defaultMsg(key, '元素必须唯一')
|
|
249
|
-
);
|
|
188
|
+
pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '元素必须唯一'))
|
|
250
189
|
}
|
|
251
190
|
if (elementRule) {
|
|
252
|
-
(value as unknown[]).forEach((v, i) => {
|
|
191
|
+
;(value as unknown[]).forEach((v, i) => {
|
|
253
192
|
validateRule(
|
|
254
193
|
`${String(key)}[${i}]` as keyof T,
|
|
255
194
|
v as unknown,
|
|
256
195
|
elementRule as FieldRule<unknown, T>,
|
|
257
196
|
data,
|
|
258
|
-
fieldErrors
|
|
259
|
-
)
|
|
260
|
-
})
|
|
197
|
+
fieldErrors,
|
|
198
|
+
)
|
|
199
|
+
})
|
|
261
200
|
}
|
|
262
201
|
}
|
|
263
202
|
|
|
264
203
|
// 自定义 validator
|
|
265
204
|
if (rule.validator) {
|
|
266
205
|
const res = (
|
|
267
|
-
rule.validator as
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (res === false)
|
|
272
|
-
pushError(
|
|
273
|
-
fieldErrors,
|
|
274
|
-
key,
|
|
275
|
-
rule.message ?? defaultMsg(key, '校验未通过')
|
|
276
|
-
);
|
|
277
|
-
else if (typeof res === 'string') pushError(fieldErrors, key, res);
|
|
206
|
+
rule.validator as ((value: unknown, data: Partial<T>) => boolean | string) | undefined
|
|
207
|
+
)?.(value as unknown, data)
|
|
208
|
+
if (res === false) pushError(fieldErrors, key, rule.message ?? defaultMsg(key, '校验未通过'))
|
|
209
|
+
else if (typeof res === 'string') pushError(fieldErrors, key, res)
|
|
278
210
|
}
|
|
279
211
|
}
|
|
280
212
|
|
|
281
213
|
// ==== 主函数 ====
|
|
282
|
-
export function ruleChecker<
|
|
283
|
-
T extends Record<string, unknown>,
|
|
284
|
-
R extends RuleDescription<T>,
|
|
285
|
-
>(
|
|
214
|
+
export function ruleChecker<T extends Record<string, unknown>, R extends RuleDescription<T>>(
|
|
286
215
|
data: Partial<T>,
|
|
287
|
-
rules: R
|
|
216
|
+
rules: R,
|
|
288
217
|
):
|
|
289
|
-
| {
|
|
290
|
-
| {
|
|
291
|
-
const fieldErrors: FieldErrors<T> = {}
|
|
218
|
+
| {valid: true; data: ApplyRules<T, R>}
|
|
219
|
+
| {valid: false; errors: string[]; fieldErrors: FieldErrors<T>} {
|
|
220
|
+
const fieldErrors: FieldErrors<T> = {}
|
|
292
221
|
|
|
293
222
|
for (const k in rules) {
|
|
294
|
-
const key = k as keyof T
|
|
223
|
+
const key = k as keyof T
|
|
295
224
|
const ruleOrRules = rules[key] as
|
|
296
225
|
| FieldRule<T[typeof key] | undefined, T>
|
|
297
226
|
| FieldRule<T[typeof key] | undefined, T>[]
|
|
298
|
-
| undefined
|
|
299
|
-
if (!ruleOrRules) continue
|
|
227
|
+
| undefined
|
|
228
|
+
if (!ruleOrRules) continue
|
|
300
229
|
|
|
301
|
-
const value = data[key] as T[typeof key] | undefined
|
|
230
|
+
const value = data[key] as T[typeof key] | undefined
|
|
302
231
|
|
|
303
232
|
// 支持单个规则或规则数组
|
|
304
233
|
if (Array.isArray(ruleOrRules)) {
|
|
305
234
|
// 处理规则数组
|
|
306
235
|
for (const rule of ruleOrRules) {
|
|
307
|
-
validateRule<T, T[typeof key] | undefined>(
|
|
308
|
-
key,
|
|
309
|
-
value,
|
|
310
|
-
rule,
|
|
311
|
-
data,
|
|
312
|
-
fieldErrors
|
|
313
|
-
);
|
|
236
|
+
validateRule<T, T[typeof key] | undefined>(key, value, rule, data, fieldErrors)
|
|
314
237
|
}
|
|
315
238
|
} else {
|
|
316
239
|
// 处理单个规则
|
|
317
|
-
validateRule<T, T[typeof key] | undefined>(
|
|
318
|
-
key,
|
|
319
|
-
value,
|
|
320
|
-
ruleOrRules,
|
|
321
|
-
data,
|
|
322
|
-
fieldErrors
|
|
323
|
-
);
|
|
240
|
+
validateRule<T, T[typeof key] | undefined>(key, value, ruleOrRules, data, fieldErrors)
|
|
324
241
|
}
|
|
325
242
|
}
|
|
326
243
|
|
|
327
244
|
// 聚合错误时,避免 [] 被推断为 never[] 导致的类型问题
|
|
328
|
-
const errors: string[] = (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (v) acc.push(...v)
|
|
332
|
-
return acc
|
|
333
|
-
}, [])
|
|
245
|
+
const errors: string[] = (Object.values(fieldErrors) as Array<string[] | undefined>).reduce<
|
|
246
|
+
string[]
|
|
247
|
+
>((acc, v) => {
|
|
248
|
+
if (v) acc.push(...v)
|
|
249
|
+
return acc
|
|
250
|
+
}, [])
|
|
334
251
|
|
|
335
252
|
if (errors.length > 0) {
|
|
336
|
-
return {
|
|
253
|
+
return {valid: false, errors, fieldErrors}
|
|
337
254
|
}
|
|
338
255
|
|
|
339
|
-
return {
|
|
256
|
+
return {valid: true, data: data as ApplyRules<T, R>}
|
|
340
257
|
}
|
package/src/utils/sundry.ts
CHANGED
|
@@ -28,104 +28,103 @@
|
|
|
28
28
|
* a | am | pm
|
|
29
29
|
*/
|
|
30
30
|
export function formatDate(schema: string, date?: Date): string {
|
|
31
|
-
const d = date || new Date()
|
|
32
|
-
const year = d.getFullYear()
|
|
33
|
-
const month = d.getMonth() + 1
|
|
34
|
-
const day = d.getDate()
|
|
35
|
-
const hour = d.getHours()
|
|
36
|
-
const minute = d.getMinutes()
|
|
37
|
-
const second = d.getSeconds()
|
|
38
|
-
const millisecond = d.getMilliseconds()
|
|
39
|
-
const week = d.getDay()
|
|
40
|
-
const weekName = [
|
|
31
|
+
const d = date || new Date()
|
|
32
|
+
const year = d.getFullYear()
|
|
33
|
+
const month = d.getMonth() + 1
|
|
34
|
+
const day = d.getDate()
|
|
35
|
+
const hour = d.getHours()
|
|
36
|
+
const minute = d.getMinutes()
|
|
37
|
+
const second = d.getSeconds()
|
|
38
|
+
const millisecond = d.getMilliseconds()
|
|
39
|
+
const week = d.getDay()
|
|
40
|
+
const weekName = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
41
41
|
const weekFullName = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
]
|
|
42
|
+
'Sunday',
|
|
43
|
+
'Monday',
|
|
44
|
+
'Tuesday',
|
|
45
|
+
'Wednesday',
|
|
46
|
+
'Thursday',
|
|
47
|
+
'Friday',
|
|
48
|
+
'Saturday',
|
|
49
|
+
]
|
|
50
50
|
const monthName = [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
]
|
|
51
|
+
'Jan',
|
|
52
|
+
'Feb',
|
|
53
|
+
'Mar',
|
|
54
|
+
'Apr',
|
|
55
|
+
'May',
|
|
56
|
+
'Jun',
|
|
57
|
+
'Jul',
|
|
58
|
+
'Aug',
|
|
59
|
+
'Sep',
|
|
60
|
+
'Oct',
|
|
61
|
+
'Nov',
|
|
62
|
+
'Dec',
|
|
63
|
+
]
|
|
64
64
|
const monthFullName = [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
]
|
|
65
|
+
'January',
|
|
66
|
+
'February',
|
|
67
|
+
'March',
|
|
68
|
+
'April',
|
|
69
|
+
'May',
|
|
70
|
+
'June',
|
|
71
|
+
'July',
|
|
72
|
+
'August',
|
|
73
|
+
'September',
|
|
74
|
+
'October',
|
|
75
|
+
'November',
|
|
76
|
+
'December',
|
|
77
|
+
]
|
|
78
78
|
// 直接使用 week 索引,不进行转换,因为 getDay() 已经返回了正确的星期索引 (0-6)
|
|
79
|
-
const weekFull = weekFullName[week]
|
|
80
|
-
const weekShort = weekName[week]
|
|
81
|
-
const monthIndex = month - 1
|
|
82
|
-
const monthFull = monthFullName[monthIndex]
|
|
83
|
-
const monthShort = monthName[monthIndex]
|
|
79
|
+
const weekFull = weekFullName[week]!
|
|
80
|
+
const weekShort = weekName[week]!
|
|
81
|
+
const monthIndex = month - 1
|
|
82
|
+
const monthFull = monthFullName[monthIndex]!
|
|
83
|
+
const monthShort = monthName[monthIndex]!
|
|
84
84
|
const map: Record<string, string> = {
|
|
85
85
|
YY: year.toString().slice(2),
|
|
86
86
|
YYYY: year.toString(),
|
|
87
87
|
M: month.toString(),
|
|
88
|
-
MM: month.toString().padStart(2,
|
|
88
|
+
MM: month.toString().padStart(2, '0'),
|
|
89
89
|
MMM: monthShort,
|
|
90
90
|
MMMM: monthFull,
|
|
91
91
|
D: day.toString(),
|
|
92
|
-
DD: day.toString().padStart(2,
|
|
92
|
+
DD: day.toString().padStart(2, '0'),
|
|
93
93
|
d: week.toString(),
|
|
94
94
|
dd: weekShort,
|
|
95
95
|
ddd: weekShort,
|
|
96
96
|
dddd: weekFull,
|
|
97
97
|
H: hour.toString(),
|
|
98
|
-
HH: hour.toString().padStart(2,
|
|
98
|
+
HH: hour.toString().padStart(2, '0'),
|
|
99
99
|
h: (hour % 12).toString(),
|
|
100
|
-
hh: (hour % 12).toString().padStart(2,
|
|
100
|
+
hh: (hour % 12).toString().padStart(2, '0'),
|
|
101
101
|
m: minute.toString(),
|
|
102
|
-
mm: minute.toString().padStart(2,
|
|
102
|
+
mm: minute.toString().padStart(2, '0'),
|
|
103
103
|
s: second.toString(),
|
|
104
|
-
ss: second.toString().padStart(2,
|
|
105
|
-
SSS: millisecond.toString().padStart(3,
|
|
106
|
-
Z:
|
|
107
|
-
ZZ:
|
|
108
|
-
A: hour < 12 ?
|
|
109
|
-
a: hour < 12 ?
|
|
110
|
-
}
|
|
104
|
+
ss: second.toString().padStart(2, '0'),
|
|
105
|
+
SSS: millisecond.toString().padStart(3, '0'),
|
|
106
|
+
Z: '+08:00',
|
|
107
|
+
ZZ: '+0800',
|
|
108
|
+
A: hour < 12 ? 'AM' : 'PM',
|
|
109
|
+
a: hour < 12 ? 'am' : 'pm',
|
|
110
|
+
}
|
|
111
111
|
|
|
112
112
|
return schema.replace(
|
|
113
113
|
/YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|SSS|Z{1,2}|A|a/g,
|
|
114
114
|
(match) => {
|
|
115
|
-
return map[match]
|
|
116
|
-
}
|
|
117
|
-
)
|
|
115
|
+
return map[match]!
|
|
116
|
+
},
|
|
117
|
+
)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export class Counter {
|
|
121
|
-
count = 0
|
|
121
|
+
count = 0
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
124
|
* @description 获取下一个计数值,不考虑越界。
|
|
125
125
|
* @description_en Get the next count value, without considering overflow.
|
|
126
126
|
*/
|
|
127
127
|
next() {
|
|
128
|
-
return this.count
|
|
128
|
+
return this.count++
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
|