| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- <template>
- <div class="login-page">
- <div class="login-container">
- <!-- Logo 区域 -->
- <div class="login-header">
- <div class="logo">🚀 灵越智报</div>
- <p class="subtitle">智能报告生成平台</p>
- </div>
- <!-- 登录表单 -->
- <el-card class="login-card">
- <template #header>
- <div class="card-header">
- <span>用户登录</span>
- <router-link to="/register" class="switch-link">没有账号?立即注册</router-link>
- </div>
- </template>
- <el-form
- ref="formRef"
- :model="form"
- :rules="rules"
- label-width="0"
- size="large"
- @submit.prevent="handleLogin"
- >
- <el-form-item prop="usernameOrEmail">
- <el-input
- v-model="form.usernameOrEmail"
- placeholder="用户名或邮箱"
- :prefix-icon="User"
- clearable
- />
- </el-form-item>
- <el-form-item prop="password">
- <el-input
- v-model="form.password"
- type="password"
- placeholder="密码"
- :prefix-icon="Lock"
- show-password
- @keyup.enter="handleLogin"
- />
- </el-form-item>
- <el-form-item>
- <div class="login-options">
- <el-checkbox v-model="rememberMe">记住我</el-checkbox>
- <a href="#" class="forgot-password">忘记密码?</a>
- </div>
- </el-form-item>
- <el-form-item>
- <el-button
- type="primary"
- class="login-btn"
- :loading="loading"
- @click="handleLogin"
- >
- {{ loading ? '登录中...' : '登 录' }}
- </el-button>
- </el-form-item>
- </el-form>
- <!-- 其他登录方式 -->
- <div class="other-login">
- <el-divider>其他登录方式</el-divider>
- <div class="social-login">
- <el-button :icon="ChatDotRound" circle title="微信登录" />
- <el-button :icon="Message" circle title="企业微信" />
- <el-button :icon="Phone" circle title="手机验证码" />
- </div>
- </div>
- </el-card>
- <!-- 底部信息 -->
- <div class="login-footer">
- <p>© 2026 灵越智报 - 企业级智能报告生成平台</p>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, reactive } from 'vue'
- import { useRouter } from 'vue-router'
- import { User, Lock, ChatDotRound, Message, Phone } from '@element-plus/icons-vue'
- import { ElMessage } from 'element-plus'
- import { authApi } from '@/api'
- const router = useRouter()
- const formRef = ref(null)
- const loading = ref(false)
- const rememberMe = ref(false)
- const form = reactive({
- usernameOrEmail: '',
- password: ''
- })
- const rules = {
- usernameOrEmail: [
- { required: true, message: '请输入用户名或邮箱', trigger: 'blur' }
- ],
- password: [
- { required: true, message: '请输入密码', trigger: 'blur' }
- ]
- }
- async function handleLogin() {
- if (!formRef.value) return
-
- await formRef.value.validate(async (valid) => {
- if (!valid) return
-
- loading.value = true
- try {
- const response = await authApi.login(form)
-
- // 保存 Token(新后端返回结构: { accessToken, refreshToken, expiresIn, user: { id, username, realName, roles } })
- localStorage.setItem('accessToken', response.accessToken)
- localStorage.setItem('refreshToken', response.refreshToken)
- localStorage.setItem('userId', response.user?.id)
- localStorage.setItem('username', response.user?.realName || response.user?.username)
-
- if (rememberMe.value) {
- localStorage.setItem('rememberMe', 'true')
- }
-
- ElMessage.success('登录成功')
- router.push('/')
- } catch (error) {
- console.error('登录失败:', error)
- ElMessage.error(error.message || '登录失败,请检查用户名和密码')
- } finally {
- loading.value = false
- }
- })
- }
- </script>
- <style lang="scss" scoped>
- .login-page {
- min-height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
- background: linear-gradient(135deg, #1890ff 0%, #096dd9 50%, #722ed1 100%);
- padding: 20px;
- }
- .login-container {
- width: 100%;
- max-width: 420px;
- }
- .login-header {
- text-align: center;
- margin-bottom: 30px;
- color: #fff;
- .logo {
- font-size: 32px;
- font-weight: 700;
- margin-bottom: 8px;
- }
- .subtitle {
- font-size: 14px;
- opacity: 0.9;
- }
- }
- .login-card {
- border-radius: 12px;
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- span {
- font-size: 18px;
- font-weight: 600;
- }
- .switch-link {
- font-size: 13px;
- color: var(--el-color-primary);
- text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
- }
- }
- }
- .login-options {
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- .forgot-password {
- font-size: 13px;
- color: var(--el-color-primary);
- text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
- }
- }
- .login-btn {
- width: 100%;
- height: 44px;
- font-size: 16px;
- }
- .other-login {
- margin-top: 10px;
- .social-login {
- display: flex;
- justify-content: center;
- gap: 16px;
- }
- }
- .login-footer {
- text-align: center;
- margin-top: 24px;
- color: rgba(255, 255, 255, 0.7);
- font-size: 12px;
- }
- </style>
|