FxCaptchaPointSelect 点选验证
在验证码图片上按提示顺序点击目标文字位置,点击落点以带序号的圆标标注;点击最后一个已点的位置可倒序撤销重选(只能倒着清,不可跳删中间点)。真实点选的图为目标字与干扰字混排(只点提示指定的目标字,点到干扰字即失败),底部支持提示文本或提示图片,confirm 事件携带坐标序列(序号 + 容器内像素坐标 + 时间戳)交服务端校验。
演示可真点
下面演示的验证码图为内联 SVG 动态生成:「伏 羲 氏」三个目标字 + 3 个随机干扰字混排(同样式,注意区分)。按提示顺序点对三个目标字后点「确认」提示通过;点到干扰字、多点或少点都会判定失败并自动换一题。
vue
<script setup lang="ts">
import { ref } from "vue"
import { FxCaptchaPointSelect } from "@fx/components"
import type { CaptchaPoint } from "@fx/components"
// 目标字 + 干扰字(真实场景由服务端生成:图里混排、答案在服务端)
const TARGETS = ["伏", "羲", "氏"]
const DISTRACTORS = "风花雪月山水人间夜雨春秋星辰天地".split("")
const IMAGE_W = 320
const IMAGE_H = 200
const chars = ref<Array<{ char: string; x: number; y: number; isTarget: boolean }>>([])
const demoImage = ref("")
// 生成验证码图:目标字与干扰字随机混排 + 干扰线(演示用 SVG,真实场景为服务端图片)
function regenerate() {
const slots = [
{ x: 12, y: 22 }, { x: 45, y: 60 }, { x: 82, y: 28 },
{ x: 22, y: 68 }, { x: 62, y: 18 }, { x: 85, y: 66 },
].sort(() => Math.random() - 0.5)
const pool = [...DISTRACTORS].sort(() => Math.random() - 0.5)
chars.value = [
...TARGETS.map((char, i) => ({ char, ...slots[i], isTarget: true })),
...pool.slice(0, 3).map((char, i) => ({ char, ...slots[3 + i], isTarget: false })),
]
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${IMAGE_W}" height="${IMAGE_H}">
<rect width="100%" height="100%" fill="#1e293b"/>
${chars.value.map((c, i) =>
`<text x="${(c.x / 100) * IMAGE_W}" y="${(c.y / 100) * IMAGE_H}" font-size="26"
fill="#e2e8f0" text-anchor="middle">${c.char}</text>`).join("")}
</svg>`
demoImage.value = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`
}
regenerate()
// confirm 校验:点数须与目标字数一致,且第 i 次点击落在第 i 个目标字框内
const onConfirm = (points: CaptchaPoint[], clear: () => void) => {
const targets = chars.value.filter((c) => c.isTarget)
const pass =
points.length === targets.length &&
points.every((p, i) => {
const b = targets[i]
return (
Math.abs((p.x / IMAGE_W) * 100 - b.x) < 8 &&
Math.abs((p.y / IMAGE_H) * 100 - b.y) < 12
)
})
if (pass) {
window.alert("点选校验通过")
} else {
window.alert("点选校验失败,已换一题")
clear()
regenerate()
}
}
</script>
<template>
<FxCaptchaPointSelect
:captcha-image="demoImage"
hint-text="伏, 羲, 氏"
:max="3"
show-confirm
@refresh="regenerate"
@confirm="onConfirm"
/>
</template>真实接入
真实场景验证码图与答案由服务端生成下发(目标字 + 干扰字 + 干扰线/扭曲),前端只负责采集点击坐标并在 confirm 里提交;max 设为目标字数实现定长点选,坐标按图片原始尺寸做比例换算。
Attributes
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| captchaImage | 验证码图片地址 | string | — |
| width / height | 图片宽高(px 或 css 单位) | number | string | 320 / 200 |
| paddingX / paddingY | 卡片水平/垂直内边距(px) | number | 12 / 16 |
| title | 标题 | string | '请按图依次点击' |
| hintText | 底部提示文本 | string | — |
| hintImage | 底部提示图片(优先于 hintText) | string | — |
| showConfirm | 是否展示确认按钮 | boolean | false |
| max | 最多可点选的点位数(达到后忽略点击;配合定长校验。showConfirm 时点满前确认按钮禁用,captchaImage 变化自动清空已点标记,与已点圆标间距过小的重复点击忽略) | number | — |
Slots
| 插槽 | 说明 |
|---|---|
| title | 标题栏(默认标题文字) |
| extra | 标题栏右侧(默认刷新按钮) |
| footer | 底部(默认提示文本/提示图片) |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| click | 单次点击 | (point: CaptchaPoint) |
| confirm | 确认(需 showConfirm) | (points: CaptchaPoint[], clear: () => void) |
| refresh | 点击刷新(圆标已自动清空,换图由消费方处理) | — |
组合形态
卡片容器独立导出为 FxCaptchaPointSelectCard(PointSelectionCaptchaCardProps:captchaImage / width / height / paddingX / paddingY / title + title/extra/default/footer 插槽 + click 事件),需要自绘点选逻辑时可直接组合卡片,完整点选能力用 FxCaptchaPointSelect。