360 lines
8.2 KiB
Vue
360 lines
8.2 KiB
Vue
<template>
|
||
<div class="login-container">
|
||
<!-- 动态网格背景 -->
|
||
<div class="bg-grid"></div>
|
||
<!-- 旋转光晕 -->
|
||
<div class="bg-glow"></div>
|
||
<!-- 飘浮粒子 -->
|
||
<div class="particles"></div>
|
||
|
||
<el-card class="login-card" :body-style="{ padding: '40px' }">
|
||
<div class="login-header">
|
||
<h2>星阙 · 终端系统</h2>
|
||
<p>专业护航 · 安全高效</p >
|
||
</div>
|
||
|
||
<el-form :model="form" label-width="0" class="login-form">
|
||
<el-form-item>
|
||
<el-input
|
||
v-model="form.username"
|
||
placeholder="账号"
|
||
:prefix-icon="User"
|
||
size="large"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-input
|
||
v-model="form.password"
|
||
type="password"
|
||
placeholder="密码"
|
||
:prefix-icon="Lock"
|
||
size="large"
|
||
show-password
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-input
|
||
v-model="form.ip"
|
||
placeholder="动态码"
|
||
:prefix-icon="Key"
|
||
size="large"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button
|
||
type="primary"
|
||
:loading="loading"
|
||
@click="handleLogin"
|
||
size="large"
|
||
class="login-btn"
|
||
>
|
||
登录
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { reactive, ref, onMounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { ElMessage } from 'element-plus'
|
||
import { User, Lock, Key } from '@element-plus/icons-vue'
|
||
import axios from 'axios' // 直接使用 axios,不经过封装的 request
|
||
import { JITUAN_API, getDefaultLandingPath, setAdminClubContext, setMenuAccess } from '@/utils/club-context'
|
||
|
||
const router = useRouter()
|
||
const loading = ref(false)
|
||
|
||
const form = reactive({
|
||
username: '',
|
||
password: '',
|
||
ip: ''
|
||
})
|
||
|
||
onMounted(() => {
|
||
const token = localStorage.getItem('token')
|
||
if (token) {
|
||
router.push('/')
|
||
}
|
||
})
|
||
|
||
const handleLogin = async () => {
|
||
if (!form.username || !form.password || !form.ip) {
|
||
ElMessage.warning('请填写完整信息')
|
||
return
|
||
}
|
||
|
||
loading.value = true
|
||
try {
|
||
// 直接使用 axios 发送请求,baseURL 从 window.$baseURL 获取
|
||
const response = await axios.post(
|
||
window.$baseURL + JITUAN_API.kefuLogin,
|
||
{
|
||
phone: form.username,
|
||
password: form.password,
|
||
erjimima: form.ip
|
||
},
|
||
{
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
}
|
||
)
|
||
|
||
// 后端返回的数据结构为 { code, msg, data }
|
||
const res = response.data
|
||
|
||
if (res.code === 0) {
|
||
const token = res.data.token
|
||
localStorage.setItem('token', token)
|
||
localStorage.setItem('username', form.username)
|
||
if (res.data.club_context) {
|
||
const ctx = { ...res.data.club_context }
|
||
if (res.data.menu_access) {
|
||
setMenuAccess(res.data.menu_access)
|
||
ctx.can_switch_club = res.data.menu_access.can_switch_club
|
||
}
|
||
setAdminClubContext(ctx)
|
||
} else if (res.data.menu_access) {
|
||
setMenuAccess(res.data.menu_access)
|
||
}
|
||
const ma = res.data.menu_access || {}
|
||
const landing = getDefaultLandingPath(ma)
|
||
ElMessage.success('登录成功')
|
||
router.push(landing || '/welcome')
|
||
} else if (res.code === 4) {
|
||
// 账号被封禁
|
||
ElMessage.error('账号已被封禁')
|
||
} else {
|
||
// 其他错误统一提示
|
||
ElMessage.error('登录失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('登录异常:', error)
|
||
// 网络错误或后端未返回,统一提示
|
||
ElMessage.error('登录失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 整体容器 - 确保全屏无白边 */
|
||
.login-container {
|
||
position: relative;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
background: linear-gradient(135deg, #0b0c10 0%, #1a1f2f 100%);
|
||
overflow: hidden;
|
||
font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
/* 动态网格背景 - 缓慢移动 */
|
||
.bg-grid {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 200%;
|
||
height: 200%;
|
||
background-image:
|
||
linear-gradient(rgba(0, 255, 255, 0.05) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(0, 255, 255, 0.05) 1px, transparent 1px);
|
||
background-size: 50px 50px;
|
||
transform-origin: 0 0;
|
||
animation: moveGrid 20s linear infinite;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@keyframes moveGrid {
|
||
0% { transform: translate(0, 0); }
|
||
100% { transform: translate(-50px, -50px); }
|
||
}
|
||
|
||
/* 旋转光晕 */
|
||
.bg-glow {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 80vmax;
|
||
height: 80vmax;
|
||
transform: translate(-50%, -50%);
|
||
background: radial-gradient(circle at center, rgba(0, 255, 255, 0.15) 0%, transparent 70%);
|
||
filter: blur(60px);
|
||
animation: rotateGlow 20s linear infinite;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@keyframes rotateGlow {
|
||
0% { transform: translate(-50%, -50%) rotate(0deg); }
|
||
100% { transform: translate(-50%, -50%) rotate(360deg); }
|
||
}
|
||
|
||
/* 粒子效果(简单实现) */
|
||
.particles {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-image: radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
|
||
background-size: 60px 60px;
|
||
pointer-events: none;
|
||
animation: moveParticles 30s linear infinite;
|
||
}
|
||
|
||
@keyframes moveParticles {
|
||
0% { background-position: 0 0; }
|
||
100% { background-position: 60px 60px; }
|
||
}
|
||
|
||
/* 登录卡片 */
|
||
.login-card {
|
||
width: 90%;
|
||
max-width: 420px;
|
||
min-width: 320px;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
backdrop-filter: blur(10px);
|
||
-webkit-backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||
border-radius: 16px;
|
||
box-shadow: 0 8px 32px rgba(0, 255, 255, 0.1);
|
||
transition: all 0.3s;
|
||
z-index: 1;
|
||
animation: cardGlow 3s infinite alternate;
|
||
}
|
||
|
||
@keyframes cardGlow {
|
||
0% { box-shadow: 0 8px 32px rgba(0, 255, 255, 0.1); }
|
||
100% { box-shadow: 0 8px 48px rgba(0, 255, 255, 0.3); }
|
||
}
|
||
|
||
.login-card:hover {
|
||
border-color: rgba(0, 255, 255, 0.4);
|
||
box-shadow: 0 8px 40px rgba(0, 255, 255, 0.2);
|
||
}
|
||
|
||
/* 头部 */
|
||
.login-header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.login-header h2 {
|
||
color: #00ffff;
|
||
font-weight: 400;
|
||
letter-spacing: 2px;
|
||
margin: 0 0 8px 0;
|
||
text-shadow: 0 0 8px rgba(0, 255, 255, 0.5);
|
||
font-size: clamp(20px, 5vw, 28px); /* 响应式字体 */
|
||
}
|
||
|
||
.login-header p {
|
||
color: rgba(255, 255, 255, 0.6);
|
||
font-size: 14px;
|
||
margin: 0;
|
||
}
|
||
|
||
/* 表单 */
|
||
.login-form {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
/* 输入框样式覆盖 */
|
||
:deep(.el-input__wrapper) {
|
||
background-color: rgba(0, 0, 0, 0.3);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 8px;
|
||
box-shadow: none;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
:deep(.el-input__wrapper:hover) {
|
||
border-color: #00ffff;
|
||
box-shadow: 0 0 8px rgba(0, 255, 255, 0.3);
|
||
}
|
||
|
||
:deep(.el-input__wrapper.is-focus) {
|
||
border-color: #00ffff;
|
||
box-shadow: 0 0 12px rgba(0, 255, 255, 0.5);
|
||
}
|
||
|
||
:deep(.el-input__inner) {
|
||
color: #fff;
|
||
caret-color: #00ffff;
|
||
}
|
||
|
||
:deep(.el-input__prefix-inner) {
|
||
color: #00ffff;
|
||
}
|
||
|
||
/* 登录按钮 */
|
||
.login-btn {
|
||
width: 100%;
|
||
height: 48px;
|
||
background: linear-gradient(135deg, #00c6ff, #0072ff);
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
letter-spacing: 4px;
|
||
color: #fff;
|
||
transition: all 0.3s;
|
||
margin-top: 10px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.login-btn::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: radial-gradient(circle, rgba(255,255,255,0.3) 0%, transparent 70%);
|
||
opacity: 0;
|
||
transition: opacity 0.3s;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.login-btn:hover {
|
||
background: linear-gradient(135deg, #00a6ff, #0052cc);
|
||
box-shadow: 0 4px 20px rgba(0, 114, 255, 0.4);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.login-btn:hover::after {
|
||
opacity: 1;
|
||
}
|
||
|
||
.login-btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
/* 适配手机屏幕 */
|
||
@media screen and (max-width: 768px) {
|
||
.login-card {
|
||
padding: 20px;
|
||
}
|
||
|
||
.login-header h2 {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.login-btn {
|
||
height: 44px;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
</style> |