diff --git a/web/admin/src/components/SlideCaptcha.vue b/web/admin/src/components/SlideCaptcha.vue index 253cd2b..5142ab9 100644 --- a/web/admin/src/components/SlideCaptcha.vue +++ b/web/admin/src/components/SlideCaptcha.vue @@ -106,6 +106,7 @@ onBeforeUnmount(() => { window.removeEventListener('scroll', onScroll, true) window.removeEventListener('resize', onResize) document.removeEventListener('mousedown', onClickOutside) + releaseDrag() }) watch(activated, (val) => { @@ -179,6 +180,12 @@ function emitValue() { }) } +function releaseDrag() { + document.removeEventListener('pointermove', onPointerMove) + document.removeEventListener('pointerup', onPointerUp) + document.removeEventListener('pointercancel', onPointerCancel) +} + function onPointerDown(e: PointerEvent) { if (!canInteract.value) return e.preventDefault() @@ -186,7 +193,9 @@ function onPointerDown(e: PointerEvent) { isReturning.value = false startX.value = e.clientX startLeft.value = sliderLeft.value - thumbRef.value?.setPointerCapture(e.pointerId) + document.addEventListener('pointermove', onPointerMove) + document.addEventListener('pointerup', onPointerUp) + document.addEventListener('pointercancel', onPointerCancel) } function onPointerMove(e: PointerEvent) { @@ -195,9 +204,15 @@ function onPointerMove(e: PointerEvent) { sliderLeft.value = Math.max(0, Math.min(maxSliderLeft.value, startLeft.value + delta)) } +function onPointerCancel() { + isDragging.value = false + releaseDrag() +} + async function onPointerUp() { if (!isDragging.value) return isDragging.value = false + releaseDrag() if (!captchaData.value || sliderLeft.value < 5) { springBack() return @@ -292,8 +307,6 @@ function delay(ms: number) { :class="{ 'sc-thumb--on': isDragging, 'sc-thumb--ok': status === 'success' }" :style="{ left: sliderLeft + 'px' }" @pointerdown="onPointerDown" - @pointermove="onPointerMove" - @pointerup="onPointerUp" > @@ -346,6 +359,7 @@ function delay(ms: number) { z-index: 9999; background: #fff; border-radius: 12px; + touch-action: none; border: 1px solid #e5e7eb; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06); overflow: hidden; @@ -490,6 +504,7 @@ function delay(ms: number) { position: relative; height: 48px; background: #fafbfc; + touch-action: none; border-top: 1px solid #f3f4f6; } diff --git a/web/tenant/src/components/common/SlideCaptcha.vue b/web/tenant/src/components/common/SlideCaptcha.vue index 0b6c667..f82d63d 100644 --- a/web/tenant/src/components/common/SlideCaptcha.vue +++ b/web/tenant/src/components/common/SlideCaptcha.vue @@ -118,6 +118,7 @@ onBeforeUnmount(() => { window.removeEventListener('scroll', onScroll, true) window.removeEventListener('resize', onResize) document.removeEventListener('mousedown', onClickOutside) + releaseDrag() }) watch(activated, (val) => { @@ -191,6 +192,12 @@ function emitValue() { }) } +function releaseDrag() { + document.removeEventListener('pointermove', onPointerMove) + document.removeEventListener('pointerup', onPointerUp) + document.removeEventListener('pointercancel', onPointerCancel) +} + function onPointerDown(e: PointerEvent) { if (!canInteract.value) return e.preventDefault() @@ -198,7 +205,9 @@ function onPointerDown(e: PointerEvent) { isReturning.value = false startX.value = e.clientX startLeft.value = sliderLeft.value - thumbRef.value?.setPointerCapture(e.pointerId) + document.addEventListener('pointermove', onPointerMove) + document.addEventListener('pointerup', onPointerUp) + document.addEventListener('pointercancel', onPointerCancel) } function onPointerMove(e: PointerEvent) { @@ -207,9 +216,15 @@ function onPointerMove(e: PointerEvent) { sliderLeft.value = Math.max(0, Math.min(maxSliderLeft.value, startLeft.value + delta)) } +function onPointerCancel() { + isDragging.value = false + releaseDrag() +} + async function onPointerUp() { if (!isDragging.value) return isDragging.value = false + releaseDrag() if (!captchaData.value || sliderLeft.value < 5) { springBack() return @@ -289,9 +304,6 @@ function delay(ms: number) {
-
@@ -306,7 +318,7 @@ function delay(ms: number) {
-
+
@@ -318,13 +330,11 @@ function delay(ms: number) { class="sc-thumb absolute top-[2px] w-11 h-11 rounded-xl flex items-center justify-center z-[2] transition-[box-shadow,border-color,color,background] duration-200" :class="{ 'bg-white border border-gray-200 text-gray-400 shadow-sm cursor-grab hover:shadow-md hover:text-gray-500': !isDragging && status !== 'success', - '!cursor-grabbing shadow-lg border-primary-300 text-primary-500 scale-[1.03]': isDragging, + 'bg-white border border-primary-300 text-primary-500 shadow-lg !cursor-grabbing': isDragging, 'bg-emerald-500 border-emerald-500 text-white shadow-md shadow-emerald-500/30 !cursor-default': status === 'success', }" :style="{ left: sliderLeft + 'px' }" @pointerdown="onPointerDown" - @pointermove="onPointerMove" - @pointerup="onPointerUp" >
@@ -371,6 +381,7 @@ function delay(ms: number) { z-index: 9999; background: #fff; border-radius: 16px; + touch-action: none; border: 1px solid #e5e7eb; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1), 0 2px 8px rgba(0, 0, 0, 0.05); overflow: hidden;