# 登录页面设计文档 ## 1. 页面概述 登录页面是用户进入系统的主要入口,提供账号密码登录功能,同时支持记住登录状态和找回密码功能。 ## 2. 页面布局 ![登录页面布局示意图](https://placeholder-for-login-page-mockup.png) ### 2.1 布局结构 登录页面采用居中卡片式布局,包含以下主要区域: - 顶部 Logo 和系统名称区域 - 中部登录表单区域 - 底部版权信息和帮助链接区域 ### 2.2 响应式设计 - **桌面端**:居中卡片,宽度 400px,两侧留白 - **平板端**:居中卡片,宽度 80%,两侧留白 - **移动端**:全宽卡片,上下留白,左右边距 16px ## 3. 组件结构 ```vue ``` ## 4. 数据结构 ```typescript interface LoginForm { username: string; password: string; remember: boolean; } interface LoginResponse { token: string; user: { id: number; username: string; role: string; permissions: string[]; }; } ``` ## 5. 状态管理 ```typescript // 组件内状态 const loginForm = ref({ username: '', password: '', remember: false }); const loading = ref(false); const loginFormRef = ref(); const enableOtherLoginMethods = ref(true); // 全局状态 (Pinia Store) const authStore = useAuthStore(); ``` ## 6. 交互逻辑 ### 6.1 表单验证规则 ```typescript const loginRules = { username: [ { required: true, message: '请输入用户名', trigger: 'blur' }, { min: 3, max: 20, message: '用户名长度应为 3-20 个字符', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 20, message: '密码长度应为 6-20 个字符', trigger: 'blur' } ] }; ``` ### 6.2 登录流程 ```typescript const handleLogin = async () => { if (!loginFormRef.value) return; await loginFormRef.value.validate(async (valid) => { if (!valid) return; try { loading.value = true; // 调用登录 API await authStore.login({ username: loginForm.value.username, password: loginForm.value.password }); // 如果选择记住我,设置本地存储 if (loginForm.value.remember) { localStorage.setItem('remember_username', loginForm.value.username); } else { localStorage.removeItem('remember_username'); } // 登录成功提示 ElMessage.success('登录成功'); // 获取重定向地址或跳转到首页 const redirect = route.query.redirect as string || '/'; router.push(redirect); } catch (error) { // 错误处理 ElMessage.error(error.response?.data?.message || '登录失败,请检查用户名和密码'); } finally { loading.value = false; } }); }; ``` ### 6.3 其他交互功能 ```typescript // 跳转到注册页面 const goToRegister = () => { router.push('/auth/register'); }; // 跳转到忘记密码页面 const forgotPassword = () => { router.push('/auth/forgot-password'); }; // 生命周期钩子,检查是否有记住的用户名 onMounted(() => { const rememberedUsername = localStorage.getItem('remember_username'); if (rememberedUsername) { loginForm.value.username = rememberedUsername; loginForm.value.remember = true; } }); ``` ## 7. API 调用 ### 7.1 登录 API ```typescript // api/auth.ts export const authApi = { login: (credentials: { username: string; password: string }) => apiClient.post('/auth/login', credentials) }; // stores/auth.ts export const useAuthStore = defineStore('auth', { // ... 其他状态和 getters actions: { async login(credentials) { try { const { data } = await authApi.login(credentials); this.token = data.token; this.user = data.user; // 存储 token 到本地存储 localStorage.setItem('token', data.token); return data; } catch (error) { this.error = error.response?.data?.message || '登录失败'; throw error; } } } }); ``` ## 8. 样式设计 ```scss .login-container { display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; background-color: #f5f7fa; padding: 20px; .login-header { text-align: center; margin-bottom: 30px; .login-logo { width: 80px; height: 80px; } .login-title { font-size: 24px; color: #303133; margin-top: 16px; } } .login-card { width: 400px; max-width: 100%; .login-card-title { font-size: 20px; text-align: center; margin-bottom: 30px; font-weight: 500; } .login-form { margin-bottom: 20px; .login-options { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; } .login-button { width: 100%; height: 40px; font-size: 16px; } } .other-login-methods { margin-top: 24px; .divider { display: flex; align-items: center; margin: 16px 0; &::before, &::after { content: ''; flex: 1; height: 1px; background-color: #dcdfe6; } span { padding: 0 16px; color: #909399; font-size: 14px; } } .login-icons { display: flex; justify-content: center; gap: 20px; margin-top: 16px; .icon-button { font-size: 20px; &.wechat { color: #07c160; background-color: #f0f9eb; border-color: #e1f3d8; &:hover { background-color: #e1f3d8; } } } } } .register-link { text-align: center; margin-top: 24px; font-size: 14px; } } .login-footer { margin-top: 40px; text-align: center; color: #909399; font-size: 12px; p { margin: 8px 0; } } } // 响应式样式 @media screen and (max-width: 768px) { .login-container { padding: 16px; .login-card { width: 100%; } } } ``` ## 9. 安全考虑 1. **密码安全** - 密码输入框使用 `type="password"` 和 `show-password` 属性 - 密码不在前端存储,只在登录时传输 - 密码传输使用 HTTPS 加密 2. **Token 安全** - Token 存储在 localStorage 中,有 XSS 风险,可考虑使用 HttpOnly Cookie - Token 过期处理在响应拦截器中统一处理 3. **表单安全** - 实施前端输入验证,防止基本的注入攻击 - 防止表单重复提交(登录按钮 loading 状态) ## 10. 测试用例 1. **单元测试** - 测试表单验证逻辑 - 测试记住用户名功能 2. **集成测试** - 测试登录成功流程 - 测试登录失败处理 - 测试重定向功能 3. **端到端测试** - 测试完整登录流程 - 测试页面响应式布局 ## 11. 性能优化 1. **延迟加载** - 第三方图标库按需引入 - 忘记密码页面使用动态导入 2. **缓存策略** - 记住用户名使用 localStorage - 静态资源(logo 等)设置适当的缓存策略