Files
Environment-Monitoring-System/Design/frontend_Design/LoginPage_Design.md
ChuXun 4ce487588a 1
2025-10-19 20:31:01 +08:00

436 lines
9.7 KiB
Markdown
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.
# 登录页面设计文档
## 1. 页面概述
登录页面是用户进入系统的主要入口,提供账号密码登录功能,同时支持记住登录状态和找回密码功能。
## 2. 页面布局
![登录页面布局示意图](https://placeholder-for-login-page-mockup.png)
### 2.1 布局结构
登录页面采用居中卡片式布局,包含以下主要区域:
- 顶部 Logo 和系统名称区域
- 中部登录表单区域
- 底部版权信息和帮助链接区域
### 2.2 响应式设计
- **桌面端**:居中卡片,宽度 400px两侧留白
- **平板端**:居中卡片,宽度 80%,两侧留白
- **移动端**:全宽卡片,上下留白,左右边距 16px
## 3. 组件结构
```vue
<template>
<div class="login-container">
<!-- 顶部 Logo 和系统名称 -->
<div class="login-header">
<img src="@/assets/images/logo.svg" alt="系统 Logo" class="login-logo" />
<h1 class="login-title">环境监测系统</h1>
</div>
<!-- 登录表单 -->
<el-card class="login-card">
<h2 class="login-card-title">用户登录</h2>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<!-- 用户名输入框 -->
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="用户名"
prefix-icon="User"
/>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="密码"
prefix-icon="Lock"
show-password
/>
</el-form-item>
<!-- 记住我和忘记密码 -->
<div class="login-options">
<el-checkbox v-model="loginForm.remember">记住我</el-checkbox>
<el-link type="primary" @click="forgotPassword">忘记密码</el-link>
</div>
<!-- 登录按钮 -->
<el-button
type="primary"
class="login-button"
:loading="loading"
@click="handleLogin"
>
登录
</el-button>
</el-form>
<!-- 其他登录方式 -->
<div v-if="enableOtherLoginMethods" class="other-login-methods">
<div class="divider">
<span>其他登录方式</span>
</div>
<div class="login-icons">
<el-button circle icon="Wechat" class="icon-button wechat" />
</div>
</div>
<!-- 注册链接 -->
<div class="register-link">
<span>还没有账号</span>
<el-link type="primary" @click="goToRegister">立即注册</el-link>
</div>
</el-card>
<!-- 底部版权信息 -->
<div class="login-footer">
<p>© 2025 环境监测系统 版权所有</p>
<p>
<el-link type="info" href="#">用户协议</el-link>
<el-divider direction="vertical" />
<el-link type="info" href="#">隐私政策</el-link>
</p>
</div>
</div>
</template>
```
## 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<LoginForm>({
username: '',
password: '',
remember: false
});
const loading = ref<boolean>(false);
const loginFormRef = ref<FormInstance>();
const enableOtherLoginMethods = ref<boolean>(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<LoginResponse>('/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 等)设置适当的缓存策略