Files
happy-life-star/web-flowith/src/views/Login/index.vue
T
peanut c77352877d feat: 完成Nacos配置优化和WebSocket集成
主要更新:
1. 统一所有微服务端口配置(19000-19008)
2. 为所有服务创建本地/测试/生产三套环境配置
3. 配置Nacos认证密码(本地:Peanut2817*#, 测试/生产:EmotionMuseum2025)
4. 优化网关路由配置,支持负载均衡和WebSocket
5. 新增emotion-websocket模块,支持实时聊天
6. 前端集成WebSocket,替代HTTP轮询
7. 添加配置验证和管理工具脚本

技术特性:
- 完整的环境隔离和服务发现
- WebSocket实时通信支持
- 负载均衡路由配置
- 跨域和安全配置
- 自动重连和心跳检测
2025-07-17 18:10:45 +08:00

321 lines
7.8 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-page">
<div class="login-container">
<div class="login-card">
<!-- Logo和标题 -->
<div class="login-header">
<router-link to="/" class="logo">
<span class="logo-text">开心APP</span>
</router-link>
<h1 class="login-title">欢迎回来</h1>
<p class="login-subtitle">登录您的账户继续与开开的对话</p>
</div>
<!-- 登录表单 -->
<a-form
:model="loginForm"
:rules="loginRules"
@finish="handleLogin"
@finishFailed="handleLoginFailed"
layout="vertical"
class="login-form"
>
<a-form-item label="账号" name="account">
<a-input
v-model:value="loginForm.account"
placeholder="请输入手机号或邮箱"
size="large"
:prefix="h(UserOutlined)"
/>
</a-form-item>
<a-form-item label="密码" name="password">
<a-input-password
v-model:value="loginForm.password"
placeholder="请输入密码"
size="large"
:prefix="h(LockOutlined)"
/>
</a-form-item>
<!-- 验证码 -->
<a-form-item label="验证码" name="captcha">
<div class="captcha-container">
<a-input
v-model:value="loginForm.captcha"
placeholder="请输入验证码"
size="large"
style="flex: 1"
/>
<div class="captcha-image" @click="refreshCaptcha">
<img
v-if="captchaImage"
:src="captchaImage"
alt="验证码"
style="width: 100%; height: 100%; cursor: pointer;"
/>
<div v-else class="captcha-loading">
<a-spin size="small" />
</div>
</div>
</div>
<div class="captcha-tip">点击图片刷新验证码</div>
</a-form-item>
<a-form-item>
<div class="login-options">
<a-checkbox v-model:checked="loginForm.remember">记住我</a-checkbox>
<a href="#" class="forgot-password">忘记密码</a>
</div>
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
:loading="loginLoading"
class="login-button"
block
>
登录
</a-button>
</a-form-item>
</a-form>
<!-- 注册链接 -->
<div class="register-link">
还没有账户
<router-link to="/register" class="register-btn">立即注册</router-link>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, h } from 'vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
import { authService } from '@/services/auth'
import { useUserStore } from '@/stores/user'
import type { LoginRequest } from '@/types/auth'
const router = useRouter()
const userStore = useUserStore()
// 表单数据
const loginForm = reactive<LoginRequest>({
account: '',
password: '',
captcha: '',
remember: false
})
// 表单验证规则
const loginRules = {
account: [
{ required: true, message: '请输入账号', trigger: 'blur' },
{ min: 3, message: '账号长度不能少于3位', 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 loginLoading = ref(false)
const captchaImage = ref('')
const captchaKey = ref('')
// 获取验证码
const getCaptcha = async () => {
try {
const response = await authService.getCaptcha()
captchaImage.value = `data:image/png;base64,${response.image}`
captchaKey.value = response.key
} catch (error) {
message.error('获取验证码失败')
}
}
// 刷新验证码
const refreshCaptcha = () => {
getCaptcha()
}
// 登录处理
const handleLogin = async (values: LoginRequest) => {
loginLoading.value = true
try {
const loginData = {
...values,
captchaKey: captchaKey.value
}
const result = await userStore.loginWithAuth(loginData)
if (result.success) {
message.success('登录成功')
// 跳转到首页或之前的页面
const redirect = router.currentRoute.value.query.redirect as string
router.push(redirect || '/')
} else {
message.error(result.message || '登录失败')
refreshCaptcha() // 刷新验证码
}
} catch (error: any) {
message.error(error.message || '登录失败,请稍后重试')
refreshCaptcha() // 刷新验证码
} finally {
loginLoading.value = false
}
}
// 登录失败处理
const handleLoginFailed = (errorInfo: any) => {
console.log('Login failed:', errorInfo)
}
// 初始化
onMounted(() => {
getCaptcha()
})
</script>
<style lang="scss" scoped>
.login-page {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-container {
width: 100%;
max-width: 400px;
}
.login-card {
background: white;
border-radius: 16px;
padding: 40px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.login-header {
text-align: center;
margin-bottom: 32px;
.logo {
display: inline-block;
text-decoration: none;
color: #4A90E2;
font-size: 24px;
font-weight: bold;
margin-bottom: 16px;
}
.login-title {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.login-subtitle {
color: #888;
font-size: 14px;
margin: 0;
}
}
.login-form {
.captcha-container {
display: flex;
gap: 12px;
align-items: center;
}
.captcha-image {
width: 100px;
height: 40px;
border: 1px solid #d9d9d9;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
cursor: pointer;
transition: border-color 0.3s;
&:hover {
border-color: #4A90E2;
}
}
.captcha-loading {
display: flex;
align-items: center;
justify-content: center;
}
.captcha-tip {
font-size: 12px;
color: #888;
margin-top: 4px;
}
.login-options {
display: flex;
justify-content: space-between;
align-items: center;
.forgot-password {
color: #4A90E2;
text-decoration: none;
font-size: 14px;
&:hover {
text-decoration: underline;
}
}
}
.login-button {
background: linear-gradient(135deg, #4A90E2 0%, #5BA0F2 100%);
border: none;
border-radius: 8px;
font-weight: 600;
height: 48px;
font-size: 16px;
}
}
.register-link {
text-align: center;
margin-top: 24px;
color: #888;
font-size: 14px;
.register-btn {
color: #4A90E2;
text-decoration: none;
font-weight: 500;
&:hover {
text-decoration: underline;
}
}
}
</style>