slider-captcha-sdk 1.0.18 → 1.0.20

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