Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Improve the problem of inaccurate captcha accuracy #4401

Merged
merged 16 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { CaptchaPoint } from '../types';

import { reactive } from 'vue';

export function useCaptchaPoints() {
const points = reactive<CaptchaPoint[]>([]);
function addPoint(point: CaptchaPoint) {
points.push(point);
}
function clearPoints() {
points.splice(0, points.length);
}
return {
addPoint,
clearPoints,
points,
};
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<script setup lang="ts">
import type { CaptchaPoint, PointSelectionCaptchaProps } from './types';

import { ref } from 'vue';

import { RotateCw } from '@vben/icons';
import { $t } from '@vben/locales';
import { VbenButton, VbenIconButton } from '@vben-core/shadcn-ui';

import { CaptchaCard } from '.';
import { useCaptchaPoints } from './hooks/useCaptchaPoints';

const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), {
height: '220px',
Expand All @@ -19,44 +18,24 @@ const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), {
title: '',
width: '300px',
});

const emit = defineEmits<{
click: [CaptchaPoint];
confirm: [Array<CaptchaPoint>, clear: () => void];
refresh: [];
}>();
const { addPoint, clearPoints, points } = useCaptchaPoints();

if (!props.hintImage && !props.hintText) {
throw new Error('At least one of hint image or hint text must be provided');
console.warn('At least one of hint image or hint text must be provided');
}

const points = ref<CaptchaPoint[]>([]);
const POINT_OFFSET = 11;

function getElementPosition(element: HTMLElement) {
let posX = 0;
let posY = 0;
if (element.getBoundingClientRect) {
const rect = element.getBoundingClientRect();
const doc = document.documentElement;
posX =
rect.left +
Math.max(doc.scrollLeft, document.body.scrollLeft) -
doc.clientLeft;
posY =
rect.top +
Math.max(doc.scrollTop, document.body.scrollTop) -
doc.clientTop;
} else {
while (element !== document.body) {
posX += element.offsetLeft;
posY += element.offsetTop;
element = element.offsetParent as HTMLElement;
}
}
const rect = element.getBoundingClientRect();
return {
x: posX,
y: posY,
x: rect.left + window.scrollX,
y: rect.top + window.scrollY,
};
}

Expand All @@ -67,25 +46,35 @@ function handleClick(e: MouseEvent) {

const { x: domX, y: domY } = getElementPosition(dom);

const mouseX = e.pageX || e.clientX;
const mouseY = e.pageY || e.clientY;
const mouseX = e.clientX + window.scrollX;
const mouseY = e.clientY + window.scrollY;

if (mouseX === undefined || mouseY === undefined)
throw new Error('Mouse coordinates not found');
if (typeof mouseX !== 'number' || typeof mouseY !== 'number') {
throw new TypeError('Mouse coordinates not found');
}

const xPos = mouseX - domX;
const yPos = mouseY - domY;

const rect = dom.getBoundingClientRect();

// 点击位置边界校验
if (xPos < 0 || yPos < 0 || xPos > rect.width || yPos > rect.height) {
console.warn('Click position is out of the valid range');
return;
}

const x = Math.ceil(xPos);
const y = Math.ceil(yPos);

const point = {
i: points.value.length,
i: points.length,
t: Date.now(),
x,
y,
};
points.value.push(point);

addPoint(point);

emit('click', point);
e.stopPropagation();
Expand All @@ -97,7 +86,7 @@ function handleClick(e: MouseEvent) {

function clear() {
try {
points.value = [];
clearPoints();
} catch (error) {
console.error('Error in clear:', error);
}
Expand All @@ -115,7 +104,7 @@ function handleRefresh() {
function handleConfirm() {
if (!props.showConfirm) return;
try {
emit('confirm', points.value, clear);
emit('confirm', points, clear);
} catch (error) {
console.error('Error in handleConfirm:', error);
}
Expand Down Expand Up @@ -164,6 +153,7 @@ function handleConfirm() {
}"
class="bg-primary text-primary-50 border-primary-50 absolute z-20 flex h-5 w-5 cursor-default items-center justify-center rounded-full border-2"
role="button"
tabindex="0"
>
{{ index + 1 }}
</div>
Expand Down
5 changes: 4 additions & 1 deletion playground/src/views/examples/captcha/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ const handleClick = (point: CaptchaPoint) => {
:description="$t('page.examples.captcha.pageDescription')"
:title="$t('page.examples.captcha.pageTitle')"
>
<Card :title="$t('page.examples.captcha.basic')" class="mb-4">
<Card
:title="$t('page.examples.captcha.basic')"
class="mb-4 overflow-x-auto"
>
<div class="mb-3 flex items-center justify-start">
<Input
v-model:value="params.title"
Expand Down
Loading