slider-captcha-sdk 1.0.17 → 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 +182 -211
- package/dist/index.min.js +1 -1
- package/dist/password-validator.esm.js +34 -59
- package/dist/password-validator.min.js +1 -1
- package/dist/slider-captcha.esm.js +148 -152
- package/package.json +1 -1
|
@@ -8,22 +8,22 @@ class PopupSliderCaptcha {
|
|
|
8
8
|
sliderSize: 38,
|
|
9
9
|
maxRetries: 3,
|
|
10
10
|
timeout: 30000,
|
|
11
|
-
apiUrl:
|
|
12
|
-
verifyUrl:
|
|
11
|
+
apiUrl: '/api/captcha',
|
|
12
|
+
verifyUrl: '/api/captcha/verify',
|
|
13
13
|
throttleDelay: 16,
|
|
14
|
-
clickMaskClose: false
|
|
14
|
+
clickMaskClose: false,
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
static CSS_CLASSES = {
|
|
18
|
-
overlay:
|
|
19
|
-
modal:
|
|
20
|
-
header:
|
|
21
|
-
container:
|
|
22
|
-
track:
|
|
23
|
-
btn:
|
|
24
|
-
hint:
|
|
25
|
-
loading:
|
|
26
|
-
error:
|
|
18
|
+
overlay: 'slider-captcha-overlay',
|
|
19
|
+
modal: 'slider-captcha-modal',
|
|
20
|
+
header: 'slider-captcha-header',
|
|
21
|
+
container: 'slider-captcha-container',
|
|
22
|
+
track: 'slider-captcha-track',
|
|
23
|
+
btn: 'slider-captcha-btn',
|
|
24
|
+
hint: 'slider-captcha-hint',
|
|
25
|
+
loading: 'slider-captcha-loading',
|
|
26
|
+
error: 'slider-captcha-error',
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// 优化:提取CSS样式为独立方法,减少主体代码长度
|
|
@@ -37,29 +37,26 @@ class PopupSliderCaptcha {
|
|
|
37
37
|
MAX_RETRY_ATTEMPTS: 3,
|
|
38
38
|
DEFAULT_TIMEOUT: 30000,
|
|
39
39
|
FLOATING_TIME_DURATION: 2500,
|
|
40
|
-
THROTTLE_DELAY: 16
|
|
40
|
+
THROTTLE_DELAY: 16,
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// 优化:添加错误类型枚举
|
|
44
43
|
static ERROR_TYPES = {
|
|
45
44
|
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
46
45
|
TIMEOUT_ERROR: 'TIMEOUT_ERROR',
|
|
47
46
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
48
47
|
IMAGE_LOAD_ERROR: 'IMAGE_LOAD_ERROR',
|
|
49
|
-
CAPTCHA_DATA_ERROR: 'CAPTCHA_DATA_ERROR'
|
|
48
|
+
CAPTCHA_DATA_ERROR: 'CAPTCHA_DATA_ERROR',
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
constructor(options = {}) {
|
|
53
|
-
// 优化:合并默认配置,使用常量
|
|
54
52
|
this.options = {
|
|
55
53
|
...PopupSliderCaptcha.DEFAULTS,
|
|
56
54
|
...options,
|
|
57
55
|
timeout: options.timeout || PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,
|
|
58
56
|
maxRetries: options.maxRetries || PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,
|
|
59
|
-
throttleDelay: options.throttleDelay || PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY
|
|
57
|
+
throttleDelay: options.throttleDelay || PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY,
|
|
60
58
|
};
|
|
61
59
|
|
|
62
|
-
// 优化:初始化所有属性
|
|
63
60
|
this.elements = {};
|
|
64
61
|
this.state = this.createInitialState();
|
|
65
62
|
this.captchaData = null;
|
|
@@ -72,10 +69,8 @@ class PopupSliderCaptcha {
|
|
|
72
69
|
this.imageCache = new Map();
|
|
73
70
|
this.abortController = null;
|
|
74
71
|
|
|
75
|
-
// 优化:使用箭头函数绑定this,避免重复bind调用
|
|
76
72
|
this.throttledHandleMove = this.throttle((e) => this.handleMove(e), this.options.throttleDelay);
|
|
77
73
|
|
|
78
|
-
// 优化:添加错误处理
|
|
79
74
|
try {
|
|
80
75
|
this.init();
|
|
81
76
|
} catch (error) {
|
|
@@ -84,7 +79,6 @@ class PopupSliderCaptcha {
|
|
|
84
79
|
}
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
// 优化:提取初始状态创建为独立方法
|
|
88
82
|
createInitialState() {
|
|
89
83
|
return {
|
|
90
84
|
isVisible: false,
|
|
@@ -92,7 +86,7 @@ class PopupSliderCaptcha {
|
|
|
92
86
|
currentX: 0,
|
|
93
87
|
startX: 0,
|
|
94
88
|
retryCount: 0,
|
|
95
|
-
isLoading: false
|
|
89
|
+
isLoading: false,
|
|
96
90
|
}
|
|
97
91
|
}
|
|
98
92
|
|
|
@@ -103,15 +97,14 @@ class PopupSliderCaptcha {
|
|
|
103
97
|
}
|
|
104
98
|
|
|
105
99
|
injectStyles() {
|
|
106
|
-
if (document.querySelector(
|
|
100
|
+
if (document.querySelector('#slider-captcha-styles')) return
|
|
107
101
|
|
|
108
|
-
const style = document.createElement(
|
|
109
|
-
style.id =
|
|
102
|
+
const style = document.createElement('style');
|
|
103
|
+
style.id = 'slider-captcha-styles';
|
|
110
104
|
style.textContent = PopupSliderCaptcha.getStyles();
|
|
111
105
|
document.head.appendChild(style);
|
|
112
106
|
}
|
|
113
107
|
|
|
114
|
-
// 优化:简化元素创建逻辑
|
|
115
108
|
createElements() {
|
|
116
109
|
const { elements, options } = this;
|
|
117
110
|
|
|
@@ -133,7 +126,7 @@ class PopupSliderCaptcha {
|
|
|
133
126
|
['btn', 'div', PopupSliderCaptcha.CSS_CLASSES.btn],
|
|
134
127
|
['icon', 'div', '', '→'],
|
|
135
128
|
['hint', 'div', PopupSliderCaptcha.CSS_CLASSES.hint, '向右滑动完成验证'],
|
|
136
|
-
['error', 'div', PopupSliderCaptcha.CSS_CLASSES.error]
|
|
129
|
+
['error', 'div', PopupSliderCaptcha.CSS_CLASSES.error],
|
|
137
130
|
];
|
|
138
131
|
|
|
139
132
|
// 批量创建元素
|
|
@@ -151,19 +144,18 @@ class PopupSliderCaptcha {
|
|
|
151
144
|
this.setInitialState();
|
|
152
145
|
}
|
|
153
146
|
|
|
154
|
-
createElement(tag, className =
|
|
147
|
+
createElement(tag, className = '', textContent = '') {
|
|
155
148
|
const element = document.createElement(tag);
|
|
156
149
|
if (className) element.className = className;
|
|
157
150
|
if (textContent) element.textContent = textContent;
|
|
158
151
|
return element
|
|
159
152
|
}
|
|
160
153
|
|
|
161
|
-
// 优化:简化DOM组装逻辑
|
|
162
154
|
assembleDOM() {
|
|
163
155
|
const { elements } = this;
|
|
164
156
|
|
|
165
157
|
// 组装头部
|
|
166
|
-
const headerButtons = this.createElement(
|
|
158
|
+
const headerButtons = this.createElement('div', 'slider-captcha-header-buttons');
|
|
167
159
|
headerButtons.append(elements.refreshBtn, elements.closeBtn);
|
|
168
160
|
elements.header.append(elements.title, headerButtons);
|
|
169
161
|
|
|
@@ -172,24 +164,15 @@ class PopupSliderCaptcha {
|
|
|
172
164
|
elements.backgroundImg,
|
|
173
165
|
elements.sliderImg,
|
|
174
166
|
elements.loadingText,
|
|
175
|
-
elements.floatingTime
|
|
167
|
+
elements.floatingTime,
|
|
176
168
|
);
|
|
177
169
|
|
|
178
170
|
// 组装滑块轨道
|
|
179
171
|
elements.btn.appendChild(elements.icon);
|
|
180
|
-
elements.track.append(
|
|
181
|
-
elements.fingerAnimation,
|
|
182
|
-
elements.btn,
|
|
183
|
-
elements.hint
|
|
184
|
-
);
|
|
172
|
+
elements.track.append(elements.fingerAnimation, elements.btn, elements.hint);
|
|
185
173
|
|
|
186
174
|
// 组装模态框
|
|
187
|
-
elements.modal.append(
|
|
188
|
-
elements.header,
|
|
189
|
-
elements.container,
|
|
190
|
-
elements.track,
|
|
191
|
-
elements.error
|
|
192
|
-
);
|
|
175
|
+
elements.modal.append(elements.header, elements.container, elements.track, elements.error);
|
|
193
176
|
|
|
194
177
|
// 组装到覆盖层
|
|
195
178
|
elements.overlay.appendChild(elements.modal);
|
|
@@ -198,24 +181,23 @@ class PopupSliderCaptcha {
|
|
|
198
181
|
|
|
199
182
|
setInitialState() {
|
|
200
183
|
// 批量设置初始状态
|
|
201
|
-
Object.assign(this.elements.container.style, { display:
|
|
202
|
-
Object.assign(this.elements.track.style, { display:
|
|
184
|
+
Object.assign(this.elements.container.style, { display: 'none' });
|
|
185
|
+
Object.assign(this.elements.track.style, { display: 'none' });
|
|
203
186
|
}
|
|
204
187
|
|
|
205
|
-
// 优化:简化事件绑定
|
|
206
188
|
bindEvents() {
|
|
207
189
|
const { elements } = this;
|
|
208
190
|
|
|
209
191
|
// 基础事件
|
|
210
|
-
this.addEventListener(elements.closeBtn,
|
|
211
|
-
this.addEventListener(elements.refreshBtn,
|
|
212
|
-
this.addEventListener(elements.overlay,
|
|
192
|
+
this.addEventListener(elements.closeBtn, 'click', () => this.hide());
|
|
193
|
+
this.addEventListener(elements.refreshBtn, 'click', () => this.refresh());
|
|
194
|
+
this.addEventListener(elements.overlay, 'click', (e) => {
|
|
213
195
|
if (e.target === elements.overlay && this.options.clickMaskClose) this.hide();
|
|
214
196
|
});
|
|
215
|
-
this.addEventListener(document,
|
|
216
|
-
if (e.key ===
|
|
197
|
+
this.addEventListener(document, 'keydown', (e) => {
|
|
198
|
+
if (e.key === 'Escape' && this.state.isVisible) this.hide();
|
|
217
199
|
});
|
|
218
|
-
this.addEventListener(document,
|
|
200
|
+
this.addEventListener(document, 'visibilitychange', () => this.handleVisibilityChange());
|
|
219
201
|
|
|
220
202
|
this.bindSliderEvents();
|
|
221
203
|
}
|
|
@@ -225,21 +207,20 @@ class PopupSliderCaptcha {
|
|
|
225
207
|
const handlers = {
|
|
226
208
|
start: (e) => this.handleStart(e),
|
|
227
209
|
move: this.throttledHandleMove,
|
|
228
|
-
end: () => this.handleEnd()
|
|
210
|
+
end: () => this.handleEnd(),
|
|
229
211
|
};
|
|
230
212
|
|
|
231
213
|
// 滑块事件
|
|
232
|
-
this.addEventListener(elements.btn,
|
|
233
|
-
this.addEventListener(elements.btn,
|
|
234
|
-
this.addEventListener(elements.sliderImg,
|
|
235
|
-
this.addEventListener(elements.sliderImg,
|
|
236
|
-
this.addEventListener(document,
|
|
237
|
-
this.addEventListener(document,
|
|
238
|
-
this.addEventListener(document,
|
|
239
|
-
this.addEventListener(document,
|
|
214
|
+
this.addEventListener(elements.btn, 'mousedown', handlers.start);
|
|
215
|
+
this.addEventListener(elements.btn, 'touchstart', handlers.start);
|
|
216
|
+
this.addEventListener(elements.sliderImg, 'mousedown', handlers.start);
|
|
217
|
+
this.addEventListener(elements.sliderImg, 'touchstart', handlers.start);
|
|
218
|
+
this.addEventListener(document, 'mousemove', handlers.move, { passive: false });
|
|
219
|
+
this.addEventListener(document, 'touchmove', handlers.move, { passive: false });
|
|
220
|
+
this.addEventListener(document, 'mouseup', handlers.end);
|
|
221
|
+
this.addEventListener(document, 'touchend', handlers.end);
|
|
240
222
|
}
|
|
241
223
|
|
|
242
|
-
// 优化:改进事件管理
|
|
243
224
|
addEventListener(element, event, handler, options = {}) {
|
|
244
225
|
if (!element || typeof handler !== 'function') return
|
|
245
226
|
|
|
@@ -258,7 +239,6 @@ class PopupSliderCaptcha {
|
|
|
258
239
|
this.eventListeners.length = 0;
|
|
259
240
|
}
|
|
260
241
|
|
|
261
|
-
// 优化:缓存尺寸计算
|
|
262
242
|
getDimensions() {
|
|
263
243
|
if (!this.cachedDimensions) {
|
|
264
244
|
const trackWidth = this.elements.track.offsetWidth;
|
|
@@ -266,7 +246,7 @@ class PopupSliderCaptcha {
|
|
|
266
246
|
this.cachedDimensions = {
|
|
267
247
|
trackWidth,
|
|
268
248
|
btnWidth,
|
|
269
|
-
maxX: trackWidth - btnWidth
|
|
249
|
+
maxX: trackWidth - btnWidth,
|
|
270
250
|
};
|
|
271
251
|
}
|
|
272
252
|
return this.cachedDimensions
|
|
@@ -278,7 +258,6 @@ class PopupSliderCaptcha {
|
|
|
278
258
|
return Math.round(percentage * (this.options.width - this.options.sliderSize))
|
|
279
259
|
}
|
|
280
260
|
|
|
281
|
-
// 优化:简化拖拽处理
|
|
282
261
|
handleStart(e) {
|
|
283
262
|
if (!this.captchaData || this.state.isDragging || this.state.isLoading) return
|
|
284
263
|
|
|
@@ -289,7 +268,7 @@ class PopupSliderCaptcha {
|
|
|
289
268
|
this.times = [{ time: Date.now(), position: this.getPosition() }];
|
|
290
269
|
|
|
291
270
|
this.setTransition(false);
|
|
292
|
-
this.updateUIState(
|
|
271
|
+
this.updateUIState('dragging');
|
|
293
272
|
this.cachedDimensions = null; // 清除缓存
|
|
294
273
|
}
|
|
295
274
|
|
|
@@ -331,11 +310,11 @@ class PopupSliderCaptcha {
|
|
|
331
310
|
}
|
|
332
311
|
|
|
333
312
|
getClientX(e) {
|
|
334
|
-
return e.type.includes(
|
|
313
|
+
return e.type.includes('touch') ? e.touches[0].clientX : e.clientX
|
|
335
314
|
}
|
|
336
315
|
|
|
337
316
|
setTransition(enabled) {
|
|
338
|
-
const transition = enabled ?
|
|
317
|
+
const transition = enabled ? 'all 0.3s ease' : 'none';
|
|
339
318
|
requestAnimationFrame(() => {
|
|
340
319
|
this.elements.btn.style.transition = transition;
|
|
341
320
|
this.elements.sliderImg.style.transition = transition;
|
|
@@ -347,38 +326,38 @@ class PopupSliderCaptcha {
|
|
|
347
326
|
const { elements } = this;
|
|
348
327
|
const updates = {
|
|
349
328
|
dragging: () => {
|
|
350
|
-
elements.hint.style.opacity =
|
|
351
|
-
elements.fingerAnimation.style.display =
|
|
329
|
+
elements.hint.style.opacity = '0';
|
|
330
|
+
elements.fingerAnimation.style.display = 'none';
|
|
352
331
|
},
|
|
353
332
|
success: () => {
|
|
354
|
-
Object.assign(elements.btn.style, { background:
|
|
355
|
-
Object.assign(elements.icon.style, { innerHTML:
|
|
356
|
-
elements.icon.innerHTML =
|
|
333
|
+
Object.assign(elements.btn.style, { background: 'var(--sc-success)' });
|
|
334
|
+
Object.assign(elements.icon.style, { innerHTML: '✓', color: 'white' });
|
|
335
|
+
elements.icon.innerHTML = '✓';
|
|
357
336
|
},
|
|
358
337
|
fail: () => {
|
|
359
|
-
Object.assign(elements.btn.style, { background:
|
|
360
|
-
Object.assign(elements.icon.style, { innerHTML:
|
|
361
|
-
elements.icon.innerHTML =
|
|
338
|
+
Object.assign(elements.btn.style, { background: 'var(--sc-danger)' });
|
|
339
|
+
Object.assign(elements.icon.style, { innerHTML: '✗', color: 'white' });
|
|
340
|
+
elements.icon.innerHTML = '✗';
|
|
362
341
|
},
|
|
363
342
|
reset: () => {
|
|
364
|
-
Object.assign(elements.btn.style, { background:
|
|
365
|
-
Object.assign(elements.icon.style, { color:
|
|
366
|
-
elements.icon.innerHTML =
|
|
367
|
-
elements.fingerAnimation.style.display =
|
|
368
|
-
this.updateHintText(
|
|
343
|
+
Object.assign(elements.btn.style, { background: 'white' });
|
|
344
|
+
Object.assign(elements.icon.style, { color: '#666' });
|
|
345
|
+
elements.icon.innerHTML = '→';
|
|
346
|
+
elements.fingerAnimation.style.display = 'block';
|
|
347
|
+
this.updateHintText('向右滑动完成验证', 'var(--sc-text-light)');
|
|
369
348
|
},
|
|
370
349
|
loading: () => {
|
|
371
|
-
elements.hint.style.opacity =
|
|
372
|
-
elements.fingerAnimation.style.display =
|
|
373
|
-
Object.assign(elements.track.style, { pointerEvents:
|
|
374
|
-
}
|
|
350
|
+
elements.hint.style.opacity = '0';
|
|
351
|
+
elements.fingerAnimation.style.display = 'none';
|
|
352
|
+
Object.assign(elements.track.style, { pointerEvents: 'none', opacity: '0.6' });
|
|
353
|
+
},
|
|
375
354
|
};
|
|
376
355
|
|
|
377
356
|
if (updates[state]) {
|
|
378
357
|
requestAnimationFrame(() => {
|
|
379
358
|
updates[state]();
|
|
380
|
-
if (state !==
|
|
381
|
-
Object.assign(elements.track.style, { pointerEvents:
|
|
359
|
+
if (state !== 'loading') {
|
|
360
|
+
Object.assign(elements.track.style, { pointerEvents: 'auto', opacity: '1' });
|
|
382
361
|
}
|
|
383
362
|
});
|
|
384
363
|
}
|
|
@@ -387,7 +366,7 @@ class PopupSliderCaptcha {
|
|
|
387
366
|
updateHintText(text, color) {
|
|
388
367
|
requestAnimationFrame(() => {
|
|
389
368
|
Object.assign(this.elements.hint, { textContent: text });
|
|
390
|
-
Object.assign(this.elements.hint.style, { color, opacity:
|
|
369
|
+
Object.assign(this.elements.hint.style, { color, opacity: '1' });
|
|
391
370
|
});
|
|
392
371
|
}
|
|
393
372
|
|
|
@@ -400,18 +379,18 @@ class PopupSliderCaptcha {
|
|
|
400
379
|
requestAnimationFrame(() => {
|
|
401
380
|
elements.btn.style.transform = `translateX(${state.currentX}px)`;
|
|
402
381
|
elements.sliderImg.style.transform = `translateX(${pieceX}px)`;
|
|
403
|
-
elements.fingerAnimation.style.opacity = progress >= 0.8 ?
|
|
382
|
+
elements.fingerAnimation.style.opacity = progress >= 0.8 ? '0' : '0.6';
|
|
404
383
|
});
|
|
405
384
|
}
|
|
406
385
|
|
|
407
386
|
// 优化:简化显示/隐藏逻辑
|
|
408
387
|
show() {
|
|
409
388
|
this.state.isVisible = true;
|
|
410
|
-
this.elements.overlay.style.display =
|
|
389
|
+
this.elements.overlay.style.display = 'flex';
|
|
411
390
|
|
|
412
391
|
requestAnimationFrame(() => {
|
|
413
|
-
this.elements.overlay.classList.add(
|
|
414
|
-
this.elements.modal.classList.add(
|
|
392
|
+
this.elements.overlay.classList.add('show');
|
|
393
|
+
this.elements.modal.classList.add('show');
|
|
415
394
|
});
|
|
416
395
|
|
|
417
396
|
this.loadCaptcha();
|
|
@@ -419,11 +398,11 @@ class PopupSliderCaptcha {
|
|
|
419
398
|
|
|
420
399
|
hide() {
|
|
421
400
|
this.state.isVisible = false;
|
|
422
|
-
this.elements.overlay.classList.remove(
|
|
423
|
-
this.elements.modal.classList.remove(
|
|
401
|
+
this.elements.overlay.classList.remove('show');
|
|
402
|
+
this.elements.modal.classList.remove('show');
|
|
424
403
|
|
|
425
404
|
this.safeSetTimeout(() => {
|
|
426
|
-
this.elements.overlay.style.display =
|
|
405
|
+
this.elements.overlay.style.display = 'none';
|
|
427
406
|
document.body.removeChild(this.elements.overlay);
|
|
428
407
|
this.reset();
|
|
429
408
|
this.options.onClose?.();
|
|
@@ -438,7 +417,7 @@ class PopupSliderCaptcha {
|
|
|
438
417
|
[PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR]: '请求超时,请重试',
|
|
439
418
|
[PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR]: '验证失败,请重试',
|
|
440
419
|
[PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR]: '图片加载失败,请刷新重试',
|
|
441
|
-
[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]: '验证码数据错误,请刷新重试'
|
|
420
|
+
[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]: '验证码数据错误,请刷新重试',
|
|
442
421
|
};
|
|
443
422
|
|
|
444
423
|
const errorMessage = errorMessages[errorType] || message || '未知错误';
|
|
@@ -448,7 +427,7 @@ class PopupSliderCaptcha {
|
|
|
448
427
|
this.options.onError({
|
|
449
428
|
type: errorType,
|
|
450
429
|
message: errorMessage,
|
|
451
|
-
originalError
|
|
430
|
+
originalError,
|
|
452
431
|
});
|
|
453
432
|
}
|
|
454
433
|
|
|
@@ -469,16 +448,16 @@ class PopupSliderCaptcha {
|
|
|
469
448
|
this.abortController = new AbortController();
|
|
470
449
|
|
|
471
450
|
const response = await fetch(this.options.apiUrl, {
|
|
472
|
-
method:
|
|
451
|
+
method: 'POST',
|
|
473
452
|
headers: {
|
|
474
|
-
|
|
475
|
-
...this.options.headers
|
|
453
|
+
'Content-Type': 'application/json',
|
|
454
|
+
...this.options.headers,
|
|
476
455
|
},
|
|
477
456
|
body: JSON.stringify({
|
|
478
457
|
timestamp: Date.now(),
|
|
479
|
-
...this.options.requestData
|
|
458
|
+
...this.options.requestData,
|
|
480
459
|
}),
|
|
481
|
-
signal: this.abortController.signal
|
|
460
|
+
signal: this.abortController.signal,
|
|
482
461
|
});
|
|
483
462
|
|
|
484
463
|
if (!response.ok) {
|
|
@@ -489,7 +468,7 @@ class PopupSliderCaptcha {
|
|
|
489
468
|
|
|
490
469
|
// 优化:更严格的数据验证
|
|
491
470
|
if (!this.validateCaptchaData(data)) {
|
|
492
|
-
throw new Error(
|
|
471
|
+
throw new Error('验证码数据格式错误')
|
|
493
472
|
}
|
|
494
473
|
|
|
495
474
|
this.captchaData = data.data;
|
|
@@ -498,7 +477,10 @@ class PopupSliderCaptcha {
|
|
|
498
477
|
} catch (error) {
|
|
499
478
|
if (error.name === 'AbortError') {
|
|
500
479
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR, '请求被取消');
|
|
501
|
-
} else if (
|
|
480
|
+
} else if (
|
|
481
|
+
error.message.includes('Failed to fetch') ||
|
|
482
|
+
error.message.includes('NetworkError')
|
|
483
|
+
) {
|
|
502
484
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR, '网络连接失败');
|
|
503
485
|
} else {
|
|
504
486
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR, error.message, error);
|
|
@@ -510,10 +492,19 @@ class PopupSliderCaptcha {
|
|
|
510
492
|
validateCaptchaData(data) {
|
|
511
493
|
if (!data || typeof data !== 'object') return false
|
|
512
494
|
|
|
513
|
-
const requiredFields = [
|
|
495
|
+
const requiredFields = [
|
|
496
|
+
'canvasSrc',
|
|
497
|
+
'blockSrc',
|
|
498
|
+
'canvasWidth',
|
|
499
|
+
'canvasHeight',
|
|
500
|
+
'blockWidth',
|
|
501
|
+
'blockHeight',
|
|
502
|
+
'blockY',
|
|
503
|
+
'nonceStr',
|
|
504
|
+
];
|
|
514
505
|
const dataObj = data.data || data;
|
|
515
506
|
|
|
516
|
-
return requiredFields.every(field => {
|
|
507
|
+
return requiredFields.every((field) => {
|
|
517
508
|
const value = dataObj[field];
|
|
518
509
|
return value !== null && value !== undefined && value !== ''
|
|
519
510
|
})
|
|
@@ -527,13 +518,13 @@ class PopupSliderCaptcha {
|
|
|
527
518
|
const loadPromises = [
|
|
528
519
|
this.loadImageAsync(this.elements.backgroundImg, this.captchaData.canvasSrc, {
|
|
529
520
|
width: this.captchaData.canvasWidth,
|
|
530
|
-
height: this.captchaData.canvasHeight
|
|
521
|
+
height: this.captchaData.canvasHeight,
|
|
531
522
|
}),
|
|
532
523
|
this.loadImageAsync(this.elements.sliderImg, this.captchaData.blockSrc, {
|
|
533
524
|
width: this.captchaData.blockWidth,
|
|
534
525
|
height: this.captchaData.blockHeight,
|
|
535
|
-
top: this.captchaData.blockY
|
|
536
|
-
})
|
|
526
|
+
top: this.captchaData.blockY,
|
|
527
|
+
}),
|
|
537
528
|
];
|
|
538
529
|
|
|
539
530
|
Promise.all(loadPromises)
|
|
@@ -588,7 +579,7 @@ class PopupSliderCaptcha {
|
|
|
588
579
|
// 优化:提取样式应用逻辑
|
|
589
580
|
applyStyles(element, styles) {
|
|
590
581
|
Object.entries(styles).forEach(([key, value]) => {
|
|
591
|
-
element.style[key] = typeof value ===
|
|
582
|
+
element.style[key] = typeof value === 'number' ? value + 'px' : value;
|
|
592
583
|
});
|
|
593
584
|
}
|
|
594
585
|
|
|
@@ -596,31 +587,31 @@ class PopupSliderCaptcha {
|
|
|
596
587
|
showLoading() {
|
|
597
588
|
this.state.isLoading = true;
|
|
598
589
|
this.batchUpdateStyles({
|
|
599
|
-
container: { display:
|
|
600
|
-
loadingText: { display:
|
|
601
|
-
error: { display:
|
|
590
|
+
container: { display: 'block' },
|
|
591
|
+
loadingText: { display: 'flex' },
|
|
592
|
+
error: { display: 'none' },
|
|
602
593
|
});
|
|
603
|
-
this.updateUIState(
|
|
594
|
+
this.updateUIState('loading');
|
|
604
595
|
}
|
|
605
596
|
|
|
606
597
|
hideLoading() {
|
|
607
598
|
this.state.isLoading = false;
|
|
608
|
-
this.batchUpdateStyles({ loadingText: { display:
|
|
609
|
-
this.updateUIState(
|
|
599
|
+
this.batchUpdateStyles({ loadingText: { display: 'none' } });
|
|
600
|
+
this.updateUIState('reset');
|
|
610
601
|
}
|
|
611
602
|
|
|
612
603
|
showCaptcha() {
|
|
613
604
|
this.batchUpdateStyles({
|
|
614
|
-
container: { display:
|
|
615
|
-
track: { display:
|
|
616
|
-
error: { display:
|
|
605
|
+
container: { display: 'block' },
|
|
606
|
+
track: { display: 'block' },
|
|
607
|
+
error: { display: 'none' },
|
|
617
608
|
});
|
|
618
609
|
}
|
|
619
610
|
|
|
620
611
|
showError(message) {
|
|
621
612
|
this.hideLoading();
|
|
622
613
|
this.batchUpdateStyles({
|
|
623
|
-
error: { display:
|
|
614
|
+
error: { display: 'block', textContent: message },
|
|
624
615
|
});
|
|
625
616
|
}
|
|
626
617
|
|
|
@@ -644,7 +635,7 @@ class PopupSliderCaptcha {
|
|
|
644
635
|
|
|
645
636
|
async verify() {
|
|
646
637
|
if (!this.captchaData) {
|
|
647
|
-
this.onVerifyFail(
|
|
638
|
+
this.onVerifyFail('验证码数据丢失');
|
|
648
639
|
return
|
|
649
640
|
}
|
|
650
641
|
|
|
@@ -656,20 +647,20 @@ class PopupSliderCaptcha {
|
|
|
656
647
|
this.abortController = new AbortController();
|
|
657
648
|
|
|
658
649
|
const response = await fetch(this.options.verifyUrl, {
|
|
659
|
-
method:
|
|
650
|
+
method: 'POST',
|
|
660
651
|
headers: {
|
|
661
|
-
|
|
662
|
-
...this.options.headers
|
|
652
|
+
'Content-Type': 'application/json',
|
|
653
|
+
...this.options.headers,
|
|
663
654
|
},
|
|
664
655
|
body: JSON.stringify({
|
|
665
656
|
loginVo: {
|
|
666
657
|
nonceStr: this.captchaData.nonceStr,
|
|
667
|
-
value: this.getPosition()
|
|
658
|
+
value: this.getPosition(),
|
|
668
659
|
},
|
|
669
660
|
dragEventList: [...this.times],
|
|
670
|
-
...this.options.verifyData
|
|
661
|
+
...this.options.verifyData,
|
|
671
662
|
}),
|
|
672
|
-
signal: this.abortController.signal
|
|
663
|
+
signal: this.abortController.signal,
|
|
673
664
|
});
|
|
674
665
|
|
|
675
666
|
if (!response.ok) {
|
|
@@ -682,12 +673,15 @@ class PopupSliderCaptcha {
|
|
|
682
673
|
if (this.isVerifySuccess(data)) {
|
|
683
674
|
this.onVerifySuccess(data.data || data.result);
|
|
684
675
|
} else {
|
|
685
|
-
this.onVerifyFail(data.message || data.msg ||
|
|
676
|
+
this.onVerifyFail(data.message || data.msg || '验证失败,请重试!');
|
|
686
677
|
}
|
|
687
678
|
} catch (error) {
|
|
688
679
|
if (error.name === 'AbortError') {
|
|
689
680
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR, '验证请求被取消');
|
|
690
|
-
} else if (
|
|
681
|
+
} else if (
|
|
682
|
+
error.message.includes('Failed to fetch') ||
|
|
683
|
+
error.message.includes('NetworkError')
|
|
684
|
+
) {
|
|
691
685
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR, '网络连接失败');
|
|
692
686
|
} else {
|
|
693
687
|
this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR, error.message, error);
|
|
@@ -701,48 +695,50 @@ class PopupSliderCaptcha {
|
|
|
701
695
|
|
|
702
696
|
// 支持多种成功标识
|
|
703
697
|
const successIndicators = [
|
|
704
|
-
data.code ===
|
|
698
|
+
data.code === '0',
|
|
705
699
|
data.code === 0,
|
|
706
700
|
data.success === true,
|
|
707
701
|
data.status === 'success',
|
|
708
|
-
data.result === true
|
|
702
|
+
data.result === true,
|
|
709
703
|
];
|
|
710
704
|
|
|
711
|
-
return successIndicators.some(indicator => indicator === true)
|
|
705
|
+
return successIndicators.some((indicator) => indicator === true)
|
|
712
706
|
}
|
|
713
707
|
|
|
714
708
|
onVerifySuccess(ticket) {
|
|
715
|
-
const duration = this.dragStartTime
|
|
709
|
+
const duration = this.dragStartTime
|
|
710
|
+
? Date.now() - this.dragStartTime
|
|
711
|
+
: Date.now() - this.startTime;
|
|
716
712
|
const durationText = `验证成功!耗时:${(duration / 1000).toFixed(2)}s`;
|
|
717
713
|
|
|
718
|
-
this.updateUIState(
|
|
719
|
-
this.showFloatingTime(durationText,
|
|
714
|
+
this.updateUIState('success');
|
|
715
|
+
this.showFloatingTime(durationText, 'success');
|
|
720
716
|
|
|
721
717
|
this.safeSetTimeout(() => {
|
|
722
718
|
this.options.onSuccess?.({
|
|
723
719
|
ticket: ticket,
|
|
724
720
|
timestamp: Date.now(),
|
|
725
|
-
duration
|
|
721
|
+
duration,
|
|
726
722
|
});
|
|
727
723
|
this.hide();
|
|
728
724
|
}, 2000);
|
|
729
725
|
}
|
|
730
726
|
|
|
731
|
-
showFloatingTime(text, type =
|
|
727
|
+
showFloatingTime(text, type = 'success') {
|
|
732
728
|
const { elements } = this;
|
|
733
729
|
elements.floatingTime.textContent = text;
|
|
734
730
|
elements.floatingTime.className = `slider-captcha-floating-time ${type}`;
|
|
735
731
|
|
|
736
|
-
this.safeSetTimeout(() => elements.floatingTime.classList.add(
|
|
732
|
+
this.safeSetTimeout(() => elements.floatingTime.classList.add('show'), 100);
|
|
737
733
|
this.safeSetTimeout(() => {
|
|
738
|
-
elements.floatingTime.className =
|
|
734
|
+
elements.floatingTime.className = 'slider-captcha-floating-time';
|
|
739
735
|
}, 2500); // 优化:延长显示时间,避免被reset清除
|
|
740
736
|
}
|
|
741
737
|
|
|
742
738
|
onVerifyFail(message) {
|
|
743
739
|
this.state.retryCount++;
|
|
744
|
-
this.updateUIState(
|
|
745
|
-
this.showFloatingTime(message,
|
|
740
|
+
this.updateUIState('fail');
|
|
741
|
+
this.showFloatingTime(message, 'fail');
|
|
746
742
|
|
|
747
743
|
this.safeSetTimeout(() => {
|
|
748
744
|
if (this.state.retryCount >= this.options.maxRetries) {
|
|
@@ -761,7 +757,7 @@ class PopupSliderCaptcha {
|
|
|
761
757
|
isDragging: false,
|
|
762
758
|
currentX: 0,
|
|
763
759
|
startX: 0,
|
|
764
|
-
isLoading: false
|
|
760
|
+
isLoading: false,
|
|
765
761
|
});
|
|
766
762
|
|
|
767
763
|
this.times = [];
|
|
@@ -772,10 +768,10 @@ class PopupSliderCaptcha {
|
|
|
772
768
|
// 重置UI
|
|
773
769
|
requestAnimationFrame(() => {
|
|
774
770
|
this.setTransition(true);
|
|
775
|
-
this.elements.btn.style.transform =
|
|
776
|
-
this.elements.sliderImg.style.transform =
|
|
777
|
-
this.updateUIState(
|
|
778
|
-
this.elements.error.style.display =
|
|
771
|
+
this.elements.btn.style.transform = 'translateX(0px)';
|
|
772
|
+
this.elements.sliderImg.style.transform = 'translateX(0px)';
|
|
773
|
+
this.updateUIState('reset');
|
|
774
|
+
this.elements.error.style.display = 'none';
|
|
779
775
|
});
|
|
780
776
|
}
|
|
781
777
|
|
|
@@ -803,7 +799,7 @@ class PopupSliderCaptcha {
|
|
|
803
799
|
}
|
|
804
800
|
|
|
805
801
|
clearAllTimers() {
|
|
806
|
-
this.timers.forEach(timer => {
|
|
802
|
+
this.timers.forEach((timer) => {
|
|
807
803
|
clearTimeout(timer);
|
|
808
804
|
clearInterval(timer);
|
|
809
805
|
});
|
|
@@ -852,8 +848,8 @@ class PopupSliderCaptcha {
|
|
|
852
848
|
|
|
853
849
|
// 恢复body样式
|
|
854
850
|
if (document.body) {
|
|
855
|
-
document.body.style.userSelect =
|
|
856
|
-
document.body.style.cursor =
|
|
851
|
+
document.body.style.userSelect = '';
|
|
852
|
+
document.body.style.cursor = '';
|
|
857
853
|
}
|
|
858
854
|
|
|
859
855
|
// 清理所有定时器
|
|
@@ -881,7 +877,7 @@ class PopupSliderCaptcha {
|
|
|
881
877
|
this.cachedDimensions = null;
|
|
882
878
|
|
|
883
879
|
// 清空所有属性
|
|
884
|
-
Object.keys(this).forEach(key => {
|
|
880
|
+
Object.keys(this).forEach((key) => {
|
|
885
881
|
if (key !== 'constructor') {
|
|
886
882
|
this[key] = null;
|
|
887
883
|
}
|
|
@@ -908,12 +904,12 @@ class PopupSliderCaptcha {
|
|
|
908
904
|
}
|
|
909
905
|
|
|
910
906
|
// 模块导出
|
|
911
|
-
if (typeof module !==
|
|
907
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
912
908
|
module.exports = PopupSliderCaptcha;
|
|
913
909
|
module.exports.default = PopupSliderCaptcha;
|
|
914
|
-
} else if (typeof define ===
|
|
910
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
915
911
|
define([], () => PopupSliderCaptcha);
|
|
916
|
-
} else if (typeof window !==
|
|
912
|
+
} else if (typeof window !== 'undefined') {
|
|
917
913
|
window.PopupSliderCaptcha = PopupSliderCaptcha;
|
|
918
914
|
window.SliderCaptcha = PopupSliderCaptcha;
|
|
919
915
|
}
|