Compare commits
2
Commits
v6.0.0-Vue
...
6.X-Vue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c982ecee68 | ||
|
|
ff19c2a0fb |
@@ -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'
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
window.location.href = '/ie.html';
|
||||
</script>
|
||||
<![endif]-->
|
||||
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
@@ -209,6 +210,9 @@
|
||||
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/tac/load.min.js"></script>
|
||||
<script src="/tac/css/tac.css"></script>
|
||||
<script src="/tac/js/tac.min.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#tianai-captcha-parent{box-shadow:0 0 11px 0 #999;width:318px;height:318px;overflow:hidden;position:relative;z-index:997;box-sizing:border-box;border-radius:5px;padding:8px}#tianai-captcha-parent #tianai-captcha-box{height:260px;width:100%;position:relative;overflow:hidden}#tianai-captcha-parent #tianai-captcha-box .loading{width:120px;height:20px;-webkit-mask:linear-gradient(90deg, #000 70%, rgba(0, 0, 0, 0) 0) 0/20%;background:linear-gradient(#f7b645 0 0) 0/0% no-repeat rgba(221,221,221,.4196078431);animation:cartoon 1s infinite steps(6);margin:120px auto}@keyframes cartoon{100%{background-size:120%}}#tianai-captcha-parent #tianai-captcha-box #tianai-captcha{transform-style:preserve-3d;will-change:transform;transition-duration:.45s;transform:translateX(-300px)}#tianai-captcha-parent #tianai-captcha-bg-img{background-color:#fff;background-position:top;background-size:cover;z-index:-1;width:100%;height:100%;top:0;left:0;position:absolute;border-radius:6px}#tianai-captcha-parent .slider-bottom{height:19px;width:100%}#tianai-captcha-parent .slider-bottom .close-btn{width:20px;height:20px;background-image:url(../../tac/images/icon.png);background-repeat:no-repeat;background-position:0 -14px;float:right;margin-right:2px;cursor:pointer}#tianai-captcha-parent .slider-bottom .refresh-btn{width:20px;height:20px;background-image:url(../../tac/images/icon.png);background-position:0 -167px;background-repeat:no-repeat;float:right;margin-right:10px;cursor:pointer}#tianai-captcha-parent .slider-bottom .logo{height:30px;float:left}#tianai-captcha-parent .slider-move-shadow{animation:myanimation 2s infinite;height:100%;width:5px;background-color:#fff;position:absolute;top:0;left:0;filter:opacity(0.5);box-shadow:1px 1px 1px #fff;border-radius:50%}#tianai-captcha-parent #tianai-captcha-slider-move-track-mask{border-width:1px;border-style:solid;border-color:#00f4ab;width:0;height:32px;background-color:#a9ffe5;opacity:.5;position:absolute;top:-1px;left:-1px;border-radius:5px}
|
||||
#tianai-captcha{text-align:left;box-sizing:content-box;width:300px;height:260px;z-index:999}#tianai-captcha .slider-bottom .logo{height:30px}#tianai-captcha .slider-bottom{height:19px;width:100%}#tianai-captcha .content .tianai-captcha-tips{height:25px;width:100%;position:absolute;bottom:-25px;left:0;z-index:999;font-size:15px;line-height:25px;color:#fff;text-align:center;transition:bottom .3s ease-in-out}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-error{background-color:#ff5d39}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-success{background-color:#39c522}#tianai-captcha .content .tianai-captcha-tips.tianai-captcha-tips-on{bottom:0}#tianai-captcha .content #tianai-captcha-loading{z-index:9999;background-color:#f5f5f5;text-align:center;height:100%;overflow:hidden;position:relative;display:flex;justify-content:center;align-items:center}#tianai-captcha .content #tianai-captcha-loading img{display:block;width:45px;height:45px}#tianai-captcha #tianai-captcha-slider-bg-canvas{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px}#tianai-captcha #tianai-captcha-slider-bg-div{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px}#tianai-captcha #tianai-captcha-slider-bg-div .tianai-captcha-slider-bg-div-slice{position:absolute}@keyframes myanimation{from{left:0}to{left:289px}}
|
||||
#tianai-captcha.tianai-captcha-slider{z-index:999;position:absolute;left:0;top:0;user-select:none}#tianai-captcha.tianai-captcha-slider .content{width:100%;height:180px;position:relative;overflow:hidden}#tianai-captcha.tianai-captcha-slider .bg-img-div{width:100%;height:100%;position:absolute;transform:translate(0px, 0px)}#tianai-captcha.tianai-captcha-slider .bg-img-div img{height:100%;width:100%;border-radius:5px}#tianai-captcha.tianai-captcha-slider .slider-img-div{height:100%;position:absolute;left:0;transform:translate(0px, 0px)}#tianai-captcha.tianai-captcha-slider .slider-img-div #tianai-captcha-slider-move-img{height:100%}#tianai-captcha.tianai-captcha-slider .slider-move{height:34px;width:100%;margin:11px 0;position:relative}#tianai-captcha.tianai-captcha-slider .slider-move-track{position:relative;height:32px;line-height:32px;text-align:center;background:#f5f5f5;color:#999;transition:0s;font-size:14px;box-sizing:content-box;border:1px solid #f5f5f5;border-radius:4px}#tianai-captcha.tianai-captcha-slider .refresh-btn,#tianai-captcha.tianai-captcha-slider .close-btn{display:inline-block}#tianai-captcha.tianai-captcha-slider .slider-move{line-height:38px;font-size:14px;text-align:center;white-space:nowrap;color:#88949d;-moz-user-select:none;-webkit-user-select:none;user-select:none;filter:opacity(0.8)}#tianai-captcha.tianai-captcha-slider .slider-move .slider-move-btn{transform:translate(0px, 0px);position:absolute;top:-6px;left:0;width:63px;height:45px;background-color:#fff;background-repeat:no-repeat;background-size:contain;border-radius:5px}#tianai-captcha.tianai-captcha-slider .slider-tip{margin-bottom:5px;font-weight:bold;font-size:15px;line-height:normal;color:#000}#tianai-captcha.tianai-captcha-slider .slider-move-btn:hover{cursor:pointer}
|
||||
#tianai-captcha.tianai-captcha-rotate .rotate-img-div{height:100%;text-align:center}#tianai-captcha.tianai-captcha-rotate .rotate-img-div img{height:100%;transform:rotate(0deg);display:inline-block}
|
||||
#tianai-captcha.tianai-captcha-concat .tianai-captcha-slider-concat-img-div{background-size:100% 180px;position:absolute;transform:translate(0px, 0px);z-index:1;width:100%}#tianai-captcha.tianai-captcha-concat .tianai-captcha-slider-concat-bg-img{width:100%;height:100%;position:absolute;transform:translate(0px, 0px);background-size:100% 180px}
|
||||
#tianai-captcha.tianai-captcha-disable{z-index:999;position:absolute;left:0;top:0}#tianai-captcha.tianai-captcha-disable .content{width:100%;height:180px;position:relative;overflow:hidden}#tianai-captcha.tianai-captcha-disable .content .bg-img-div{background-image:url(../../tac/images/dun.jpeg);width:100%;height:100%;overflow:hidden}#tianai-captcha.tianai-captcha-disable .content .bg-img-div #content-span{color:#fff;overflow:hidden;margin-top:132px;display:block;text-align:center}
|
||||
#tianai-captcha.tianai-captcha-word-click{box-sizing:border-box}#tianai-captcha.tianai-captcha-word-click .click-tip{position:relative;height:40px;width:100%}#tianai-captcha.tianai-captcha-word-click .click-tip .tip-img{height:35px;position:absolute;right:15px}#tianai-captcha.tianai-captcha-word-click .click-tip #tianai-captcha-click-track-font{font-size:18px;display:inline-block;height:40px;line-height:40px;position:absolute}#tianai-captcha.tianai-captcha-word-click .slider-bottom{position:relative;top:6px}#tianai-captcha.tianai-captcha-word-click .content #bg-img-click-mask{width:100%;height:100%;position:absolute;left:0;top:0}#tianai-captcha.tianai-captcha-word-click .content #bg-img-click-mask .click-span{position:absolute;left:0;top:0;border-radius:50px;background-color:#409eff;width:20px;height:20px;text-align:center;line-height:20px;color:#fff;border:2px solid #fff;box-sizing:content-box}#tianai-captcha.tianai-captcha-word-click .click-confirm-btn{width:100%;height:35px;border-radius:4px;background-image:linear-gradient(173deg, hsl(38.09, 91%, 57.89%) 0%, hsl(38.09, 89.38%, 71.74%) 100%);font-size:15px;text-align:center;box-sizing:border-box;line-height:35px;color:#fff;margin-top:3px}#tianai-captcha.tianai-captcha-word-click .click-confirm-btn:hover{cursor:pointer}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
const Math=window.Math,head=document.getElementsByTagName("head")[0],TIMEOUT=1e4,TAC_LOADING_DIV='<div id="tac-loading" style="\n border: 1px solid #eee;\n /* background-color: #409EFF; */\n border-radius: 5px;\n width: 318px;\n height: 318px;\n line-height: 318px;\n color: #606266;\n text-align: center;\n position: relative;\n box-sizing: border-box;\n">璇风◢绛�...</div>';function showLoading(e){var t=document.querySelector(e);t&&(t.innerHTML=TAC_LOADING_DIV)}function hideLoading(e){let t=document.querySelector(e);t&&(t.innerHTML="")}function loadCaptchaScript(e,t,n,r,o){const i=e.scriptUrls,c=e.cssUrls,l=e.timeout||TIMEOUT;let s=i.length+c.length;function d(e,i){if(s--,e&&0===s){if(hideLoading(t.bindEl),!window.TAC)throw new Error("TAC鏈姞杞斤紝璇锋鏌ュ湴鍧€鏄惁姝g‘");r(new TAC(t,n))}else e||(hideLoading(t.bindEl),o(i))}setTimeout(()=>{0!==s&&showLoading(t.bindEl)},10),i.forEach(function(e){loadResource("string"==typeof e?{url:e}:e,d,"script",l)}),c.forEach(function(e){loadResource("string"==typeof e?{url:e}:e,d,"link",l)})}function loadResource(e,t,n="script",r){if(document.querySelector(`${n}[${"script"===n?"src":"href"}="${e.url}"]`))return void t(!0,e);let o=!1;const i=document.createElement(n);"link"===n?i.rel="stylesheet":i.async=!0,i["script"===n?"src":"href"]=e.url;let c;i.onload=i.onreadystatechange=(()=>{o||i.readyState&&"loaded"!==i.readyState&&"complete"!==i.readyState||function t(n){e.checkOnReady?c=setTimeout(()=>{e.checkOnReady()?n():t(n)},10):n()}(()=>{o=!0,setTimeout(()=>t(o,e),0)})}),i.onerror=(()=>{t(o=!1,e)}),head.appendChild(i),setTimeout(()=>{o||(c&&clearTimeout(c),i.onload=i.onerror=null,i.remove&&i.remove(),t(o,e))},r||TIMEOUT)}function loadTAC(e,t,n){return new Promise((r,o)=>{let i={..."string"==typeof e?{url:e}:e};i.url&&(i.url.endsWith("/")||(i.url+="/"),i.scriptUrls||(i.scriptUrls=[i.url+"js/tac.min.js"]),i.cssUrls||(i.cssUrls=[i.url+"css/tac.css"])),i.scriptUrls&&i.cssUrls?loadCaptchaScript(i,t,n,r,o):o("璇锋寜鐓ф枃妗i厤缃畉ac")})}setTimeout(()=>{let e=document.scripts,t=null;for(let n=0;n<e.length;n++)if(e[n].src.indexOf("load.js")>1||e[n].src.indexOf("load.min.js")>1){t=e[n].src.substring(e[n].src.indexOf("/"),e[n].src.lastIndexOf("/"));break}},100),window.loadCaptchaScript=loadCaptchaScript,window.loadTAC=loadTAC,window.initTAC=loadTAC;
|
||||
+1
-1
@@ -73,7 +73,7 @@ export function logout() {
|
||||
*/
|
||||
export function getCodeImg(): AxiosPromise<VerifyCodeResult> {
|
||||
return request({
|
||||
url: '/auth/code',
|
||||
url: '/resource/captcha/image',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
|
||||
Vendored
+17
@@ -3,6 +3,23 @@ import { LanguageEnum } from '@/enums/LanguageEnum';
|
||||
import { NavTypeEnum } from '@/enums/NavTypeEnum';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/**
|
||||
* 天艾验证码初始化方法
|
||||
* @param assetPath 静态资源目录,public下tac文件夹填 "/tac"
|
||||
* @param config 验证码配置项
|
||||
*/
|
||||
initTAC: (
|
||||
assetPath: string,
|
||||
captchaConfig: {
|
||||
requestCaptchaDataUrl: string;
|
||||
validCaptchaUrl: string;
|
||||
bindEl: string;
|
||||
validSuccess: (res, c, t) => void
|
||||
}
|
||||
, style: { logoUrl: null }) => Promise<{ init: () => void; destroyWindow: () => void }>;
|
||||
}
|
||||
|
||||
/** vue Instance */
|
||||
type ComponentInternalInstance = ComponentInstance;
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// 返回状态定义
|
||||
export type CaptchaResult = -1 | 0 | 1
|
||||
type CaptchaStatusCallback = (status: CaptchaResult) => void
|
||||
|
||||
/**
|
||||
* 唤起 tianai TAC行为验证码
|
||||
* ✅ 动态创建全局遮罩容器,无需模板写div
|
||||
* ✅ 弹窗视口永久居中,全局最高层级
|
||||
* ✅ 完全对齐官方loadTAC配置参数
|
||||
* @param onStatusChange 状态回调,0=未完成
|
||||
* @returns Promise<CaptchaResult> 1成功,-1失败/关闭/加载异常
|
||||
*/
|
||||
export async function showTACCaptcha(onStatusChange?: CaptchaStatusCallback): Promise<CaptchaResult> {
|
||||
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
|
||||
})
|
||||
})
|
||||
}
|
||||
+19
-354
@@ -1,111 +1,29 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<section class="hero-panel">
|
||||
<div class="hero-copy">
|
||||
<h1>RuoYi-Vue-Plus 控制台</h1>
|
||||
<p>
|
||||
企业级后台管理系统 重写 RuoYi-Vue 所有功能 集成 Sa-Token、Mybatis-Plus、WarmFlow、SpringDoc、Hutool、OSS
|
||||
等组件
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<el-button type="primary" @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">查看源码</el-button>
|
||||
<el-button plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')">
|
||||
更新日志
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="content-grid">
|
||||
<section class="section-card">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2>项目矩阵</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-list">
|
||||
<article v-for="product in products" :key="product.name" class="product-card">
|
||||
<div class="product-top">
|
||||
<div>
|
||||
<h3>{{ product.name }}</h3>
|
||||
<p>{{ product.summary }}</p>
|
||||
</div>
|
||||
<span class="product-version">{{ product.version }}</span>
|
||||
</div>
|
||||
<div class="product-tags">
|
||||
<el-tag v-for="tag in product.tags" :key="tag" effect="plain">{{ tag }}</el-tag>
|
||||
</div>
|
||||
<div class="product-actions">
|
||||
<el-button type="primary" plain @click="goTarget(product.primaryUrl)">
|
||||
{{ product.primaryLabel }}
|
||||
</el-button>
|
||||
<el-button plain @click="goTarget(product.secondaryUrl)">{{ product.secondaryLabel }}</el-button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card capability-card">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2>能力地图</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="capability-groups">
|
||||
<article v-for="group in capabilityGroups" :key="group.title" class="capability-group">
|
||||
<h3>{{ group.title }}</h3>
|
||||
<ul>
|
||||
<li v-for="item in group.items" :key="item">{{ item }}</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<button @click="login">登录</button>
|
||||
<div id="captcha-box"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Index" lang="ts">
|
||||
const products = [
|
||||
{
|
||||
name: 'RuoYi-Vue-Plus',
|
||||
version: 'v6.0.0',
|
||||
summary: '面向分布式集群场景的后台管理系统,保持现有业务接口与权限逻辑,适合先完成前端壳升级。',
|
||||
tags: ['Vue 3', 'Element Plus', 'Spring Boot', 'Sa-Token'],
|
||||
primaryLabel: '访问 GitHub',
|
||||
primaryUrl: 'https://github.com/dromara/RuoYi-Vue-Plus',
|
||||
secondaryLabel: '查看更新日志',
|
||||
secondaryUrl: 'https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog'
|
||||
},
|
||||
{
|
||||
name: 'RuoYi-Cloud-Plus',
|
||||
version: 'v6.0.0',
|
||||
summary: '微服务通用权限管理系统,适合更复杂的服务治理场景,也可以沿用同样的前端升级思路。',
|
||||
tags: ['Spring Cloud', 'Gateway', 'Nacos', 'Dubbo'],
|
||||
primaryLabel: '访问 GitHub',
|
||||
primaryUrl: 'https://github.com/dromara/RuoYi-Cloud-Plus',
|
||||
secondaryLabel: '查看更新日志',
|
||||
secondaryUrl: 'https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog'
|
||||
}
|
||||
];
|
||||
<script setup lang="ts">
|
||||
|
||||
const capabilityGroups = [
|
||||
{
|
||||
title: '后端基建',
|
||||
items: ['Spring Boot / Spring Cloud', 'Sa-Token 认证与权限', 'MySQL / Redis', '代码生成器']
|
||||
},
|
||||
{
|
||||
title: '平台能力',
|
||||
items: ['动态菜单与按钮权限', '监控、日志、在线用户', '任务调度与工作流', '文件存储与多云适配']
|
||||
},
|
||||
{
|
||||
title: '前端方向',
|
||||
items: ['UI 卡片化', '主题与布局统一', '通用页面容器规范化', '企业化布局']
|
||||
}
|
||||
];
|
||||
|
||||
const goTarget = (url: string) => {
|
||||
window.open(url, '__blank');
|
||||
};
|
||||
import {CaptchaResult,showTACCaptcha} from "@/utils/captcha";
|
||||
|
||||
async function login() {
|
||||
const captchaRes: CaptchaResult = await showTACCaptcha((status) => {
|
||||
console.log('验证码实时状态:', status)
|
||||
})
|
||||
switch (captchaRes) {
|
||||
case 1:
|
||||
console.log("✅验证码校验成功,拿token调用登录接口")
|
||||
// todo: 在这里调用登录,带上后端返回的token
|
||||
break
|
||||
case -1:
|
||||
console.log("❌ 关闭弹窗 / 加载失败")
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -114,257 +32,4 @@ const goTarget = (url: string) => {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hero-panel,
|
||||
.section-card {
|
||||
border-radius: 28px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.hero-panel {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.3fr) minmax(280px, 0.7fr);
|
||||
gap: 18px;
|
||||
padding: 30px;
|
||||
background: radial-gradient(circle at top left, rgba(53, 109, 255, 0.16), transparent 30%), var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: clamp(30px, 4vw, 46px);
|
||||
line-height: 1.06;
|
||||
letter-spacing: -0.04em;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
max-width: 760px;
|
||||
color: var(--app-text-muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-badge {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(53, 109, 255, 0.12);
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 18px 20px;
|
||||
border-radius: 22px;
|
||||
background: var(--app-elevated-soft-bg);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
strong {
|
||||
color: var(--app-text-title);
|
||||
font-size: 24px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(300px, 0.85fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
|
||||
h2 {
|
||||
margin: 6px 0 0;
|
||||
color: var(--app-text-title);
|
||||
font-size: 26px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
}
|
||||
|
||||
.section-kicker {
|
||||
color: var(--app-text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.product-list,
|
||||
.capability-groups {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
padding: 22px;
|
||||
border-radius: 24px;
|
||||
background: var(--app-elevated-soft-bg);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
transition:
|
||||
transform 0.25s ease,
|
||||
box-shadow 0.25s ease,
|
||||
border-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: rgba(53, 109, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.product-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
color: var(--app-text-title);
|
||||
font-size: 22px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--app-text-muted);
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
.product-version {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(53, 109, 255, 0.12);
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.product-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.product-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.capability-card {
|
||||
background: radial-gradient(circle at top right, rgba(14, 165, 233, 0.12), transparent 28%), var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.capability-group {
|
||||
padding: 18px 18px 18px 20px;
|
||||
border-radius: 22px;
|
||||
background: var(--app-elevated-soft-bg);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
|
||||
h3 {
|
||||
margin: 0 0 12px;
|
||||
color: var(--app-text-title);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
padding-left: 16px;
|
||||
color: var(--app-text-muted);
|
||||
line-height: 1.7;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--app-accent-strong);
|
||||
box-shadow: 0 0 0 5px rgba(53, 109, 255, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.hero-panel,
|
||||
.content-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.hero-panel,
|
||||
.section-card {
|
||||
padding: 20px;
|
||||
border-radius: 22px;
|
||||
}
|
||||
|
||||
.product-top {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
html.dark {
|
||||
.hero-panel {
|
||||
background: radial-gradient(circle at top left, rgba(53, 109, 255, 0.18), transparent 30%), var(--app-surface-bg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
-2
@@ -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), '')
|
||||
|
||||
Reference in New Issue
Block a user