slider-captcha-sdk 1.0.16 → 1.0.18

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
@@ -38,7 +38,6 @@ class PopupSliderCaptcha {
38
38
  CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存
39
39
  MAX_RETRY_ATTEMPTS: 3,
40
40
  DEFAULT_TIMEOUT: 30000,
41
- ANIMATION_DURATION: 300,
42
41
  FLOATING_TIME_DURATION: 2500,
43
42
  THROTTLE_DELAY: 16
44
43
  }
@@ -54,14 +53,14 @@ class PopupSliderCaptcha {
54
53
 
55
54
  constructor(options = {}) {
56
55
  // 优化:合并默认配置,使用常量
57
- this.options = {
58
- ...PopupSliderCaptcha.DEFAULTS,
56
+ this.options = {
57
+ ...PopupSliderCaptcha.DEFAULTS,
59
58
  ...options,
60
59
  timeout: options.timeout || PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,
61
60
  maxRetries: options.maxRetries || PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,
62
61
  throttleDelay: options.throttleDelay || PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY
63
62
  };
64
-
63
+
65
64
  // 优化:初始化所有属性
66
65
  this.elements = {};
67
66
  this.state = this.createInitialState();
@@ -288,7 +287,7 @@ class PopupSliderCaptcha {
288
287
  e.preventDefault();
289
288
  this.state.isDragging = true;
290
289
  this.state.startX = this.getClientX(e) - this.state.currentX;
291
- this.startTime = this.startTime || Date.now();
290
+ this.dragStartTime = Date.now();
292
291
  this.times = [{ time: Date.now(), position: this.getPosition() }];
293
292
 
294
293
  this.setTransition(false);
@@ -445,7 +444,7 @@ class PopupSliderCaptcha {
445
444
  };
446
445
 
447
446
  const errorMessage = errorMessages[errorType] || message || '未知错误';
448
-
447
+
449
448
  // 调用用户自定义错误处理
450
449
  if (this.options.onError) {
451
450
  this.options.onError({
@@ -473,12 +472,11 @@ class PopupSliderCaptcha {
473
472
 
474
473
  const response = await fetch(this.options.apiUrl, {
475
474
  method: "POST",
476
- headers: {
475
+ headers: {
477
476
  "Content-Type": "application/json",
478
477
  ...this.options.headers
479
478
  },
480
- body: JSON.stringify({
481
- place: 2,
479
+ body: JSON.stringify({
482
480
  timestamp: Date.now(),
483
481
  ...this.options.requestData
484
482
  }),
@@ -513,10 +511,10 @@ class PopupSliderCaptcha {
513
511
  // 优化:添加验证码数据验证方法
514
512
  validateCaptchaData(data) {
515
513
  if (!data || typeof data !== 'object') return false
516
-
514
+
517
515
  const requiredFields = ['canvasSrc', 'blockSrc', 'canvasWidth', 'canvasHeight', 'blockWidth', 'blockHeight', 'blockY', 'nonceStr'];
518
516
  const dataObj = data.data || data;
519
-
517
+
520
518
  return requiredFields.every(field => {
521
519
  const value = dataObj[field];
522
520
  return value !== null && value !== undefined && value !== ''
@@ -578,36 +576,17 @@ class PopupSliderCaptcha {
578
576
  this.imageCache.set(src, imgElement.cloneNode());
579
577
  resolve();
580
578
  };
581
-
579
+
582
580
  imgElement.onerror = (error) => {
583
581
  this.safeClearTimeout(timeoutId);
584
582
  reject(error);
585
583
  };
586
-
584
+
587
585
  imgElement.src = src;
588
586
  this.applyStyles(imgElement, styles);
589
587
  })
590
588
  }
591
589
 
592
- loadImage(imgElement, src, styles, onLoad, onError) {
593
- // 检查缓存
594
- if (this.imageCache.has(src)) {
595
- const cachedImg = this.imageCache.get(src);
596
- imgElement.src = cachedImg.src;
597
- this.applyStyles(imgElement, styles);
598
- onLoad();
599
- return
600
- }
601
-
602
- imgElement.onload = () => {
603
- this.imageCache.set(src, imgElement.cloneNode());
604
- onLoad();
605
- };
606
- imgElement.onerror = onError;
607
- imgElement.src = src;
608
- this.applyStyles(imgElement, styles);
609
- }
610
-
611
590
  // 优化:提取样式应用逻辑
612
591
  applyStyles(element, styles) {
613
592
  Object.entries(styles).forEach(([key, value]) => {
@@ -680,7 +659,7 @@ class PopupSliderCaptcha {
680
659
 
681
660
  const response = await fetch(this.options.verifyUrl, {
682
661
  method: "POST",
683
- headers: {
662
+ headers: {
684
663
  "Content-Type": "application/json",
685
664
  ...this.options.headers
686
665
  },
@@ -721,7 +700,7 @@ class PopupSliderCaptcha {
721
700
  // 优化:添加验证成功判断方法
722
701
  isVerifySuccess(data) {
723
702
  if (!data || typeof data !== 'object') return false
724
-
703
+
725
704
  // 支持多种成功标识
726
705
  const successIndicators = [
727
706
  data.code === "0",
@@ -730,12 +709,12 @@ class PopupSliderCaptcha {
730
709
  data.status === 'success',
731
710
  data.result === true
732
711
  ];
733
-
712
+
734
713
  return successIndicators.some(indicator => indicator === true)
735
714
  }
736
715
 
737
716
  onVerifySuccess(ticket) {
738
- const duration = Date.now() - this.startTime;
717
+ const duration = this.dragStartTime ? Date.now() - this.dragStartTime : (Date.now() - this.startTime);
739
718
  const durationText = `验证成功!耗时:${(duration / 1000).toFixed(2)}s`;
740
719
 
741
720
  this.updateUIState("success");
@@ -789,6 +768,7 @@ class PopupSliderCaptcha {
789
768
 
790
769
  this.times = [];
791
770
  this.startTime = null;
771
+ this.dragStartTime = null; // 优化:重置拖拽开始时间
792
772
  this.cachedDimensions = null;
793
773
 
794
774
  // 重置UI
@@ -864,15 +844,6 @@ class PopupSliderCaptcha {
864
844
  }
865
845
  }
866
846
 
867
- // 工具函数:防抖
868
- debounce(func, delay) {
869
- let timeoutId;
870
- return function (...args) {
871
- clearTimeout(timeoutId);
872
- timeoutId = setTimeout(() => func.apply(this, args), delay);
873
- }
874
- }
875
-
876
847
  destroy() {
877
848
  try {
878
849
  // 取消所有进行中的请求
@@ -901,9 +872,9 @@ class PopupSliderCaptcha {
901
872
  this.elements.overlay.parentNode.removeChild(this.elements.overlay);
902
873
  }
903
874
 
904
- // 清理样式表(只有在没有其他实例使用时才删除)
875
+ // 清理样式表
905
876
  const styleElement = document.getElementById('slider-captcha-styles');
906
- if (styleElement && !this.hasOtherInstances()) {
877
+ if (styleElement) {
907
878
  styleElement.remove();
908
879
  }
909
880
 
@@ -927,15 +898,6 @@ class PopupSliderCaptcha {
927
898
  }
928
899
  }
929
900
 
930
- // 优化:检查是否有其他实例在使用样式
931
- hasOtherInstances() {
932
- // 简单的检查方式,可以通过全局计数器实现
933
- if (typeof window !== 'undefined') {
934
- return window.sliderCaptchaInstanceCount > 1
935
- }
936
- return false
937
- }
938
-
939
901
  static create(options) {
940
902
  return new PopupSliderCaptcha(options)
941
903
  }
@@ -970,7 +932,6 @@ class PasswordValidator {
970
932
  static CONSTANTS = {
971
933
  DEFAULT_TIMEOUT: 10000,
972
934
  CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存
973
- MAX_RETRY_ATTEMPTS: 3,
974
935
  MIN_PASSWORD_LENGTH: 1
975
936
  }
976
937
 
@@ -990,7 +951,6 @@ class PasswordValidator {
990
951
  validateUrl: options.validateUrl || '/microservice/strongPassword/checkPassword',
991
952
  timeout: options.timeout || PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,
992
953
  headers: options.headers || {},
993
- retryAttempts: options.retryAttempts || PasswordValidator.CONSTANTS.MAX_RETRY_ATTEMPTS,
994
954
  cacheDuration: options.cacheDuration || PasswordValidator.CONSTANTS.CACHE_DURATION,
995
955
  ...options
996
956
  };
@@ -998,7 +958,6 @@ class PasswordValidator {
998
958
  // 缓存公钥,避免重复请求
999
959
  this.publicKeyCache = null;
1000
960
  this.publicKeyExpiry = null;
1001
- this.retryCount = 0;
1002
961
  this.abortController = null;
1003
962
  }
1004
963
 
@@ -1024,7 +983,6 @@ class PasswordValidator {
1024
983
  signal: this.abortController.signal
1025
984
  });
1026
985
 
1027
- // 优化:更灵活的成功判断
1028
986
  if (!this.isSuccessResponse(response)) {
1029
987
  throw new Error('获取公钥失败:' + (response.message || response.msg || '未知错误'))
1030
988
  }
@@ -1103,7 +1061,7 @@ class PasswordValidator {
1103
1061
  encryptPassword(password, publicKey) {
1104
1062
  try {
1105
1063
  // 优化:输入验证
1106
- if (!password || typeof password !== 'string') {
1064
+ if (!password) {
1107
1065
  throw new Error('密码不能为空')
1108
1066
  }
1109
1067
 
@@ -1111,7 +1069,7 @@ class PasswordValidator {
1111
1069
  throw new Error('密码长度不足')
1112
1070
  }
1113
1071
 
1114
- if (!publicKey || typeof publicKey !== 'string') {
1072
+ if (!publicKey) {
1115
1073
  throw new Error('公钥不能为空')
1116
1074
  }
1117
1075
 
@@ -1146,17 +1104,14 @@ class PasswordValidator {
1146
1104
  async validatePassword(password, userName, additionalData = {}) {
1147
1105
  try {
1148
1106
  // 优化:输入验证
1149
- if (!password || typeof password !== 'string') {
1107
+ if (!password ) {
1150
1108
  throw new Error('密码不能为空')
1151
1109
  }
1152
1110
 
1153
- if (!userName || typeof userName !== 'string') {
1111
+ if (!userName) {
1154
1112
  throw new Error('用户名不能为空')
1155
1113
  }
1156
1114
 
1157
- // 重置重试计数
1158
- this.retryCount = 0;
1159
-
1160
1115
  // 1. 获取公钥
1161
1116
  const publicKey = await this.getPublicKey();
1162
1117
 
@@ -1325,9 +1280,6 @@ class PasswordValidator {
1325
1280
  // 清除缓存
1326
1281
  this.clearCache();
1327
1282
 
1328
- // 重置状态
1329
- this.retryCount = 0;
1330
-
1331
1283
  // 调用销毁回调
1332
1284
  if (this.options.onDestroy) {
1333
1285
  this.options.onDestroy();
package/dist/index.min.js CHANGED
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jsencrypt")):"function"==typeof define&&define.amd?define(["exports","jsencrypt"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).SliderCaptchaSDK={},t.JSEncrypt)}(this,function(t,JSEncrypt){"use strict";function e(t){return new PasswordValidator(t)}async function s(t,e={},s={}){const i=new PasswordValidator(e),a=s.userName||"";return await i.validatePassword(t,a,s)}var i,a,r;class PopupSliderCaptcha{static DEFAULTS={width:320,height:155,sliderSize:38,maxRetries:3,timeout:3e4,apiUrl:"/api/captcha",verifyUrl:"/api/captcha/verify",throttleDelay:16,clickMaskClose:!1};static CSS_CLASSES={overlay:"slider-captcha-overlay",modal:"slider-captcha-modal",header:"slider-captcha-header",container:"slider-captcha-container",track:"slider-captcha-track",btn:"slider-captcha-btn",hint:"slider-captcha-hint",loading:"slider-captcha-loading",error:"slider-captcha-error"};static getStyles(){return":root{--sc-primary:#409eff;--sc-success:#67c23a;--sc-danger:#f56c6c;--sc-border:#e4e7eb;--sc-bg:linear-gradient(90deg, #f7f9fa 0%, #e8f4fd 100%);--sc-text:#333;--sc-text-light:#999;--sc-shadow:0 4px 20px rgba(0,0,0,.3);--sc-radius:8px;--sc-transition:.3s ease}.slider-captcha-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:9999;display:none;justify-content:center;align-items:center;opacity:0;transition:opacity var(--sc-transition)}.slider-captcha-overlay.show{opacity:1}.slider-captcha-modal{background:#fff;border-radius:var(--sc-radius);padding:20px;box-shadow:var(--sc-shadow);position:relative;max-width:90vw;max-height:90vh;transform:scale(.8) translateY(-20px);opacity:0;transition:all var(--sc-transition)}.slider-captcha-modal.show{transform:scale(1) translateY(0);opacity:1}.slider-captcha-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid var(--sc-border)}.slider-captcha-container{display:flex;align-items:center;position:relative;border-radius:4px;overflow:hidden;margin-bottom:15px;background:#837a7a;justify-content:center}.slider-captcha-track{width:100%;height:40px;line-height:40px;background:var(--sc-bg);border:1px solid var(--sc-border);border-radius:20px;position:relative;margin-bottom:15px;overflow:hidden}.slider-captcha-btn{width:38px;height:38px;background:#fff;border:1px solid #ccc;border-radius:50%;position:absolute;top:0;left:0;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,.1);transition:all var(--sc-transition);user-select:none;z-index:1}.slider-captcha-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;flex-direction:column;color:#666;font-size:14px;z-index:10;border-radius:4px}.slider-captcha-error{color:var(--sc-danger);font-size:12px;text-align:center;margin-top:10px;display:none}.slider-captcha-title{margin:0;font-size:16px;color:var(--sc-text)}.slider-captcha-close,.slider-captcha-refresh{background:none;border:none;cursor:pointer;color:var(--sc-text-light);padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all var(--sc-transition);position:relative;font-size:0}.slider-captcha-close::before,.slider-captcha-close::after{content:'';position:absolute;width:16px;height:2px;background-color:var(--sc-text-light);border-radius:1px;transition:all var(--sc-transition)}.slider-captcha-close::before{transform:rotate(45deg)}.slider-captcha-close::after{transform:rotate(-45deg)}.slider-captcha-close:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-close:hover::before,.slider-captcha-close:hover::after{background-color:var(--sc-danger)}.slider-captcha-refresh{margin-left:10px}.slider-captcha-refresh svg{width:20px;height:20px;fill:var(--sc-text-light);transition:all var(--sc-transition)}.slider-captcha-refresh:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-refresh:hover svg{fill:var(--sc-primary);transform:rotate(180deg)}.slider-captcha-floating-time{position:absolute;bottom:-40px;left:50%;transform:translateX(-50%);color:#fff;font-size:12px;white-space:nowrap;opacity:0;pointer-events:none;z-index:10;transition:all var(--sc-transition);background:#fff;padding:2px 15px;border-radius:10px}.slider-captcha-floating-time.show{opacity:1;transform:translateX(-50%) translateY(-45px)}.slider-captcha-floating-time.success{color:var(--sc-success)}.slider-captcha-floating-time.fail{color:var(--sc-danger)}.slider-captcha-bg{width:100%;height:100%;object-fit:cover;display:block}.slider-captcha-piece{position:absolute;top:0;left:0;cursor:pointer;transition:none;z-index:2}.slider-captcha-finger{position:absolute;top:50%;left:10px;transform:translateY(-50%);font-size:20px;animation:fingerSlide 2s ease-in-out infinite;pointer-events:none;z-index:1;opacity:.6}.slider-captcha-hint{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--sc-text-light);font-size:14px;pointer-events:none;z-index:1;transition:all var(--sc-transition)}.slider-captcha-header-buttons{display:flex;align-items:center}@keyframes fingerSlide{0%{left:10px;opacity:.6}50%{opacity:1}100%{left:calc(50% - 10px);opacity:.6}}"}static CONSTANTS={CACHE_DURATION:3e5,MAX_RETRY_ATTEMPTS:3,DEFAULT_TIMEOUT:3e4,ANIMATION_DURATION:300,FLOATING_TIME_DURATION:2500,THROTTLE_DELAY:16};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",IMAGE_LOAD_ERROR:"IMAGE_LOAD_ERROR",CAPTCHA_DATA_ERROR:"CAPTCHA_DATA_ERROR"};constructor(t={}){this.options={...PopupSliderCaptcha.DEFAULTS,...t,timeout:t.timeout||PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,maxRetries:t.maxRetries||PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,throttleDelay:t.throttleDelay||PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY},this.elements={},this.state=this.createInitialState(),this.captchaData=null,this.times=[],this.startTime=null,this.eventListeners=[],this.timers=new Set,this.rafId=null,this.cachedDimensions=null,this.imageCache=new Map,this.abortController=null,this.throttledHandleMove=this.throttle(t=>this.handleMove(t),this.options.throttleDelay);try{this.init()}catch(t){this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message)}}createInitialState(){return{isVisible:!1,isDragging:!1,currentX:0,startX:0,retryCount:0,isLoading:!1}}init(){this.injectStyles(),this.createElements(),this.bindEvents()}injectStyles(){if(document.querySelector("#slider-captcha-styles"))return;const t=document.createElement("style");t.id="slider-captcha-styles",t.textContent=PopupSliderCaptcha.getStyles(),document.head.appendChild(t)}createElements(){const{elements:t,options:e}=this;[["overlay","div",PopupSliderCaptcha.CSS_CLASSES.overlay],["modal","div",PopupSliderCaptcha.CSS_CLASSES.modal],["header","div",PopupSliderCaptcha.CSS_CLASSES.header],["title","h3","slider-captcha-title","安全验证"],["closeBtn","button","slider-captcha-close"],["refreshBtn","button","slider-captcha-refresh"],["container","div",PopupSliderCaptcha.CSS_CLASSES.container],["backgroundImg","img","slider-captcha-bg"],["sliderImg","img","slider-captcha-piece"],["loadingText","div",PopupSliderCaptcha.CSS_CLASSES.loading,"加载中..."],["floatingTime","div","slider-captcha-floating-time"],["track","div",PopupSliderCaptcha.CSS_CLASSES.track],["fingerAnimation","div","slider-captcha-finger","👉"],["btn","div",PopupSliderCaptcha.CSS_CLASSES.btn],["icon","div","","→"],["hint","div",PopupSliderCaptcha.CSS_CLASSES.hint,"向右滑动完成验证"],["error","div",PopupSliderCaptcha.CSS_CLASSES.error]].forEach(([e,s,i,a])=>{t[e]=this.createElement(s,i,a)}),t.container.style.cssText=`width:${e.width}px;height:${e.height}px`,t.refreshBtn.innerHTML='<svg viewBox="0 0 24 24"><path d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z"/></svg>',this.assembleDOM(),this.setInitialState()}createElement(t,e="",s=""){const i=document.createElement(t);return e&&(i.className=e),s&&(i.textContent=s),i}assembleDOM(){const{elements:t}=this,e=this.createElement("div","slider-captcha-header-buttons");e.append(t.refreshBtn,t.closeBtn),t.header.append(t.title,e),t.container.append(t.backgroundImg,t.sliderImg,t.loadingText,t.floatingTime),t.btn.appendChild(t.icon),t.track.append(t.fingerAnimation,t.btn,t.hint),t.modal.append(t.header,t.container,t.track,t.error),t.overlay.appendChild(t.modal),document.body.appendChild(t.overlay)}setInitialState(){Object.assign(this.elements.container.style,{display:"none"}),Object.assign(this.elements.track.style,{display:"none"})}bindEvents(){const{elements:t}=this;this.addEventListener(t.closeBtn,"click",()=>this.hide()),this.addEventListener(t.refreshBtn,"click",()=>this.refresh()),this.addEventListener(t.overlay,"click",e=>{e.target===t.overlay&&this.options.clickMaskClose&&this.hide()}),this.addEventListener(document,"keydown",t=>{"Escape"===t.key&&this.state.isVisible&&this.hide()}),this.addEventListener(document,"visibilitychange",()=>this.handleVisibilityChange()),this.bindSliderEvents()}bindSliderEvents(){const{elements:t}=this,e={start:t=>this.handleStart(t),move:this.throttledHandleMove,end:()=>this.handleEnd()};this.addEventListener(t.btn,"mousedown",e.start),this.addEventListener(t.btn,"touchstart",e.start),this.addEventListener(t.sliderImg,"mousedown",e.start),this.addEventListener(t.sliderImg,"touchstart",e.start),this.addEventListener(document,"mousemove",e.move,{passive:!1}),this.addEventListener(document,"touchmove",e.move,{passive:!1}),this.addEventListener(document,"mouseup",e.end),this.addEventListener(document,"touchend",e.end)}addEventListener(t,e,s,i={}){t&&"function"==typeof s&&(t.addEventListener(e,s,i),this.eventListeners.push({element:t,event:e,handler:s,options:i}))}removeAllEventListeners(){this.eventListeners.forEach(({element:t,event:e,handler:s,options:i})=>{try{t?.removeEventListener?.(e,s,i)}catch(t){}}),this.eventListeners.length=0}getDimensions(){if(!this.cachedDimensions){const t=this.elements.track.offsetWidth,e=this.elements.btn.offsetWidth;this.cachedDimensions={trackWidth:t,btnWidth:e,maxX:t-e}}return this.cachedDimensions}getPosition(){const{maxX:t}=this.getDimensions(),e=this.state.currentX/t;return Math.round(e*(this.options.width-this.options.sliderSize))}handleStart(t){!this.captchaData||this.state.isDragging||this.state.isLoading||(t.preventDefault(),this.state.isDragging=!0,this.state.startX=this.getClientX(t)-this.state.currentX,this.startTime=this.startTime||Date.now(),this.times=[{time:Date.now(),position:this.getPosition()}],this.setTransition(!1),this.updateUIState("dragging"),this.cachedDimensions=null)}handleMove(t){if(!this.state.isDragging)return;t.preventDefault();const e=this.getClientX(t)-this.state.startX,{maxX:s}=this.getDimensions();this.state.currentX=Math.max(0,Math.min(e,s)),this.times.push({time:Date.now(),position:this.getPosition()}),this.rafId&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(()=>this.updateSliderPosition())}handleEnd(){this.state.isDragging&&(this.times.push({time:Date.now(),position:this.getPosition()}),this.state.isDragging=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.verify())}handleVisibilityChange(){const t=document.hidden?"paused":"running";this.elements.fingerAnimation&&(this.elements.fingerAnimation.style.animationPlayState=t)}getClientX(t){return t.type.includes("touch")?t.touches[0].clientX:t.clientX}setTransition(t){const e=t?"all 0.3s ease":"none";requestAnimationFrame(()=>{this.elements.btn.style.transition=e,this.elements.sliderImg.style.transition=e})}updateUIState(t){const{elements:e}=this,s={dragging:()=>{e.hint.style.opacity="0",e.fingerAnimation.style.display="none"},success:()=>{Object.assign(e.btn.style,{background:"var(--sc-success)"}),Object.assign(e.icon.style,{innerHTML:"✓",color:"white"}),e.icon.innerHTML="✓"},fail:()=>{Object.assign(e.btn.style,{background:"var(--sc-danger)"}),Object.assign(e.icon.style,{innerHTML:"✗",color:"white"}),e.icon.innerHTML="✗"},reset:()=>{Object.assign(e.btn.style,{background:"white"}),Object.assign(e.icon.style,{color:"#666"}),e.icon.innerHTML="→",e.fingerAnimation.style.display="block",this.updateHintText("向右滑动完成验证","var(--sc-text-light)")},loading:()=>{e.hint.style.opacity="0",e.fingerAnimation.style.display="none",Object.assign(e.track.style,{pointerEvents:"none",opacity:"0.6"})}};s[t]&&requestAnimationFrame(()=>{s[t](),"loading"!==t&&Object.assign(e.track.style,{pointerEvents:"auto",opacity:"1"})})}updateHintText(t,e){requestAnimationFrame(()=>{Object.assign(this.elements.hint,{textContent:t}),Object.assign(this.elements.hint.style,{color:e,opacity:"1"})})}updateSliderPosition(){const{elements:t,options:e,state:s}=this,{maxX:i}=this.getDimensions(),a=s.currentX/i*(e.width-e.sliderSize),r=s.currentX/i;requestAnimationFrame(()=>{t.btn.style.transform=`translateX(${s.currentX}px)`,t.sliderImg.style.transform=`translateX(${a}px)`,t.fingerAnimation.style.opacity=.8>r?"0.6":"0"})}show(){this.state.isVisible=!0,this.elements.overlay.style.display="flex",requestAnimationFrame(()=>{this.elements.overlay.classList.add("show"),this.elements.modal.classList.add("show")}),this.loadCaptcha()}hide(){this.state.isVisible=!1,this.elements.overlay.classList.remove("show"),this.elements.modal.classList.remove("show"),this.safeSetTimeout(()=>{this.elements.overlay.style.display="none",document.body.removeChild(this.elements.overlay),this.reset(),this.options.onClose?.(),this.elements.overlay=null},300)}handleError(t,e,s=null){const i={[PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR]:"验证失败,请重试",[PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR]:"图片加载失败,请刷新重试",[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]:"验证码数据错误,请刷新重试"}[t]||e||"未知错误";this.options.onError&&this.options.onError({type:t,message:i,originalError:s}),this.showError(i)}async loadCaptcha(){try{this.showLoading(),this.startTime=Date.now(),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.apiUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({place:2,timestamp:Date.now(),...this.options.requestData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const e=await t.json();if(!this.validateCaptchaData(e))throw Error("验证码数据格式错误");this.captchaData=e.data,this.showCaptcha(),await this.renderCaptcha()}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR,t.message,t)}}validateCaptchaData(t){if(!t||"object"!=typeof t)return!1;const e=t.data||t;return["canvasSrc","blockSrc","canvasWidth","canvasHeight","blockWidth","blockHeight","blockY","nonceStr"].every(t=>{const s=e[t];return null!=s&&""!==s})}async renderCaptcha(){return new Promise((t,e)=>{let s=!1;const i=[this.loadImageAsync(this.elements.backgroundImg,this.captchaData.canvasSrc,{width:this.captchaData.canvasWidth,height:this.captchaData.canvasHeight}),this.loadImageAsync(this.elements.sliderImg,this.captchaData.blockSrc,{width:this.captchaData.blockWidth,height:this.captchaData.blockHeight,top:this.captchaData.blockY})];Promise.all(i).then(()=>{s||(this.hideLoading(),t())}).catch(t=>{s||(s=!0,this.handleError(PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR,"图片加载失败",t),e(t))})})}async loadImageAsync(t,e,s){return new Promise((i,a)=>{if(this.imageCache.has(e)){const a=this.imageCache.get(e);return t.src=a.src,this.applyStyles(t,s),void i()}const r=this.safeSetTimeout(()=>{a(Error("图片加载超时"))},1e4);t.onload=()=>{this.safeClearTimeout(r),this.imageCache.set(e,t.cloneNode()),i()},t.onerror=t=>{this.safeClearTimeout(r),a(t)},t.src=e,this.applyStyles(t,s)})}loadImage(t,e,s,i,a){if(this.imageCache.has(e)){const a=this.imageCache.get(e);return t.src=a.src,this.applyStyles(t,s),void i()}t.onload=()=>{this.imageCache.set(e,t.cloneNode()),i()},t.onerror=a,t.src=e,this.applyStyles(t,s)}applyStyles(t,e){Object.entries(e).forEach(([e,s])=>{t.style[e]="number"==typeof s?s+"px":s})}showLoading(){this.state.isLoading=!0,this.batchUpdateStyles({container:{display:"block"},loadingText:{display:"flex"},error:{display:"none"}}),this.updateUIState("loading")}hideLoading(){this.state.isLoading=!1,this.batchUpdateStyles({loadingText:{display:"none"}}),this.updateUIState("reset")}showCaptcha(){this.batchUpdateStyles({container:{display:"block"},track:{display:"block"},error:{display:"none"}})}showError(t){this.hideLoading(),this.batchUpdateStyles({error:{display:"block",textContent:t}})}batchUpdateStyles(t){requestAnimationFrame(()=>{Object.entries(t).forEach(([t,e])=>{const s=this.elements[t];s&&Object.entries(e).forEach(([t,e])=>{"textContent"===t?s.textContent=e:s.style[t]=e})})})}async verify(){if(this.captchaData)try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.verifyUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({loginVo:{nonceStr:this.captchaData.nonceStr,value:this.getPosition()},dragEventList:[...this.times],...this.options.verifyData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const e=await t.json();this.isVerifySuccess(e)?this.onVerifySuccess(e.data||e.result):this.onVerifyFail(e.message||e.msg||"验证失败,请重试!")}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"验证请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message,t)}else this.onVerifyFail("验证码数据丢失")}isVerifySuccess(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some(t=>!0===t)}onVerifySuccess(t){const e=Date.now()-this.startTime,s=`验证成功!耗时:${(e/1e3).toFixed(2)}s`;this.updateUIState("success"),this.showFloatingTime(s,"success"),this.safeSetTimeout(()=>{this.options.onSuccess?.({ticket:t,timestamp:Date.now(),duration:e}),this.hide()},2e3)}showFloatingTime(t,e="success"){const{elements:s}=this;s.floatingTime.textContent=t,s.floatingTime.className="slider-captcha-floating-time "+e,this.safeSetTimeout(()=>s.floatingTime.classList.add("show"),100),this.safeSetTimeout(()=>{s.floatingTime.className="slider-captcha-floating-time"},2500)}onVerifyFail(t){this.state.retryCount++,this.updateUIState("fail"),this.showFloatingTime(t,"fail"),this.safeSetTimeout(()=>{this.state.retryCount<this.options.maxRetries?this.reset():this.refresh()},2500)}reset(){this.clearAllTimers(),Object.assign(this.state,{isDragging:!1,currentX:0,startX:0,isLoading:!1}),this.times=[],this.startTime=null,this.cachedDimensions=null,requestAnimationFrame(()=>{this.setTransition(!0),this.elements.btn.style.transform="translateX(0px)",this.elements.sliderImg.style.transform="translateX(0px)",this.updateUIState("reset"),this.elements.error.style.display="none"})}refresh(){this.reset(),this.state.retryCount=0,this.loadCaptcha()}safeSetTimeout(t,e){const s=setTimeout(()=>{this.timers.delete(s),t()},e);return this.timers.add(s),s}safeClearTimeout(t){t&&(clearTimeout(t),this.timers.delete(t))}clearAllTimers(){this.timers.forEach(t=>{clearTimeout(t),clearInterval(t)}),this.timers.clear(),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}cleanupImages(){this.elements.backgroundImg&&(this.elements.backgroundImg.src="",this.elements.backgroundImg.onload=null,this.elements.backgroundImg.onerror=null),this.elements.sliderImg&&(this.elements.sliderImg.src="",this.elements.sliderImg.onload=null,this.elements.sliderImg.onerror=null),this.imageCache.clear()}throttle(t,e){let s=0;return function(...i){const a=Date.now();if(a-s>=e)return s=a,t.apply(this,i)}}debounce(t,e){let s;return function(...i){clearTimeout(s),s=setTimeout(()=>t.apply(this,i),e)}}destroy(){try{this.abortController&&(this.abortController.abort(),this.abortController=null),document.body&&(document.body.style.userSelect="",document.body.style.cursor=""),this.clearAllTimers(),this.removeAllEventListeners(),this.cleanupImages(),this.elements?.overlay?.parentNode&&this.elements.overlay.parentNode.removeChild(this.elements.overlay);const t=document.getElementById("slider-captcha-styles");t&&!this.hasOtherInstances()&&t.remove(),this.imageCache.clear(),this.cachedDimensions=null,Object.keys(this).forEach(t=>{"constructor"!==t&&(this[t]=null)}),this.options.onDestroy&&this.options.onDestroy()}catch(t){}}hasOtherInstances(){return"undefined"!=typeof window&&window.sliderCaptchaInstanceCount>1}static create(t){return new PopupSliderCaptcha(t)}static show(t){const e=new PopupSliderCaptcha(t);return e.show(),e}}"undefined"!=typeof module&&module.exports?(module.exports=PopupSliderCaptcha,module.exports.default=PopupSliderCaptcha):"function"==typeof define&&define.amd?define([],()=>PopupSliderCaptcha):"undefined"!=typeof window&&(window.PopupSliderCaptcha=PopupSliderCaptcha,window.SliderCaptcha=PopupSliderCaptcha),i=PopupSliderCaptcha;class PasswordValidator{static CONSTANTS={DEFAULT_TIMEOUT:1e4,CACHE_DURATION:3e5,MAX_RETRY_ATTEMPTS:3,MIN_PASSWORD_LENGTH:1};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",ENCRYPTION_ERROR:"ENCRYPTION_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PUBLIC_KEY_ERROR:"PUBLIC_KEY_ERROR"};constructor(t={}){this.options={publicKeyUrl:t.publicKeyUrl||"/microservice/strongPassword/getPublicKey",validateUrl:t.validateUrl||"/microservice/strongPassword/checkPassword",timeout:t.timeout||PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,headers:t.headers||{},retryAttempts:t.retryAttempts||PasswordValidator.CONSTANTS.MAX_RETRY_ATTEMPTS,cacheDuration:t.cacheDuration||PasswordValidator.CONSTANTS.CACHE_DURATION,...t},this.publicKeyCache=null,this.publicKeyExpiry=null,this.retryCount=0,this.abortController=null}async getPublicKey(){if(this.publicKeyCache&&this.publicKeyExpiry&&Date.now()<this.publicKeyExpiry)return this.publicKeyCache;try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await this.makeRequest(this.options.publicKeyUrl,{method:"GET",signal:this.abortController.signal});if(!this.isSuccessResponse(t))throw Error("获取公钥失败:"+(t.message||t.msg||"未知错误"));const e=t.data||t.result;if(!this.validatePublicKey(e))throw Error("公钥格式无效");return this.publicKeyCache=e,this.publicKeyExpiry=Date.now()+this.options.cacheDuration,this.publicKeyCache}catch(t){throw this.handleError(PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR,t.message,t),Error("获取公钥失败: "+t.message)}}isSuccessResponse(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some(t=>!0===t)}validatePublicKey(t){return!(!t||"string"!=typeof t)}handleError(t,e,s=null){const i={[PasswordValidator.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR]:"密码加密失败",[PasswordValidator.ERROR_TYPES.VALIDATION_ERROR]:"密码验证失败",[PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR]:"获取公钥失败"}[t]||e||"未知错误";this.options.onError&&this.options.onError({type:t,message:i,originalError:s})}encryptPassword(t,e){try{if(!t||"string"!=typeof t)throw Error("密码不能为空");if(t.length<PasswordValidator.CONSTANTS.MIN_PASSWORD_LENGTH)throw Error("密码长度不足");if(!e||"string"!=typeof e)throw Error("公钥不能为空");if(void 0===JSEncrypt)throw Error("JSEncrypt库未正确加载,请确保已引入JSEncrypt库");const s=new JSEncrypt;s.setPublicKey(e);const i=s.encrypt(t);if(!i)throw Error("密码加密失败,可能是公钥格式不正确");return i}catch(t){throw this.handleError(PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR,t.message,t),Error("密码加密失败: "+t.message)}}async validatePassword(t,e,s={}){try{if(!t||"string"!=typeof t)throw Error("密码不能为空");if(!e||"string"!=typeof e)throw Error("用户名不能为空");this.retryCount=0;const i=await this.getPublicKey(),a=this.encryptPassword(t,i),r=await this.makeRequest(this.options.validateUrl,{method:"POST",body:JSON.stringify({userName:e,password:a,timestamp:Date.now(),...s})});return{success:this.isSuccessResponse(r),data:r.data||r.result,message:r.message||r.msg||"验证完成",code:r.code||r.status,originalResponse:r}}catch(t){let e=PasswordValidator.ERROR_TYPES.VALIDATION_ERROR;return t.message.includes("网络")||t.message.includes("fetch")?e=PasswordValidator.ERROR_TYPES.NETWORK_ERROR:t.message.includes("超时")||t.message.includes("timeout")?e=PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR:t.message.includes("加密")&&(e=PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR),this.handleError(e,t.message,t),{success:!1,message:t.message||"密码校验失败",code:e,error:t}}}async makeRequest(t,e={}){this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const s=setTimeout(()=>{this.abortController.abort()},this.options.timeout);try{const i=await fetch(t,{...e,headers:{"Content-Type":"application/json",...this.options.headers,...e.headers},signal:this.abortController.signal});if(clearTimeout(s),!i.ok)throw Error(`HTTP错误: ${i.status} ${i.statusText}`);const a=await i.json();return this.options.debug,a}catch(t){if(clearTimeout(s),"AbortError"===t.name)throw this.handleError(PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR,"请求超时",t),Error("请求超时");if(t.message.includes("Failed to fetch")||t.message.includes("NetworkError"))throw this.handleError(PasswordValidator.ERROR_TYPES.NETWORK_ERROR,"网络连接失败",t),Error("网络连接失败");throw t}}clearCache(){this.publicKeyCache=null,this.publicKeyExpiry=null}updateOptions(t){this.options={...this.options,...t},this.clearCache()}cancelRequest(){this.abortController&&(this.abortController.abort(),this.abortController=null)}isRequesting(){return this.abortController&&!this.abortController.signal.aborted}getCacheStatus(){return{hasCache:!!this.publicKeyCache,isExpired:!this.publicKeyExpiry||Date.now()>this.publicKeyExpiry,expiryTime:this.publicKeyExpiry,remainingTime:this.publicKeyExpiry?Math.max(0,this.publicKeyExpiry-Date.now()):0}}destroy(){try{this.cancelRequest(),this.clearCache(),this.retryCount=0,this.options.onDestroy&&this.options.onDestroy()}catch(t){}}}r={PopupSliderCaptcha:i,PasswordValidator:a=PasswordValidator,createPasswordValidator:e,validatePassword:s},"undefined"!=typeof window&&(window.SliderCaptcha=i,window.PopupSliderCaptcha=i,window.PasswordValidator=a,window.createPasswordValidator=e,window.validatePassword=s),t.PasswordValidator=a,t.PopupSliderCaptcha=i,t.createPasswordValidator=e,t.default=r,t.validatePassword=s,Object.defineProperty(t,"__esModule",{value:!0})});
1
+ var t,i;t=this,i=function(t,JSEncrypt){"use strict";function i(t){return new PasswordValidator(t)}async function e(t,i={},e={}){const s=new PasswordValidator(i),r=e.userName||"";return await s.validatePassword(t,r,e)}var s,r,a;class PopupSliderCaptcha{static DEFAULTS={width:320,height:155,sliderSize:38,maxRetries:3,timeout:3e4,apiUrl:"/api/captcha",verifyUrl:"/api/captcha/verify",throttleDelay:16,clickMaskClose:!1};static CSS_CLASSES={overlay:"slider-captcha-overlay",modal:"slider-captcha-modal",header:"slider-captcha-header",container:"slider-captcha-container",track:"slider-captcha-track",btn:"slider-captcha-btn",hint:"slider-captcha-hint",loading:"slider-captcha-loading",error:"slider-captcha-error"};static getStyles(){return":root{--sc-primary:#409eff;--sc-success:#67c23a;--sc-danger:#f56c6c;--sc-border:#e4e7eb;--sc-bg:linear-gradient(90deg, #f7f9fa 0%, #e8f4fd 100%);--sc-text:#333;--sc-text-light:#999;--sc-shadow:0 4px 20px rgba(0,0,0,.3);--sc-radius:8px;--sc-transition:.3s ease}.slider-captcha-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:9999;display:none;justify-content:center;align-items:center;opacity:0;transition:opacity var(--sc-transition)}.slider-captcha-overlay.show{opacity:1}.slider-captcha-modal{background:#fff;border-radius:var(--sc-radius);padding:20px;box-shadow:var(--sc-shadow);position:relative;max-width:90vw;max-height:90vh;transform:scale(.8) translateY(-20px);opacity:0;transition:all var(--sc-transition)}.slider-captcha-modal.show{transform:scale(1) translateY(0);opacity:1}.slider-captcha-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid var(--sc-border)}.slider-captcha-container{display:flex;align-items:center;position:relative;border-radius:4px;overflow:hidden;margin-bottom:15px;background:#837a7a;justify-content:center}.slider-captcha-track{width:100%;height:40px;line-height:40px;background:var(--sc-bg);border:1px solid var(--sc-border);border-radius:20px;position:relative;margin-bottom:15px;overflow:hidden}.slider-captcha-btn{width:38px;height:38px;background:#fff;border:1px solid #ccc;border-radius:50%;position:absolute;top:0;left:0;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,.1);transition:all var(--sc-transition);user-select:none;z-index:1}.slider-captcha-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;flex-direction:column;color:#666;font-size:14px;z-index:10;border-radius:4px}.slider-captcha-error{color:var(--sc-danger);font-size:12px;text-align:center;margin-top:10px;display:none}.slider-captcha-title{margin:0;font-size:16px;color:var(--sc-text)}.slider-captcha-close,.slider-captcha-refresh{background:none;border:none;cursor:pointer;color:var(--sc-text-light);padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all var(--sc-transition);position:relative;font-size:0}.slider-captcha-close::before,.slider-captcha-close::after{content:'';position:absolute;width:16px;height:2px;background-color:var(--sc-text-light);border-radius:1px;transition:all var(--sc-transition)}.slider-captcha-close::before{transform:rotate(45deg)}.slider-captcha-close::after{transform:rotate(-45deg)}.slider-captcha-close:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-close:hover::before,.slider-captcha-close:hover::after{background-color:var(--sc-danger)}.slider-captcha-refresh{margin-left:10px}.slider-captcha-refresh svg{width:20px;height:20px;fill:var(--sc-text-light);transition:all var(--sc-transition)}.slider-captcha-refresh:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-refresh:hover svg{fill:var(--sc-primary);transform:rotate(180deg)}.slider-captcha-floating-time{position:absolute;bottom:-40px;left:50%;transform:translateX(-50%);color:#fff;font-size:12px;white-space:nowrap;opacity:0;pointer-events:none;z-index:10;transition:all var(--sc-transition);background:#fff;padding:2px 15px;border-radius:10px}.slider-captcha-floating-time.show{opacity:1;transform:translateX(-50%) translateY(-45px)}.slider-captcha-floating-time.success{color:var(--sc-success)}.slider-captcha-floating-time.fail{color:var(--sc-danger)}.slider-captcha-bg{width:100%;height:100%;object-fit:cover;display:block}.slider-captcha-piece{position:absolute;top:0;left:0;cursor:pointer;transition:none;z-index:2}.slider-captcha-finger{position:absolute;top:50%;left:10px;transform:translateY(-50%);font-size:20px;animation:fingerSlide 2s ease-in-out infinite;pointer-events:none;z-index:1;opacity:.6}.slider-captcha-hint{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--sc-text-light);font-size:14px;pointer-events:none;z-index:1;transition:all var(--sc-transition)}.slider-captcha-header-buttons{display:flex;align-items:center}@keyframes fingerSlide{0%{left:10px;opacity:.6}50%{opacity:1}100%{left:calc(50% - 10px);opacity:.6}}"}static CONSTANTS={CACHE_DURATION:3e5,MAX_RETRY_ATTEMPTS:3,DEFAULT_TIMEOUT:3e4,FLOATING_TIME_DURATION:2500,THROTTLE_DELAY:16};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",IMAGE_LOAD_ERROR:"IMAGE_LOAD_ERROR",CAPTCHA_DATA_ERROR:"CAPTCHA_DATA_ERROR"};constructor(t={}){this.options={...PopupSliderCaptcha.DEFAULTS,...t,timeout:t.timeout||PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,maxRetries:t.maxRetries||PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,throttleDelay:t.throttleDelay||PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY},this.elements={},this.state=this.createInitialState(),this.captchaData=null,this.times=[],this.startTime=null,this.eventListeners=[],this.timers=new Set,this.rafId=null,this.cachedDimensions=null,this.imageCache=new Map,this.abortController=null,this.throttledHandleMove=this.throttle((t=>this.handleMove(t)),this.options.throttleDelay);try{this.init()}catch(t){void 0,this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message)}}createInitialState(){return{isVisible:!1,isDragging:!1,currentX:0,startX:0,retryCount:0,isLoading:!1}}init(){this.injectStyles(),this.createElements(),this.bindEvents()}injectStyles(){if(document.querySelector("#slider-captcha-styles"))return;const t=document.createElement("style");t.id="slider-captcha-styles",t.textContent=PopupSliderCaptcha.getStyles(),document.head.appendChild(t)}createElements(){const{elements:t,options:i}=this;[["overlay","div",PopupSliderCaptcha.CSS_CLASSES.overlay],["modal","div",PopupSliderCaptcha.CSS_CLASSES.modal],["header","div",PopupSliderCaptcha.CSS_CLASSES.header],["title","h3","slider-captcha-title","安全验证"],["closeBtn","button","slider-captcha-close"],["refreshBtn","button","slider-captcha-refresh"],["container","div",PopupSliderCaptcha.CSS_CLASSES.container],["backgroundImg","img","slider-captcha-bg"],["sliderImg","img","slider-captcha-piece"],["loadingText","div",PopupSliderCaptcha.CSS_CLASSES.loading,"加载中..."],["floatingTime","div","slider-captcha-floating-time"],["track","div",PopupSliderCaptcha.CSS_CLASSES.track],["fingerAnimation","div","slider-captcha-finger","👉"],["btn","div",PopupSliderCaptcha.CSS_CLASSES.btn],["icon","div","","→"],["hint","div",PopupSliderCaptcha.CSS_CLASSES.hint,"向右滑动完成验证"],["error","div",PopupSliderCaptcha.CSS_CLASSES.error]].forEach((([i,e,s,r])=>{t[i]=this.createElement(e,s,r)})),t.container.style.cssText=`width:${i.width}px;height:${i.height}px`,t.refreshBtn.innerHTML='<svg viewBox="0 0 24 24"><path d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z"/></svg>',this.assembleDOM(),this.setInitialState()}createElement(t,i="",e=""){const s=document.createElement(t);return i&&(s.className=i),e&&(s.textContent=e),s}assembleDOM(){const{elements:t}=this,i=this.createElement("div","slider-captcha-header-buttons");i.append(t.refreshBtn,t.closeBtn),t.header.append(t.title,i),t.container.append(t.backgroundImg,t.sliderImg,t.loadingText,t.floatingTime),t.btn.appendChild(t.icon),t.track.append(t.fingerAnimation,t.btn,t.hint),t.modal.append(t.header,t.container,t.track,t.error),t.overlay.appendChild(t.modal),document.body.appendChild(t.overlay)}setInitialState(){Object.assign(this.elements.container.style,{display:"none"}),Object.assign(this.elements.track.style,{display:"none"})}bindEvents(){const{elements:t}=this;this.addEventListener(t.closeBtn,"click",(()=>this.hide())),this.addEventListener(t.refreshBtn,"click",(()=>this.refresh())),this.addEventListener(t.overlay,"click",(i=>{i.target===t.overlay&&this.options.clickMaskClose&&this.hide()})),this.addEventListener(document,"keydown",(t=>{"Escape"===t.key&&this.state.isVisible&&this.hide()})),this.addEventListener(document,"visibilitychange",(()=>this.handleVisibilityChange())),this.bindSliderEvents()}bindSliderEvents(){const{elements:t}=this,i={start:t=>this.handleStart(t),move:this.throttledHandleMove,end:()=>this.handleEnd()};this.addEventListener(t.btn,"mousedown",i.start),this.addEventListener(t.btn,"touchstart",i.start),this.addEventListener(t.sliderImg,"mousedown",i.start),this.addEventListener(t.sliderImg,"touchstart",i.start),this.addEventListener(document,"mousemove",i.move,{passive:!1}),this.addEventListener(document,"touchmove",i.move,{passive:!1}),this.addEventListener(document,"mouseup",i.end),this.addEventListener(document,"touchend",i.end)}addEventListener(t,i,e,s={}){t&&"function"==typeof e&&(t.addEventListener(i,e,s),this.eventListeners.push({element:t,event:i,handler:e,options:s}))}removeAllEventListeners(){this.eventListeners.forEach((({element:t,event:i,handler:e,options:s})=>{try{t?.removeEventListener?.(i,e,s)}catch(t){void 0}})),this.eventListeners.length=0}getDimensions(){if(!this.cachedDimensions){const t=this.elements.track.offsetWidth,i=this.elements.btn.offsetWidth;this.cachedDimensions={trackWidth:t,btnWidth:i,maxX:t-i}}return this.cachedDimensions}getPosition(){const{maxX:t}=this.getDimensions(),i=this.state.currentX/t;return Math.round(i*(this.options.width-this.options.sliderSize))}handleStart(t){!this.captchaData||this.state.isDragging||this.state.isLoading||(t.preventDefault(),this.state.isDragging=!0,this.state.startX=this.getClientX(t)-this.state.currentX,this.dragStartTime=Date.now(),this.times=[{time:Date.now(),position:this.getPosition()}],this.setTransition(!1),this.updateUIState("dragging"),this.cachedDimensions=null)}handleMove(t){if(!this.state.isDragging)return;t.preventDefault();const i=this.getClientX(t)-this.state.startX,{maxX:e}=this.getDimensions();this.state.currentX=Math.max(0,Math.min(i,e)),this.times.push({time:Date.now(),position:this.getPosition()}),this.rafId&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame((()=>this.updateSliderPosition()))}handleEnd(){this.state.isDragging&&(this.times.push({time:Date.now(),position:this.getPosition()}),this.state.isDragging=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.verify())}handleVisibilityChange(){const t=document.hidden?"paused":"running";this.elements.fingerAnimation&&(this.elements.fingerAnimation.style.animationPlayState=t)}getClientX(t){return t.type.includes("touch")?t.touches[0].clientX:t.clientX}setTransition(t){const i=t?"all 0.3s ease":"none";requestAnimationFrame((()=>{this.elements.btn.style.transition=i,this.elements.sliderImg.style.transition=i}))}updateUIState(t){const{elements:i}=this,e={dragging:()=>{i.hint.style.opacity="0",i.fingerAnimation.style.display="none"},success:()=>{Object.assign(i.btn.style,{background:"var(--sc-success)"}),Object.assign(i.icon.style,{innerHTML:"✓",color:"white"}),i.icon.innerHTML="✓"},fail:()=>{Object.assign(i.btn.style,{background:"var(--sc-danger)"}),Object.assign(i.icon.style,{innerHTML:"✗",color:"white"}),i.icon.innerHTML="✗"},reset:()=>{Object.assign(i.btn.style,{background:"white"}),Object.assign(i.icon.style,{color:"#666"}),i.icon.innerHTML="→",i.fingerAnimation.style.display="block",this.updateHintText("向右滑动完成验证","var(--sc-text-light)")},loading:()=>{i.hint.style.opacity="0",i.fingerAnimation.style.display="none",Object.assign(i.track.style,{pointerEvents:"none",opacity:"0.6"})}};e[t]&&requestAnimationFrame((()=>{e[t](),"loading"!==t&&Object.assign(i.track.style,{pointerEvents:"auto",opacity:"1"})}))}updateHintText(t,i){requestAnimationFrame((()=>{Object.assign(this.elements.hint,{textContent:t}),Object.assign(this.elements.hint.style,{color:i,opacity:"1"})}))}updateSliderPosition(){const{elements:t,options:i,state:e}=this,{maxX:s}=this.getDimensions(),r=e.currentX/s*(i.width-i.sliderSize),a=e.currentX/s;requestAnimationFrame((()=>{t.btn.style.transform=`translateX(${e.currentX}px)`,t.sliderImg.style.transform=`translateX(${r}px)`,t.fingerAnimation.style.opacity=.8>a?"0.6":"0"}))}show(){this.state.isVisible=!0,this.elements.overlay.style.display="flex",requestAnimationFrame((()=>{this.elements.overlay.classList.add("show"),this.elements.modal.classList.add("show")})),this.loadCaptcha()}hide(){this.state.isVisible=!1,this.elements.overlay.classList.remove("show"),this.elements.modal.classList.remove("show"),this.safeSetTimeout((()=>{this.elements.overlay.style.display="none",document.body.removeChild(this.elements.overlay),this.reset(),this.options.onClose?.(),this.elements.overlay=null}),300)}handleError(t,i,e=null){const s={[PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR]:"验证失败,请重试",[PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR]:"图片加载失败,请刷新重试",[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]:"验证码数据错误,请刷新重试"}[t]||i||"未知错误";this.options.onError&&this.options.onError({type:t,message:s,originalError:e}),this.showError(s)}async loadCaptcha(){try{this.showLoading(),this.startTime=Date.now(),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.apiUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({timestamp:Date.now(),...this.options.requestData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const i=await t.json();if(!this.validateCaptchaData(i))throw Error("验证码数据格式错误");this.captchaData=i.data,this.showCaptcha(),await this.renderCaptcha()}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR,t.message,t)}}validateCaptchaData(t){if(!t||"object"!=typeof t)return!1;const i=t.data||t;return["canvasSrc","blockSrc","canvasWidth","canvasHeight","blockWidth","blockHeight","blockY","nonceStr"].every((t=>{const e=i[t];return null!=e&&""!==e}))}async renderCaptcha(){return new Promise(((t,i)=>{let e=!1;const s=[this.loadImageAsync(this.elements.backgroundImg,this.captchaData.canvasSrc,{width:this.captchaData.canvasWidth,height:this.captchaData.canvasHeight}),this.loadImageAsync(this.elements.sliderImg,this.captchaData.blockSrc,{width:this.captchaData.blockWidth,height:this.captchaData.blockHeight,top:this.captchaData.blockY})];Promise.all(s).then((()=>{e||(this.hideLoading(),t())})).catch((t=>{e||(e=!0,this.handleError(PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR,"图片加载失败",t),i(t))}))}))}async loadImageAsync(t,i,e){return new Promise(((s,r)=>{if(this.imageCache.has(i)){const r=this.imageCache.get(i);return t.src=r.src,this.applyStyles(t,e),s(),void 0}const a=this.safeSetTimeout((()=>{r(Error("图片加载超时"))}),1e4);t.onload=()=>{this.safeClearTimeout(a),this.imageCache.set(i,t.cloneNode()),s()},t.onerror=t=>{this.safeClearTimeout(a),r(t)},t.src=i,this.applyStyles(t,e)}))}applyStyles(t,i){Object.entries(i).forEach((([i,e])=>{t.style[i]="number"==typeof e?e+"px":e}))}showLoading(){this.state.isLoading=!0,this.batchUpdateStyles({container:{display:"block"},loadingText:{display:"flex"},error:{display:"none"}}),this.updateUIState("loading")}hideLoading(){this.state.isLoading=!1,this.batchUpdateStyles({loadingText:{display:"none"}}),this.updateUIState("reset")}showCaptcha(){this.batchUpdateStyles({container:{display:"block"},track:{display:"block"},error:{display:"none"}})}showError(t){this.hideLoading(),this.batchUpdateStyles({error:{display:"block",textContent:t}})}batchUpdateStyles(t){requestAnimationFrame((()=>{Object.entries(t).forEach((([t,i])=>{const e=this.elements[t];e&&Object.entries(i).forEach((([t,i])=>{"textContent"===t?e.textContent=i:e.style[t]=i}))}))}))}async verify(){if(!this.captchaData)return this.onVerifyFail("验证码数据丢失"),void 0;try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.verifyUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({loginVo:{nonceStr:this.captchaData.nonceStr,value:this.getPosition()},dragEventList:[...this.times],...this.options.verifyData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const i=await t.json();this.isVerifySuccess(i)?this.onVerifySuccess(i.data||i.result):this.onVerifyFail(i.message||i.msg||"验证失败,请重试!")}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"验证请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message,t)}}isVerifySuccess(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some((t=>!0===t))}onVerifySuccess(t){const i=this.dragStartTime?Date.now()-this.dragStartTime:Date.now()-this.startTime,e=`验证成功!耗时:${(i/1e3).toFixed(2)}s`;this.updateUIState("success"),this.showFloatingTime(e,"success"),this.safeSetTimeout((()=>{this.options.onSuccess?.({ticket:t,timestamp:Date.now(),duration:i}),this.hide()}),2e3)}showFloatingTime(t,i="success"){const{elements:e}=this;e.floatingTime.textContent=t,e.floatingTime.className="slider-captcha-floating-time "+i,this.safeSetTimeout((()=>e.floatingTime.classList.add("show")),100),this.safeSetTimeout((()=>{e.floatingTime.className="slider-captcha-floating-time"}),2500)}onVerifyFail(t){this.state.retryCount++,this.updateUIState("fail"),this.showFloatingTime(t,"fail"),this.safeSetTimeout((()=>{this.state.retryCount<this.options.maxRetries?this.reset():this.refresh()}),2500)}reset(){this.clearAllTimers(),Object.assign(this.state,{isDragging:!1,currentX:0,startX:0,isLoading:!1}),this.times=[],this.startTime=null,this.dragStartTime=null,this.cachedDimensions=null,requestAnimationFrame((()=>{this.setTransition(!0),this.elements.btn.style.transform="translateX(0px)",this.elements.sliderImg.style.transform="translateX(0px)",this.updateUIState("reset"),this.elements.error.style.display="none"}))}refresh(){this.reset(),this.state.retryCount=0,this.loadCaptcha()}safeSetTimeout(t,i){const e=setTimeout((()=>{this.timers.delete(e),t()}),i);return this.timers.add(e),e}safeClearTimeout(t){t&&(clearTimeout(t),this.timers.delete(t))}clearAllTimers(){this.timers.forEach((t=>{clearTimeout(t),clearInterval(t)})),this.timers.clear(),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}cleanupImages(){this.elements.backgroundImg&&(this.elements.backgroundImg.src="",this.elements.backgroundImg.onload=null,this.elements.backgroundImg.onerror=null),this.elements.sliderImg&&(this.elements.sliderImg.src="",this.elements.sliderImg.onload=null,this.elements.sliderImg.onerror=null),this.imageCache.clear()}throttle(t,i){let e=0;return function(...s){const r=Date.now();if(r-e>=i)return e=r,t.apply(this,s)}}destroy(){try{this.abortController&&(this.abortController.abort(),this.abortController=null),document.body&&(document.body.style.userSelect="",document.body.style.cursor=""),this.clearAllTimers(),this.removeAllEventListeners(),this.cleanupImages(),this.elements?.overlay?.parentNode&&this.elements.overlay.parentNode.removeChild(this.elements.overlay);const t=document.getElementById("slider-captcha-styles");t&&t.remove(),this.imageCache.clear(),this.cachedDimensions=null,Object.keys(this).forEach((t=>{"constructor"!==t&&(this[t]=null)})),this.options.onDestroy&&this.options.onDestroy()}catch(t){void 0}}static create(t){return new PopupSliderCaptcha(t)}static show(t){const i=new PopupSliderCaptcha(t);return i.show(),i}}"undefined"!=typeof module&&module.exports?(module.exports=PopupSliderCaptcha,module.exports.default=PopupSliderCaptcha):"function"==typeof define&&define.amd?define([],(()=>PopupSliderCaptcha)):"undefined"!=typeof window&&(window.PopupSliderCaptcha=PopupSliderCaptcha,window.SliderCaptcha=PopupSliderCaptcha),s=PopupSliderCaptcha;class PasswordValidator{static CONSTANTS={DEFAULT_TIMEOUT:1e4,CACHE_DURATION:3e5,MIN_PASSWORD_LENGTH:1};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",ENCRYPTION_ERROR:"ENCRYPTION_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PUBLIC_KEY_ERROR:"PUBLIC_KEY_ERROR"};constructor(t={}){this.options={publicKeyUrl:t.publicKeyUrl||"/microservice/strongPassword/getPublicKey",validateUrl:t.validateUrl||"/microservice/strongPassword/checkPassword",timeout:t.timeout||PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,headers:t.headers||{},cacheDuration:t.cacheDuration||PasswordValidator.CONSTANTS.CACHE_DURATION,...t},this.publicKeyCache=null,this.publicKeyExpiry=null,this.abortController=null}async getPublicKey(){if(this.publicKeyCache&&this.publicKeyExpiry&&Date.now()<this.publicKeyExpiry)return this.publicKeyCache;try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await this.makeRequest(this.options.publicKeyUrl,{method:"GET",signal:this.abortController.signal});if(!this.isSuccessResponse(t))throw Error("获取公钥失败:"+(t.message||t.msg||"未知错误"));const i=t.data||t.result;if(!this.validatePublicKey(i))throw Error("公钥格式无效");return this.publicKeyCache=i,this.publicKeyExpiry=Date.now()+this.options.cacheDuration,this.publicKeyCache}catch(t){throw void 0,this.handleError(PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR,t.message,t),Error("获取公钥失败: "+t.message)}}isSuccessResponse(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some((t=>!0===t))}validatePublicKey(t){return!(!t||"string"!=typeof t)}handleError(t,i,e=null){const s={[PasswordValidator.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR]:"密码加密失败",[PasswordValidator.ERROR_TYPES.VALIDATION_ERROR]:"密码验证失败",[PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR]:"获取公钥失败"}[t]||i||"未知错误";this.options.onError&&this.options.onError({type:t,message:s,originalError:e})}encryptPassword(t,i){try{if(!t)throw Error("密码不能为空");if(t.length<PasswordValidator.CONSTANTS.MIN_PASSWORD_LENGTH)throw Error("密码长度不足");if(!i)throw Error("公钥不能为空");if("undefined"==typeof JSEncrypt)throw Error("JSEncrypt库未正确加载,请确保已引入JSEncrypt库");const e=new JSEncrypt;e.setPublicKey(i);const s=e.encrypt(t);if(!s)throw Error("密码加密失败,可能是公钥格式不正确");return s}catch(t){throw void 0,this.handleError(PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR,t.message,t),Error("密码加密失败: "+t.message)}}async validatePassword(t,i,e={}){try{if(!t)throw Error("密码不能为空");if(!i)throw Error("用户名不能为空");const s=await this.getPublicKey(),r=this.encryptPassword(t,s),a=await this.makeRequest(this.options.validateUrl,{method:"POST",body:JSON.stringify({userName:i,password:r,timestamp:Date.now(),...e})});return{success:this.isSuccessResponse(a),data:a.data||a.result,message:a.message||a.msg||"验证完成",code:a.code||a.status,originalResponse:a}}catch(t){void 0;let i=PasswordValidator.ERROR_TYPES.VALIDATION_ERROR;return t.message.includes("网络")||t.message.includes("fetch")?i=PasswordValidator.ERROR_TYPES.NETWORK_ERROR:t.message.includes("超时")||t.message.includes("timeout")?i=PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR:t.message.includes("加密")&&(i=PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR),this.handleError(i,t.message,t),{success:!1,message:t.message||"密码校验失败",code:i,error:t}}}async makeRequest(t,i={}){this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const e=setTimeout((()=>{this.abortController.abort()}),this.options.timeout);try{const s=await fetch(t,{...i,headers:{"Content-Type":"application/json",...this.options.headers,...i.headers},signal:this.abortController.signal});if(clearTimeout(e),!s.ok)throw Error(`HTTP错误: ${s.status} ${s.statusText}`);const r=await s.json();return this.options.debug,0,r}catch(t){if(clearTimeout(e),"AbortError"===t.name)throw this.handleError(PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR,"请求超时",t),Error("请求超时");if(t.message.includes("Failed to fetch")||t.message.includes("NetworkError"))throw this.handleError(PasswordValidator.ERROR_TYPES.NETWORK_ERROR,"网络连接失败",t),Error("网络连接失败");throw t}}clearCache(){this.publicKeyCache=null,this.publicKeyExpiry=null}updateOptions(t){this.options={...this.options,...t},this.clearCache()}cancelRequest(){this.abortController&&(this.abortController.abort(),this.abortController=null)}isRequesting(){return this.abortController&&!this.abortController.signal.aborted}getCacheStatus(){return{hasCache:!!this.publicKeyCache,isExpired:!this.publicKeyExpiry||Date.now()>this.publicKeyExpiry,expiryTime:this.publicKeyExpiry,remainingTime:this.publicKeyExpiry?Math.max(0,this.publicKeyExpiry-Date.now()):0}}destroy(){try{this.cancelRequest(),this.clearCache(),this.options.onDestroy&&this.options.onDestroy()}catch(t){void 0}}}a={PopupSliderCaptcha:s,PasswordValidator:r=PasswordValidator,createPasswordValidator:i,validatePassword:e},"undefined"!=typeof window&&(window.SliderCaptcha=s,window.PopupSliderCaptcha=s,window.PasswordValidator=r,window.createPasswordValidator=i,window.validatePassword=e),t.PasswordValidator=r,t.PopupSliderCaptcha=s,t.createPasswordValidator=i,t.default=a,t.validatePassword=e,Object.defineProperty(t,"t",{value:!0})},void("object"==typeof exports&&"undefined"!=typeof module?i(exports,require("jsencrypt")):"function"==typeof define&&define.amd?define(["exports","jsencrypt"],i):i((t="undefined"!=typeof globalThis?globalThis:t||self).SliderCaptchaSDK={},t.JSEncrypt));
@@ -9,7 +9,6 @@ class PasswordValidator {
9
9
  static CONSTANTS = {
10
10
  DEFAULT_TIMEOUT: 10000,
11
11
  CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存
12
- MAX_RETRY_ATTEMPTS: 3,
13
12
  MIN_PASSWORD_LENGTH: 1
14
13
  }
15
14
 
@@ -29,7 +28,6 @@ class PasswordValidator {
29
28
  validateUrl: options.validateUrl || '/microservice/strongPassword/checkPassword',
30
29
  timeout: options.timeout || PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,
31
30
  headers: options.headers || {},
32
- retryAttempts: options.retryAttempts || PasswordValidator.CONSTANTS.MAX_RETRY_ATTEMPTS,
33
31
  cacheDuration: options.cacheDuration || PasswordValidator.CONSTANTS.CACHE_DURATION,
34
32
  ...options
35
33
  };
@@ -37,7 +35,6 @@ class PasswordValidator {
37
35
  // 缓存公钥,避免重复请求
38
36
  this.publicKeyCache = null;
39
37
  this.publicKeyExpiry = null;
40
- this.retryCount = 0;
41
38
  this.abortController = null;
42
39
  }
43
40
 
@@ -63,7 +60,6 @@ class PasswordValidator {
63
60
  signal: this.abortController.signal
64
61
  });
65
62
 
66
- // 优化:更灵活的成功判断
67
63
  if (!this.isSuccessResponse(response)) {
68
64
  throw new Error('获取公钥失败:' + (response.message || response.msg || '未知错误'))
69
65
  }
@@ -142,7 +138,7 @@ class PasswordValidator {
142
138
  encryptPassword(password, publicKey) {
143
139
  try {
144
140
  // 优化:输入验证
145
- if (!password || typeof password !== 'string') {
141
+ if (!password) {
146
142
  throw new Error('密码不能为空')
147
143
  }
148
144
 
@@ -150,7 +146,7 @@ class PasswordValidator {
150
146
  throw new Error('密码长度不足')
151
147
  }
152
148
 
153
- if (!publicKey || typeof publicKey !== 'string') {
149
+ if (!publicKey) {
154
150
  throw new Error('公钥不能为空')
155
151
  }
156
152
 
@@ -185,17 +181,14 @@ class PasswordValidator {
185
181
  async validatePassword(password, userName, additionalData = {}) {
186
182
  try {
187
183
  // 优化:输入验证
188
- if (!password || typeof password !== 'string') {
184
+ if (!password ) {
189
185
  throw new Error('密码不能为空')
190
186
  }
191
187
 
192
- if (!userName || typeof userName !== 'string') {
188
+ if (!userName) {
193
189
  throw new Error('用户名不能为空')
194
190
  }
195
191
 
196
- // 重置重试计数
197
- this.retryCount = 0;
198
-
199
192
  // 1. 获取公钥
200
193
  const publicKey = await this.getPublicKey();
201
194
 
@@ -364,9 +357,6 @@ class PasswordValidator {
364
357
  // 清除缓存
365
358
  this.clearCache();
366
359
 
367
- // 重置状态
368
- this.retryCount = 0;
369
-
370
360
  // 调用销毁回调
371
361
  if (this.options.onDestroy) {
372
362
  this.options.onDestroy();
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jsencrypt")):"function"==typeof define&&define.amd?define(["exports","jsencrypt"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).PasswordValidator={},t.JSEncrypt)}(this,function(t,JSEncrypt){"use strict";class PasswordValidator{static CONSTANTS={DEFAULT_TIMEOUT:1e4,CACHE_DURATION:3e5,MAX_RETRY_ATTEMPTS:3,MIN_PASSWORD_LENGTH:1};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",ENCRYPTION_ERROR:"ENCRYPTION_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PUBLIC_KEY_ERROR:"PUBLIC_KEY_ERROR"};constructor(t={}){this.options={publicKeyUrl:t.publicKeyUrl||"/microservice/strongPassword/getPublicKey",validateUrl:t.validateUrl||"/microservice/strongPassword/checkPassword",timeout:t.timeout||PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,headers:t.headers||{},retryAttempts:t.retryAttempts||PasswordValidator.CONSTANTS.MAX_RETRY_ATTEMPTS,cacheDuration:t.cacheDuration||PasswordValidator.CONSTANTS.CACHE_DURATION,...t},this.publicKeyCache=null,this.publicKeyExpiry=null,this.retryCount=0,this.abortController=null}async getPublicKey(){if(this.publicKeyCache&&this.publicKeyExpiry&&Date.now()<this.publicKeyExpiry)return this.publicKeyCache;try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await this.makeRequest(this.options.publicKeyUrl,{method:"GET",signal:this.abortController.signal});if(!this.isSuccessResponse(t))throw Error("获取公钥失败:"+(t.message||t.msg||"未知错误"));const e=t.data||t.result;if(!this.validatePublicKey(e))throw Error("公钥格式无效");return this.publicKeyCache=e,this.publicKeyExpiry=Date.now()+this.options.cacheDuration,this.publicKeyCache}catch(t){throw this.handleError(PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR,t.message,t),Error("获取公钥失败: "+t.message)}}isSuccessResponse(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some(t=>!0===t)}validatePublicKey(t){return!(!t||"string"!=typeof t)}handleError(t,e,r=null){const s={[PasswordValidator.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR]:"密码加密失败",[PasswordValidator.ERROR_TYPES.VALIDATION_ERROR]:"密码验证失败",[PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR]:"获取公钥失败"}[t]||e||"未知错误";this.options.onError&&this.options.onError({type:t,message:s,originalError:r})}encryptPassword(t,e){try{if(!t||"string"!=typeof t)throw Error("密码不能为空");if(t.length<PasswordValidator.CONSTANTS.MIN_PASSWORD_LENGTH)throw Error("密码长度不足");if(!e||"string"!=typeof e)throw Error("公钥不能为空");if(void 0===JSEncrypt)throw Error("JSEncrypt库未正确加载,请确保已引入JSEncrypt库");const r=new JSEncrypt;r.setPublicKey(e);const s=r.encrypt(t);if(!s)throw Error("密码加密失败,可能是公钥格式不正确");return s}catch(t){throw this.handleError(PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR,t.message,t),Error("密码加密失败: "+t.message)}}async validatePassword(t,e,r={}){try{if(!t||"string"!=typeof t)throw Error("密码不能为空");if(!e||"string"!=typeof e)throw Error("用户名不能为空");this.retryCount=0;const s=await this.getPublicKey(),o=this.encryptPassword(t,s),i=await this.makeRequest(this.options.validateUrl,{method:"POST",body:JSON.stringify({userName:e,password:o,timestamp:Date.now(),...r})});return{success:this.isSuccessResponse(i),data:i.data||i.result,message:i.message||i.msg||"验证完成",code:i.code||i.status,originalResponse:i}}catch(t){let e=PasswordValidator.ERROR_TYPES.VALIDATION_ERROR;return t.message.includes("网络")||t.message.includes("fetch")?e=PasswordValidator.ERROR_TYPES.NETWORK_ERROR:t.message.includes("超时")||t.message.includes("timeout")?e=PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR:t.message.includes("加密")&&(e=PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR),this.handleError(e,t.message,t),{success:!1,message:t.message||"密码校验失败",code:e,error:t}}}async makeRequest(t,e={}){this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const r=setTimeout(()=>{this.abortController.abort()},this.options.timeout);try{const s=await fetch(t,{...e,headers:{"Content-Type":"application/json",...this.options.headers,...e.headers},signal:this.abortController.signal});if(clearTimeout(r),!s.ok)throw Error(`HTTP错误: ${s.status} ${s.statusText}`);const o=await s.json();return this.options.debug,o}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw this.handleError(PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR,"请求超时",t),Error("请求超时");if(t.message.includes("Failed to fetch")||t.message.includes("NetworkError"))throw this.handleError(PasswordValidator.ERROR_TYPES.NETWORK_ERROR,"网络连接失败",t),Error("网络连接失败");throw t}}clearCache(){this.publicKeyCache=null,this.publicKeyExpiry=null}updateOptions(t){this.options={...this.options,...t},this.clearCache()}cancelRequest(){this.abortController&&(this.abortController.abort(),this.abortController=null)}isRequesting(){return this.abortController&&!this.abortController.signal.aborted}getCacheStatus(){return{hasCache:!!this.publicKeyCache,isExpired:!this.publicKeyExpiry||Date.now()>this.publicKeyExpiry,expiryTime:this.publicKeyExpiry,remainingTime:this.publicKeyExpiry?Math.max(0,this.publicKeyExpiry-Date.now()):0}}destroy(){try{this.cancelRequest(),this.clearCache(),this.retryCount=0,this.options.onDestroy&&this.options.onDestroy()}catch(t){}}}t.createPasswordValidator=function(t){return new PasswordValidator(t)},t.default=PasswordValidator,t.validatePassword=async function(t,e={},r={}){const s=new PasswordValidator(e),o=r.userName||"";return await s.validatePassword(t,o,r)},Object.defineProperty(t,"__esModule",{value:!0})});
1
+ var t,r;t=this,r=function(t,JSEncrypt){"use strict";class PasswordValidator{static CONSTANTS={DEFAULT_TIMEOUT:1e4,CACHE_DURATION:3e5,MIN_PASSWORD_LENGTH:1};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",ENCRYPTION_ERROR:"ENCRYPTION_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PUBLIC_KEY_ERROR:"PUBLIC_KEY_ERROR"};constructor(t={}){this.options={publicKeyUrl:t.publicKeyUrl||"/microservice/strongPassword/getPublicKey",validateUrl:t.validateUrl||"/microservice/strongPassword/checkPassword",timeout:t.timeout||PasswordValidator.CONSTANTS.DEFAULT_TIMEOUT,headers:t.headers||{},cacheDuration:t.cacheDuration||PasswordValidator.CONSTANTS.CACHE_DURATION,...t},this.publicKeyCache=null,this.publicKeyExpiry=null,this.abortController=null}async getPublicKey(){if(this.publicKeyCache&&this.publicKeyExpiry&&Date.now()<this.publicKeyExpiry)return this.publicKeyCache;try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await this.makeRequest(this.options.publicKeyUrl,{method:"GET",signal:this.abortController.signal});if(!this.isSuccessResponse(t))throw Error("获取公钥失败:"+(t.message||t.msg||"未知错误"));const r=t.data||t.result;if(!this.validatePublicKey(r))throw Error("公钥格式无效");return this.publicKeyCache=r,this.publicKeyExpiry=Date.now()+this.options.cacheDuration,this.publicKeyCache}catch(t){throw void 0,this.handleError(PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR,t.message,t),Error("获取公钥失败: "+t.message)}}isSuccessResponse(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some((t=>!0===t))}validatePublicKey(t){return!(!t||"string"!=typeof t)}handleError(t,r,s=null){const e={[PasswordValidator.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR]:"密码加密失败",[PasswordValidator.ERROR_TYPES.VALIDATION_ERROR]:"密码验证失败",[PasswordValidator.ERROR_TYPES.PUBLIC_KEY_ERROR]:"获取公钥失败"}[t]||r||"未知错误";this.options.onError&&this.options.onError({type:t,message:e,originalError:s})}encryptPassword(t,r){try{if(!t)throw Error("密码不能为空");if(t.length<PasswordValidator.CONSTANTS.MIN_PASSWORD_LENGTH)throw Error("密码长度不足");if(!r)throw Error("公钥不能为空");if("undefined"==typeof JSEncrypt)throw Error("JSEncrypt库未正确加载,请确保已引入JSEncrypt库");const s=new JSEncrypt;s.setPublicKey(r);const e=s.encrypt(t);if(!e)throw Error("密码加密失败,可能是公钥格式不正确");return e}catch(t){throw void 0,this.handleError(PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR,t.message,t),Error("密码加密失败: "+t.message)}}async validatePassword(t,r,s={}){try{if(!t)throw Error("密码不能为空");if(!r)throw Error("用户名不能为空");const e=await this.getPublicKey(),i=this.encryptPassword(t,e),o=await this.makeRequest(this.options.validateUrl,{method:"POST",body:JSON.stringify({userName:r,password:i,timestamp:Date.now(),...s})});return{success:this.isSuccessResponse(o),data:o.data||o.result,message:o.message||o.msg||"验证完成",code:o.code||o.status,originalResponse:o}}catch(t){void 0;let r=PasswordValidator.ERROR_TYPES.VALIDATION_ERROR;return t.message.includes("网络")||t.message.includes("fetch")?r=PasswordValidator.ERROR_TYPES.NETWORK_ERROR:t.message.includes("超时")||t.message.includes("timeout")?r=PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR:t.message.includes("加密")&&(r=PasswordValidator.ERROR_TYPES.ENCRYPTION_ERROR),this.handleError(r,t.message,t),{success:!1,message:t.message||"密码校验失败",code:r,error:t}}}async makeRequest(t,r={}){this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const s=setTimeout((()=>{this.abortController.abort()}),this.options.timeout);try{const e=await fetch(t,{...r,headers:{"Content-Type":"application/json",...this.options.headers,...r.headers},signal:this.abortController.signal});if(clearTimeout(s),!e.ok)throw Error(`HTTP错误: ${e.status} ${e.statusText}`);const i=await e.json();return this.options.debug,0,i}catch(t){if(clearTimeout(s),"AbortError"===t.name)throw this.handleError(PasswordValidator.ERROR_TYPES.TIMEOUT_ERROR,"请求超时",t),Error("请求超时");if(t.message.includes("Failed to fetch")||t.message.includes("NetworkError"))throw this.handleError(PasswordValidator.ERROR_TYPES.NETWORK_ERROR,"网络连接失败",t),Error("网络连接失败");throw t}}clearCache(){this.publicKeyCache=null,this.publicKeyExpiry=null}updateOptions(t){this.options={...this.options,...t},this.clearCache()}cancelRequest(){this.abortController&&(this.abortController.abort(),this.abortController=null)}isRequesting(){return this.abortController&&!this.abortController.signal.aborted}getCacheStatus(){return{hasCache:!!this.publicKeyCache,isExpired:!this.publicKeyExpiry||Date.now()>this.publicKeyExpiry,expiryTime:this.publicKeyExpiry,remainingTime:this.publicKeyExpiry?Math.max(0,this.publicKeyExpiry-Date.now()):0}}destroy(){try{this.cancelRequest(),this.clearCache(),this.options.onDestroy&&this.options.onDestroy()}catch(t){void 0}}}t.createPasswordValidator=function(t){return new PasswordValidator(t)},t.default=PasswordValidator,t.validatePassword=async function(t,r={},s={}){const e=new PasswordValidator(r),i=s.userName||"";return await e.validatePassword(t,i,s)},Object.defineProperty(t,"t",{value:!0})},void("object"==typeof exports&&"undefined"!=typeof module?r(exports,require("jsencrypt")):"function"==typeof define&&define.amd?define(["exports","jsencrypt"],r):r((t="undefined"!=typeof globalThis?globalThis:t||self).PasswordValidator={},t.JSEncrypt));
@@ -81,11 +81,6 @@ declare module 'slider-captcha-sdk/slider-captcha' {
81
81
  destroy(): void
82
82
  reset(): void
83
83
 
84
- // 状态管理
85
- getState(): SliderCaptchaState
86
- isVisible(): boolean
87
- isDragging(): boolean
88
- isLoading(): boolean
89
84
 
90
85
  // 静态方法
91
86
  static create(options?: SliderCaptchaOptions): PopupSliderCaptcha
@@ -36,7 +36,6 @@ class PopupSliderCaptcha {
36
36
  CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存
37
37
  MAX_RETRY_ATTEMPTS: 3,
38
38
  DEFAULT_TIMEOUT: 30000,
39
- ANIMATION_DURATION: 300,
40
39
  FLOATING_TIME_DURATION: 2500,
41
40
  THROTTLE_DELAY: 16
42
41
  }
@@ -52,14 +51,14 @@ class PopupSliderCaptcha {
52
51
 
53
52
  constructor(options = {}) {
54
53
  // 优化:合并默认配置,使用常量
55
- this.options = {
56
- ...PopupSliderCaptcha.DEFAULTS,
54
+ this.options = {
55
+ ...PopupSliderCaptcha.DEFAULTS,
57
56
  ...options,
58
57
  timeout: options.timeout || PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,
59
58
  maxRetries: options.maxRetries || PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,
60
59
  throttleDelay: options.throttleDelay || PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY
61
60
  };
62
-
61
+
63
62
  // 优化:初始化所有属性
64
63
  this.elements = {};
65
64
  this.state = this.createInitialState();
@@ -286,7 +285,7 @@ class PopupSliderCaptcha {
286
285
  e.preventDefault();
287
286
  this.state.isDragging = true;
288
287
  this.state.startX = this.getClientX(e) - this.state.currentX;
289
- this.startTime = this.startTime || Date.now();
288
+ this.dragStartTime = Date.now();
290
289
  this.times = [{ time: Date.now(), position: this.getPosition() }];
291
290
 
292
291
  this.setTransition(false);
@@ -443,7 +442,7 @@ class PopupSliderCaptcha {
443
442
  };
444
443
 
445
444
  const errorMessage = errorMessages[errorType] || message || '未知错误';
446
-
445
+
447
446
  // 调用用户自定义错误处理
448
447
  if (this.options.onError) {
449
448
  this.options.onError({
@@ -471,12 +470,11 @@ class PopupSliderCaptcha {
471
470
 
472
471
  const response = await fetch(this.options.apiUrl, {
473
472
  method: "POST",
474
- headers: {
473
+ headers: {
475
474
  "Content-Type": "application/json",
476
475
  ...this.options.headers
477
476
  },
478
- body: JSON.stringify({
479
- place: 2,
477
+ body: JSON.stringify({
480
478
  timestamp: Date.now(),
481
479
  ...this.options.requestData
482
480
  }),
@@ -511,10 +509,10 @@ class PopupSliderCaptcha {
511
509
  // 优化:添加验证码数据验证方法
512
510
  validateCaptchaData(data) {
513
511
  if (!data || typeof data !== 'object') return false
514
-
512
+
515
513
  const requiredFields = ['canvasSrc', 'blockSrc', 'canvasWidth', 'canvasHeight', 'blockWidth', 'blockHeight', 'blockY', 'nonceStr'];
516
514
  const dataObj = data.data || data;
517
-
515
+
518
516
  return requiredFields.every(field => {
519
517
  const value = dataObj[field];
520
518
  return value !== null && value !== undefined && value !== ''
@@ -576,36 +574,17 @@ class PopupSliderCaptcha {
576
574
  this.imageCache.set(src, imgElement.cloneNode());
577
575
  resolve();
578
576
  };
579
-
577
+
580
578
  imgElement.onerror = (error) => {
581
579
  this.safeClearTimeout(timeoutId);
582
580
  reject(error);
583
581
  };
584
-
582
+
585
583
  imgElement.src = src;
586
584
  this.applyStyles(imgElement, styles);
587
585
  })
588
586
  }
589
587
 
590
- loadImage(imgElement, src, styles, onLoad, onError) {
591
- // 检查缓存
592
- if (this.imageCache.has(src)) {
593
- const cachedImg = this.imageCache.get(src);
594
- imgElement.src = cachedImg.src;
595
- this.applyStyles(imgElement, styles);
596
- onLoad();
597
- return
598
- }
599
-
600
- imgElement.onload = () => {
601
- this.imageCache.set(src, imgElement.cloneNode());
602
- onLoad();
603
- };
604
- imgElement.onerror = onError;
605
- imgElement.src = src;
606
- this.applyStyles(imgElement, styles);
607
- }
608
-
609
588
  // 优化:提取样式应用逻辑
610
589
  applyStyles(element, styles) {
611
590
  Object.entries(styles).forEach(([key, value]) => {
@@ -678,7 +657,7 @@ class PopupSliderCaptcha {
678
657
 
679
658
  const response = await fetch(this.options.verifyUrl, {
680
659
  method: "POST",
681
- headers: {
660
+ headers: {
682
661
  "Content-Type": "application/json",
683
662
  ...this.options.headers
684
663
  },
@@ -719,7 +698,7 @@ class PopupSliderCaptcha {
719
698
  // 优化:添加验证成功判断方法
720
699
  isVerifySuccess(data) {
721
700
  if (!data || typeof data !== 'object') return false
722
-
701
+
723
702
  // 支持多种成功标识
724
703
  const successIndicators = [
725
704
  data.code === "0",
@@ -728,12 +707,12 @@ class PopupSliderCaptcha {
728
707
  data.status === 'success',
729
708
  data.result === true
730
709
  ];
731
-
710
+
732
711
  return successIndicators.some(indicator => indicator === true)
733
712
  }
734
713
 
735
714
  onVerifySuccess(ticket) {
736
- const duration = Date.now() - this.startTime;
715
+ const duration = this.dragStartTime ? Date.now() - this.dragStartTime : (Date.now() - this.startTime);
737
716
  const durationText = `验证成功!耗时:${(duration / 1000).toFixed(2)}s`;
738
717
 
739
718
  this.updateUIState("success");
@@ -787,6 +766,7 @@ class PopupSliderCaptcha {
787
766
 
788
767
  this.times = [];
789
768
  this.startTime = null;
769
+ this.dragStartTime = null; // 优化:重置拖拽开始时间
790
770
  this.cachedDimensions = null;
791
771
 
792
772
  // 重置UI
@@ -862,15 +842,6 @@ class PopupSliderCaptcha {
862
842
  }
863
843
  }
864
844
 
865
- // 工具函数:防抖
866
- debounce(func, delay) {
867
- let timeoutId;
868
- return function (...args) {
869
- clearTimeout(timeoutId);
870
- timeoutId = setTimeout(() => func.apply(this, args), delay);
871
- }
872
- }
873
-
874
845
  destroy() {
875
846
  try {
876
847
  // 取消所有进行中的请求
@@ -899,9 +870,9 @@ class PopupSliderCaptcha {
899
870
  this.elements.overlay.parentNode.removeChild(this.elements.overlay);
900
871
  }
901
872
 
902
- // 清理样式表(只有在没有其他实例使用时才删除)
873
+ // 清理样式表
903
874
  const styleElement = document.getElementById('slider-captcha-styles');
904
- if (styleElement && !this.hasOtherInstances()) {
875
+ if (styleElement) {
905
876
  styleElement.remove();
906
877
  }
907
878
 
@@ -925,15 +896,6 @@ class PopupSliderCaptcha {
925
896
  }
926
897
  }
927
898
 
928
- // 优化:检查是否有其他实例在使用样式
929
- hasOtherInstances() {
930
- // 简单的检查方式,可以通过全局计数器实现
931
- if (typeof window !== 'undefined') {
932
- return window.sliderCaptchaInstanceCount > 1
933
- }
934
- return false
935
- }
936
-
937
899
  static create(options) {
938
900
  return new PopupSliderCaptcha(options)
939
901
  }
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).SliderCaptcha=e()}(this,function(){"use strict";class PopupSliderCaptcha{static DEFAULTS={width:320,height:155,sliderSize:38,maxRetries:3,timeout:3e4,apiUrl:"/api/captcha",verifyUrl:"/api/captcha/verify",throttleDelay:16,clickMaskClose:!1};static CSS_CLASSES={overlay:"slider-captcha-overlay",modal:"slider-captcha-modal",header:"slider-captcha-header",container:"slider-captcha-container",track:"slider-captcha-track",btn:"slider-captcha-btn",hint:"slider-captcha-hint",loading:"slider-captcha-loading",error:"slider-captcha-error"};static getStyles(){return":root{--sc-primary:#409eff;--sc-success:#67c23a;--sc-danger:#f56c6c;--sc-border:#e4e7eb;--sc-bg:linear-gradient(90deg, #f7f9fa 0%, #e8f4fd 100%);--sc-text:#333;--sc-text-light:#999;--sc-shadow:0 4px 20px rgba(0,0,0,.3);--sc-radius:8px;--sc-transition:.3s ease}.slider-captcha-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:9999;display:none;justify-content:center;align-items:center;opacity:0;transition:opacity var(--sc-transition)}.slider-captcha-overlay.show{opacity:1}.slider-captcha-modal{background:#fff;border-radius:var(--sc-radius);padding:20px;box-shadow:var(--sc-shadow);position:relative;max-width:90vw;max-height:90vh;transform:scale(.8) translateY(-20px);opacity:0;transition:all var(--sc-transition)}.slider-captcha-modal.show{transform:scale(1) translateY(0);opacity:1}.slider-captcha-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid var(--sc-border)}.slider-captcha-container{display:flex;align-items:center;position:relative;border-radius:4px;overflow:hidden;margin-bottom:15px;background:#837a7a;justify-content:center}.slider-captcha-track{width:100%;height:40px;line-height:40px;background:var(--sc-bg);border:1px solid var(--sc-border);border-radius:20px;position:relative;margin-bottom:15px;overflow:hidden}.slider-captcha-btn{width:38px;height:38px;background:#fff;border:1px solid #ccc;border-radius:50%;position:absolute;top:0;left:0;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,.1);transition:all var(--sc-transition);user-select:none;z-index:1}.slider-captcha-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;flex-direction:column;color:#666;font-size:14px;z-index:10;border-radius:4px}.slider-captcha-error{color:var(--sc-danger);font-size:12px;text-align:center;margin-top:10px;display:none}.slider-captcha-title{margin:0;font-size:16px;color:var(--sc-text)}.slider-captcha-close,.slider-captcha-refresh{background:none;border:none;cursor:pointer;color:var(--sc-text-light);padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all var(--sc-transition);position:relative;font-size:0}.slider-captcha-close::before,.slider-captcha-close::after{content:'';position:absolute;width:16px;height:2px;background-color:var(--sc-text-light);border-radius:1px;transition:all var(--sc-transition)}.slider-captcha-close::before{transform:rotate(45deg)}.slider-captcha-close::after{transform:rotate(-45deg)}.slider-captcha-close:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-close:hover::before,.slider-captcha-close:hover::after{background-color:var(--sc-danger)}.slider-captcha-refresh{margin-left:10px}.slider-captcha-refresh svg{width:20px;height:20px;fill:var(--sc-text-light);transition:all var(--sc-transition)}.slider-captcha-refresh:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-refresh:hover svg{fill:var(--sc-primary);transform:rotate(180deg)}.slider-captcha-floating-time{position:absolute;bottom:-40px;left:50%;transform:translateX(-50%);color:#fff;font-size:12px;white-space:nowrap;opacity:0;pointer-events:none;z-index:10;transition:all var(--sc-transition);background:#fff;padding:2px 15px;border-radius:10px}.slider-captcha-floating-time.show{opacity:1;transform:translateX(-50%) translateY(-45px)}.slider-captcha-floating-time.success{color:var(--sc-success)}.slider-captcha-floating-time.fail{color:var(--sc-danger)}.slider-captcha-bg{width:100%;height:100%;object-fit:cover;display:block}.slider-captcha-piece{position:absolute;top:0;left:0;cursor:pointer;transition:none;z-index:2}.slider-captcha-finger{position:absolute;top:50%;left:10px;transform:translateY(-50%);font-size:20px;animation:fingerSlide 2s ease-in-out infinite;pointer-events:none;z-index:1;opacity:.6}.slider-captcha-hint{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--sc-text-light);font-size:14px;pointer-events:none;z-index:1;transition:all var(--sc-transition)}.slider-captcha-header-buttons{display:flex;align-items:center}@keyframes fingerSlide{0%{left:10px;opacity:.6}50%{opacity:1}100%{left:calc(50% - 10px);opacity:.6}}"}static CONSTANTS={CACHE_DURATION:3e5,MAX_RETRY_ATTEMPTS:3,DEFAULT_TIMEOUT:3e4,ANIMATION_DURATION:300,FLOATING_TIME_DURATION:2500,THROTTLE_DELAY:16};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",IMAGE_LOAD_ERROR:"IMAGE_LOAD_ERROR",CAPTCHA_DATA_ERROR:"CAPTCHA_DATA_ERROR"};constructor(t={}){this.options={...PopupSliderCaptcha.DEFAULTS,...t,timeout:t.timeout||PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,maxRetries:t.maxRetries||PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,throttleDelay:t.throttleDelay||PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY},this.elements={},this.state=this.createInitialState(),this.captchaData=null,this.times=[],this.startTime=null,this.eventListeners=[],this.timers=new Set,this.rafId=null,this.cachedDimensions=null,this.imageCache=new Map,this.abortController=null,this.throttledHandleMove=this.throttle(t=>this.handleMove(t),this.options.throttleDelay);try{this.init()}catch(t){this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message)}}createInitialState(){return{isVisible:!1,isDragging:!1,currentX:0,startX:0,retryCount:0,isLoading:!1}}init(){this.injectStyles(),this.createElements(),this.bindEvents()}injectStyles(){if(document.querySelector("#slider-captcha-styles"))return;const t=document.createElement("style");t.id="slider-captcha-styles",t.textContent=PopupSliderCaptcha.getStyles(),document.head.appendChild(t)}createElements(){const{elements:t,options:e}=this;[["overlay","div",PopupSliderCaptcha.CSS_CLASSES.overlay],["modal","div",PopupSliderCaptcha.CSS_CLASSES.modal],["header","div",PopupSliderCaptcha.CSS_CLASSES.header],["title","h3","slider-captcha-title","安全验证"],["closeBtn","button","slider-captcha-close"],["refreshBtn","button","slider-captcha-refresh"],["container","div",PopupSliderCaptcha.CSS_CLASSES.container],["backgroundImg","img","slider-captcha-bg"],["sliderImg","img","slider-captcha-piece"],["loadingText","div",PopupSliderCaptcha.CSS_CLASSES.loading,"加载中..."],["floatingTime","div","slider-captcha-floating-time"],["track","div",PopupSliderCaptcha.CSS_CLASSES.track],["fingerAnimation","div","slider-captcha-finger","👉"],["btn","div",PopupSliderCaptcha.CSS_CLASSES.btn],["icon","div","","→"],["hint","div",PopupSliderCaptcha.CSS_CLASSES.hint,"向右滑动完成验证"],["error","div",PopupSliderCaptcha.CSS_CLASSES.error]].forEach(([e,s,i,a])=>{t[e]=this.createElement(s,i,a)}),t.container.style.cssText=`width:${e.width}px;height:${e.height}px`,t.refreshBtn.innerHTML='<svg viewBox="0 0 24 24"><path d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z"/></svg>',this.assembleDOM(),this.setInitialState()}createElement(t,e="",s=""){const i=document.createElement(t);return e&&(i.className=e),s&&(i.textContent=s),i}assembleDOM(){const{elements:t}=this,e=this.createElement("div","slider-captcha-header-buttons");e.append(t.refreshBtn,t.closeBtn),t.header.append(t.title,e),t.container.append(t.backgroundImg,t.sliderImg,t.loadingText,t.floatingTime),t.btn.appendChild(t.icon),t.track.append(t.fingerAnimation,t.btn,t.hint),t.modal.append(t.header,t.container,t.track,t.error),t.overlay.appendChild(t.modal),document.body.appendChild(t.overlay)}setInitialState(){Object.assign(this.elements.container.style,{display:"none"}),Object.assign(this.elements.track.style,{display:"none"})}bindEvents(){const{elements:t}=this;this.addEventListener(t.closeBtn,"click",()=>this.hide()),this.addEventListener(t.refreshBtn,"click",()=>this.refresh()),this.addEventListener(t.overlay,"click",e=>{e.target===t.overlay&&this.options.clickMaskClose&&this.hide()}),this.addEventListener(document,"keydown",t=>{"Escape"===t.key&&this.state.isVisible&&this.hide()}),this.addEventListener(document,"visibilitychange",()=>this.handleVisibilityChange()),this.bindSliderEvents()}bindSliderEvents(){const{elements:t}=this,e={start:t=>this.handleStart(t),move:this.throttledHandleMove,end:()=>this.handleEnd()};this.addEventListener(t.btn,"mousedown",e.start),this.addEventListener(t.btn,"touchstart",e.start),this.addEventListener(t.sliderImg,"mousedown",e.start),this.addEventListener(t.sliderImg,"touchstart",e.start),this.addEventListener(document,"mousemove",e.move,{passive:!1}),this.addEventListener(document,"touchmove",e.move,{passive:!1}),this.addEventListener(document,"mouseup",e.end),this.addEventListener(document,"touchend",e.end)}addEventListener(t,e,s,i={}){t&&"function"==typeof s&&(t.addEventListener(e,s,i),this.eventListeners.push({element:t,event:e,handler:s,options:i}))}removeAllEventListeners(){this.eventListeners.forEach(({element:t,event:e,handler:s,options:i})=>{try{t?.removeEventListener?.(e,s,i)}catch(t){}}),this.eventListeners.length=0}getDimensions(){if(!this.cachedDimensions){const t=this.elements.track.offsetWidth,e=this.elements.btn.offsetWidth;this.cachedDimensions={trackWidth:t,btnWidth:e,maxX:t-e}}return this.cachedDimensions}getPosition(){const{maxX:t}=this.getDimensions(),e=this.state.currentX/t;return Math.round(e*(this.options.width-this.options.sliderSize))}handleStart(t){!this.captchaData||this.state.isDragging||this.state.isLoading||(t.preventDefault(),this.state.isDragging=!0,this.state.startX=this.getClientX(t)-this.state.currentX,this.startTime=this.startTime||Date.now(),this.times=[{time:Date.now(),position:this.getPosition()}],this.setTransition(!1),this.updateUIState("dragging"),this.cachedDimensions=null)}handleMove(t){if(!this.state.isDragging)return;t.preventDefault();const e=this.getClientX(t)-this.state.startX,{maxX:s}=this.getDimensions();this.state.currentX=Math.max(0,Math.min(e,s)),this.times.push({time:Date.now(),position:this.getPosition()}),this.rafId&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(()=>this.updateSliderPosition())}handleEnd(){this.state.isDragging&&(this.times.push({time:Date.now(),position:this.getPosition()}),this.state.isDragging=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.verify())}handleVisibilityChange(){const t=document.hidden?"paused":"running";this.elements.fingerAnimation&&(this.elements.fingerAnimation.style.animationPlayState=t)}getClientX(t){return t.type.includes("touch")?t.touches[0].clientX:t.clientX}setTransition(t){const e=t?"all 0.3s ease":"none";requestAnimationFrame(()=>{this.elements.btn.style.transition=e,this.elements.sliderImg.style.transition=e})}updateUIState(t){const{elements:e}=this,s={dragging:()=>{e.hint.style.opacity="0",e.fingerAnimation.style.display="none"},success:()=>{Object.assign(e.btn.style,{background:"var(--sc-success)"}),Object.assign(e.icon.style,{innerHTML:"✓",color:"white"}),e.icon.innerHTML="✓"},fail:()=>{Object.assign(e.btn.style,{background:"var(--sc-danger)"}),Object.assign(e.icon.style,{innerHTML:"✗",color:"white"}),e.icon.innerHTML="✗"},reset:()=>{Object.assign(e.btn.style,{background:"white"}),Object.assign(e.icon.style,{color:"#666"}),e.icon.innerHTML="→",e.fingerAnimation.style.display="block",this.updateHintText("向右滑动完成验证","var(--sc-text-light)")},loading:()=>{e.hint.style.opacity="0",e.fingerAnimation.style.display="none",Object.assign(e.track.style,{pointerEvents:"none",opacity:"0.6"})}};s[t]&&requestAnimationFrame(()=>{s[t](),"loading"!==t&&Object.assign(e.track.style,{pointerEvents:"auto",opacity:"1"})})}updateHintText(t,e){requestAnimationFrame(()=>{Object.assign(this.elements.hint,{textContent:t}),Object.assign(this.elements.hint.style,{color:e,opacity:"1"})})}updateSliderPosition(){const{elements:t,options:e,state:s}=this,{maxX:i}=this.getDimensions(),a=s.currentX/i*(e.width-e.sliderSize),n=s.currentX/i;requestAnimationFrame(()=>{t.btn.style.transform=`translateX(${s.currentX}px)`,t.sliderImg.style.transform=`translateX(${a}px)`,t.fingerAnimation.style.opacity=.8>n?"0.6":"0"})}show(){this.state.isVisible=!0,this.elements.overlay.style.display="flex",requestAnimationFrame(()=>{this.elements.overlay.classList.add("show"),this.elements.modal.classList.add("show")}),this.loadCaptcha()}hide(){this.state.isVisible=!1,this.elements.overlay.classList.remove("show"),this.elements.modal.classList.remove("show"),this.safeSetTimeout(()=>{this.elements.overlay.style.display="none",document.body.removeChild(this.elements.overlay),this.reset(),this.options.onClose?.(),this.elements.overlay=null},300)}handleError(t,e,s=null){const i={[PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR]:"验证失败,请重试",[PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR]:"图片加载失败,请刷新重试",[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]:"验证码数据错误,请刷新重试"}[t]||e||"未知错误";this.options.onError&&this.options.onError({type:t,message:i,originalError:s}),this.showError(i)}async loadCaptcha(){try{this.showLoading(),this.startTime=Date.now(),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.apiUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({place:2,timestamp:Date.now(),...this.options.requestData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const e=await t.json();if(!this.validateCaptchaData(e))throw Error("验证码数据格式错误");this.captchaData=e.data,this.showCaptcha(),await this.renderCaptcha()}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR,t.message,t)}}validateCaptchaData(t){if(!t||"object"!=typeof t)return!1;const e=t.data||t;return["canvasSrc","blockSrc","canvasWidth","canvasHeight","blockWidth","blockHeight","blockY","nonceStr"].every(t=>{const s=e[t];return null!=s&&""!==s})}async renderCaptcha(){return new Promise((t,e)=>{let s=!1;const i=[this.loadImageAsync(this.elements.backgroundImg,this.captchaData.canvasSrc,{width:this.captchaData.canvasWidth,height:this.captchaData.canvasHeight}),this.loadImageAsync(this.elements.sliderImg,this.captchaData.blockSrc,{width:this.captchaData.blockWidth,height:this.captchaData.blockHeight,top:this.captchaData.blockY})];Promise.all(i).then(()=>{s||(this.hideLoading(),t())}).catch(t=>{s||(s=!0,this.handleError(PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR,"图片加载失败",t),e(t))})})}async loadImageAsync(t,e,s){return new Promise((i,a)=>{if(this.imageCache.has(e)){const a=this.imageCache.get(e);return t.src=a.src,this.applyStyles(t,s),void i()}const n=this.safeSetTimeout(()=>{a(Error("图片加载超时"))},1e4);t.onload=()=>{this.safeClearTimeout(n),this.imageCache.set(e,t.cloneNode()),i()},t.onerror=t=>{this.safeClearTimeout(n),a(t)},t.src=e,this.applyStyles(t,s)})}loadImage(t,e,s,i,a){if(this.imageCache.has(e)){const a=this.imageCache.get(e);return t.src=a.src,this.applyStyles(t,s),void i()}t.onload=()=>{this.imageCache.set(e,t.cloneNode()),i()},t.onerror=a,t.src=e,this.applyStyles(t,s)}applyStyles(t,e){Object.entries(e).forEach(([e,s])=>{t.style[e]="number"==typeof s?s+"px":s})}showLoading(){this.state.isLoading=!0,this.batchUpdateStyles({container:{display:"block"},loadingText:{display:"flex"},error:{display:"none"}}),this.updateUIState("loading")}hideLoading(){this.state.isLoading=!1,this.batchUpdateStyles({loadingText:{display:"none"}}),this.updateUIState("reset")}showCaptcha(){this.batchUpdateStyles({container:{display:"block"},track:{display:"block"},error:{display:"none"}})}showError(t){this.hideLoading(),this.batchUpdateStyles({error:{display:"block",textContent:t}})}batchUpdateStyles(t){requestAnimationFrame(()=>{Object.entries(t).forEach(([t,e])=>{const s=this.elements[t];s&&Object.entries(e).forEach(([t,e])=>{"textContent"===t?s.textContent=e:s.style[t]=e})})})}async verify(){if(this.captchaData)try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.verifyUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({loginVo:{nonceStr:this.captchaData.nonceStr,value:this.getPosition()},dragEventList:[...this.times],...this.options.verifyData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const e=await t.json();this.isVerifySuccess(e)?this.onVerifySuccess(e.data||e.result):this.onVerifyFail(e.message||e.msg||"验证失败,请重试!")}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"验证请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message,t)}else this.onVerifyFail("验证码数据丢失")}isVerifySuccess(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some(t=>!0===t)}onVerifySuccess(t){const e=Date.now()-this.startTime,s=`验证成功!耗时:${(e/1e3).toFixed(2)}s`;this.updateUIState("success"),this.showFloatingTime(s,"success"),this.safeSetTimeout(()=>{this.options.onSuccess?.({ticket:t,timestamp:Date.now(),duration:e}),this.hide()},2e3)}showFloatingTime(t,e="success"){const{elements:s}=this;s.floatingTime.textContent=t,s.floatingTime.className="slider-captcha-floating-time "+e,this.safeSetTimeout(()=>s.floatingTime.classList.add("show"),100),this.safeSetTimeout(()=>{s.floatingTime.className="slider-captcha-floating-time"},2500)}onVerifyFail(t){this.state.retryCount++,this.updateUIState("fail"),this.showFloatingTime(t,"fail"),this.safeSetTimeout(()=>{this.state.retryCount<this.options.maxRetries?this.reset():this.refresh()},2500)}reset(){this.clearAllTimers(),Object.assign(this.state,{isDragging:!1,currentX:0,startX:0,isLoading:!1}),this.times=[],this.startTime=null,this.cachedDimensions=null,requestAnimationFrame(()=>{this.setTransition(!0),this.elements.btn.style.transform="translateX(0px)",this.elements.sliderImg.style.transform="translateX(0px)",this.updateUIState("reset"),this.elements.error.style.display="none"})}refresh(){this.reset(),this.state.retryCount=0,this.loadCaptcha()}safeSetTimeout(t,e){const s=setTimeout(()=>{this.timers.delete(s),t()},e);return this.timers.add(s),s}safeClearTimeout(t){t&&(clearTimeout(t),this.timers.delete(t))}clearAllTimers(){this.timers.forEach(t=>{clearTimeout(t),clearInterval(t)}),this.timers.clear(),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}cleanupImages(){this.elements.backgroundImg&&(this.elements.backgroundImg.src="",this.elements.backgroundImg.onload=null,this.elements.backgroundImg.onerror=null),this.elements.sliderImg&&(this.elements.sliderImg.src="",this.elements.sliderImg.onload=null,this.elements.sliderImg.onerror=null),this.imageCache.clear()}throttle(t,e){let s=0;return function(...i){const a=Date.now();if(a-s>=e)return s=a,t.apply(this,i)}}debounce(t,e){let s;return function(...i){clearTimeout(s),s=setTimeout(()=>t.apply(this,i),e)}}destroy(){try{this.abortController&&(this.abortController.abort(),this.abortController=null),document.body&&(document.body.style.userSelect="",document.body.style.cursor=""),this.clearAllTimers(),this.removeAllEventListeners(),this.cleanupImages(),this.elements?.overlay?.parentNode&&this.elements.overlay.parentNode.removeChild(this.elements.overlay);const t=document.getElementById("slider-captcha-styles");t&&!this.hasOtherInstances()&&t.remove(),this.imageCache.clear(),this.cachedDimensions=null,Object.keys(this).forEach(t=>{"constructor"!==t&&(this[t]=null)}),this.options.onDestroy&&this.options.onDestroy()}catch(t){}}hasOtherInstances(){return"undefined"!=typeof window&&window.sliderCaptchaInstanceCount>1}static create(t){return new PopupSliderCaptcha(t)}static show(t){const e=new PopupSliderCaptcha(t);return e.show(),e}}return"undefined"!=typeof module&&module.exports?(module.exports=PopupSliderCaptcha,module.exports.default=PopupSliderCaptcha):"function"==typeof define&&define.amd?define([],()=>PopupSliderCaptcha):"undefined"!=typeof window&&(window.PopupSliderCaptcha=PopupSliderCaptcha,window.SliderCaptcha=PopupSliderCaptcha),PopupSliderCaptcha});
1
+ var t,i;t=this,i=function(){"use strict";class PopupSliderCaptcha{static DEFAULTS={width:320,height:155,sliderSize:38,maxRetries:3,timeout:3e4,apiUrl:"/api/captcha",verifyUrl:"/api/captcha/verify",throttleDelay:16,clickMaskClose:!1};static CSS_CLASSES={overlay:"slider-captcha-overlay",modal:"slider-captcha-modal",header:"slider-captcha-header",container:"slider-captcha-container",track:"slider-captcha-track",btn:"slider-captcha-btn",hint:"slider-captcha-hint",loading:"slider-captcha-loading",error:"slider-captcha-error"};static getStyles(){return":root{--sc-primary:#409eff;--sc-success:#67c23a;--sc-danger:#f56c6c;--sc-border:#e4e7eb;--sc-bg:linear-gradient(90deg, #f7f9fa 0%, #e8f4fd 100%);--sc-text:#333;--sc-text-light:#999;--sc-shadow:0 4px 20px rgba(0,0,0,.3);--sc-radius:8px;--sc-transition:.3s ease}.slider-captcha-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:9999;display:none;justify-content:center;align-items:center;opacity:0;transition:opacity var(--sc-transition)}.slider-captcha-overlay.show{opacity:1}.slider-captcha-modal{background:#fff;border-radius:var(--sc-radius);padding:20px;box-shadow:var(--sc-shadow);position:relative;max-width:90vw;max-height:90vh;transform:scale(.8) translateY(-20px);opacity:0;transition:all var(--sc-transition)}.slider-captcha-modal.show{transform:scale(1) translateY(0);opacity:1}.slider-captcha-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid var(--sc-border)}.slider-captcha-container{display:flex;align-items:center;position:relative;border-radius:4px;overflow:hidden;margin-bottom:15px;background:#837a7a;justify-content:center}.slider-captcha-track{width:100%;height:40px;line-height:40px;background:var(--sc-bg);border:1px solid var(--sc-border);border-radius:20px;position:relative;margin-bottom:15px;overflow:hidden}.slider-captcha-btn{width:38px;height:38px;background:#fff;border:1px solid #ccc;border-radius:50%;position:absolute;top:0;left:0;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px rgba(0,0,0,.1);transition:all var(--sc-transition);user-select:none;z-index:1}.slider-captcha-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;flex-direction:column;color:#666;font-size:14px;z-index:10;border-radius:4px}.slider-captcha-error{color:var(--sc-danger);font-size:12px;text-align:center;margin-top:10px;display:none}.slider-captcha-title{margin:0;font-size:16px;color:var(--sc-text)}.slider-captcha-close,.slider-captcha-refresh{background:none;border:none;cursor:pointer;color:var(--sc-text-light);padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all var(--sc-transition);position:relative;font-size:0}.slider-captcha-close::before,.slider-captcha-close::after{content:'';position:absolute;width:16px;height:2px;background-color:var(--sc-text-light);border-radius:1px;transition:all var(--sc-transition)}.slider-captcha-close::before{transform:rotate(45deg)}.slider-captcha-close::after{transform:rotate(-45deg)}.slider-captcha-close:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-close:hover::before,.slider-captcha-close:hover::after{background-color:var(--sc-danger)}.slider-captcha-refresh{margin-left:10px}.slider-captcha-refresh svg{width:20px;height:20px;fill:var(--sc-text-light);transition:all var(--sc-transition)}.slider-captcha-refresh:hover{background:#f5f5f5;transform:scale(1.1)}.slider-captcha-refresh:hover svg{fill:var(--sc-primary);transform:rotate(180deg)}.slider-captcha-floating-time{position:absolute;bottom:-40px;left:50%;transform:translateX(-50%);color:#fff;font-size:12px;white-space:nowrap;opacity:0;pointer-events:none;z-index:10;transition:all var(--sc-transition);background:#fff;padding:2px 15px;border-radius:10px}.slider-captcha-floating-time.show{opacity:1;transform:translateX(-50%) translateY(-45px)}.slider-captcha-floating-time.success{color:var(--sc-success)}.slider-captcha-floating-time.fail{color:var(--sc-danger)}.slider-captcha-bg{width:100%;height:100%;object-fit:cover;display:block}.slider-captcha-piece{position:absolute;top:0;left:0;cursor:pointer;transition:none;z-index:2}.slider-captcha-finger{position:absolute;top:50%;left:10px;transform:translateY(-50%);font-size:20px;animation:fingerSlide 2s ease-in-out infinite;pointer-events:none;z-index:1;opacity:.6}.slider-captcha-hint{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--sc-text-light);font-size:14px;pointer-events:none;z-index:1;transition:all var(--sc-transition)}.slider-captcha-header-buttons{display:flex;align-items:center}@keyframes fingerSlide{0%{left:10px;opacity:.6}50%{opacity:1}100%{left:calc(50% - 10px);opacity:.6}}"}static CONSTANTS={CACHE_DURATION:3e5,MAX_RETRY_ATTEMPTS:3,DEFAULT_TIMEOUT:3e4,FLOATING_TIME_DURATION:2500,THROTTLE_DELAY:16};static ERROR_TYPES={NETWORK_ERROR:"NETWORK_ERROR",TIMEOUT_ERROR:"TIMEOUT_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",IMAGE_LOAD_ERROR:"IMAGE_LOAD_ERROR",CAPTCHA_DATA_ERROR:"CAPTCHA_DATA_ERROR"};constructor(t={}){this.options={...PopupSliderCaptcha.DEFAULTS,...t,timeout:t.timeout||PopupSliderCaptcha.CONSTANTS.DEFAULT_TIMEOUT,maxRetries:t.maxRetries||PopupSliderCaptcha.CONSTANTS.MAX_RETRY_ATTEMPTS,throttleDelay:t.throttleDelay||PopupSliderCaptcha.CONSTANTS.THROTTLE_DELAY},this.elements={},this.state=this.createInitialState(),this.captchaData=null,this.times=[],this.startTime=null,this.eventListeners=[],this.timers=new Set,this.rafId=null,this.cachedDimensions=null,this.imageCache=new Map,this.abortController=null,this.throttledHandleMove=this.throttle((t=>this.handleMove(t)),this.options.throttleDelay);try{this.init()}catch(t){void 0,this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message)}}createInitialState(){return{isVisible:!1,isDragging:!1,currentX:0,startX:0,retryCount:0,isLoading:!1}}init(){this.injectStyles(),this.createElements(),this.bindEvents()}injectStyles(){if(document.querySelector("#slider-captcha-styles"))return;const t=document.createElement("style");t.id="slider-captcha-styles",t.textContent=PopupSliderCaptcha.getStyles(),document.head.appendChild(t)}createElements(){const{elements:t,options:i}=this;[["overlay","div",PopupSliderCaptcha.CSS_CLASSES.overlay],["modal","div",PopupSliderCaptcha.CSS_CLASSES.modal],["header","div",PopupSliderCaptcha.CSS_CLASSES.header],["title","h3","slider-captcha-title","安全验证"],["closeBtn","button","slider-captcha-close"],["refreshBtn","button","slider-captcha-refresh"],["container","div",PopupSliderCaptcha.CSS_CLASSES.container],["backgroundImg","img","slider-captcha-bg"],["sliderImg","img","slider-captcha-piece"],["loadingText","div",PopupSliderCaptcha.CSS_CLASSES.loading,"加载中..."],["floatingTime","div","slider-captcha-floating-time"],["track","div",PopupSliderCaptcha.CSS_CLASSES.track],["fingerAnimation","div","slider-captcha-finger","👉"],["btn","div",PopupSliderCaptcha.CSS_CLASSES.btn],["icon","div","","→"],["hint","div",PopupSliderCaptcha.CSS_CLASSES.hint,"向右滑动完成验证"],["error","div",PopupSliderCaptcha.CSS_CLASSES.error]].forEach((([i,e,s,a])=>{t[i]=this.createElement(e,s,a)})),t.container.style.cssText=`width:${i.width}px;height:${i.height}px`,t.refreshBtn.innerHTML='<svg viewBox="0 0 24 24"><path d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z"/></svg>',this.assembleDOM(),this.setInitialState()}createElement(t,i="",e=""){const s=document.createElement(t);return i&&(s.className=i),e&&(s.textContent=e),s}assembleDOM(){const{elements:t}=this,i=this.createElement("div","slider-captcha-header-buttons");i.append(t.refreshBtn,t.closeBtn),t.header.append(t.title,i),t.container.append(t.backgroundImg,t.sliderImg,t.loadingText,t.floatingTime),t.btn.appendChild(t.icon),t.track.append(t.fingerAnimation,t.btn,t.hint),t.modal.append(t.header,t.container,t.track,t.error),t.overlay.appendChild(t.modal),document.body.appendChild(t.overlay)}setInitialState(){Object.assign(this.elements.container.style,{display:"none"}),Object.assign(this.elements.track.style,{display:"none"})}bindEvents(){const{elements:t}=this;this.addEventListener(t.closeBtn,"click",(()=>this.hide())),this.addEventListener(t.refreshBtn,"click",(()=>this.refresh())),this.addEventListener(t.overlay,"click",(i=>{i.target===t.overlay&&this.options.clickMaskClose&&this.hide()})),this.addEventListener(document,"keydown",(t=>{"Escape"===t.key&&this.state.isVisible&&this.hide()})),this.addEventListener(document,"visibilitychange",(()=>this.handleVisibilityChange())),this.bindSliderEvents()}bindSliderEvents(){const{elements:t}=this,i={start:t=>this.handleStart(t),move:this.throttledHandleMove,end:()=>this.handleEnd()};this.addEventListener(t.btn,"mousedown",i.start),this.addEventListener(t.btn,"touchstart",i.start),this.addEventListener(t.sliderImg,"mousedown",i.start),this.addEventListener(t.sliderImg,"touchstart",i.start),this.addEventListener(document,"mousemove",i.move,{passive:!1}),this.addEventListener(document,"touchmove",i.move,{passive:!1}),this.addEventListener(document,"mouseup",i.end),this.addEventListener(document,"touchend",i.end)}addEventListener(t,i,e,s={}){t&&"function"==typeof e&&(t.addEventListener(i,e,s),this.eventListeners.push({element:t,event:i,handler:e,options:s}))}removeAllEventListeners(){this.eventListeners.forEach((({element:t,event:i,handler:e,options:s})=>{try{t?.removeEventListener?.(i,e,s)}catch(t){void 0}})),this.eventListeners.length=0}getDimensions(){if(!this.cachedDimensions){const t=this.elements.track.offsetWidth,i=this.elements.btn.offsetWidth;this.cachedDimensions={trackWidth:t,btnWidth:i,maxX:t-i}}return this.cachedDimensions}getPosition(){const{maxX:t}=this.getDimensions(),i=this.state.currentX/t;return Math.round(i*(this.options.width-this.options.sliderSize))}handleStart(t){!this.captchaData||this.state.isDragging||this.state.isLoading||(t.preventDefault(),this.state.isDragging=!0,this.state.startX=this.getClientX(t)-this.state.currentX,this.dragStartTime=Date.now(),this.times=[{time:Date.now(),position:this.getPosition()}],this.setTransition(!1),this.updateUIState("dragging"),this.cachedDimensions=null)}handleMove(t){if(!this.state.isDragging)return;t.preventDefault();const i=this.getClientX(t)-this.state.startX,{maxX:e}=this.getDimensions();this.state.currentX=Math.max(0,Math.min(i,e)),this.times.push({time:Date.now(),position:this.getPosition()}),this.rafId&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame((()=>this.updateSliderPosition()))}handleEnd(){this.state.isDragging&&(this.times.push({time:Date.now(),position:this.getPosition()}),this.state.isDragging=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.verify())}handleVisibilityChange(){const t=document.hidden?"paused":"running";this.elements.fingerAnimation&&(this.elements.fingerAnimation.style.animationPlayState=t)}getClientX(t){return t.type.includes("touch")?t.touches[0].clientX:t.clientX}setTransition(t){const i=t?"all 0.3s ease":"none";requestAnimationFrame((()=>{this.elements.btn.style.transition=i,this.elements.sliderImg.style.transition=i}))}updateUIState(t){const{elements:i}=this,e={dragging:()=>{i.hint.style.opacity="0",i.fingerAnimation.style.display="none"},success:()=>{Object.assign(i.btn.style,{background:"var(--sc-success)"}),Object.assign(i.icon.style,{innerHTML:"✓",color:"white"}),i.icon.innerHTML="✓"},fail:()=>{Object.assign(i.btn.style,{background:"var(--sc-danger)"}),Object.assign(i.icon.style,{innerHTML:"✗",color:"white"}),i.icon.innerHTML="✗"},reset:()=>{Object.assign(i.btn.style,{background:"white"}),Object.assign(i.icon.style,{color:"#666"}),i.icon.innerHTML="→",i.fingerAnimation.style.display="block",this.updateHintText("向右滑动完成验证","var(--sc-text-light)")},loading:()=>{i.hint.style.opacity="0",i.fingerAnimation.style.display="none",Object.assign(i.track.style,{pointerEvents:"none",opacity:"0.6"})}};e[t]&&requestAnimationFrame((()=>{e[t](),"loading"!==t&&Object.assign(i.track.style,{pointerEvents:"auto",opacity:"1"})}))}updateHintText(t,i){requestAnimationFrame((()=>{Object.assign(this.elements.hint,{textContent:t}),Object.assign(this.elements.hint.style,{color:i,opacity:"1"})}))}updateSliderPosition(){const{elements:t,options:i,state:e}=this,{maxX:s}=this.getDimensions(),a=e.currentX/s*(i.width-i.sliderSize),n=e.currentX/s;requestAnimationFrame((()=>{t.btn.style.transform=`translateX(${e.currentX}px)`,t.sliderImg.style.transform=`translateX(${a}px)`,t.fingerAnimation.style.opacity=.8>n?"0.6":"0"}))}show(){this.state.isVisible=!0,this.elements.overlay.style.display="flex",requestAnimationFrame((()=>{this.elements.overlay.classList.add("show"),this.elements.modal.classList.add("show")})),this.loadCaptcha()}hide(){this.state.isVisible=!1,this.elements.overlay.classList.remove("show"),this.elements.modal.classList.remove("show"),this.safeSetTimeout((()=>{this.elements.overlay.style.display="none",document.body.removeChild(this.elements.overlay),this.reset(),this.options.onClose?.(),this.elements.overlay=null}),300)}handleError(t,i,e=null){const s={[PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR]:"网络连接失败,请检查网络设置",[PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR]:"请求超时,请重试",[PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR]:"验证失败,请重试",[PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR]:"图片加载失败,请刷新重试",[PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR]:"验证码数据错误,请刷新重试"}[t]||i||"未知错误";this.options.onError&&this.options.onError({type:t,message:s,originalError:e}),this.showError(s)}async loadCaptcha(){try{this.showLoading(),this.startTime=Date.now(),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.apiUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({timestamp:Date.now(),...this.options.requestData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const i=await t.json();if(!this.validateCaptchaData(i))throw Error("验证码数据格式错误");this.captchaData=i.data,this.showCaptcha(),await this.renderCaptcha()}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.CAPTCHA_DATA_ERROR,t.message,t)}}validateCaptchaData(t){if(!t||"object"!=typeof t)return!1;const i=t.data||t;return["canvasSrc","blockSrc","canvasWidth","canvasHeight","blockWidth","blockHeight","blockY","nonceStr"].every((t=>{const e=i[t];return null!=e&&""!==e}))}async renderCaptcha(){return new Promise(((t,i)=>{let e=!1;const s=[this.loadImageAsync(this.elements.backgroundImg,this.captchaData.canvasSrc,{width:this.captchaData.canvasWidth,height:this.captchaData.canvasHeight}),this.loadImageAsync(this.elements.sliderImg,this.captchaData.blockSrc,{width:this.captchaData.blockWidth,height:this.captchaData.blockHeight,top:this.captchaData.blockY})];Promise.all(s).then((()=>{e||(this.hideLoading(),t())})).catch((t=>{e||(e=!0,this.handleError(PopupSliderCaptcha.ERROR_TYPES.IMAGE_LOAD_ERROR,"图片加载失败",t),i(t))}))}))}async loadImageAsync(t,i,e){return new Promise(((s,a)=>{if(this.imageCache.has(i)){const a=this.imageCache.get(i);return t.src=a.src,this.applyStyles(t,e),s(),void 0}const n=this.safeSetTimeout((()=>{a(Error("图片加载超时"))}),1e4);t.onload=()=>{this.safeClearTimeout(n),this.imageCache.set(i,t.cloneNode()),s()},t.onerror=t=>{this.safeClearTimeout(n),a(t)},t.src=i,this.applyStyles(t,e)}))}applyStyles(t,i){Object.entries(i).forEach((([i,e])=>{t.style[i]="number"==typeof e?e+"px":e}))}showLoading(){this.state.isLoading=!0,this.batchUpdateStyles({container:{display:"block"},loadingText:{display:"flex"},error:{display:"none"}}),this.updateUIState("loading")}hideLoading(){this.state.isLoading=!1,this.batchUpdateStyles({loadingText:{display:"none"}}),this.updateUIState("reset")}showCaptcha(){this.batchUpdateStyles({container:{display:"block"},track:{display:"block"},error:{display:"none"}})}showError(t){this.hideLoading(),this.batchUpdateStyles({error:{display:"block",textContent:t}})}batchUpdateStyles(t){requestAnimationFrame((()=>{Object.entries(t).forEach((([t,i])=>{const e=this.elements[t];e&&Object.entries(i).forEach((([t,i])=>{"textContent"===t?e.textContent=i:e.style[t]=i}))}))}))}async verify(){if(!this.captchaData)return this.onVerifyFail("验证码数据丢失"),void 0;try{this.abortController&&this.abortController.abort(),this.abortController=new AbortController;const t=await fetch(this.options.verifyUrl,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({loginVo:{nonceStr:this.captchaData.nonceStr,value:this.getPosition()},dragEventList:[...this.times],...this.options.verifyData}),signal:this.abortController.signal});if(!t.ok)throw Error(`HTTP ${t.status}: ${t.statusText}`);const i=await t.json();this.isVerifySuccess(i)?this.onVerifySuccess(i.data||i.result):this.onVerifyFail(i.message||i.msg||"验证失败,请重试!")}catch(t){"AbortError"===t.name?this.handleError(PopupSliderCaptcha.ERROR_TYPES.TIMEOUT_ERROR,"验证请求被取消"):t.message.includes("Failed to fetch")||t.message.includes("NetworkError")?this.handleError(PopupSliderCaptcha.ERROR_TYPES.NETWORK_ERROR,"网络连接失败"):this.handleError(PopupSliderCaptcha.ERROR_TYPES.VALIDATION_ERROR,t.message,t)}}isVerifySuccess(t){return!(!t||"object"!=typeof t)&&["0"===t.code,0===t.code,!0===t.success,"success"===t.status,!0===t.result].some((t=>!0===t))}onVerifySuccess(t){const i=this.dragStartTime?Date.now()-this.dragStartTime:Date.now()-this.startTime,e=`验证成功!耗时:${(i/1e3).toFixed(2)}s`;this.updateUIState("success"),this.showFloatingTime(e,"success"),this.safeSetTimeout((()=>{this.options.onSuccess?.({ticket:t,timestamp:Date.now(),duration:i}),this.hide()}),2e3)}showFloatingTime(t,i="success"){const{elements:e}=this;e.floatingTime.textContent=t,e.floatingTime.className="slider-captcha-floating-time "+i,this.safeSetTimeout((()=>e.floatingTime.classList.add("show")),100),this.safeSetTimeout((()=>{e.floatingTime.className="slider-captcha-floating-time"}),2500)}onVerifyFail(t){this.state.retryCount++,this.updateUIState("fail"),this.showFloatingTime(t,"fail"),this.safeSetTimeout((()=>{this.state.retryCount<this.options.maxRetries?this.reset():this.refresh()}),2500)}reset(){this.clearAllTimers(),Object.assign(this.state,{isDragging:!1,currentX:0,startX:0,isLoading:!1}),this.times=[],this.startTime=null,this.dragStartTime=null,this.cachedDimensions=null,requestAnimationFrame((()=>{this.setTransition(!0),this.elements.btn.style.transform="translateX(0px)",this.elements.sliderImg.style.transform="translateX(0px)",this.updateUIState("reset"),this.elements.error.style.display="none"}))}refresh(){this.reset(),this.state.retryCount=0,this.loadCaptcha()}safeSetTimeout(t,i){const e=setTimeout((()=>{this.timers.delete(e),t()}),i);return this.timers.add(e),e}safeClearTimeout(t){t&&(clearTimeout(t),this.timers.delete(t))}clearAllTimers(){this.timers.forEach((t=>{clearTimeout(t),clearInterval(t)})),this.timers.clear(),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}cleanupImages(){this.elements.backgroundImg&&(this.elements.backgroundImg.src="",this.elements.backgroundImg.onload=null,this.elements.backgroundImg.onerror=null),this.elements.sliderImg&&(this.elements.sliderImg.src="",this.elements.sliderImg.onload=null,this.elements.sliderImg.onerror=null),this.imageCache.clear()}throttle(t,i){let e=0;return function(...s){const a=Date.now();if(a-e>=i)return e=a,t.apply(this,s)}}destroy(){try{this.abortController&&(this.abortController.abort(),this.abortController=null),document.body&&(document.body.style.userSelect="",document.body.style.cursor=""),this.clearAllTimers(),this.removeAllEventListeners(),this.cleanupImages(),this.elements?.overlay?.parentNode&&this.elements.overlay.parentNode.removeChild(this.elements.overlay);const t=document.getElementById("slider-captcha-styles");t&&t.remove(),this.imageCache.clear(),this.cachedDimensions=null,Object.keys(this).forEach((t=>{"constructor"!==t&&(this[t]=null)})),this.options.onDestroy&&this.options.onDestroy()}catch(t){void 0}}static create(t){return new PopupSliderCaptcha(t)}static show(t){const i=new PopupSliderCaptcha(t);return i.show(),i}}return"undefined"!=typeof module&&module.exports?(module.exports=PopupSliderCaptcha,module.exports.default=PopupSliderCaptcha):"function"==typeof define&&define.amd?define([],(()=>PopupSliderCaptcha)):"undefined"!=typeof window&&(window.PopupSliderCaptcha=PopupSliderCaptcha,window.SliderCaptcha=PopupSliderCaptcha),PopupSliderCaptcha},void("object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).SliderCaptcha=i());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slider-captcha-sdk",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "纯JavaScript滑块验证码SDK和密码校验工具,无依赖,支持多种模块格式",
5
5
  "type": "module",
6
6
  "main": "dist/index.min.js",
@@ -37,11 +37,14 @@
37
37
  "build": "rollup -c",
38
38
  "build:dev": "rollup -c --environment NODE_ENV:development",
39
39
  "build:prod": "rollup -c --environment NODE_ENV:production",
40
+ "build:analyze": "rollup -c --environment NODE_ENV:production && open dist/bundle-analysis.html",
40
41
  "dev": "rollup -c -w",
41
42
  "prepublishOnly": "npm run build:prod",
42
43
  "test": "echo \"Error: no test specified\" && exit 1",
43
44
  "lint": "eslint src --ext .js --fix",
44
- "clean": "rm -rf dist"
45
+ "clean": "rm -rf dist",
46
+ "size": "npm run build:prod && du -h dist/*.min.js",
47
+ "check": "npm run lint && npm run build:prod"
45
48
  },
46
49
  "keywords": [
47
50
  "captcha",
@@ -72,7 +75,8 @@
72
75
  "eslint": "^8.0.0",
73
76
  "eslint-plugin-vue": "^9.0.0",
74
77
  "rollup": "^3.0.0",
75
- "rollup-plugin-copy": "^3.4.0"
78
+ "rollup-plugin-copy": "^3.4.0",
79
+ "rollup-plugin-visualizer": "^5.9.2"
76
80
  },
77
81
  "dependencies": {
78
82
  "jsencrypt": "^3.3.2"