From c982ecee68d72bea341b27a582f9f84cd08ce36a Mon Sep 17 00:00:00 2001
From: xiaozhi <1822691309@qq.com>
Date: Thu, 10 Sep 2026 20:31:00 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A1=8C=E4=B8=BA=E9=AA=8C?=
=?UTF-8?q?=E8=AF=81=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.development | 3 +
src/components/Captcha/index.vue | 11 ----
src/utils/captcha.ts | 94 ++++++++++++++++++++++++++++++++
src/views/index.vue | 47 ++++++----------
src/views/login.vue | 22 ++++++++
vite.config.ts | 4 +-
6 files changed, 137 insertions(+), 44 deletions(-)
delete mode 100644 src/components/Captcha/index.vue
create mode 100644 src/utils/captcha.ts
diff --git a/.env.development b/.env.development
index 5bfb9da..bb23c99 100644
--- a/.env.development
+++ b/.env.development
@@ -2,6 +2,9 @@
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
+# 后台请求地址
+VITE_API_TARGET=http://localhost:8080
+
# 开发环境配置
VITE_APP_ENV = 'development'
diff --git a/src/components/Captcha/index.vue b/src/components/Captcha/index.vue
deleted file mode 100644
index 63cf316..0000000
--- a/src/components/Captcha/index.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
diff --git a/src/utils/captcha.ts b/src/utils/captcha.ts
new file mode 100644
index 0000000..80f9beb
--- /dev/null
+++ b/src/utils/captcha.ts
@@ -0,0 +1,94 @@
+// 返回状态定义
+export type CaptchaResult = -1 | 0 | 1
+type CaptchaStatusCallback = (status: CaptchaResult) => void
+
+/**
+ * 唤起 tianai TAC行为验证码
+ * ✅ 动态创建全局遮罩容器,无需模板写div
+ * ✅ 弹窗视口永久居中,全局最高层级
+ * ✅ 完全对齐官方loadTAC配置参数
+ * @param onStatusChange 状态回调,0=未完成
+ * @returns Promise 1成功,-1失败/关闭/加载异常
+ */
+export async function showTACCaptcha(onStatusChange?: CaptchaStatusCallback): Promise {
+ await nextTick()
+ return new Promise((resolve) => {
+ // ========= 动态创建全局全屏遮罩容器 =========
+ const maskWrap = document.createElement('div')
+ const tempId = 'tac-captcha-temp-' + Date.now()
+ maskWrap.id = tempId
+
+ // 全局顶层全屏遮罩 + flex居中
+ maskWrap.style.position = 'fixed'
+ maskWrap.style.top = '0'
+ maskWrap.style.left = '0'
+ maskWrap.style.width = '100vw'
+ maskWrap.style.height = '100vh'
+ maskWrap.style.zIndex = '99999'
+ maskWrap.style.backgroundColor = 'rgba(0,0,0,0.5)'
+ maskWrap.style.display = 'flex'
+ maskWrap.style.alignItems = 'center'
+ maskWrap.style.justifyContent = 'center'
+ maskWrap.style.margin = '0'
+ maskWrap.style.padding = '0'
+ maskWrap.style.overflow = 'hidden'
+ document.body.appendChild(maskWrap)
+
+ const bindSelector = `#${tempId}`
+ // 打开弹窗,状态=未完成
+ onStatusChange?.(0)
+
+ const captchaConfig = {
+ // 请求验证码接口
+ requestCaptchaDataUrl: "http://localhost:8080/resource/captcha/behavior/generate",
+ // 验证验证码接口
+ validCaptchaUrl: "http://localhost:8080/resource/captcha/behavior/verify",
+ // 绑定的div
+ bindEl: bindSelector,
+ // 验证成功回调函数
+ validSuccess: (res, c, t) => {
+ console.log("验证码验证成功回调...")
+ // 销毁验证码
+ t.destroyWindow();
+ document.body.removeChild(maskWrap)
+ console.log(res)
+ onStatusChange?.(1)
+ resolve(1) // ✅ 成功,返回1,await解除阻塞
+ },
+ // 验证失败的回调函数(可忽略,如果不自定义 validFail 方法时,会使用默认的)
+ validFail: (res, c, t) => {
+ console.log("验证码验证失败回调...");
+ // 验证失败后重新拉取验证码
+ t.reloadCaptcha();
+ },
+ // 刷新按钮回调事件
+ btnRefreshFun: (el, tac) => {
+ console.log("刷新按钮触发事件...");
+ tac.reloadCaptcha();
+ },
+ // 关闭按钮回调事件
+ btnCloseFun: (el, tac) => {
+ console.log("关闭按钮触发事件...");
+ tac.destroyWindow();
+ document.body.removeChild(maskWrap)
+ onStatusChange?.(-1)
+ resolve(-1) // ✅ 用户关闭弹窗,返回 -1
+ }
+ }
+ // 一些样式配置, 可不传
+ let style = {
+ logoUrl: null// 去除logo
+ }
+ // 参数1 为 tac文件是目录地址, 目录里包含 tac的js和css等文件
+ // 参数2 为 tac验证码相关配置
+ // 参数3 为 tac窗口一些样式配置
+ window.initTAC("./tac", captchaConfig, style).then(tac => {
+ tac.init(); // 调用init则显示验证码
+ }).catch(e => {
+ console.log("初始化tac失败", e);
+ document.body.removeChild(maskWrap)
+ onStatusChange?.(-1)
+ resolve(-1) // ✅ 初始化异常,返回 -1
+ })
+ })
+}
diff --git a/src/views/index.vue b/src/views/index.vue
index a8c1e39..6ed16a7 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -5,39 +5,24 @@
-
diff --git a/src/views/login.vue b/src/views/login.vue
index e360dcf..bfb11c9 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -121,6 +121,7 @@ import { authRouterUrl } from '@/api/system/social/auth';
import { LoginData } from '@/api/types';
import { HttpStatus } from '@/enums/RespEnum';
import { useUserStore } from '@/store/modules/user';
+import {CaptchaResult, showTACCaptcha} from "@/utils/captcha";
const title = import.meta.env.VITE_APP_TITLE;
const currentYear = new Date().getFullYear();
@@ -185,6 +186,26 @@ const handleLogin = () => {
loginRef.value?.validate(async (valid: boolean, fields: any) => {
if (valid) {
loading.value = true;
+ const captchaRes: CaptchaResult = await showTACCaptcha((status) => {
+ console.log('验证码实时状态:', status)
+ })
+ console.log("captchaRes结果:", captchaRes) // 打印看实际返回值!重点调试
+
+ switch (captchaRes) {
+ case 1:
+ console.log("✅验证码校验成功,拿token调用登录接口")
+ break // 跳出switch,继续往下执行登录
+ case -1:
+ console.log("❌ 关闭弹窗 / 加载失败")
+ loading.value = false;
+ return; // 退出当前async回调,不再执行后面登录代码
+ default:
+ console.log("⚠️ 验证码其他状态", captchaRes)
+ loading.value = false;
+ return;
+ }
+
+ // ========== 验证码成功才会执行到这里 ==========
if (loginForm.value.rememberMe) {
localStorage.setItem('username', String(loginForm.value.username));
localStorage.setItem('rememberMe', String(loginForm.value.rememberMe));
@@ -210,6 +231,7 @@ const handleLogin = () => {
});
};
+
const getCode = async () => {
const res = await getCodeImg();
const { data } = res;
diff --git a/vite.config.ts b/vite.config.ts
index fe142c7..a896a6c 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,5 +1,5 @@
import { defineConfig, loadEnv } from 'vite';
-import createPlugins from './vite/plugins';
+import createPlugins from './vite/plugins/index';
import autoprefixer from 'autoprefixer'; // css自动添加兼容性前缀
export default defineConfig(({ mode, command }) => {
@@ -30,7 +30,7 @@ export default defineConfig(({ mode, command }) => {
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
- target: 'http://localhost:8080',
+ target: env.VITE_API_TARGET,
changeOrigin: true,
ws: true,
rewrite: path => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')