|
|
@@ -0,0 +1,207 @@
|
|
|
+<script lang="ts" setup>
|
|
|
+const dialogVisible = ref(false)
|
|
|
+
|
|
|
+const openDialog = () => {
|
|
|
+ dialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const position = ref(0)
|
|
|
+const isDragging = ref(false)
|
|
|
+const verifyText = ref('Swipe right to verify')
|
|
|
+const sliderState = ref<'start' | 'success' | 'error' | 'dragging'>('start')
|
|
|
+const styleMap = {
|
|
|
+ start: {
|
|
|
+ thumbColor: 'var(--color-neutral-1)',
|
|
|
+ thumbIcon: 'icon-icon_drag__line_b',
|
|
|
+ trackBackground: '#87909e'
|
|
|
+ },
|
|
|
+ dragging: {
|
|
|
+ thumbColor: 'var(--color-neutral-1)',
|
|
|
+ thumbIcon: 'icon-icon_drag__line_b',
|
|
|
+ trackBackground: 'var(--color-success)'
|
|
|
+ },
|
|
|
+ success: {
|
|
|
+ thumbColor: '#fff',
|
|
|
+ thumbIcon: 'icon-icon_confirm_b',
|
|
|
+ trackBackground: 'var(--color-success)'
|
|
|
+ },
|
|
|
+ error: {
|
|
|
+ thumbColor: '#fff',
|
|
|
+ thumbIcon: 'icon-icon_reject_b',
|
|
|
+ trackBackground: '#c7353f'
|
|
|
+ }
|
|
|
+}
|
|
|
+const trackRef = ref<HTMLElement | null>(null)
|
|
|
+
|
|
|
+const getTrackBackground = () => {
|
|
|
+ const trackWidth = trackRef.value?.offsetWidth || 320
|
|
|
+ const progress = (position.value / (trackWidth - 40)) * 100 // 百分比
|
|
|
+ if (sliderState.value === 'start') {
|
|
|
+ return styleMap.start.trackBackground // 初始时灰色
|
|
|
+ } else if (sliderState.value === 'dragging') {
|
|
|
+ return `linear-gradient(90deg, ${styleMap.success.trackBackground} ${progress}%, ${styleMap.start.trackBackground} ${progress}%)`
|
|
|
+ } else if (sliderState.value === 'error') {
|
|
|
+ return `linear-gradient(90deg, ${styleMap.error.trackBackground} ${progress}%, ${styleMap.start.trackBackground} ${progress}%)`
|
|
|
+ }
|
|
|
+ return styleMap.success.trackBackground // 成功时整条绿色
|
|
|
+}
|
|
|
+
|
|
|
+const startDrag = () => {
|
|
|
+ if (sliderState.value === 'success') {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ isDragging.value = true
|
|
|
+ document.addEventListener('mousemove', onDrag)
|
|
|
+ document.addEventListener('mouseup', stopDrag)
|
|
|
+}
|
|
|
+
|
|
|
+const onDrag = (event: MouseEvent) => {
|
|
|
+ if (isDragging.value) {
|
|
|
+ if (trackRef.value) {
|
|
|
+ sliderState.value = 'dragging'
|
|
|
+ verifyText.value = 'Swipe right to verify'
|
|
|
+ const rect = trackRef.value.getBoundingClientRect()
|
|
|
+ const offsetX = event.clientX - rect.left
|
|
|
+ position.value = Math.min(Math.max(offsetX, 0), rect.width - 40) // 40是滑块的宽度
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const emit = defineEmits<{
|
|
|
+ verifySuccess: []
|
|
|
+}>()
|
|
|
+const stopDrag = () => {
|
|
|
+ isDragging.value = false
|
|
|
+ document.removeEventListener('mousemove', onDrag)
|
|
|
+ document.removeEventListener('mouseup', stopDrag)
|
|
|
+
|
|
|
+ if (trackRef.value) {
|
|
|
+ const trackWidth = trackRef.value.offsetWidth
|
|
|
+ if (position.value >= trackWidth - 40) {
|
|
|
+ sliderState.value = 'success'
|
|
|
+ verifyText.value = 'Verification successful'
|
|
|
+ setTimeout(() => {
|
|
|
+ dialogVisible.value = false
|
|
|
+ emit('verifySuccess')
|
|
|
+ }, 500)
|
|
|
+ } else {
|
|
|
+ sliderState.value = 'error'
|
|
|
+ verifyText.value = 'Verification failed'
|
|
|
+ setTimeout(() => {
|
|
|
+ sliderState.value = 'start'
|
|
|
+ verifyText.value = 'Swipe right to verify'
|
|
|
+ position.value = 0
|
|
|
+ }, 3000)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const moveSlider = (event: MouseEvent) => {
|
|
|
+ if (sliderState.value !== 'success') {
|
|
|
+ onDrag(event)
|
|
|
+ }
|
|
|
+}
|
|
|
+defineExpose({
|
|
|
+ openDialog
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <el-dialog top="30vh" v-model="dialogVisible" width="400" class="slide-verify-dialog">
|
|
|
+ <div class="content">
|
|
|
+ <p>Please drag the slider below to complete the</p>
|
|
|
+ <p>verification to ensure normal access</p>
|
|
|
+ <div class="slider-container">
|
|
|
+ <div
|
|
|
+ class="slider-track"
|
|
|
+ :style="{ background: getTrackBackground() }"
|
|
|
+ @click="moveSlider"
|
|
|
+ ref="trackRef"
|
|
|
+ >
|
|
|
+ {{ verifyText }}
|
|
|
+ <div
|
|
|
+ class="slider-thumb"
|
|
|
+ :style="{ left: `${position}px`, borderColor: styleMap[sliderState].trackBackground }"
|
|
|
+ @mousedown="startDrag"
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ v-if="sliderState === 'start' || sliderState === 'dragging'"
|
|
|
+ class="font_family"
|
|
|
+ :style="{ color: styleMap[sliderState].thumbColor }"
|
|
|
+ :class="[styleMap[sliderState].thumbIcon]"
|
|
|
+ ></span>
|
|
|
+ <span
|
|
|
+ v-else
|
|
|
+ class="font_family other-state"
|
|
|
+ :style="{
|
|
|
+ color: styleMap[sliderState].thumbColor,
|
|
|
+ backgroundColor: styleMap[sliderState].trackBackground
|
|
|
+ }"
|
|
|
+ :class="[styleMap[sliderState].thumbIcon]"
|
|
|
+ ></span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.content {
|
|
|
+ padding: 40px 0;
|
|
|
+ text-align: center;
|
|
|
+ & > p {
|
|
|
+ line-height: 21px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.slider-container {
|
|
|
+ width: 320px;
|
|
|
+ margin: 16px auto 0;
|
|
|
+ text-align: center;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+.slider-track {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 40px;
|
|
|
+ padding: 1px;
|
|
|
+ background: #868f9d;
|
|
|
+ border-radius: 6px;
|
|
|
+ line-height: 38px;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+.slider-thumb {
|
|
|
+ position: absolute;
|
|
|
+ top: 0px;
|
|
|
+ left: 10px;
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ background: #fff;
|
|
|
+ cursor: pointer;
|
|
|
+ border-radius: 6px;
|
|
|
+ border: 1px solid #868f9d;
|
|
|
+ .font_family {
|
|
|
+ font-size: 14px;
|
|
|
+ &.other-state {
|
|
|
+ height: 16px;
|
|
|
+ width: 16px;
|
|
|
+ padding: 1px;
|
|
|
+ border-radius: 50%;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.slide-verify-dialog {
|
|
|
+ .el-dialog__header {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|