|
@@ -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',
|
|
@@ -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,
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -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();
|
|
@@ -97,7 +86,7 @@ function handleClick(e: MouseEvent) {
|
|
|
|
|
|
function clear() {
|
|
|
try {
|
|
|
- points.value = [];
|
|
|
+ clearPoints();
|
|
|
} catch (error) {
|
|
|
console.error('Error in clear:', error);
|
|
|
}
|
|
@@ -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);
|
|
|
}
|
|
@@ -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>
|