Files
schoolNews/schoolNewsWeb/src/views/login/Login.vue
2025-10-08 16:15:47 +08:00

287 lines
6.6 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="login-box">
<div class="login-header">
<div class="logo">
<img src="@/assets/logo.png" alt="Logo" />
</div>
<h1 class="title">校园新闻管理系统</h1>
<p class="subtitle">登录您的账户</p>
</div>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
size="large"
@submit.prevent="handleLogin"
>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="请输入用户名"
prefix-icon="User"
clearable
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
prefix-icon="Lock"
show-password
clearable
/>
</el-form-item>
<!-- <el-form-item prop="captcha" v-if="showCaptcha">
<div class="captcha-input">
<el-input
v-model="loginForm.captcha"
placeholder="请输入验证码"
prefix-icon="PictureRounded"
clearable
/>
<div class="captcha-image" @click="refreshCaptcha">
<img :src="captchaImage" alt="验证码" v-if="captchaImage" />
<div class="captcha-loading" v-else>加载中...</div>
</div>
</div>
</el-form-item> -->
<el-form-item>
<div class="login-options">
<el-checkbox v-model="loginForm.rememberMe">
记住我
</el-checkbox>
<el-link type="primary" @click="goToForgotPassword">
忘记密码
</el-link>
</div>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="large"
:loading="loginLoading"
@click="handleLogin"
class="login-button"
>
登录
</el-button>
</el-form-item>
</el-form>
<div class="login-footer">
<p>
还没有账户
<el-link type="primary" @click="goToRegister">立即注册</el-link>
</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import type { LoginParam } from '@/types';
import { authApi } from '@/apis/system/auth';
// 响应式引用
const loginFormRef = ref<FormInstance>();
const loginLoading = ref(false);
const showCaptcha = ref(false);
const captchaImage = ref('');
// Composition API
const router = useRouter();
const route = useRoute();
const store = useStore();
// 表单数据
const loginForm = reactive<LoginParam>({
username: '',
password: '',
captcha: '',
captchaId: '',
rememberMe: false
});
// 表单验证规则
const loginRules: FormRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度为3-20个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码至少6个字符', trigger: 'blur' }
],
captcha: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 4, message: '验证码为4位', trigger: 'blur' }
]
};
// 方法
const handleLogin = async () => {
if (!loginFormRef.value) return;
try {
const valid = await loginFormRef.value.validate();
if (!valid) return;
loginLoading.value = true;
// 调用store中的登录action
const result = await store.dispatch('auth/login', loginForm);
ElMessage.success('登录成功!');
// 优先使用 query 中的 redirect其次使用返回的 redirectUrl最后使用默认首页
const redirectPath = (route.query.redirect as string) || result.redirectUrl || '/home';
router.push(redirectPath);
} catch (error: any) {
console.error('登录失败:', error);
ElMessage.error(error.message || '登录失败,请检查用户名和密码');
// 登录失败后显示验证码
showCaptcha.value = true;
refreshCaptcha();
} finally {
loginLoading.value = false;
}
};
const refreshCaptcha = async () => {
try {
const captchaData = await authApi.getCaptcha();
captchaImage.value = captchaData.captchaImage;
loginForm.captchaId = captchaData.captchaId;
} catch (error) {
console.error('获取验证码失败:', error);
ElMessage.error('获取验证码失败');
}
};
function goToRegister() {
router.push('/register');
}
function goToForgotPassword() {
router.push('/forgot-password');
}
// 组件挂载时检查是否需要显示验证码
onMounted(() => {
// 可以根据需要决定是否默认显示验证码
// refreshCaptcha();
});
</script>
<style lang="scss" scoped>
.login-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
}
.login-box {
width: 100%;
max-width: 400px;
background: white;
border-radius: 12px;
padding: 40px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.login-header {
text-align: center;
margin-bottom: 32px;
.logo img {
width: 64px;
height: 64px;
margin-bottom: 16px;
}
.title {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0 0 8px 0;
}
.subtitle {
color: #666;
font-size: 14px;
margin: 0;
}
}
.login-form {
.captcha-input {
display: flex;
gap: 12px;
.el-input {
flex: 1;
}
.captcha-image {
width: 100px;
height: 40px;
border: 1px solid #dcdfe6;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
img {
max-width: 100%;
max-height: 100%;
}
.captcha-loading {
font-size: 12px;
color: #999;
}
}
}
.login-options {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.login-button {
width: 100%;
}
}
.login-footer {
text-align: center;
margin-top: 24px;
p {
color: #666;
font-size: 14px;
margin: 0;
}
}
</style>