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

9.7 KiB
Raw Blame History

登录页面设计文档

1. 页面概述

登录页面是用户进入系统的主要入口,提供账号密码登录功能,同时支持记住登录状态和找回密码功能。

2. 页面布局

登录页面布局示意图

2.1 布局结构

登录页面采用居中卡片式布局,包含以下主要区域:

  • 顶部 Logo 和系统名称区域
  • 中部登录表单区域
  • 底部版权信息和帮助链接区域

2.2 响应式设计

  • 桌面端:居中卡片,宽度 400px两侧留白
  • 平板端:居中卡片,宽度 80%,两侧留白
  • 移动端:全宽卡片,上下留白,左右边距 16px

3. 组件结构

<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. 数据结构

interface LoginForm {
  username: string;
  password: string;
  remember: boolean;
}

interface LoginResponse {
  token: string;
  user: {
    id: number;
    username: string;
    role: string;
    permissions: string[];
  };
}

5. 状态管理

// 组件内状态
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 表单验证规则

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 登录流程

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 其他交互功能

// 跳转到注册页面
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

// 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. 样式设计

.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 等)设置适当的缓存策略