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 CHANGED
@@ -10,22 +10,22 @@ class PopupSliderCaptcha {
10
10
  sliderSize: 38,
11
11
  maxRetries: 3,
12
12
  timeout: 30000,
13
- apiUrl: "/api/captcha",
14
- verifyUrl: "/api/captcha/verify",
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: "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"
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("#slider-captcha-styles")) return
102
+ if (document.querySelector('#slider-captcha-styles')) return
109
103
 
110
- const style = document.createElement("style");
111
- style.id = "slider-captcha-styles";
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 = "", textContent = "") {
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("div", "slider-captcha-header-buttons");
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: "none" });
204
- Object.assign(this.elements.track.style, { display: "none" });
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, "click", () => this.hide());
213
- this.addEventListener(elements.refreshBtn, "click", () => this.refresh());
214
- this.addEventListener(elements.overlay, "click", (e) => {
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, "keydown", (e) => {
218
- if (e.key === "Escape" && this.state.isVisible) this.hide();
199
+ this.addEventListener(document, 'keydown', (e) => {
200
+ if (e.key === 'Escape' && this.state.isVisible) this.hide();
219
201
  });
220
- this.addEventListener(document, "visibilitychange", () => this.handleVisibilityChange());
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, "mousedown", handlers.start);
235
- this.addEventListener(elements.btn, "touchstart", handlers.start);
236
- this.addEventListener(elements.sliderImg, "mousedown", handlers.start);
237
- this.addEventListener(elements.sliderImg, "touchstart", handlers.start);
238
- this.addEventListener(document, "mousemove", handlers.move, { passive: false });
239
- this.addEventListener(document, "touchmove", handlers.move, { passive: false });
240
- this.addEventListener(document, "mouseup", handlers.end);
241
- this.addEventListener(document, "touchend", handlers.end);
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("dragging");
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("touch") ? e.touches[0].clientX : e.clientX
315
+ return e.type.includes('touch') ? e.touches[0].clientX : e.clientX
337
316
  }
338
317
 
339
318
  setTransition(enabled) {
340
- const transition = enabled ? "all 0.3s ease" : "none";
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 = "0";
353
- elements.fingerAnimation.style.display = "none";
331
+ elements.hint.style.opacity = '0';
332
+ elements.fingerAnimation.style.display = 'none';
354
333
  },
355
334
  success: () => {
356
- Object.assign(elements.btn.style, { background: "var(--sc-success)" });
357
- Object.assign(elements.icon.style, { innerHTML: "", color: "white" });
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: "var(--sc-danger)" });
362
- Object.assign(elements.icon.style, { innerHTML: "", color: "white" });
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: "white" });
367
- Object.assign(elements.icon.style, { color: "#666" });
368
- elements.icon.innerHTML = "";
369
- elements.fingerAnimation.style.display = "block";
370
- this.updateHintText("向右滑动完成验证", "var(--sc-text-light)");
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 = "0";
374
- elements.fingerAnimation.style.display = "none";
375
- Object.assign(elements.track.style, { pointerEvents: "none", opacity: "0.6" });
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 !== "loading") {
383
- Object.assign(elements.track.style, { pointerEvents: "auto", opacity: "1" });
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: "1" });
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 ? "0" : "0.6";
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 = "flex";
391
+ this.elements.overlay.style.display = 'flex';
413
392
 
414
393
  requestAnimationFrame(() => {
415
- this.elements.overlay.classList.add("show");
416
- this.elements.modal.classList.add("show");
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("show");
425
- this.elements.modal.classList.remove("show");
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 = "none";
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: "POST",
453
+ method: 'POST',
475
454
  headers: {
476
- "Content-Type": "application/json",
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 (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
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 = ['canvasSrc', 'blockSrc', 'canvasWidth', 'canvasHeight', 'blockWidth', 'blockHeight', 'blockY', 'nonceStr'];
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 === "number" ? value + "px" : 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: "block" },
602
- loadingText: { display: "flex" },
603
- error: { display: "none" }
592
+ container: { display: 'block' },
593
+ loadingText: { display: 'flex' },
594
+ error: { display: 'none' },
604
595
  });
605
- this.updateUIState("loading");
596
+ this.updateUIState('loading');
606
597
  }
607
598
 
608
599
  hideLoading() {
609
600
  this.state.isLoading = false;
610
- this.batchUpdateStyles({ loadingText: { display: "none" } });
611
- this.updateUIState("reset");
601
+ this.batchUpdateStyles({ loadingText: { display: 'none' } });
602
+ this.updateUIState('reset');
612
603
  }
613
604
 
614
605
  showCaptcha() {
615
606
  this.batchUpdateStyles({
616
- container: { display: "block" },
617
- track: { display: "block" },
618
- error: { display: "none" }
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: "block", textContent: message }
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: "POST",
652
+ method: 'POST',
662
653
  headers: {
663
- "Content-Type": "application/json",
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 (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
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 === "0",
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 ? Date.now() - this.dragStartTime : (Date.now() - this.startTime);
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("success");
721
- this.showFloatingTime(durationText, "success");
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 = "success") {
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("show"), 100);
734
+ this.safeSetTimeout(() => elements.floatingTime.classList.add('show'), 100);
739
735
  this.safeSetTimeout(() => {
740
- elements.floatingTime.className = "slider-captcha-floating-time";
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("fail");
747
- this.showFloatingTime(message, "fail");
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 = "translateX(0px)";
778
- this.elements.sliderImg.style.transform = "translateX(0px)";
779
- this.updateUIState("reset");
780
- this.elements.error.style.display = "none";
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 !== "undefined" && module.exports) {
909
+ if (typeof module !== 'undefined' && module.exports) {
914
910
  module.exports = PopupSliderCaptcha;
915
911
  module.exports.default = PopupSliderCaptcha;
916
- } else if (typeof define === "function" && define.amd) {
912
+ } else if (typeof define === 'function' && define.amd) {
917
913
  define([], () => PopupSliderCaptcha);
918
- } else if (typeof window !== "undefined") {
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 (!this.validatePublicKey(publicKey)) {
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') return false
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,8 +1039,7 @@ class PasswordValidator {
1060
1039
  */
1061
1040
  encryptPassword(password, publicKey) {
1062
1041
  try {
1063
- // 优化:输入验证
1064
- if (!password || typeof password !== 'string') {
1042
+ if (!password) {
1065
1043
  throw new Error('密码不能为空')
1066
1044
  }
1067
1045
 
@@ -1069,11 +1047,10 @@ class PasswordValidator {
1069
1047
  throw new Error('密码长度不足')
1070
1048
  }
1071
1049
 
1072
- if (!publicKey || typeof publicKey !== 'string') {
1050
+ if (!publicKey) {
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
- if (!password || typeof password !== 'string') {
1108
- throw new Error('密码不能为空')
1109
- }
1110
-
1111
- if (!userName || typeof userName !== 'string') {
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
- // 优化:使用实例的abortController
1172
- if (this.abortController) {
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
- this.abortController.abort();
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: this.abortController.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
- if (this.abortController) {
1246
- this.abortController.abort();
1247
- this.abortController = null;
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
- return this.abortController && !this.abortController.signal.aborted
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