This commit is contained in:
Baobhan Sith
2025-04-25 23:57:47 +08:00
parent a0a73f9142
commit 2199986a97
4 changed files with 121 additions and 25 deletions
+106
View File
@@ -745,6 +745,21 @@
"node": ">=18.12.0"
}
},
"node_modules/@nuxt/schema": {
"version": "3.16.2",
"resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-3.16.2.tgz",
"integrity": "sha512-2HZPM372kuI/uw9VU/hOoYuzv803oZAtyoEKC5dQCQTKAQ293AjypF3WljMXUSReFS/hcbBSgGzYUPHr3Qo+pg==",
"license": "MIT",
"dependencies": {
"consola": "^3.4.2",
"defu": "^6.1.4",
"pathe": "^2.0.3",
"std-env": "^3.8.1"
},
"engines": {
"node": "^14.18.0 || >=16.10.0"
}
},
"node_modules/@peculiar/asn1-android": {
"version": "2.3.16",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.3.16.tgz",
@@ -1916,6 +1931,18 @@
"integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
"license": "MIT"
},
"node_modules/@vueuse/shared": {
"version": "10.11.1",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
"license": "MIT",
"dependencies": {
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.10",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
@@ -5086,6 +5113,18 @@
"wrappy": "1"
}
},
"node_modules/p-defer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-4.0.1.tgz",
"integrity": "sha512-Mr5KC5efvAK5VUptYEIopP1bakB85k2IWXaRC0rsh1uwn1L6M0LVml8OIQ4Gudg4oyZakf7FmeRLkMMtZW1i5A==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
@@ -6646,6 +6685,18 @@
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"license": "Unlicense"
},
"node_modules/type-fest": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
"integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
@@ -7031,6 +7082,32 @@
}
}
},
"node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-i18n": {
"version": "9.14.4",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.4.tgz",
@@ -7057,6 +7134,34 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/vue-recaptcha": {
"version": "3.0.0-alpha.6",
"resolved": "https://registry.npmjs.org/vue-recaptcha/-/vue-recaptcha-3.0.0-alpha.6.tgz",
"integrity": "sha512-hwxxAXENLN6GKJhH6s+NJV1f6llSFEQ0jMTxjEunpR6CMbQ5xc7DAHFtwrsDutGnbS8wl9yp30jsYcCToZEhTQ==",
"license": "MIT",
"workspaces": [
".",
"docs"
],
"dependencies": {
"@nuxt/kit": "^3.4.3",
"@nuxt/schema": "^3.4.3",
"@vueuse/shared": "^10.1.0",
"defu": "^6.1.2",
"p-defer": "^4.0.0",
"std-env": "^3.3.2",
"type-fest": "^3.9.0",
"vue-demi": "^0.14.0"
},
"peerDependencies": {
"vue": "^3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-recaptcha-v3": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/vue-recaptcha-v3/-/vue-recaptcha-v3-2.0.1.tgz",
@@ -7389,6 +7494,7 @@
"vite-plugin-monaco-editor": "^1.1.0",
"vue": "^3.3.0",
"vue-i18n": "^9.14.4",
"vue-recaptcha": "^3.0.0-alpha.6",
"vue-recaptcha-v3": "^2.0.1",
"vue-router": "^4.5.0",
"vuedraggable": "^4.1.0",
+1
View File
@@ -23,6 +23,7 @@
"vite-plugin-monaco-editor": "^1.1.0",
"vue": "^3.3.0",
"vue-i18n": "^9.14.4",
"vue-recaptcha": "^3.0.0-alpha.6",
"vue-recaptcha-v3": "^2.0.1",
"vue-router": "^4.5.0",
"vuedraggable": "^4.1.0",
+4 -10
View File
@@ -12,8 +12,8 @@ import './style.css';
import '@fortawesome/fontawesome-free/css/all.min.css';
// 导入 splitpanes CSS
import 'splitpanes/dist/splitpanes.css';
// 导入 reCAPTCHA v3
import { VueReCaptcha } from 'vue-recaptcha-v3';
// 导入 vue-recaptcha (用于 v2)
import VueRecaptchaPlugin from 'vue-recaptcha';
const pinia = createPinia(); // 创建 Pinia 实例
pinia.use(piniaPluginPersistedstate); // 使用持久化插件
@@ -24,14 +24,8 @@ app.use(pinia); // 使用配置好的 Pinia 实例
// 注意:在状态初始化完成前,暂时不 use(router)
app.use(i18n); // 使用 i18n
// 初始化 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 徽章
}
});
// 注册 vue-recaptcha 插件,传递一个空选项对象
app.use(VueRecaptchaPlugin, {});
// --- 应用初始化逻辑 ---
// 使用 async IIFE 来允许顶层 await
+10 -15
View File
@@ -5,7 +5,7 @@ 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
// TODO: Import a reCAPTCHA v2 component, e.g., import VueRecaptchaV2 from 'vue-recaptcha-v2';
import TheVueRecaptcha from 'vue-recaptcha'; // <-- Use a different name for import
const { t } = useI18n();
const authStore = useAuthStore();
@@ -21,9 +21,9 @@ const twoFactorToken = ref(''); // 用于存储 2FA 验证码
const rememberMe = ref(false); // 新增:记住我状态,默认为 false
const captchaToken = ref<string | null>(null); // NEW: Store CAPTCHA token
const captchaError = ref<string | null>(null); // NEW: Store CAPTCHA specific error
const hcaptchaWidget = ref<InstanceType<typeof VueHcaptcha> | null>(null); // NEW: Ref for hCaptcha component instance
const hcaptchaWidget = ref<InstanceType<typeof VueHcaptcha> | null>(null); // Ref for hCaptcha component instance
// const recaptchaInstance = useReCaptcha(); // v3 instance, not needed for v2 widget
const recaptchaV2Widget = ref<any | null>(null); // NEW: Ref for reCAPTCHA v2 component instance (replace 'any' with actual type)
// No specific ref needed for VueRecaptcha v2 component usually, events handle token
// --- CAPTCHA Event Handlers ---
@@ -46,8 +46,7 @@ const resetCaptchaWidget = () => {
captchaToken.value = null;
// Reset hCaptcha if it exists
hcaptchaWidget.value?.reset();
// Reset reCAPTCHA v2 if it exists and component supports it
// recaptchaV2Widget.value?.reset(); // Assuming the component has a reset method
// vue-recaptcha v2 component might reset automatically or not have an explicit method easily accessible via ref
};
// --- End CAPTCHA Event Handlers ---
@@ -164,21 +163,17 @@ onMounted(() => {
theme="auto"
></VueHcaptcha>
</div>
<!-- reCAPTCHA v2 Component Placeholder -->
<!-- Google reCAPTCHA v2 Component -->
<div v-else-if="publicCaptchaConfig?.provider === 'recaptcha' && publicCaptchaConfig.recaptchaSiteKey">
<!-- TODO: Replace this with an actual reCAPTCHA v2 component -->
<!-- Example using a hypothetical VueRecaptchaV2 component:
<VueRecaptchaV2
ref="recaptchaV2Widget"
<!-- @ts-ignore - Bypassing type check due to potential library type definition issues -->
<TheVueRecaptcha
:sitekey="publicCaptchaConfig.recaptchaSiteKey"
@verify="handleCaptchaVerified"
@expired="handleCaptchaExpired"
@error="handleCaptchaError"
></VueRecaptchaV2>
-->
<div class="p-4 border border-dashed border-warning text-warning bg-warning/10 rounded">
此处需要集成 Google reCAPTCHA v2 组件
</div>
theme="light"
size="normal"
></TheVueRecaptcha>
<p class="text-xs text-text-secondary italic mt-1">
{{ t('login.recaptchaV3Notice') }} <!-- Keep the notice -->
</p>