diff --git a/packages/frontend/src/main.ts b/packages/frontend/src/main.ts index 132351d..dc266b9 100644 --- a/packages/frontend/src/main.ts +++ b/packages/frontend/src/main.ts @@ -12,8 +12,8 @@ import './style.css'; import '@fortawesome/fontawesome-free/css/all.min.css'; // 导入 splitpanes CSS import 'splitpanes/dist/splitpanes.css'; -// 导入 vue-recaptcha (用于 v2) -import VueRecaptchaPlugin from 'vue-recaptcha'; +// 恢复导入 reCAPTCHA v3 +import { VueReCaptcha } from 'vue-recaptcha-v3'; const pinia = createPinia(); // 创建 Pinia 实例 pinia.use(piniaPluginPersistedstate); // 使用持久化插件 @@ -24,8 +24,14 @@ app.use(pinia); // 使用配置好的 Pinia 实例 // 注意:在状态初始化完成前,暂时不 use(router) app.use(i18n); // 使用 i18n -// 注册 vue-recaptcha 插件,传递一个空选项对象 -app.use(VueRecaptchaPlugin, {}); +// 恢复初始化 reCAPTCHA v3 +// 重要提示:请将 'YOUR_RECAPTCHA_V3_SITE_KEY' 替换为您从 Google reCAPTCHA 获取的实际 Site Key +app.use(VueReCaptcha, { + siteKey: 'YOUR_RECAPTCHA_V3_SITE_KEY', // <-- 在此处替换您的 Site Key + loaderOptions: { + autoHideBadge: true // 可选:自动隐藏 reCAPTCHA 徽章 + } +}); // --- 应用初始化逻辑 --- // 使用 async IIFE 来允许顶层 await diff --git a/packages/frontend/src/views/LoginView.vue b/packages/frontend/src/views/LoginView.vue index 021a382..a3fd31b 100644 --- a/packages/frontend/src/views/LoginView.vue +++ b/packages/frontend/src/views/LoginView.vue @@ -4,8 +4,7 @@ import { storeToRefs } from 'pinia'; import { useI18n } from 'vue-i18n'; import { useAuthStore } from '../stores/auth.store'; import VueHcaptcha from '@hcaptcha/vue3-hcaptcha'; // <-- Import hCaptcha component -// import { useReCaptcha } from 'vue-recaptcha-v3'; // <-- v3 hook, not needed for v2 widget -import TheVueRecaptcha from 'vue-recaptcha'; // <-- Use a different name for import +import { useReCaptcha } from 'vue-recaptcha-v3'; // <-- Restore reCAPTCHA v3 hook const { t } = useI18n(); const authStore = useAuthStore(); @@ -21,9 +20,10 @@ const twoFactorToken = ref(''); // 用于存储 2FA 验证码 const rememberMe = ref(false); // 新增:记住我状态,默认为 false const captchaToken = ref(null); // NEW: Store CAPTCHA token const captchaError = ref(null); // NEW: Store CAPTCHA specific error -const hcaptchaWidget = ref | null>(null); // Ref for hCaptcha component instance -// const recaptchaInstance = useReCaptcha(); // v3 instance, not needed for v2 widget -// No specific ref needed for VueRecaptcha v2 component usually, events handle token +const hcaptchaWidget = ref | null>(null); // NEW: Ref for hCaptcha component instance + +// --- reCAPTCHA v3 Initialization --- +const recaptchaInstance = useReCaptcha(); // Restore v3 instance // --- CAPTCHA Event Handlers --- @@ -46,7 +46,7 @@ const resetCaptchaWidget = () => { captchaToken.value = null; // Reset hCaptcha if it exists hcaptchaWidget.value?.reset(); - // vue-recaptcha v2 component might reset automatically or not have an explicit method easily accessible via ref + // reCAPTCHA v3 doesn't typically need explicit reset in the same way }; // --- End CAPTCHA Event Handlers --- @@ -56,11 +56,30 @@ const handleSubmit = async () => { captchaError.value = null; // Clear previous CAPTCHA error // --- CAPTCHA Execution & Check --- - // For v2/hCaptcha, the token should already be set by the component's @verify event if (publicCaptchaConfig.value?.enabled && !loginRequires2FA.value) { - // No programmatic execution needed for v2/hCaptcha here. + // Restore If reCAPTCHA v3, execute it now to get the token + if (publicCaptchaConfig.value.provider === 'recaptcha') { + // Check if instance and methods are available + if (recaptchaInstance?.recaptchaLoaded && recaptchaInstance?.executeRecaptcha) { + try { + await recaptchaInstance.recaptchaLoaded(); // Ensure library is loaded + const token = await recaptchaInstance.executeRecaptcha('login'); // Execute with action 'login' + console.log('reCAPTCHA v3 token obtained:', token); + captchaToken.value = token; // Store the obtained token + } catch (reError: any) { + console.error('reCAPTCHA v3 execution failed:', reError); + captchaError.value = t('login.error.captchaLoadFailed'); + return; // Stop submission if reCAPTCHA execution fails + } + } else { + // Handle case where reCAPTCHA is not ready/initialized + console.error('reCAPTCHA v3 not initialized or ready.'); + captchaError.value = t('login.error.captchaLoadFailed'); // Or a more specific error + return; + } + } - // Check if token exists (obtained via @verify callback from either component) + // Check if token exists (for both hCaptcha and reCAPTCHA) if (!captchaToken.value) { captchaError.value = t('login.error.captchaRequired'); return; // Stop submission if CAPTCHA is required but not completed/obtained @@ -163,20 +182,12 @@ onMounted(() => { theme="auto" > - -
- - -

- {{ t('login.recaptchaV3Notice') }} + +

+

+ {{ t('login.recaptchaV3Notice') }}

+