Files
Vue3/src/views/Login.vue
2026-06-28 23:59:33 +08:00

360 lines
8.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>