Files
pc_frontend/src/Views/Login.vue
qingfeng1121 73cf25e586 feat: 添加分页组件和地址选择组件,优化样式和类型定义
refactor: 重构路由配置,移除订单页面路由

style: 统一使用CSS变量替换硬编码颜色值

docs: 添加前端数据需求分析文档

fix: 修复登录页面记住我功能,更新用户信息页面链接

perf: 优化搜索组件动画效果和响应式设计

chore: 更新TypeScript类型定义,添加订单和地址相关类型
2026-01-19 11:35:50 +08:00

387 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 id="login-container-bg">
<div class="login-container">
<div class="login-card">
<div class="login-header">
<h1 class="login-title">欢迎登录</h1>
<p class="login-subtitle">请输入您的账号和密码</p>
</div>
<form class="login-form" @submit.prevent="login">
<div class="form-group">
<label class="form-label" for="username">用户名</label>
<input type="text" id="username" v-model="loginform.username" name="username" required class="form-input"
placeholder="请输入用户名" :class="{ 'input-error': errorMessage && !loginform.username }">
</div>
<div class="form-group">
<label class="form-label" for="password">密码</label>
<input type="password" id="password" v-model="loginform.password" name="password" required
class="form-input" placeholder="请输入密码" :class="{ 'input-error': errorMessage && !loginform.password }">
</div>
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<!-- 记住我 如果用户登录成功记住用户信息5天内无需重新登录 -->
<div class="form-group remember-me">
<label class="checkbox-label">
<input type="checkbox" v-model="loginform.rememberMe" class="checkbox-input">
<span class="checkbox-text">记住我</span>
</label>
</div>
<!-- 登录按钮 -->
<button type="submit" class="login-button" :disabled="isLoading">
<span v-if="isLoading" class="loading-spinner"></span>
{{ isLoading ? '登录中...' : '登录' }}
</button>
<!-- 注册链接 -->
<div class="login-footer">
<p class="register-link">
还没有账号 <a href="#" @click.prevent="router.push({ name: 'register' })">立即注册</a>
</p>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import userService from "@/Service/UserService";
import { useRouter } from 'vue-router'
import type { Login } from "@/Util/Type";
import { useGlobalStore } from "@/Util/globalStore";
const router = useRouter()
const loginform = ref<Login>({
username: '',
password: '',
rememberMe: false
})
const errorMessage = ref('')
const isLoading = ref(false)
const login = async () => {
// 重置错误信息
errorMessage.value = ''
// 简单的表单验证
if (!loginform.value.username || !loginform.value.password) {
errorMessage.value = '请输入用户名和密码'
return
}
try {
isLoading.value = true
const userLoginResponse = await userService.login(loginform.value)
// 注意根据响应拦截器userLoginResponse已经是response.data
if (userLoginResponse.data.code === 200) {
// 登录成功后,跳转到首页
router.push({ name: 'home' })
// 登录成功后将token存储到全局状态管理中
useGlobalStore().setToken(userLoginResponse.data.token)
} else {
// 登录失败后,显示错误信息给用户
errorMessage.value = userLoginResponse.data.msg || '登录失败,请检查用户名和密码'
}
} catch (error: any) {
console.error('登录失败:', error)
// 登录失败后,显示错误信息给用户
errorMessage.value = error.response?.data?.msg || '网络错误,请稍后重试'
} finally {
isLoading.value = false
}
}
</script>
<style scoped>
/* 登录容器背景 */
#login-container-bg {
background-image: url('/b1.jpeg');
background-size: 100vw 100vh; /* 与视口大小一致 */
background-position: center;
position: fixed;
width: 100%;
height: 100%;
background-repeat: no-repeat;
}
/* 登录容器 */
.login-container {
position: absolute;
/* 调整定位,使其在背景中居中显示 */
top: 50%;
right: 10%; /* 调整右侧位置,避免超出屏幕 */
transform: translateY(-50%);
width: 100%;
max-width: 400px;
padding: 20px;
animation: fadeIn 0.5s ease-in;
}
/* 登录卡片 */
.login-card {
/* 实现从父容器背景截取的效果 */
position: relative;
border-radius: 12px;
padding: 40px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
overflow: hidden;
/* 关键:使用固定背景定位,与父容器背景完全对齐 */
background-image: url('/b1.jpeg');
background-size: 100vw 100vh; /* 与视口大小一致 */
background-position: 82.5% 49%;
background-attachment: fixed;
/* 确保背景图片不会重复 */
background-repeat: no-repeat;
}
.login-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.15);
}
/* 记住我复选框样式 */
.remember-me {
display: flex;
align-items: center;
margin: 15px 0;
}
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
}
.checkbox-input {
margin-right: 8px;
width: 16px;
height: 16px;
cursor: pointer;
}
.checkbox-text {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
}
.checkbox-text:hover {
color: #fff;
}
/* 登录头部 */
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-title {
color: #333;
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
}
.login-subtitle {
color: #666;
font-size: 14px;
font-weight: 400;
}
/* 登录表单 */
.login-form {
display: flex;
flex-direction: column;
gap: 20px;
}
/* 表单组 */
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
/* 表单标签 */
.form-label {
color: #333;
font-size: 14px;
font-weight: 500;
}
/* 表单输入框 */
.form-input {
padding: 12px 16px;
border: 2px solid #e1e5e9;
border-radius: 8px;
font-size: 16px;
transition: all 0.3s ease;
outline: none;
background-color: #f9fafb;
}
.form-input:focus {
border-color: #667eea;
background-color: white;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.form-input.input-error {
border-color: #ef4444;
}
/* 错误信息 */
.error-message {
background-color: #fee2e2;
color: #dc2626;
padding: 10px 16px;
border-radius: 6px;
font-size: 14px;
margin-top: -10px;
margin-bottom: 10px;
animation: shake 0.5s ease-in-out;
}
/* 登录按钮 */
.login-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 14px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-top: 10px;
}
.login-button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.login-button:active:not(:disabled) {
transform: translateY(0);
}
.login-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* 加载状态 */
.loading-spinner {
width: 18px;
height: 18px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
/* 登录页脚 */
.login-footer {
margin-top: 20px;
text-align: center;
}
.register-link {
color: #666;
font-size: 14px;
}
.register-link a {
color: #667eea;
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
}
.register-link a:hover {
color: #764ba2;
text-decoration: underline;
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
10%,
30%,
50%,
70%,
90% {
transform: translateX(-5px);
}
20%,
40%,
60%,
80% {
transform: translateX(5px);
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* 响应式设计 */
@media (max-width: 480px) {
.login-container {
padding: 15px;
}
.login-card {
padding: 30px 20px;
}
.login-title {
font-size: 24px;
}
.form-input {
padding: 10px 14px;
font-size: 14px;
}
.login-button {
padding: 12px 20px;
font-size: 14px;
}
}
</style>