slider-captcha-sdk 1.0.18 → 1.0.20
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/dist/index.esm.js +183 -218
- package/dist/index.min.js +1 -1
- package/dist/password-validator.d.ts +13 -17
- package/dist/password-validator.esm.js +22 -73
- package/dist/password-validator.min.js +1 -1
- package/dist/slider-captcha.esm.js +161 -145
- package/dist/slider-captcha.min.js +1 -1
- package/package.json +12 -9
package/dist/index.esm.js
CHANGED
|
@@ -10,22 +10,22 @@ class PopupSliderCaptcha {
|
|
|
10
10
|
sliderSize: 38,
|
|
11
11
|
maxRetries: 3,
|
|
12
12
|
timeout: 30000,
|
|
13
|
-
apiUrl:
|
|
14
|
-
verifyUrl:
|
|
13
|
+
apiUrl: '/api/captcha',
|
|
14
|
+
verifyUrl: '/api/captcha/verify',
|
|
15
15
|
throttleDelay: 16,
|
|
16
16
|
clickMaskClose: false
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
static CSS_CLASSES = {
|
|
20
|
-
overlay:
|
|
21
|
-
modal:
|
|
22
|
-
header:
|
|
23
|
-
container:
|
|
24
|
-
track:
|
|
25
|
-
btn:
|
|
26
|
-
hint:
|
|
27
|
-
loading:
|
|
28
|
-
error:
|
|
20
|
+
overlay: 'slider-captcha-overlay',
|
|
21
|
+
modal: 'slider-captcha-modal',
|
|
22
|
+
header: 'slider-captcha-header',
|
|
23
|
+
container: 'slider-captcha-container',
|
|
24
|
+
track: 'slider-captcha-track',
|
|
25
|
+
btn: 'slider-captcha-btn',
|
|
26
|
+
hint: 'slider-captcha-hint',
|
|
27
|
+
loading: 'slider-captcha-loading',
|
|
28
|
+
error: 'slider-captcha-error'
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// 优化:提取CSS样式为独立方法,减少主体代码长度
|
|
@@ -42,7 +42,6 @@ class PopupSliderCaptcha {
|
|
|
42
42
|
THROTTLE_DELAY: 16
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// 优化:添加错误类型枚举
|
|
46
45
|
static ERROR_TYPES = {
|
|
47
46
|
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
48
47
|
TIMEOUT_ERROR: 'TIMEOUT_ERROR',
|
|
@@ -52,7 +51,6 @@ class PopupSliderCaptcha {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
constructor(options = {}) {
|
|
55
|
-
// 优化:合并默认配置,使用常量
|
|
56
54
|
this.options = {
|
|
57
55
|
...PopupSliderCaptcha.DEFAULTS,
|
|
58
56
|
...options,
|
|
@@ -61,7 +59,6 @@ class PopupSliderCaptcha {
|
|
|
61
59
|
throttleDelay: options.throttleDelay || PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY
|
|
62
60
|
};
|
|
63
61
|
|
|
64
|
-
// 优化:初始化所有属性
|
|
65
62
|
this.elements = {};
|
|
66
63
|
this.state = this.createInitialState();
|
|
67
64
|
this.captchaData = null;
|
|
@@ -74,10 +71,8 @@ class PopupSliderCaptcha {
|
|
|
74
71
|
this.imageCache = new Map();
|
|
75
72
|
this.abortController = null;
|
|
76
73
|
|
|
77
|
-
// 优化:使用箭头函数绑定this,避免重复bind调用
|
|
78
74
|
this.throttledHandleMove = this.throttle((e) => this.handleMove(e), this.options.throttleDelay);
|
|
79
75
|
|
|
80
|
-
// 优化:添加错误处理
|
|
81
76
|
try {
|
|
82
77
|
this.init();
|
|
83
78
|
} catch (error) {
|
|
@@ -86,7 +81,6 @@ class PopupSliderCaptcha {
|
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
83
|
|
|
89
|
-
// 优化:提取初始状态创建为独立方法
|
|
90
84
|
createInitialState() {
|
|
91
85
|
return {
|
|
92
86
|
isVisible: false,
|
|
@@ -105,15 +99,14 @@ class PopupSliderCaptcha {
|
|
|
105
99
|
}
|
|
106
100
|
|
|
107
101
|
injectStyles() {
|
|
108
|
-
if (document.querySelector(
|
|
102
|
+
if (document.querySelector('#slider-captcha-styles')) { return }
|
|
109
103
|
|
|
110
|
-
const style = document.createElement(
|
|
111
|
-
style.id =
|
|
104
|
+
const style = document.createElement('style');
|
|
105
|
+
style.id = 'slider-captcha-styles';
|
|
112
106
|
style.textContent = PopupSliderCaptcha.getStyles();
|
|
113
107
|
document.head.appendChild(style);
|
|
114
108
|
}
|
|
115
109
|
|
|
116
|
-
// 优化:简化元素创建逻辑
|
|
117
110
|
createElements() {
|
|
118
111
|
const { elements, options } = this;
|
|
119
112
|
|
|
@@ -153,19 +146,18 @@ class PopupSliderCaptcha {
|
|
|
153
146
|
this.setInitialState();
|
|
154
147
|
}
|
|
155
148
|
|
|
156
|
-
createElement(tag, className =
|
|
149
|
+
createElement(tag, className = '', textContent = '') {
|
|
157
150
|
const element = document.createElement(tag);
|
|
158
|
-
if (className) element.className = className;
|
|
159
|
-
if (textContent) element.textContent = textContent;
|
|
151
|
+
if (className) { element.className = className; }
|
|
152
|
+
if (textContent) { element.textContent = textContent; }
|
|
160
153
|
return element
|
|
161
154
|
}
|
|
162
155
|
|
|
163
|
-
// 优化:简化DOM组装逻辑
|
|
164
156
|
assembleDOM() {
|
|
165
157
|
const { elements } = this;
|
|
166
158
|
|
|
167
159
|
// 组装头部
|
|
168
|
-
const headerButtons = this.createElement(
|
|
160
|
+
const headerButtons = this.createElement('div', 'slider-captcha-header-buttons');
|
|
169
161
|
headerButtons.append(elements.refreshBtn, elements.closeBtn);
|
|
170
162
|
elements.header.append(elements.title, headerButtons);
|
|
171
163
|
|
|
@@ -179,19 +171,10 @@ class PopupSliderCaptcha {
|
|
|
179
171
|
|
|
180
172
|
// 组装滑块轨道
|
|
181
173
|
elements.btn.appendChild(elements.icon);
|
|
182
|
-
elements.track.append(
|
|
183
|
-
elements.fingerAnimation,
|
|
184
|
-
elements.btn,
|
|
185
|
-
elements.hint
|
|
186
|
-
);
|
|
174
|
+
elements.track.append(elements.fingerAnimation, elements.btn, elements.hint);
|
|
187
175
|
|
|
188
176
|
// 组装模态框
|
|
189
|
-
elements.modal.append(
|
|
190
|
-
elements.header,
|
|
191
|
-
elements.container,
|
|
192
|
-
elements.track,
|
|
193
|
-
elements.error
|
|
194
|
-
);
|
|
177
|
+
elements.modal.append(elements.header, elements.container, elements.track, elements.error);
|
|
195
178
|
|
|
196
179
|
// 组装到覆盖层
|
|
197
180
|
elements.overlay.appendChild(elements.modal);
|
|
@@ -200,24 +183,23 @@ class PopupSliderCaptcha {
|
|
|
200
183
|
|
|
201
184
|
setInitialState() {
|
|
202
185
|
// 批量设置初始状态
|
|
203
|
-
Object.assign(this.elements.container.style, { display:
|
|
204
|
-
Object.assign(this.elements.track.style, { display:
|
|
186
|
+
Object.assign(this.elements.container.style, { display: 'none' });
|
|
187
|
+
Object.assign(this.elements.track.style, { display: 'none' });
|
|
205
188
|
}
|
|
206
189
|
|
|
207
|
-
// 优化:简化事件绑定
|
|
208
190
|
bindEvents() {
|
|
209
191
|
const { elements } = this;
|
|
210
192
|
|
|
211
193
|
// 基础事件
|
|
212
|
-
this.addEventListener(elements.closeBtn,
|
|
213
|
-
this.addEventListener(elements.refreshBtn,
|
|
214
|
-
this.addEventListener(elements.overlay,
|
|
215
|
-
if (e.target === elements.overlay && this.options.clickMaskClose) this.hide();
|
|
194
|
+
this.addEventListener(elements.closeBtn, 'click', () => this.hide());
|
|
195
|
+
this.addEventListener(elements.refreshBtn, 'click', () => this.refresh());
|
|
196
|
+
this.addEventListener(elements.overlay, 'click', (e) => {
|
|
197
|
+
if (e.target === elements.overlay && this.options.clickMaskClose) { this.hide(); }
|
|
216
198
|
});
|
|
217
|
-
this.addEventListener(document,
|
|
218
|
-
if (e.key ===
|
|
199
|
+
this.addEventListener(document, 'keydown', (e) => {
|
|
200
|
+
if (e.key === 'Escape' && this.state.isVisible) { this.hide(); }
|
|
219
201
|
});
|
|
220
|
-
this.addEventListener(document,
|
|
202
|
+
this.addEventListener(document, 'visibilitychange', () => this.handleVisibilityChange());
|
|
221
203
|
|
|
222
204
|
this.bindSliderEvents();
|
|
223
205
|
}
|
|
@@ -231,19 +213,18 @@ class PopupSliderCaptcha {
|
|
|
231
213
|
};
|
|
232
214
|
|
|
233
215
|
// 滑块事件
|
|
234
|
-
this.addEventListener(elements.btn,
|
|
235
|
-
this.addEventListener(elements.btn,
|
|
236
|
-
this.addEventListener(elements.sliderImg,
|
|
237
|
-
this.addEventListener(elements.sliderImg,
|
|
238
|
-
this.addEventListener(document,
|
|
239
|
-
this.addEventListener(document,
|
|
240
|
-
this.addEventListener(document,
|
|
241
|
-
this.addEventListener(document,
|
|
216
|
+
this.addEventListener(elements.btn, 'mousedown', handlers.start);
|
|
217
|
+
this.addEventListener(elements.btn, 'touchstart', handlers.start);
|
|
218
|
+
this.addEventListener(elements.sliderImg, 'mousedown', handlers.start);
|
|
219
|
+
this.addEventListener(elements.sliderImg, 'touchstart', handlers.start);
|
|
220
|
+
this.addEventListener(document, 'mousemove', handlers.move, { passive: false });
|
|
221
|
+
this.addEventListener(document, 'touchmove', handlers.move, { passive: false });
|
|
222
|
+
this.addEventListener(document, 'mouseup', handlers.end);
|
|
223
|
+
this.addEventListener(document, 'touchend', handlers.end);
|
|
242
224
|
}
|
|
243
225
|
|
|
244
|
-
// 优化:改进事件管理
|
|
245
226
|
addEventListener(element, event, handler, options = {}) {
|
|
246
|
-
if (!element || typeof handler !== 'function') return
|
|
227
|
+
if (!element || typeof handler !== 'function') { return }
|
|
247
228
|
|
|
248
229
|
element.addEventListener(event, handler, options);
|
|
249
230
|
this.eventListeners.push({ element, event, handler, options });
|
|
@@ -260,7 +241,6 @@ class PopupSliderCaptcha {
|
|
|
260
241
|
this.eventListeners.length = 0;
|
|
261
242
|
}
|
|
262
243
|
|
|
263
|
-
// 优化:缓存尺寸计算
|
|
264
244
|
getDimensions() {
|
|
265
245
|
if (!this.cachedDimensions) {
|
|
266
246
|
const trackWidth = this.elements.track.offsetWidth;
|
|
@@ -280,9 +260,8 @@ class PopupSliderCaptcha {
|
|
|
280
260
|
return Math.round(percentage * (this.options.width - this.options.sliderSize))
|
|
281
261
|
}
|
|
282
262
|
|
|
283
|
-
// 优化:简化拖拽处理
|
|
284
263
|
handleStart(e) {
|
|
285
|
-
if (!this.captchaData || this.state.isDragging || this.state.isLoading) return
|
|
264
|
+
if (!this.captchaData || this.state.isDragging || this.state.isLoading) { return }
|
|
286
265
|
|
|
287
266
|
e.preventDefault();
|
|
288
267
|
this.state.isDragging = true;
|
|
@@ -291,12 +270,12 @@ class PopupSliderCaptcha {
|
|
|
291
270
|
this.times = [{ time: Date.now(), position: this.getPosition() }];
|
|
292
271
|
|
|
293
272
|
this.setTransition(false);
|
|
294
|
-
this.updateUIState(
|
|
273
|
+
this.updateUIState('dragging');
|
|
295
274
|
this.cachedDimensions = null; // 清除缓存
|
|
296
275
|
}
|
|
297
276
|
|
|
298
277
|
handleMove(e) {
|
|
299
|
-
if (!this.state.isDragging) return
|
|
278
|
+
if (!this.state.isDragging) { return }
|
|
300
279
|
e.preventDefault();
|
|
301
280
|
|
|
302
281
|
const clientX = this.getClientX(e);
|
|
@@ -307,12 +286,14 @@ class PopupSliderCaptcha {
|
|
|
307
286
|
this.times.push({ time: Date.now(), position: this.getPosition() });
|
|
308
287
|
|
|
309
288
|
// 优化:使用RAF批量更新
|
|
310
|
-
|
|
289
|
+
if (this.rafId) {
|
|
290
|
+
cancelAnimationFrame(this.rafId);
|
|
291
|
+
}
|
|
311
292
|
this.rafId = requestAnimationFrame(() => this.updateSliderPosition());
|
|
312
293
|
}
|
|
313
294
|
|
|
314
295
|
handleEnd() {
|
|
315
|
-
if (!this.state.isDragging) return
|
|
296
|
+
if (!this.state.isDragging) { return }
|
|
316
297
|
|
|
317
298
|
this.times.push({ time: Date.now(), position: this.getPosition() });
|
|
318
299
|
this.state.isDragging = false;
|
|
@@ -333,11 +314,11 @@ class PopupSliderCaptcha {
|
|
|
333
314
|
}
|
|
334
315
|
|
|
335
316
|
getClientX(e) {
|
|
336
|
-
return e.type.includes(
|
|
317
|
+
return e.type.includes('touch') ? e.touches[0].clientX : e.clientX
|
|
337
318
|
}
|
|
338
319
|
|
|
339
320
|
setTransition(enabled) {
|
|
340
|
-
const transition = enabled ?
|
|
321
|
+
const transition = enabled ? 'all 0.3s ease' : 'none';
|
|
341
322
|
requestAnimationFrame(() => {
|
|
342
323
|
this.elements.btn.style.transition = transition;
|
|
343
324
|
this.elements.sliderImg.style.transition = transition;
|
|
@@ -349,38 +330,38 @@ class PopupSliderCaptcha {
|
|
|
349
330
|
const { elements } = this;
|
|
350
331
|
const updates = {
|
|
351
332
|
dragging: () => {
|
|
352
|
-
elements.hint.style.opacity =
|
|
353
|
-
elements.fingerAnimation.style.display =
|
|
333
|
+
elements.hint.style.opacity = '0';
|
|
334
|
+
elements.fingerAnimation.style.display = 'none';
|
|
354
335
|
},
|
|
355
336
|
success: () => {
|
|
356
|
-
Object.assign(elements.btn.style, { background:
|
|
357
|
-
Object.assign(elements.icon.style, { innerHTML:
|
|
358
|
-
elements.icon.innerHTML =
|
|
337
|
+
Object.assign(elements.btn.style, { background: 'var(--sc-success)' });
|
|
338
|
+
Object.assign(elements.icon.style, { innerHTML: '✓', color: 'white' });
|
|
339
|
+
elements.icon.innerHTML = '✓';
|
|
359
340
|
},
|
|
360
341
|
fail: () => {
|
|
361
|
-
Object.assign(elements.btn.style, { background:
|
|
362
|
-
Object.assign(elements.icon.style, { innerHTML:
|
|
363
|
-
elements.icon.innerHTML =
|
|
342
|
+
Object.assign(elements.btn.style, { background: 'var(--sc-danger)' });
|
|
343
|
+
Object.assign(elements.icon.style, { innerHTML: '✗', color: 'white' });
|
|
344
|
+
elements.icon.innerHTML = '✗';
|
|
364
345
|
},
|
|
365
346
|
reset: () => {
|
|
366
|
-
Object.assign(elements.btn.style, { background:
|
|
367
|
-
Object.assign(elements.icon.style, { color:
|
|
368
|
-
elements.icon.innerHTML =
|
|
369
|
-
elements.fingerAnimation.style.display =
|
|
370
|
-
this.updateHintText(
|
|
347
|
+
Object.assign(elements.btn.style, { background: 'white' });
|
|
348
|
+
Object.assign(elements.icon.style, { color: '#666' });
|
|
349
|
+
elements.icon.innerHTML = '→';
|
|
350
|
+
elements.fingerAnimation.style.display = 'block';
|
|
351
|
+
this.updateHintText('向右滑动完成验证', 'var(--sc-text-light)');
|
|
371
352
|
},
|
|
372
353
|
loading: () => {
|
|
373
|
-
elements.hint.style.opacity =
|
|
374
|
-
elements.fingerAnimation.style.display =
|
|
375
|
-
Object.assign(elements.track.style, { pointerEvents:
|
|
354
|
+
elements.hint.style.opacity = '0';
|
|
355
|
+
elements.fingerAnimation.style.display = 'none';
|
|
356
|
+
Object.assign(elements.track.style, { pointerEvents: 'none', opacity: '0.6' });
|
|
376
357
|
}
|
|
377
358
|
};
|
|
378
359
|
|
|
379
360
|
if (updates[state]) {
|
|
380
361
|
requestAnimationFrame(() => {
|
|
381
362
|
updates[state]();
|
|
382
|
-
if (state !==
|
|
383
|
-
Object.assign(elements.track.style, { pointerEvents:
|
|
363
|
+
if (state !== 'loading') {
|
|
364
|
+
Object.assign(elements.track.style, { pointerEvents: 'auto', opacity: '1' });
|
|
384
365
|
}
|
|
385
366
|
});
|
|
386
367
|
}
|
|
@@ -389,7 +370,7 @@ class PopupSliderCaptcha {
|
|
|
389
370
|
updateHintText(text, color) {
|
|
390
371
|
requestAnimationFrame(() => {
|
|
391
372
|
Object.assign(this.elements.hint, { textContent: text });
|
|
392
|
-
Object.assign(this.elements.hint.style, { color, opacity:
|
|
373
|
+
Object.assign(this.elements.hint.style, { color, opacity: '1' });
|
|
393
374
|
});
|
|
394
375
|
}
|
|
395
376
|
|
|
@@ -402,18 +383,18 @@ class PopupSliderCaptcha {
|
|
|
402
383
|
requestAnimationFrame(() => {
|
|
403
384
|
elements.btn.style.transform = `translateX(${state.currentX}px)`;
|
|
404
385
|
elements.sliderImg.style.transform = `translateX(${pieceX}px)`;
|
|
405
|
-
elements.fingerAnimation.style.opacity = progress >= 0.8 ?
|
|
386
|
+
elements.fingerAnimation.style.opacity = progress >= 0.8 ? '0' : '0.6';
|
|
406
387
|
});
|
|
407
388
|
}
|
|
408
389
|
|
|
409
390
|
// 优化:简化显示/隐藏逻辑
|
|
410
391
|
show() {
|
|
411
392
|
this.state.isVisible = true;
|
|
412
|
-
this.elements.overlay.style.display =
|
|
393
|
+
this.elements.overlay.style.display = 'flex';
|
|
413
394
|
|
|
414
395
|
requestAnimationFrame(() => {
|
|
415
|
-
this.elements.overlay.classList.add(
|
|
416
|
-
this.elements.modal.classList.add(
|
|
396
|
+
this.elements.overlay.classList.add('show');
|
|
397
|
+
this.elements.modal.classList.add('show');
|
|
417
398
|
});
|
|
418
399
|
|
|
419
400
|
this.loadCaptcha();
|
|
@@ -421,11 +402,11 @@ class PopupSliderCaptcha {
|
|
|
421
402
|
|
|
422
403
|
hide() {
|
|
423
404
|
this.state.isVisible = false;
|
|
424
|
-
this.elements.overlay.classList.remove(
|
|
425
|
-
this.elements.modal.classList.remove(
|
|
405
|
+
this.elements.overlay.classList.remove('show');
|
|
406
|
+
this.elements.modal.classList.remove('show');
|
|
426
407
|
|
|
427
408
|
this.safeSetTimeout(() => {
|
|
428
|
-
this.elements.overlay.style.display =
|
|
409
|
+
this.elements.overlay.style.display = 'none';
|
|
429
410
|
document.body.removeChild(this.elements.overlay);
|
|
430
411
|
this.reset();
|
|
431
412
|
this.options.onClose?.();
|
|
@@ -471,9 +452,9 @@ class PopupSliderCaptcha {
|
|
|
471
452
|
this.abortController = new AbortController();
|
|
472
453
|
|
|
473
454
|
const response = await fetch(this.options.apiUrl, {
|
|
474
|
-
method:
|
|
455
|
+
method: 'POST',
|
|
475
456
|
headers: {
|
|
476
|
-
|
|
457
|
+
'Content-Type': 'application/json',
|
|
477
458
|
...this.options.headers
|
|
478
459
|
},
|
|
479
460
|
body: JSON.stringify({
|
|
@@ -491,7 +472,7 @@ class PopupSliderCaptcha {
|
|
|
491
472
|
|
|
492
473
|
// 优化:更严格的数据验证
|
|
493
474
|
if (!this.validateCaptchaData(data)) {
|
|
494
|
-
throw new Error(
|
|
475
|
+
throw new Error('验证码数据格式错误')
|
|
495
476
|
}
|
|
496
477
|
|
|
497
478
|
this.captchaData = data.data;
|
|
@@ -500,7 +481,10 @@ class PopupSliderCaptcha {
|
|
|
500
481
|
} catch (error) {
|
|
501
482
|
if (error.name === 'AbortError') {
|
|
502
483
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR, '请求被取消');
|
|
503
|
-
} else if (
|
|
484
|
+
} else if (
|
|
485
|
+
error.message.includes('Failed to fetch') ||
|
|
486
|
+
error.message.includes('NetworkError')
|
|
487
|
+
) {
|
|
504
488
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR, '网络连接失败');
|
|
505
489
|
} else {
|
|
506
490
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR, error.message, error);
|
|
@@ -510,12 +494,21 @@ class PopupSliderCaptcha {
|
|
|
510
494
|
|
|
511
495
|
// 优化:添加验证码数据验证方法
|
|
512
496
|
validateCaptchaData(data) {
|
|
513
|
-
if (!data || typeof data !== 'object') return false
|
|
514
|
-
|
|
515
|
-
const requiredFields = [
|
|
497
|
+
if (!data || typeof data !== 'object') { return false }
|
|
498
|
+
|
|
499
|
+
const requiredFields = [
|
|
500
|
+
'canvasSrc',
|
|
501
|
+
'blockSrc',
|
|
502
|
+
'canvasWidth',
|
|
503
|
+
'canvasHeight',
|
|
504
|
+
'blockWidth',
|
|
505
|
+
'blockHeight',
|
|
506
|
+
'blockY',
|
|
507
|
+
'nonceStr'
|
|
508
|
+
];
|
|
516
509
|
const dataObj = data.data || data;
|
|
517
510
|
|
|
518
|
-
return requiredFields.every(field => {
|
|
511
|
+
return requiredFields.every((field) => {
|
|
519
512
|
const value = dataObj[field];
|
|
520
513
|
return value !== null && value !== undefined && value !== ''
|
|
521
514
|
})
|
|
@@ -525,6 +518,22 @@ class PopupSliderCaptcha {
|
|
|
525
518
|
return new Promise((resolve, reject) => {
|
|
526
519
|
let hasError = false;
|
|
527
520
|
|
|
521
|
+
// const _onLoad = () => {
|
|
522
|
+
// if (hasError) { return }
|
|
523
|
+
// loadedCount++
|
|
524
|
+
// if (loadedCount === 2) {
|
|
525
|
+
// this.hideLoading()
|
|
526
|
+
// resolve()
|
|
527
|
+
// }
|
|
528
|
+
// }
|
|
529
|
+
//
|
|
530
|
+
// const _onError = (error) => {
|
|
531
|
+
// if (hasError) { return }
|
|
532
|
+
// hasError = true
|
|
533
|
+
// this.handleError(PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR, '图片加载失败', error)
|
|
534
|
+
// reject(new Error('图片加载失败'))
|
|
535
|
+
// }
|
|
536
|
+
|
|
528
537
|
// 优化:并行加载图片,提高性能
|
|
529
538
|
const loadPromises = [
|
|
530
539
|
this.loadImageAsync(this.elements.backgroundImg, this.captchaData.canvasSrc, {
|
|
@@ -571,16 +580,16 @@ class PopupSliderCaptcha {
|
|
|
571
580
|
reject(new Error('图片加载超时'));
|
|
572
581
|
}, 10000); // 10秒超时
|
|
573
582
|
|
|
574
|
-
imgElement.onload = ()
|
|
583
|
+
imgElement.onload = function onImageLoad() {
|
|
575
584
|
this.safeClearTimeout(timeoutId);
|
|
576
585
|
this.imageCache.set(src, imgElement.cloneNode());
|
|
577
586
|
resolve();
|
|
578
|
-
};
|
|
587
|
+
}.bind(this);
|
|
579
588
|
|
|
580
|
-
imgElement.onerror = (error)
|
|
589
|
+
imgElement.onerror = function onImageError(error) {
|
|
581
590
|
this.safeClearTimeout(timeoutId);
|
|
582
591
|
reject(error);
|
|
583
|
-
};
|
|
592
|
+
}.bind(this);
|
|
584
593
|
|
|
585
594
|
imgElement.src = src;
|
|
586
595
|
this.applyStyles(imgElement, styles);
|
|
@@ -590,7 +599,7 @@ class PopupSliderCaptcha {
|
|
|
590
599
|
// 优化:提取样式应用逻辑
|
|
591
600
|
applyStyles(element, styles) {
|
|
592
601
|
Object.entries(styles).forEach(([key, value]) => {
|
|
593
|
-
element.style[key] = typeof value ===
|
|
602
|
+
element.style[key] = typeof value === 'number' ? `${value}px` : value;
|
|
594
603
|
});
|
|
595
604
|
}
|
|
596
605
|
|
|
@@ -598,31 +607,31 @@ class PopupSliderCaptcha {
|
|
|
598
607
|
showLoading() {
|
|
599
608
|
this.state.isLoading = true;
|
|
600
609
|
this.batchUpdateStyles({
|
|
601
|
-
container: { display:
|
|
602
|
-
loadingText: { display:
|
|
603
|
-
error: { display:
|
|
610
|
+
container: { display: 'block' },
|
|
611
|
+
loadingText: { display: 'flex' },
|
|
612
|
+
error: { display: 'none' }
|
|
604
613
|
});
|
|
605
|
-
this.updateUIState(
|
|
614
|
+
this.updateUIState('loading');
|
|
606
615
|
}
|
|
607
616
|
|
|
608
617
|
hideLoading() {
|
|
609
618
|
this.state.isLoading = false;
|
|
610
|
-
this.batchUpdateStyles({ loadingText: { display:
|
|
611
|
-
this.updateUIState(
|
|
619
|
+
this.batchUpdateStyles({ loadingText: { display: 'none' } });
|
|
620
|
+
this.updateUIState('reset');
|
|
612
621
|
}
|
|
613
622
|
|
|
614
623
|
showCaptcha() {
|
|
615
624
|
this.batchUpdateStyles({
|
|
616
|
-
container: { display:
|
|
617
|
-
track: { display:
|
|
618
|
-
error: { display:
|
|
625
|
+
container: { display: 'block' },
|
|
626
|
+
track: { display: 'block' },
|
|
627
|
+
error: { display: 'none' }
|
|
619
628
|
});
|
|
620
629
|
}
|
|
621
630
|
|
|
622
631
|
showError(message) {
|
|
623
632
|
this.hideLoading();
|
|
624
633
|
this.batchUpdateStyles({
|
|
625
|
-
error: { display:
|
|
634
|
+
error: { display: 'block', textContent: message }
|
|
626
635
|
});
|
|
627
636
|
}
|
|
628
637
|
|
|
@@ -646,7 +655,7 @@ class PopupSliderCaptcha {
|
|
|
646
655
|
|
|
647
656
|
async verify() {
|
|
648
657
|
if (!this.captchaData) {
|
|
649
|
-
this.onVerifyFail(
|
|
658
|
+
this.onVerifyFail('验证码数据丢失');
|
|
650
659
|
return
|
|
651
660
|
}
|
|
652
661
|
|
|
@@ -658,9 +667,9 @@ class PopupSliderCaptcha {
|
|
|
658
667
|
this.abortController = new AbortController();
|
|
659
668
|
|
|
660
669
|
const response = await fetch(this.options.verifyUrl, {
|
|
661
|
-
method:
|
|
670
|
+
method: 'POST',
|
|
662
671
|
headers: {
|
|
663
|
-
|
|
672
|
+
'Content-Type': 'application/json',
|
|
664
673
|
...this.options.headers
|
|
665
674
|
},
|
|
666
675
|
body: JSON.stringify({
|
|
@@ -684,12 +693,15 @@ class PopupSliderCaptcha {
|
|
|
684
693
|
if (this.isVerifySuccess(data)) {
|
|
685
694
|
this.onVerifySuccess(data.data || data.result);
|
|
686
695
|
} else {
|
|
687
|
-
this.onVerifyFail(data.message || data.msg ||
|
|
696
|
+
this.onVerifyFail(data.message || data.msg || '验证失败,请重试!');
|
|
688
697
|
}
|
|
689
698
|
} catch (error) {
|
|
690
699
|
if (error.name === 'AbortError') {
|
|
691
700
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR, '验证请求被取消');
|
|
692
|
-
} else if (
|
|
701
|
+
} else if (
|
|
702
|
+
error.message.includes('Failed to fetch') ||
|
|
703
|
+
error.message.includes('NetworkError')
|
|
704
|
+
) {
|
|
693
705
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR, '网络连接失败');
|
|
694
706
|
} else {
|
|
695
707
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR, error.message, error);
|
|
@@ -699,30 +711,32 @@ class PopupSliderCaptcha {
|
|
|
699
711
|
|
|
700
712
|
// 优化:添加验证成功判断方法
|
|
701
713
|
isVerifySuccess(data) {
|
|
702
|
-
if (!data || typeof data !== 'object') return false
|
|
714
|
+
if (!data || typeof data !== 'object') { return false }
|
|
703
715
|
|
|
704
716
|
// 支持多种成功标识
|
|
705
717
|
const successIndicators = [
|
|
706
|
-
data.code ===
|
|
718
|
+
data.code === '0',
|
|
707
719
|
data.code === 0,
|
|
708
720
|
data.success === true,
|
|
709
721
|
data.status === 'success',
|
|
710
722
|
data.result === true
|
|
711
723
|
];
|
|
712
724
|
|
|
713
|
-
return successIndicators.some(indicator => indicator === true)
|
|
725
|
+
return successIndicators.some((indicator) => indicator === true)
|
|
714
726
|
}
|
|
715
727
|
|
|
716
728
|
onVerifySuccess(ticket) {
|
|
717
|
-
const duration = this.dragStartTime ?
|
|
729
|
+
const duration = this.dragStartTime ?
|
|
730
|
+
Date.now() - this.dragStartTime :
|
|
731
|
+
Date.now() - this.startTime;
|
|
718
732
|
const durationText = `验证成功!耗时:${(duration / 1000).toFixed(2)}s`;
|
|
719
733
|
|
|
720
|
-
this.updateUIState(
|
|
721
|
-
this.showFloatingTime(durationText,
|
|
734
|
+
this.updateUIState('success');
|
|
735
|
+
this.showFloatingTime(durationText, 'success');
|
|
722
736
|
|
|
723
737
|
this.safeSetTimeout(() => {
|
|
724
738
|
this.options.onSuccess?.({
|
|
725
|
-
ticket
|
|
739
|
+
ticket,
|
|
726
740
|
timestamp: Date.now(),
|
|
727
741
|
duration
|
|
728
742
|
});
|
|
@@ -730,21 +744,21 @@ class PopupSliderCaptcha {
|
|
|
730
744
|
}, 2000);
|
|
731
745
|
}
|
|
732
746
|
|
|
733
|
-
showFloatingTime(text, type =
|
|
747
|
+
showFloatingTime(text, type = 'success') {
|
|
734
748
|
const { elements } = this;
|
|
735
749
|
elements.floatingTime.textContent = text;
|
|
736
750
|
elements.floatingTime.className = `slider-captcha-floating-time ${type}`;
|
|
737
751
|
|
|
738
|
-
this.safeSetTimeout(() => elements.floatingTime.classList.add(
|
|
752
|
+
this.safeSetTimeout(() => elements.floatingTime.classList.add('show'), 100);
|
|
739
753
|
this.safeSetTimeout(() => {
|
|
740
|
-
elements.floatingTime.className =
|
|
754
|
+
elements.floatingTime.className = 'slider-captcha-floating-time';
|
|
741
755
|
}, 2500); // 优化:延长显示时间,避免被reset清除
|
|
742
756
|
}
|
|
743
757
|
|
|
744
758
|
onVerifyFail(message) {
|
|
745
759
|
this.state.retryCount++;
|
|
746
|
-
this.updateUIState(
|
|
747
|
-
this.showFloatingTime(message,
|
|
760
|
+
this.updateUIState('fail');
|
|
761
|
+
this.showFloatingTime(message, 'fail');
|
|
748
762
|
|
|
749
763
|
this.safeSetTimeout(() => {
|
|
750
764
|
if (this.state.retryCount >= this.options.maxRetries) {
|
|
@@ -774,10 +788,10 @@ class PopupSliderCaptcha {
|
|
|
774
788
|
// 重置UI
|
|
775
789
|
requestAnimationFrame(() => {
|
|
776
790
|
this.setTransition(true);
|
|
777
|
-
this.elements.btn.style.transform =
|
|
778
|
-
this.elements.sliderImg.style.transform =
|
|
779
|
-
this.updateUIState(
|
|
780
|
-
this.elements.error.style.display =
|
|
791
|
+
this.elements.btn.style.transform = 'translateX(0px)';
|
|
792
|
+
this.elements.sliderImg.style.transform = 'translateX(0px)';
|
|
793
|
+
this.updateUIState('reset');
|
|
794
|
+
this.elements.error.style.display = 'none';
|
|
781
795
|
});
|
|
782
796
|
}
|
|
783
797
|
|
|
@@ -805,7 +819,7 @@ class PopupSliderCaptcha {
|
|
|
805
819
|
}
|
|
806
820
|
|
|
807
821
|
clearAllTimers() {
|
|
808
|
-
this.timers.forEach(timer => {
|
|
822
|
+
this.timers.forEach((timer) => {
|
|
809
823
|
clearTimeout(timer);
|
|
810
824
|
clearInterval(timer);
|
|
811
825
|
});
|
|
@@ -835,13 +849,15 @@ class PopupSliderCaptcha {
|
|
|
835
849
|
// 工具函数:节流
|
|
836
850
|
throttle(func, delay) {
|
|
837
851
|
let lastCall = 0;
|
|
838
|
-
|
|
852
|
+
const throttledFunction = (...args) => {
|
|
839
853
|
const now = Date.now();
|
|
840
854
|
if (now - lastCall >= delay) {
|
|
841
855
|
lastCall = now;
|
|
842
856
|
return func.apply(this, args)
|
|
843
857
|
}
|
|
844
|
-
|
|
858
|
+
return undefined
|
|
859
|
+
};
|
|
860
|
+
return throttledFunction
|
|
845
861
|
}
|
|
846
862
|
|
|
847
863
|
destroy() {
|
|
@@ -854,8 +870,8 @@ class PopupSliderCaptcha {
|
|
|
854
870
|
|
|
855
871
|
// 恢复body样式
|
|
856
872
|
if (document.body) {
|
|
857
|
-
document.body.style.userSelect =
|
|
858
|
-
document.body.style.cursor =
|
|
873
|
+
document.body.style.userSelect = '';
|
|
874
|
+
document.body.style.cursor = '';
|
|
859
875
|
}
|
|
860
876
|
|
|
861
877
|
// 清理所有定时器
|
|
@@ -883,7 +899,7 @@ class PopupSliderCaptcha {
|
|
|
883
899
|
this.cachedDimensions = null;
|
|
884
900
|
|
|
885
901
|
// 清空所有属性
|
|
886
|
-
Object.keys(this).forEach(key => {
|
|
902
|
+
Object.keys(this).forEach((key) => {
|
|
887
903
|
if (key !== 'constructor') {
|
|
888
904
|
this[key] = null;
|
|
889
905
|
}
|
|
@@ -910,12 +926,12 @@ class PopupSliderCaptcha {
|
|
|
910
926
|
}
|
|
911
927
|
|
|
912
928
|
// 模块导出
|
|
913
|
-
if (typeof module !==
|
|
929
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
914
930
|
module.exports = PopupSliderCaptcha;
|
|
915
931
|
module.exports.default = PopupSliderCaptcha;
|
|
916
|
-
} else if (typeof define ===
|
|
932
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
917
933
|
define([], () => PopupSliderCaptcha);
|
|
918
|
-
} else if (typeof window !==
|
|
934
|
+
} else if (typeof window !== 'undefined') {
|
|
919
935
|
window.PopupSliderCaptcha = PopupSliderCaptcha;
|
|
920
936
|
window.SliderCaptcha = PopupSliderCaptcha;
|
|
921
937
|
}
|
|
@@ -928,14 +944,12 @@ var PopupSliderCaptcha$1 = PopupSliderCaptcha;
|
|
|
928
944
|
* 提供密码加密和校验功能
|
|
929
945
|
*/
|
|
930
946
|
class PasswordValidator {
|
|
931
|
-
// 优化:添加常量定义
|
|
932
947
|
static CONSTANTS = {
|
|
933
948
|
DEFAULT_TIMEOUT: 10000,
|
|
934
949
|
CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存
|
|
935
950
|
MIN_PASSWORD_LENGTH: 1
|
|
936
951
|
}
|
|
937
952
|
|
|
938
|
-
// 优化:添加错误类型枚举
|
|
939
953
|
static ERROR_TYPES = {
|
|
940
954
|
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
941
955
|
TIMEOUT_ERROR: 'TIMEOUT_ERROR',
|
|
@@ -945,7 +959,6 @@ class PasswordValidator {
|
|
|
945
959
|
}
|
|
946
960
|
|
|
947
961
|
constructor(options = {}) {
|
|
948
|
-
// 优化:合并配置,使用常量
|
|
949
962
|
this.options = {
|
|
950
963
|
publicKeyUrl: options.publicKeyUrl || '/microservice/strongPassword/getPublicKey',
|
|
951
964
|
validateUrl: options.validateUrl || '/microservice/strongPassword/checkPassword',
|
|
@@ -958,7 +971,6 @@ class PasswordValidator {
|
|
|
958
971
|
// 缓存公钥,避免重复请求
|
|
959
972
|
this.publicKeyCache = null;
|
|
960
973
|
this.publicKeyExpiry = null;
|
|
961
|
-
this.abortController = null;
|
|
962
974
|
}
|
|
963
975
|
|
|
964
976
|
/**
|
|
@@ -972,25 +984,18 @@ class PasswordValidator {
|
|
|
972
984
|
}
|
|
973
985
|
|
|
974
986
|
try {
|
|
975
|
-
// 取消之前的请求
|
|
976
|
-
if (this.abortController) {
|
|
977
|
-
this.abortController.abort();
|
|
978
|
-
}
|
|
979
|
-
this.abortController = new AbortController();
|
|
980
|
-
|
|
981
987
|
const response = await this.makeRequest(this.options.publicKeyUrl, {
|
|
982
|
-
method: 'GET'
|
|
983
|
-
signal: this.abortController.signal
|
|
988
|
+
method: 'GET'
|
|
984
989
|
});
|
|
985
990
|
|
|
986
991
|
if (!this.isSuccessResponse(response)) {
|
|
987
|
-
throw new Error(
|
|
992
|
+
throw new Error(`获取公钥失败:${response.message || response.msg || '未知错误'}`)
|
|
988
993
|
}
|
|
989
994
|
|
|
990
995
|
// 验证公钥格式
|
|
991
996
|
const publicKey = response.data || response.result;
|
|
992
|
-
if (!
|
|
993
|
-
throw new Error('
|
|
997
|
+
if (!publicKey) {
|
|
998
|
+
throw new Error('公钥为空')
|
|
994
999
|
}
|
|
995
1000
|
|
|
996
1001
|
// 缓存公钥
|
|
@@ -1001,13 +1006,15 @@ class PasswordValidator {
|
|
|
1001
1006
|
} catch (error) {
|
|
1002
1007
|
console.error('获取公钥失败:', error);
|
|
1003
1008
|
this.handleError(PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR, error.message, error);
|
|
1004
|
-
throw new Error(
|
|
1009
|
+
throw new Error(`获取公钥失败: ${error.message}`)
|
|
1005
1010
|
}
|
|
1006
1011
|
}
|
|
1007
1012
|
|
|
1008
1013
|
// 优化:添加响应成功判断方法
|
|
1009
1014
|
isSuccessResponse(response) {
|
|
1010
|
-
if (!response || typeof response !== 'object')
|
|
1015
|
+
if (!response || typeof response !== 'object') {
|
|
1016
|
+
return false
|
|
1017
|
+
}
|
|
1011
1018
|
|
|
1012
1019
|
const successIndicators = [
|
|
1013
1020
|
response.code === '0',
|
|
@@ -1017,18 +1024,9 @@ class PasswordValidator {
|
|
|
1017
1024
|
response.result === true
|
|
1018
1025
|
];
|
|
1019
1026
|
|
|
1020
|
-
return successIndicators.some(indicator => indicator === true)
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// 优化:添加公钥验证方法
|
|
1024
|
-
validatePublicKey(publicKey) {
|
|
1025
|
-
if (!publicKey || typeof publicKey !== 'string') return false
|
|
1026
|
-
|
|
1027
|
-
// 基本的RSA公钥格式检查
|
|
1028
|
-
return true
|
|
1027
|
+
return successIndicators.some((indicator) => indicator === true)
|
|
1029
1028
|
}
|
|
1030
1029
|
|
|
1031
|
-
// 优化:添加统一错误处理方法
|
|
1032
1030
|
handleError(errorType, message, originalError = null) {
|
|
1033
1031
|
const errorMessages = {
|
|
1034
1032
|
[PasswordValidator.ERROR_TYPES.NETWORK_ERROR]: '网络连接失败,请检查网络设置',
|
|
@@ -1060,7 +1058,6 @@ class PasswordValidator {
|
|
|
1060
1058
|
*/
|
|
1061
1059
|
encryptPassword(password, publicKey) {
|
|
1062
1060
|
try {
|
|
1063
|
-
// 优化:输入验证
|
|
1064
1061
|
if (!password) {
|
|
1065
1062
|
throw new Error('密码不能为空')
|
|
1066
1063
|
}
|
|
@@ -1073,7 +1070,6 @@ class PasswordValidator {
|
|
|
1073
1070
|
throw new Error('公钥不能为空')
|
|
1074
1071
|
}
|
|
1075
1072
|
|
|
1076
|
-
// 使用导入的JSEncrypt进行RSA加密
|
|
1077
1073
|
if (typeof JSEncrypt === 'undefined') {
|
|
1078
1074
|
throw new Error('JSEncrypt库未正确加载,请确保已引入JSEncrypt库')
|
|
1079
1075
|
}
|
|
@@ -1090,7 +1086,7 @@ class PasswordValidator {
|
|
|
1090
1086
|
} catch (error) {
|
|
1091
1087
|
console.error('密码加密失败:', error);
|
|
1092
1088
|
this.handleError(PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR, error.message, error);
|
|
1093
|
-
throw new Error(
|
|
1089
|
+
throw new Error(`密码加密失败: ${error.message}`)
|
|
1094
1090
|
}
|
|
1095
1091
|
}
|
|
1096
1092
|
|
|
@@ -1103,13 +1099,8 @@ class PasswordValidator {
|
|
|
1103
1099
|
*/
|
|
1104
1100
|
async validatePassword(password, userName, additionalData = {}) {
|
|
1105
1101
|
try {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
throw new Error('密码不能为空')
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
if (!userName) {
|
|
1112
|
-
throw new Error('用户名不能为空')
|
|
1102
|
+
if (!password || !userName) {
|
|
1103
|
+
throw new Error('用户名密码不能为空')
|
|
1113
1104
|
}
|
|
1114
1105
|
|
|
1115
1106
|
// 1. 获取公钥
|
|
@@ -1122,14 +1113,13 @@ class PasswordValidator {
|
|
|
1122
1113
|
const response = await this.makeRequest(this.options.validateUrl, {
|
|
1123
1114
|
method: 'POST',
|
|
1124
1115
|
body: JSON.stringify({
|
|
1125
|
-
userName
|
|
1116
|
+
userName,
|
|
1126
1117
|
password: encryptedPassword,
|
|
1127
1118
|
timestamp: Date.now(),
|
|
1128
1119
|
...additionalData
|
|
1129
1120
|
})
|
|
1130
1121
|
});
|
|
1131
1122
|
|
|
1132
|
-
// 优化:统一返回格式
|
|
1133
1123
|
return {
|
|
1134
1124
|
success: this.isSuccessResponse(response),
|
|
1135
1125
|
data: response.data || response.result,
|
|
@@ -1156,7 +1146,7 @@ class PasswordValidator {
|
|
|
1156
1146
|
success: false,
|
|
1157
1147
|
message: error.message || '密码校验失败',
|
|
1158
1148
|
code: errorType,
|
|
1159
|
-
error
|
|
1149
|
+
error
|
|
1160
1150
|
}
|
|
1161
1151
|
}
|
|
1162
1152
|
}
|
|
@@ -1168,14 +1158,11 @@ class PasswordValidator {
|
|
|
1168
1158
|
* @returns {Promise<Object>} 响应结果
|
|
1169
1159
|
*/
|
|
1170
1160
|
async makeRequest(url, options = {}) {
|
|
1171
|
-
//
|
|
1172
|
-
|
|
1173
|
-
this.abortController.abort();
|
|
1174
|
-
}
|
|
1175
|
-
this.abortController = new AbortController();
|
|
1161
|
+
// 优化:为每个请求创建独立的AbortController,避免多个请求互相取消
|
|
1162
|
+
const abortController = new AbortController();
|
|
1176
1163
|
|
|
1177
1164
|
const timeoutId = setTimeout(() => {
|
|
1178
|
-
|
|
1165
|
+
abortController.abort();
|
|
1179
1166
|
}, this.options.timeout);
|
|
1180
1167
|
|
|
1181
1168
|
try {
|
|
@@ -1186,7 +1173,7 @@ class PasswordValidator {
|
|
|
1186
1173
|
...this.options.headers,
|
|
1187
1174
|
...options.headers
|
|
1188
1175
|
},
|
|
1189
|
-
signal:
|
|
1176
|
+
signal: abortController.signal
|
|
1190
1177
|
});
|
|
1191
1178
|
|
|
1192
1179
|
clearTimeout(timeoutId);
|
|
@@ -1238,31 +1225,13 @@ class PasswordValidator {
|
|
|
1238
1225
|
this.clearCache();
|
|
1239
1226
|
}
|
|
1240
1227
|
|
|
1241
|
-
/**
|
|
1242
|
-
* 取消当前请求
|
|
1243
|
-
*/
|
|
1244
|
-
cancelRequest() {
|
|
1245
|
-
if (this.abortController) {
|
|
1246
|
-
this.abortController.abort();
|
|
1247
|
-
this.abortController = null;
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/**
|
|
1252
|
-
* 检查是否正在请求中
|
|
1253
|
-
* @returns {boolean} 是否正在请求
|
|
1254
|
-
*/
|
|
1255
|
-
isRequesting() {
|
|
1256
|
-
return this.abortController && !this.abortController.signal.aborted
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
1228
|
/**
|
|
1260
1229
|
* 获取缓存状态
|
|
1261
1230
|
* @returns {Object} 缓存状态信息
|
|
1262
1231
|
*/
|
|
1263
1232
|
getCacheStatus() {
|
|
1264
1233
|
return {
|
|
1265
|
-
hasCache:
|
|
1234
|
+
hasCache: Boolean(this.publicKeyCache),
|
|
1266
1235
|
isExpired: this.publicKeyExpiry ? Date.now() > this.publicKeyExpiry : true,
|
|
1267
1236
|
expiryTime: this.publicKeyExpiry,
|
|
1268
1237
|
remainingTime: this.publicKeyExpiry ? Math.max(0, this.publicKeyExpiry - Date.now()) : 0
|
|
@@ -1274,9 +1243,6 @@ class PasswordValidator {
|
|
|
1274
1243
|
*/
|
|
1275
1244
|
destroy() {
|
|
1276
1245
|
try {
|
|
1277
|
-
// 取消所有进行中的请求
|
|
1278
|
-
this.cancelRequest();
|
|
1279
|
-
|
|
1280
1246
|
// 清除缓存
|
|
1281
1247
|
this.clearCache();
|
|
1282
1248
|
|
|
@@ -1305,15 +1271,14 @@ function createPasswordValidator(options) {
|
|
|
1305
1271
|
/**
|
|
1306
1272
|
* 快速校验密码的便捷函数
|
|
1307
1273
|
* @param {string} password 密码
|
|
1274
|
+
* @param {string} userName 用户名
|
|
1308
1275
|
* @param {Object} options 配置选项
|
|
1309
1276
|
* @param {Object} additionalData 额外数据
|
|
1310
1277
|
* @returns {Promise<Object>} 校验结果
|
|
1311
1278
|
*/
|
|
1312
|
-
|
|
1279
|
+
function validatePassword(password, userName, options = {}, additionalData = {}) {
|
|
1313
1280
|
const validator = new PasswordValidator(options);
|
|
1314
|
-
|
|
1315
|
-
const userName = additionalData.userName || '';
|
|
1316
|
-
return await validator.validatePassword(password, userName, additionalData)
|
|
1281
|
+
return validator.validatePassword(password, userName, additionalData)
|
|
1317
1282
|
}
|
|
1318
1283
|
|
|
1319
1284
|
// 导入滑块验证码组件(使用默认导入)
|