slider-captcha-sdk 1.0.18 → 1.0.19
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 +180 -209
- package/dist/index.min.js +1 -1
- package/dist/password-validator.esm.js +32 -57
- package/dist/password-validator.min.js +1 -1
- package/dist/slider-captcha.esm.js +148 -152
- package/dist/slider-captcha.min.js +1 -1
- package/package.json +3 -7
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
|
-
clickMaskClose: false
|
|
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样式为独立方法,减少主体代码长度
|
|
@@ -39,29 +39,26 @@ class PopupSliderCaptcha {
|
|
|
39
39
|
MAX_RETRY_ATTEMPTS: 3,
|
|
40
40
|
DEFAULT_TIMEOUT: 30000,
|
|
41
41
|
FLOATING_TIME_DURATION: 2500,
|
|
42
|
-
THROTTLE_DELAY: 16
|
|
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',
|
|
49
48
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
50
49
|
IMAGE_LOAD_ERROR: 'IMAGE_LOAD_ERROR',
|
|
51
|
-
CAPTCHA_DATA_ERROR: 'CAPTCHA_DATA_ERROR'
|
|
50
|
+
CAPTCHA_DATA_ERROR: 'CAPTCHA_DATA_ERROR',
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
constructor(options = {}) {
|
|
55
|
-
// 优化:合并默认配置,使用常量
|
|
56
54
|
this.options = {
|
|
57
55
|
...PopupSliderCaptcha.DEFAULTS,
|
|
58
56
|
...options,
|
|
59
57
|
timeout: options.timeout || PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,
|
|
60
58
|
maxRetries: options.maxRetries || PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,
|
|
61
|
-
throttleDelay: options.throttleDelay || PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY
|
|
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,
|
|
@@ -94,7 +88,7 @@ class PopupSliderCaptcha {
|
|
|
94
88
|
currentX: 0,
|
|
95
89
|
startX: 0,
|
|
96
90
|
retryCount: 0,
|
|
97
|
-
isLoading: false
|
|
91
|
+
isLoading: false,
|
|
98
92
|
}
|
|
99
93
|
}
|
|
100
94
|
|
|
@@ -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
|
|
|
@@ -135,7 +128,7 @@ class PopupSliderCaptcha {
|
|
|
135
128
|
['btn', 'div', PopupSliderCaptcha.CSS_CLASSES.btn],
|
|
136
129
|
['icon', 'div', '', '→'],
|
|
137
130
|
['hint', 'div', PopupSliderCaptcha.CSS_CLASSES.hint, '向右滑动完成验证'],
|
|
138
|
-
['error', 'div', PopupSliderCaptcha.CSS_CLASSES.error]
|
|
131
|
+
['error', 'div', PopupSliderCaptcha.CSS_CLASSES.error],
|
|
139
132
|
];
|
|
140
133
|
|
|
141
134
|
// 批量创建元素
|
|
@@ -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
151
|
if (className) element.className = className;
|
|
159
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
|
|
|
@@ -174,24 +166,15 @@ class PopupSliderCaptcha {
|
|
|
174
166
|
elements.backgroundImg,
|
|
175
167
|
elements.sliderImg,
|
|
176
168
|
elements.loadingText,
|
|
177
|
-
elements.floatingTime
|
|
169
|
+
elements.floatingTime,
|
|
178
170
|
);
|
|
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,
|
|
194
|
+
this.addEventListener(elements.closeBtn, 'click', () => this.hide());
|
|
195
|
+
this.addEventListener(elements.refreshBtn, 'click', () => this.refresh());
|
|
196
|
+
this.addEventListener(elements.overlay, 'click', (e) => {
|
|
215
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
|
}
|
|
@@ -227,21 +209,20 @@ class PopupSliderCaptcha {
|
|
|
227
209
|
const handlers = {
|
|
228
210
|
start: (e) => this.handleStart(e),
|
|
229
211
|
move: this.throttledHandleMove,
|
|
230
|
-
end: () => this.handleEnd()
|
|
212
|
+
end: () => this.handleEnd(),
|
|
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
227
|
if (!element || typeof handler !== 'function') return
|
|
247
228
|
|
|
@@ -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;
|
|
@@ -268,7 +248,7 @@ class PopupSliderCaptcha {
|
|
|
268
248
|
this.cachedDimensions = {
|
|
269
249
|
trackWidth,
|
|
270
250
|
btnWidth,
|
|
271
|
-
maxX: trackWidth - btnWidth
|
|
251
|
+
maxX: trackWidth - btnWidth,
|
|
272
252
|
};
|
|
273
253
|
}
|
|
274
254
|
return this.cachedDimensions
|
|
@@ -280,7 +260,6 @@ class PopupSliderCaptcha {
|
|
|
280
260
|
return Math.round(percentage * (this.options.width - this.options.sliderSize))
|
|
281
261
|
}
|
|
282
262
|
|
|
283
|
-
// 优化:简化拖拽处理
|
|
284
263
|
handleStart(e) {
|
|
285
264
|
if (!this.captchaData || this.state.isDragging || this.state.isLoading) return
|
|
286
265
|
|
|
@@ -291,7 +270,7 @@ 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
|
|
|
@@ -333,11 +312,11 @@ class PopupSliderCaptcha {
|
|
|
333
312
|
}
|
|
334
313
|
|
|
335
314
|
getClientX(e) {
|
|
336
|
-
return e.type.includes(
|
|
315
|
+
return e.type.includes('touch') ? e.touches[0].clientX : e.clientX
|
|
337
316
|
}
|
|
338
317
|
|
|
339
318
|
setTransition(enabled) {
|
|
340
|
-
const transition = enabled ?
|
|
319
|
+
const transition = enabled ? 'all 0.3s ease' : 'none';
|
|
341
320
|
requestAnimationFrame(() => {
|
|
342
321
|
this.elements.btn.style.transition = transition;
|
|
343
322
|
this.elements.sliderImg.style.transition = transition;
|
|
@@ -349,38 +328,38 @@ class PopupSliderCaptcha {
|
|
|
349
328
|
const { elements } = this;
|
|
350
329
|
const updates = {
|
|
351
330
|
dragging: () => {
|
|
352
|
-
elements.hint.style.opacity =
|
|
353
|
-
elements.fingerAnimation.style.display =
|
|
331
|
+
elements.hint.style.opacity = '0';
|
|
332
|
+
elements.fingerAnimation.style.display = 'none';
|
|
354
333
|
},
|
|
355
334
|
success: () => {
|
|
356
|
-
Object.assign(elements.btn.style, { background:
|
|
357
|
-
Object.assign(elements.icon.style, { innerHTML:
|
|
358
|
-
elements.icon.innerHTML =
|
|
335
|
+
Object.assign(elements.btn.style, { background: 'var(--sc-success)' });
|
|
336
|
+
Object.assign(elements.icon.style, { innerHTML: '✓', color: 'white' });
|
|
337
|
+
elements.icon.innerHTML = '✓';
|
|
359
338
|
},
|
|
360
339
|
fail: () => {
|
|
361
|
-
Object.assign(elements.btn.style, { background:
|
|
362
|
-
Object.assign(elements.icon.style, { innerHTML:
|
|
363
|
-
elements.icon.innerHTML =
|
|
340
|
+
Object.assign(elements.btn.style, { background: 'var(--sc-danger)' });
|
|
341
|
+
Object.assign(elements.icon.style, { innerHTML: '✗', color: 'white' });
|
|
342
|
+
elements.icon.innerHTML = '✗';
|
|
364
343
|
},
|
|
365
344
|
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(
|
|
345
|
+
Object.assign(elements.btn.style, { background: 'white' });
|
|
346
|
+
Object.assign(elements.icon.style, { color: '#666' });
|
|
347
|
+
elements.icon.innerHTML = '→';
|
|
348
|
+
elements.fingerAnimation.style.display = 'block';
|
|
349
|
+
this.updateHintText('向右滑动完成验证', 'var(--sc-text-light)');
|
|
371
350
|
},
|
|
372
351
|
loading: () => {
|
|
373
|
-
elements.hint.style.opacity =
|
|
374
|
-
elements.fingerAnimation.style.display =
|
|
375
|
-
Object.assign(elements.track.style, { pointerEvents:
|
|
376
|
-
}
|
|
352
|
+
elements.hint.style.opacity = '0';
|
|
353
|
+
elements.fingerAnimation.style.display = 'none';
|
|
354
|
+
Object.assign(elements.track.style, { pointerEvents: 'none', opacity: '0.6' });
|
|
355
|
+
},
|
|
377
356
|
};
|
|
378
357
|
|
|
379
358
|
if (updates[state]) {
|
|
380
359
|
requestAnimationFrame(() => {
|
|
381
360
|
updates[state]();
|
|
382
|
-
if (state !==
|
|
383
|
-
Object.assign(elements.track.style, { pointerEvents:
|
|
361
|
+
if (state !== 'loading') {
|
|
362
|
+
Object.assign(elements.track.style, { pointerEvents: 'auto', opacity: '1' });
|
|
384
363
|
}
|
|
385
364
|
});
|
|
386
365
|
}
|
|
@@ -389,7 +368,7 @@ class PopupSliderCaptcha {
|
|
|
389
368
|
updateHintText(text, color) {
|
|
390
369
|
requestAnimationFrame(() => {
|
|
391
370
|
Object.assign(this.elements.hint, { textContent: text });
|
|
392
|
-
Object.assign(this.elements.hint.style, { color, opacity:
|
|
371
|
+
Object.assign(this.elements.hint.style, { color, opacity: '1' });
|
|
393
372
|
});
|
|
394
373
|
}
|
|
395
374
|
|
|
@@ -402,18 +381,18 @@ class PopupSliderCaptcha {
|
|
|
402
381
|
requestAnimationFrame(() => {
|
|
403
382
|
elements.btn.style.transform = `translateX(${state.currentX}px)`;
|
|
404
383
|
elements.sliderImg.style.transform = `translateX(${pieceX}px)`;
|
|
405
|
-
elements.fingerAnimation.style.opacity = progress >= 0.8 ?
|
|
384
|
+
elements.fingerAnimation.style.opacity = progress >= 0.8 ? '0' : '0.6';
|
|
406
385
|
});
|
|
407
386
|
}
|
|
408
387
|
|
|
409
388
|
// 优化:简化显示/隐藏逻辑
|
|
410
389
|
show() {
|
|
411
390
|
this.state.isVisible = true;
|
|
412
|
-
this.elements.overlay.style.display =
|
|
391
|
+
this.elements.overlay.style.display = 'flex';
|
|
413
392
|
|
|
414
393
|
requestAnimationFrame(() => {
|
|
415
|
-
this.elements.overlay.classList.add(
|
|
416
|
-
this.elements.modal.classList.add(
|
|
394
|
+
this.elements.overlay.classList.add('show');
|
|
395
|
+
this.elements.modal.classList.add('show');
|
|
417
396
|
});
|
|
418
397
|
|
|
419
398
|
this.loadCaptcha();
|
|
@@ -421,11 +400,11 @@ class PopupSliderCaptcha {
|
|
|
421
400
|
|
|
422
401
|
hide() {
|
|
423
402
|
this.state.isVisible = false;
|
|
424
|
-
this.elements.overlay.classList.remove(
|
|
425
|
-
this.elements.modal.classList.remove(
|
|
403
|
+
this.elements.overlay.classList.remove('show');
|
|
404
|
+
this.elements.modal.classList.remove('show');
|
|
426
405
|
|
|
427
406
|
this.safeSetTimeout(() => {
|
|
428
|
-
this.elements.overlay.style.display =
|
|
407
|
+
this.elements.overlay.style.display = 'none';
|
|
429
408
|
document.body.removeChild(this.elements.overlay);
|
|
430
409
|
this.reset();
|
|
431
410
|
this.options.onClose?.();
|
|
@@ -440,7 +419,7 @@ class PopupSliderCaptcha {
|
|
|
440
419
|
[PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR]: '请求超时,请重试',
|
|
441
420
|
[PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR]: '验证失败,请重试',
|
|
442
421
|
[PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR]: '图片加载失败,请刷新重试',
|
|
443
|
-
[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]: '验证码数据错误,请刷新重试'
|
|
422
|
+
[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]: '验证码数据错误,请刷新重试',
|
|
444
423
|
};
|
|
445
424
|
|
|
446
425
|
const errorMessage = errorMessages[errorType] || message || '未知错误';
|
|
@@ -450,7 +429,7 @@ class PopupSliderCaptcha {
|
|
|
450
429
|
this.options.onError({
|
|
451
430
|
type: errorType,
|
|
452
431
|
message: errorMessage,
|
|
453
|
-
originalError
|
|
432
|
+
originalError,
|
|
454
433
|
});
|
|
455
434
|
}
|
|
456
435
|
|
|
@@ -471,16 +450,16 @@ class PopupSliderCaptcha {
|
|
|
471
450
|
this.abortController = new AbortController();
|
|
472
451
|
|
|
473
452
|
const response = await fetch(this.options.apiUrl, {
|
|
474
|
-
method:
|
|
453
|
+
method: 'POST',
|
|
475
454
|
headers: {
|
|
476
|
-
|
|
477
|
-
...this.options.headers
|
|
455
|
+
'Content-Type': 'application/json',
|
|
456
|
+
...this.options.headers,
|
|
478
457
|
},
|
|
479
458
|
body: JSON.stringify({
|
|
480
459
|
timestamp: Date.now(),
|
|
481
|
-
...this.options.requestData
|
|
460
|
+
...this.options.requestData,
|
|
482
461
|
}),
|
|
483
|
-
signal: this.abortController.signal
|
|
462
|
+
signal: this.abortController.signal,
|
|
484
463
|
});
|
|
485
464
|
|
|
486
465
|
if (!response.ok) {
|
|
@@ -491,7 +470,7 @@ class PopupSliderCaptcha {
|
|
|
491
470
|
|
|
492
471
|
// 优化:更严格的数据验证
|
|
493
472
|
if (!this.validateCaptchaData(data)) {
|
|
494
|
-
throw new Error(
|
|
473
|
+
throw new Error('验证码数据格式错误')
|
|
495
474
|
}
|
|
496
475
|
|
|
497
476
|
this.captchaData = data.data;
|
|
@@ -500,7 +479,10 @@ class PopupSliderCaptcha {
|
|
|
500
479
|
} catch (error) {
|
|
501
480
|
if (error.name === 'AbortError') {
|
|
502
481
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR, '请求被取消');
|
|
503
|
-
} else if (
|
|
482
|
+
} else if (
|
|
483
|
+
error.message.includes('Failed to fetch') ||
|
|
484
|
+
error.message.includes('NetworkError')
|
|
485
|
+
) {
|
|
504
486
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR, '网络连接失败');
|
|
505
487
|
} else {
|
|
506
488
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR, error.message, error);
|
|
@@ -512,10 +494,19 @@ class PopupSliderCaptcha {
|
|
|
512
494
|
validateCaptchaData(data) {
|
|
513
495
|
if (!data || typeof data !== 'object') return false
|
|
514
496
|
|
|
515
|
-
const requiredFields = [
|
|
497
|
+
const requiredFields = [
|
|
498
|
+
'canvasSrc',
|
|
499
|
+
'blockSrc',
|
|
500
|
+
'canvasWidth',
|
|
501
|
+
'canvasHeight',
|
|
502
|
+
'blockWidth',
|
|
503
|
+
'blockHeight',
|
|
504
|
+
'blockY',
|
|
505
|
+
'nonceStr',
|
|
506
|
+
];
|
|
516
507
|
const dataObj = data.data || data;
|
|
517
508
|
|
|
518
|
-
return requiredFields.every(field => {
|
|
509
|
+
return requiredFields.every((field) => {
|
|
519
510
|
const value = dataObj[field];
|
|
520
511
|
return value !== null && value !== undefined && value !== ''
|
|
521
512
|
})
|
|
@@ -529,13 +520,13 @@ class PopupSliderCaptcha {
|
|
|
529
520
|
const loadPromises = [
|
|
530
521
|
this.loadImageAsync(this.elements.backgroundImg, this.captchaData.canvasSrc, {
|
|
531
522
|
width: this.captchaData.canvasWidth,
|
|
532
|
-
height: this.captchaData.canvasHeight
|
|
523
|
+
height: this.captchaData.canvasHeight,
|
|
533
524
|
}),
|
|
534
525
|
this.loadImageAsync(this.elements.sliderImg, this.captchaData.blockSrc, {
|
|
535
526
|
width: this.captchaData.blockWidth,
|
|
536
527
|
height: this.captchaData.blockHeight,
|
|
537
|
-
top: this.captchaData.blockY
|
|
538
|
-
})
|
|
528
|
+
top: this.captchaData.blockY,
|
|
529
|
+
}),
|
|
539
530
|
];
|
|
540
531
|
|
|
541
532
|
Promise.all(loadPromises)
|
|
@@ -590,7 +581,7 @@ class PopupSliderCaptcha {
|
|
|
590
581
|
// 优化:提取样式应用逻辑
|
|
591
582
|
applyStyles(element, styles) {
|
|
592
583
|
Object.entries(styles).forEach(([key, value]) => {
|
|
593
|
-
element.style[key] = typeof value ===
|
|
584
|
+
element.style[key] = typeof value === 'number' ? value + 'px' : value;
|
|
594
585
|
});
|
|
595
586
|
}
|
|
596
587
|
|
|
@@ -598,31 +589,31 @@ class PopupSliderCaptcha {
|
|
|
598
589
|
showLoading() {
|
|
599
590
|
this.state.isLoading = true;
|
|
600
591
|
this.batchUpdateStyles({
|
|
601
|
-
container: { display:
|
|
602
|
-
loadingText: { display:
|
|
603
|
-
error: { display:
|
|
592
|
+
container: { display: 'block' },
|
|
593
|
+
loadingText: { display: 'flex' },
|
|
594
|
+
error: { display: 'none' },
|
|
604
595
|
});
|
|
605
|
-
this.updateUIState(
|
|
596
|
+
this.updateUIState('loading');
|
|
606
597
|
}
|
|
607
598
|
|
|
608
599
|
hideLoading() {
|
|
609
600
|
this.state.isLoading = false;
|
|
610
|
-
this.batchUpdateStyles({ loadingText: { display:
|
|
611
|
-
this.updateUIState(
|
|
601
|
+
this.batchUpdateStyles({ loadingText: { display: 'none' } });
|
|
602
|
+
this.updateUIState('reset');
|
|
612
603
|
}
|
|
613
604
|
|
|
614
605
|
showCaptcha() {
|
|
615
606
|
this.batchUpdateStyles({
|
|
616
|
-
container: { display:
|
|
617
|
-
track: { display:
|
|
618
|
-
error: { display:
|
|
607
|
+
container: { display: 'block' },
|
|
608
|
+
track: { display: 'block' },
|
|
609
|
+
error: { display: 'none' },
|
|
619
610
|
});
|
|
620
611
|
}
|
|
621
612
|
|
|
622
613
|
showError(message) {
|
|
623
614
|
this.hideLoading();
|
|
624
615
|
this.batchUpdateStyles({
|
|
625
|
-
error: { display:
|
|
616
|
+
error: { display: 'block', textContent: message },
|
|
626
617
|
});
|
|
627
618
|
}
|
|
628
619
|
|
|
@@ -646,7 +637,7 @@ class PopupSliderCaptcha {
|
|
|
646
637
|
|
|
647
638
|
async verify() {
|
|
648
639
|
if (!this.captchaData) {
|
|
649
|
-
this.onVerifyFail(
|
|
640
|
+
this.onVerifyFail('验证码数据丢失');
|
|
650
641
|
return
|
|
651
642
|
}
|
|
652
643
|
|
|
@@ -658,20 +649,20 @@ class PopupSliderCaptcha {
|
|
|
658
649
|
this.abortController = new AbortController();
|
|
659
650
|
|
|
660
651
|
const response = await fetch(this.options.verifyUrl, {
|
|
661
|
-
method:
|
|
652
|
+
method: 'POST',
|
|
662
653
|
headers: {
|
|
663
|
-
|
|
664
|
-
...this.options.headers
|
|
654
|
+
'Content-Type': 'application/json',
|
|
655
|
+
...this.options.headers,
|
|
665
656
|
},
|
|
666
657
|
body: JSON.stringify({
|
|
667
658
|
loginVo: {
|
|
668
659
|
nonceStr: this.captchaData.nonceStr,
|
|
669
|
-
value: this.getPosition()
|
|
660
|
+
value: this.getPosition(),
|
|
670
661
|
},
|
|
671
662
|
dragEventList: [...this.times],
|
|
672
|
-
...this.options.verifyData
|
|
663
|
+
...this.options.verifyData,
|
|
673
664
|
}),
|
|
674
|
-
signal: this.abortController.signal
|
|
665
|
+
signal: this.abortController.signal,
|
|
675
666
|
});
|
|
676
667
|
|
|
677
668
|
if (!response.ok) {
|
|
@@ -684,12 +675,15 @@ class PopupSliderCaptcha {
|
|
|
684
675
|
if (this.isVerifySuccess(data)) {
|
|
685
676
|
this.onVerifySuccess(data.data || data.result);
|
|
686
677
|
} else {
|
|
687
|
-
this.onVerifyFail(data.message || data.msg ||
|
|
678
|
+
this.onVerifyFail(data.message || data.msg || '验证失败,请重试!');
|
|
688
679
|
}
|
|
689
680
|
} catch (error) {
|
|
690
681
|
if (error.name === 'AbortError') {
|
|
691
682
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR, '验证请求被取消');
|
|
692
|
-
} else if (
|
|
683
|
+
} else if (
|
|
684
|
+
error.message.includes('Failed to fetch') ||
|
|
685
|
+
error.message.includes('NetworkError')
|
|
686
|
+
) {
|
|
693
687
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR, '网络连接失败');
|
|
694
688
|
} else {
|
|
695
689
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR, error.message, error);
|
|
@@ -703,48 +697,50 @@ class PopupSliderCaptcha {
|
|
|
703
697
|
|
|
704
698
|
// 支持多种成功标识
|
|
705
699
|
const successIndicators = [
|
|
706
|
-
data.code ===
|
|
700
|
+
data.code === '0',
|
|
707
701
|
data.code === 0,
|
|
708
702
|
data.success === true,
|
|
709
703
|
data.status === 'success',
|
|
710
|
-
data.result === true
|
|
704
|
+
data.result === true,
|
|
711
705
|
];
|
|
712
706
|
|
|
713
|
-
return successIndicators.some(indicator => indicator === true)
|
|
707
|
+
return successIndicators.some((indicator) => indicator === true)
|
|
714
708
|
}
|
|
715
709
|
|
|
716
710
|
onVerifySuccess(ticket) {
|
|
717
|
-
const duration = this.dragStartTime
|
|
711
|
+
const duration = this.dragStartTime
|
|
712
|
+
? Date.now() - this.dragStartTime
|
|
713
|
+
: Date.now() - this.startTime;
|
|
718
714
|
const durationText = `验证成功!耗时:${(duration / 1000).toFixed(2)}s`;
|
|
719
715
|
|
|
720
|
-
this.updateUIState(
|
|
721
|
-
this.showFloatingTime(durationText,
|
|
716
|
+
this.updateUIState('success');
|
|
717
|
+
this.showFloatingTime(durationText, 'success');
|
|
722
718
|
|
|
723
719
|
this.safeSetTimeout(() => {
|
|
724
720
|
this.options.onSuccess?.({
|
|
725
721
|
ticket: ticket,
|
|
726
722
|
timestamp: Date.now(),
|
|
727
|
-
duration
|
|
723
|
+
duration,
|
|
728
724
|
});
|
|
729
725
|
this.hide();
|
|
730
726
|
}, 2000);
|
|
731
727
|
}
|
|
732
728
|
|
|
733
|
-
showFloatingTime(text, type =
|
|
729
|
+
showFloatingTime(text, type = 'success') {
|
|
734
730
|
const { elements } = this;
|
|
735
731
|
elements.floatingTime.textContent = text;
|
|
736
732
|
elements.floatingTime.className = `slider-captcha-floating-time ${type}`;
|
|
737
733
|
|
|
738
|
-
this.safeSetTimeout(() => elements.floatingTime.classList.add(
|
|
734
|
+
this.safeSetTimeout(() => elements.floatingTime.classList.add('show'), 100);
|
|
739
735
|
this.safeSetTimeout(() => {
|
|
740
|
-
elements.floatingTime.className =
|
|
736
|
+
elements.floatingTime.className = 'slider-captcha-floating-time';
|
|
741
737
|
}, 2500); // 优化:延长显示时间,避免被reset清除
|
|
742
738
|
}
|
|
743
739
|
|
|
744
740
|
onVerifyFail(message) {
|
|
745
741
|
this.state.retryCount++;
|
|
746
|
-
this.updateUIState(
|
|
747
|
-
this.showFloatingTime(message,
|
|
742
|
+
this.updateUIState('fail');
|
|
743
|
+
this.showFloatingTime(message, 'fail');
|
|
748
744
|
|
|
749
745
|
this.safeSetTimeout(() => {
|
|
750
746
|
if (this.state.retryCount >= this.options.maxRetries) {
|
|
@@ -763,7 +759,7 @@ class PopupSliderCaptcha {
|
|
|
763
759
|
isDragging: false,
|
|
764
760
|
currentX: 0,
|
|
765
761
|
startX: 0,
|
|
766
|
-
isLoading: false
|
|
762
|
+
isLoading: false,
|
|
767
763
|
});
|
|
768
764
|
|
|
769
765
|
this.times = [];
|
|
@@ -774,10 +770,10 @@ class PopupSliderCaptcha {
|
|
|
774
770
|
// 重置UI
|
|
775
771
|
requestAnimationFrame(() => {
|
|
776
772
|
this.setTransition(true);
|
|
777
|
-
this.elements.btn.style.transform =
|
|
778
|
-
this.elements.sliderImg.style.transform =
|
|
779
|
-
this.updateUIState(
|
|
780
|
-
this.elements.error.style.display =
|
|
773
|
+
this.elements.btn.style.transform = 'translateX(0px)';
|
|
774
|
+
this.elements.sliderImg.style.transform = 'translateX(0px)';
|
|
775
|
+
this.updateUIState('reset');
|
|
776
|
+
this.elements.error.style.display = 'none';
|
|
781
777
|
});
|
|
782
778
|
}
|
|
783
779
|
|
|
@@ -805,7 +801,7 @@ class PopupSliderCaptcha {
|
|
|
805
801
|
}
|
|
806
802
|
|
|
807
803
|
clearAllTimers() {
|
|
808
|
-
this.timers.forEach(timer => {
|
|
804
|
+
this.timers.forEach((timer) => {
|
|
809
805
|
clearTimeout(timer);
|
|
810
806
|
clearInterval(timer);
|
|
811
807
|
});
|
|
@@ -854,8 +850,8 @@ class PopupSliderCaptcha {
|
|
|
854
850
|
|
|
855
851
|
// 恢复body样式
|
|
856
852
|
if (document.body) {
|
|
857
|
-
document.body.style.userSelect =
|
|
858
|
-
document.body.style.cursor =
|
|
853
|
+
document.body.style.userSelect = '';
|
|
854
|
+
document.body.style.cursor = '';
|
|
859
855
|
}
|
|
860
856
|
|
|
861
857
|
// 清理所有定时器
|
|
@@ -883,7 +879,7 @@ class PopupSliderCaptcha {
|
|
|
883
879
|
this.cachedDimensions = null;
|
|
884
880
|
|
|
885
881
|
// 清空所有属性
|
|
886
|
-
Object.keys(this).forEach(key => {
|
|
882
|
+
Object.keys(this).forEach((key) => {
|
|
887
883
|
if (key !== 'constructor') {
|
|
888
884
|
this[key] = null;
|
|
889
885
|
}
|
|
@@ -910,12 +906,12 @@ class PopupSliderCaptcha {
|
|
|
910
906
|
}
|
|
911
907
|
|
|
912
908
|
// 模块导出
|
|
913
|
-
if (typeof module !==
|
|
909
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
914
910
|
module.exports = PopupSliderCaptcha;
|
|
915
911
|
module.exports.default = PopupSliderCaptcha;
|
|
916
|
-
} else if (typeof define ===
|
|
912
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
917
913
|
define([], () => PopupSliderCaptcha);
|
|
918
|
-
} else if (typeof window !==
|
|
914
|
+
} else if (typeof window !== 'undefined') {
|
|
919
915
|
window.PopupSliderCaptcha = PopupSliderCaptcha;
|
|
920
916
|
window.SliderCaptcha = PopupSliderCaptcha;
|
|
921
917
|
}
|
|
@@ -932,33 +928,30 @@ class PasswordValidator {
|
|
|
932
928
|
static CONSTANTS = {
|
|
933
929
|
DEFAULT_TIMEOUT: 10000,
|
|
934
930
|
CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存
|
|
935
|
-
MIN_PASSWORD_LENGTH: 1
|
|
931
|
+
MIN_PASSWORD_LENGTH: 1,
|
|
936
932
|
}
|
|
937
933
|
|
|
938
|
-
// 优化:添加错误类型枚举
|
|
939
934
|
static ERROR_TYPES = {
|
|
940
935
|
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
941
936
|
TIMEOUT_ERROR: 'TIMEOUT_ERROR',
|
|
942
937
|
ENCRYPTION_ERROR: 'ENCRYPTION_ERROR',
|
|
943
938
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
944
|
-
PUBLIC_KEY_ERROR: 'PUBLIC_KEY_ERROR'
|
|
939
|
+
PUBLIC_KEY_ERROR: 'PUBLIC_KEY_ERROR',
|
|
945
940
|
}
|
|
946
941
|
|
|
947
942
|
constructor(options = {}) {
|
|
948
|
-
// 优化:合并配置,使用常量
|
|
949
943
|
this.options = {
|
|
950
944
|
publicKeyUrl: options.publicKeyUrl || '/microservice/strongPassword/getPublicKey',
|
|
951
945
|
validateUrl: options.validateUrl || '/microservice/strongPassword/checkPassword',
|
|
952
946
|
timeout: options.timeout || PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,
|
|
953
947
|
headers: options.headers || {},
|
|
954
948
|
cacheDuration: options.cacheDuration || PasswordValidator.CONSTANTS.CACHE_DURATION,
|
|
955
|
-
...options
|
|
949
|
+
...options,
|
|
956
950
|
};
|
|
957
951
|
|
|
958
952
|
// 缓存公钥,避免重复请求
|
|
959
953
|
this.publicKeyCache = null;
|
|
960
954
|
this.publicKeyExpiry = null;
|
|
961
|
-
this.abortController = null;
|
|
962
955
|
}
|
|
963
956
|
|
|
964
957
|
/**
|
|
@@ -972,15 +965,8 @@ class PasswordValidator {
|
|
|
972
965
|
}
|
|
973
966
|
|
|
974
967
|
try {
|
|
975
|
-
// 取消之前的请求
|
|
976
|
-
if (this.abortController) {
|
|
977
|
-
this.abortController.abort();
|
|
978
|
-
}
|
|
979
|
-
this.abortController = new AbortController();
|
|
980
|
-
|
|
981
968
|
const response = await this.makeRequest(this.options.publicKeyUrl, {
|
|
982
969
|
method: 'GET',
|
|
983
|
-
signal: this.abortController.signal
|
|
984
970
|
});
|
|
985
971
|
|
|
986
972
|
if (!this.isSuccessResponse(response)) {
|
|
@@ -989,8 +975,8 @@ class PasswordValidator {
|
|
|
989
975
|
|
|
990
976
|
// 验证公钥格式
|
|
991
977
|
const publicKey = response.data || response.result;
|
|
992
|
-
if (!
|
|
993
|
-
throw new Error('
|
|
978
|
+
if (!publicKey) {
|
|
979
|
+
throw new Error('公钥为空')
|
|
994
980
|
}
|
|
995
981
|
|
|
996
982
|
// 缓存公钥
|
|
@@ -1007,35 +993,28 @@ class PasswordValidator {
|
|
|
1007
993
|
|
|
1008
994
|
// 优化:添加响应成功判断方法
|
|
1009
995
|
isSuccessResponse(response) {
|
|
1010
|
-
if (!response || typeof response !== 'object')
|
|
996
|
+
if (!response || typeof response !== 'object') {
|
|
997
|
+
return false
|
|
998
|
+
}
|
|
1011
999
|
|
|
1012
1000
|
const successIndicators = [
|
|
1013
1001
|
response.code === '0',
|
|
1014
1002
|
response.code === 0,
|
|
1015
1003
|
response.success === true,
|
|
1016
1004
|
response.status === 'success',
|
|
1017
|
-
response.result === true
|
|
1005
|
+
response.result === true,
|
|
1018
1006
|
];
|
|
1019
1007
|
|
|
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
|
|
1008
|
+
return successIndicators.some((indicator) => indicator === true)
|
|
1029
1009
|
}
|
|
1030
1010
|
|
|
1031
|
-
// 优化:添加统一错误处理方法
|
|
1032
1011
|
handleError(errorType, message, originalError = null) {
|
|
1033
1012
|
const errorMessages = {
|
|
1034
1013
|
[PasswordValidator.ERROR_TYPES.NETWORK_ERROR]: '网络连接失败,请检查网络设置',
|
|
1035
1014
|
[PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR]: '请求超时,请重试',
|
|
1036
1015
|
[PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR]: '密码加密失败',
|
|
1037
1016
|
[PasswordValidator.ERROR_TYPES.VALIDATION_ERROR]: '密码验证失败',
|
|
1038
|
-
[PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR]: '获取公钥失败'
|
|
1017
|
+
[PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR]: '获取公钥失败',
|
|
1039
1018
|
};
|
|
1040
1019
|
|
|
1041
1020
|
const errorMessage = errorMessages[errorType] || message || '未知错误';
|
|
@@ -1045,7 +1024,7 @@ class PasswordValidator {
|
|
|
1045
1024
|
this.options.onError({
|
|
1046
1025
|
type: errorType,
|
|
1047
1026
|
message: errorMessage,
|
|
1048
|
-
originalError
|
|
1027
|
+
originalError,
|
|
1049
1028
|
});
|
|
1050
1029
|
}
|
|
1051
1030
|
|
|
@@ -1060,7 +1039,6 @@ class PasswordValidator {
|
|
|
1060
1039
|
*/
|
|
1061
1040
|
encryptPassword(password, publicKey) {
|
|
1062
1041
|
try {
|
|
1063
|
-
// 优化:输入验证
|
|
1064
1042
|
if (!password) {
|
|
1065
1043
|
throw new Error('密码不能为空')
|
|
1066
1044
|
}
|
|
@@ -1073,7 +1051,6 @@ class PasswordValidator {
|
|
|
1073
1051
|
throw new Error('公钥不能为空')
|
|
1074
1052
|
}
|
|
1075
1053
|
|
|
1076
|
-
// 使用导入的JSEncrypt进行RSA加密
|
|
1077
1054
|
if (typeof JSEncrypt === 'undefined') {
|
|
1078
1055
|
throw new Error('JSEncrypt库未正确加载,请确保已引入JSEncrypt库')
|
|
1079
1056
|
}
|
|
@@ -1103,13 +1080,8 @@ class PasswordValidator {
|
|
|
1103
1080
|
*/
|
|
1104
1081
|
async validatePassword(password, userName, additionalData = {}) {
|
|
1105
1082
|
try {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
throw new Error('密码不能为空')
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
if (!userName) {
|
|
1112
|
-
throw new Error('用户名不能为空')
|
|
1083
|
+
if (!password || !userName) {
|
|
1084
|
+
throw new Error('用户名密码不能为空')
|
|
1113
1085
|
}
|
|
1114
1086
|
|
|
1115
1087
|
// 1. 获取公钥
|
|
@@ -1125,17 +1097,16 @@ class PasswordValidator {
|
|
|
1125
1097
|
userName: userName,
|
|
1126
1098
|
password: encryptedPassword,
|
|
1127
1099
|
timestamp: Date.now(),
|
|
1128
|
-
...additionalData
|
|
1129
|
-
})
|
|
1100
|
+
...additionalData,
|
|
1101
|
+
}),
|
|
1130
1102
|
});
|
|
1131
1103
|
|
|
1132
|
-
// 优化:统一返回格式
|
|
1133
1104
|
return {
|
|
1134
1105
|
success: this.isSuccessResponse(response),
|
|
1135
1106
|
data: response.data || response.result,
|
|
1136
1107
|
message: response.message || response.msg || '验证完成',
|
|
1137
1108
|
code: response.code || response.status,
|
|
1138
|
-
originalResponse: response
|
|
1109
|
+
originalResponse: response,
|
|
1139
1110
|
}
|
|
1140
1111
|
} catch (error) {
|
|
1141
1112
|
console.error('密码校验失败:', error);
|
|
@@ -1156,7 +1127,7 @@ class PasswordValidator {
|
|
|
1156
1127
|
success: false,
|
|
1157
1128
|
message: error.message || '密码校验失败',
|
|
1158
1129
|
code: errorType,
|
|
1159
|
-
error: error
|
|
1130
|
+
error: error,
|
|
1160
1131
|
}
|
|
1161
1132
|
}
|
|
1162
1133
|
}
|
|
@@ -1168,14 +1139,11 @@ class PasswordValidator {
|
|
|
1168
1139
|
* @returns {Promise<Object>} 响应结果
|
|
1169
1140
|
*/
|
|
1170
1141
|
async makeRequest(url, options = {}) {
|
|
1171
|
-
//
|
|
1172
|
-
|
|
1173
|
-
this.abortController.abort();
|
|
1174
|
-
}
|
|
1175
|
-
this.abortController = new AbortController();
|
|
1142
|
+
// 优化:为每个请求创建独立的AbortController,避免多个请求互相取消
|
|
1143
|
+
const abortController = new AbortController();
|
|
1176
1144
|
|
|
1177
1145
|
const timeoutId = setTimeout(() => {
|
|
1178
|
-
|
|
1146
|
+
abortController.abort();
|
|
1179
1147
|
}, this.options.timeout);
|
|
1180
1148
|
|
|
1181
1149
|
try {
|
|
@@ -1184,9 +1152,9 @@ class PasswordValidator {
|
|
|
1184
1152
|
headers: {
|
|
1185
1153
|
'Content-Type': 'application/json',
|
|
1186
1154
|
...this.options.headers,
|
|
1187
|
-
...options.headers
|
|
1155
|
+
...options.headers,
|
|
1188
1156
|
},
|
|
1189
|
-
signal:
|
|
1157
|
+
signal: abortController.signal,
|
|
1190
1158
|
});
|
|
1191
1159
|
|
|
1192
1160
|
clearTimeout(timeoutId);
|
|
@@ -1240,20 +1208,23 @@ class PasswordValidator {
|
|
|
1240
1208
|
|
|
1241
1209
|
/**
|
|
1242
1210
|
* 取消当前请求
|
|
1211
|
+
* 注意:由于现在每个请求使用独立的AbortController,此方法主要用于向后兼容
|
|
1243
1212
|
*/
|
|
1244
1213
|
cancelRequest() {
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1214
|
+
// 由于现在每个请求使用独立的AbortController,无法直接取消所有请求
|
|
1215
|
+
// 如果需要取消特定请求,建议在调用validatePassword时保存返回的Promise并手动处理
|
|
1216
|
+
console.warn('cancelRequest方法已过时,现在每个请求使用独立的AbortController');
|
|
1249
1217
|
}
|
|
1250
1218
|
|
|
1251
1219
|
/**
|
|
1252
1220
|
* 检查是否正在请求中
|
|
1253
1221
|
* @returns {boolean} 是否正在请求
|
|
1222
|
+
* 注意:由于现在每个请求使用独立的AbortController,此方法无法准确判断
|
|
1254
1223
|
*/
|
|
1255
1224
|
isRequesting() {
|
|
1256
|
-
|
|
1225
|
+
// 由于现在每个请求使用独立的AbortController,无法准确判断是否正在请求
|
|
1226
|
+
console.warn('isRequesting方法已过时,现在每个请求使用独立的AbortController');
|
|
1227
|
+
return false
|
|
1257
1228
|
}
|
|
1258
1229
|
|
|
1259
1230
|
/**
|
|
@@ -1265,7 +1236,7 @@ class PasswordValidator {
|
|
|
1265
1236
|
hasCache: !!this.publicKeyCache,
|
|
1266
1237
|
isExpired: this.publicKeyExpiry ? Date.now() > this.publicKeyExpiry : true,
|
|
1267
1238
|
expiryTime: this.publicKeyExpiry,
|
|
1268
|
-
remainingTime: this.publicKeyExpiry ? Math.max(0, this.publicKeyExpiry - Date.now()) : 0
|
|
1239
|
+
remainingTime: this.publicKeyExpiry ? Math.max(0, this.publicKeyExpiry - Date.now()) : 0,
|
|
1269
1240
|
}
|
|
1270
1241
|
}
|
|
1271
1242
|
|