feat: 增强版验证码全套实现 — 16种类型生成器/加密/风控/站点管理/平台后端/前端SDK/UI

- 核心新增: ICON_CLICK(PNG图标方案,预渲染资源)/SCRATCH/JIGSAW/CURVE_SLIDER(V1-V3)/ANGLE/CURVE_DRAW/WORD_ORDER_CLICK/PROOF_OF_WORK 等生成器
- 图标点选: classpath PNG 加载替代运行时字体渲染(Linux容器无emoji字体),提示条图作为 templateImage 返回前端
- 新增模块: crypto(AES+RSA)/obfuscator(背景乱序/噪声/扭曲)/risk(风控/IP黑名单/限流)/site(站点管理)/ml(轨迹规则引擎)
- 平台后端: 站点管理/验证码API(generate/verify/secondary-verify)/统计/ML轨迹学习
- 前端SDK: TPCaptcha兼容,支持全部新型号渲染与交互
- 工具: tools/icon-render 图标预渲染工具
This commit is contained in:
abcv7
2026-08-26 09:58:15 +08:00
parent d56958727c
commit dbe9b58b0c
115 changed files with 10365 additions and 36 deletions
@@ -0,0 +1,49 @@
<template>
<n-space vertical :size="24">
<n-h2>仪表盘</n-h2>
<n-grid :cols="4" :x-gap="16" :y-gap="16">
<n-gi><n-statistic label="请求总数" :value="stats.total" /></n-gi>
<n-gi><n-statistic label="通过总数" :value="stats.success" /></n-gi>
<n-gi><n-statistic label="失败总数" :value="stats.fail" /></n-gi>
<n-gi><n-statistic label="总通过率" :value="stats.passRate" /></n-gi>
</n-grid>
<n-h3>验证码类型</n-h3>
<n-grid :cols="4" :x-gap="12" :y-gap="12">
<n-gi v-for="t in types" :key="t.key">
<n-card size="small" :title="t.label">
<template #header-extra><n-tag size="small">{{ t.key }}</n-tag></template>
已注册
</n-card>
</n-gi>
</n-grid>
</n-space>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { NSpace, NH2, NH3, NGrid, NGi, NStatistic, NCard, NTag } from 'naive-ui'
import axios from 'axios'
const stats = ref({ total: 0, success: 0, fail: 0, passRate: '0%' })
const types = [
{ key: 'SLIDER', label: '滑块验证' },
{ key: 'SLIDER_V2', label: '增强滑块' },
{ key: 'ROTATE', label: '旋转验证' },
{ key: 'CONCAT', label: '滑动还原' },
{ key: 'WORD_IMAGE_CLICK', label: '文字点选' },
{ key: 'ICON_CLICK', label: '图标点选' },
{ key: 'WORD_ORDER_CLICK', label: '语序点选' },
{ key: 'CURVE_SLIDER', label: '曲线滑块' },
{ key: 'ANGLE', label: '角度验证' },
{ key: 'SCRATCH', label: '刮刮乐' },
{ key: 'JIGSAW', label: '乱序拼图' },
{ key: 'CURVE_DRAW', label: '曲线绘制' },
]
onMounted(async () => {
try {
const resp = await axios.get('/api/admin/stats?days=7')
if (resp.data?.code === 200) stats.value = resp.data.data
} catch {}
})
</script>