All files / src/views/logic Login.logic.ts

92.03% Statements 231/251
89.28% Branches 25/28
80% Functions 4/5
92.03% Lines 231/251

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 2531x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 7x 7x 4x 4x 3x 3x 3x 3x 3x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 1x 1x 1x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 16x                                         16x 16x 16x 7x 7x 16x 5x 5x 5x 5x 5x 5x 5x 5x 3x 3x 5x 5x 5x 5x 16x 2x 2x 2x 16x 16x 9x 9x 9x 9x 5x 5x 5x 5x 5x 2x 5x 1x 3x 2x 2x 9x 2x 2x 2x 2x 2x 2x 9x 9x 16x 16x 16x 16x 16x    
/**
 * 登录业务逻辑模块
 * 提供用户登录、退出、token验证等核心功能
 * 
 * @fileOverview 登录逻辑处理模块,基于Vue 3 Composition API实现
 * @author InspectionFlowSystem Team
 * @version 1.0.0
 * @since 2025-09-16
 */
 
import { ref } from "vue";
import api, { authAPI } from '../../api/index';
 
/**
 * 用户名响应式引用
 * @type {import('vue').Ref<string>}
 * @description 绑定到登录表单的用户名输入框
 */
export const username = ref("");
 
/**
 * 密码响应式引用
 * @type {import('vue').Ref<string>}
 * @description 绑定到登录表单的密码输入框
 */
export const password = ref("");
 
/**
 * 错误信息响应式引用
 * @type {import('vue').Ref<string>}
 * @description 用于显示登录过程中的错误提示信息
 */
export const errorMsg = ref("");
 
/**
 * 加载状态响应式引用
 * @type {import('vue').Ref<boolean>}
 * @description 控制登录按钮的加载状态,防止重复提交
 */
export const loading = ref(false);
 
/**
 * 是否使用本地假数据模式
 * @constant {boolean}
 * @description 开发模式开关,为true时使用本地模拟数据,为false时调用真实API
 * @default false
 */
const FAKE_MODE = false;
 
/**
 * JWT Token有效时间(毫秒)
 * @constant {number}
 * @description Token的过期时间,默认为2小时
 * @default 7200000
 */
const TOKEN_EXPIRE_MS = 12 * 60 * 60 * 1000; // 2小时
// const TOKEN_EXPIRE_MS = 2 * 1000; // 2秒
 
/**
 * 检查JWT Token是否有效
 * 
 * @function isTokenValid
 * @returns {boolean} Token是否有效
 * @description 检查localStorage中的token和过期时间,判断当前登录状态是否有效
 * 
 * @example
 * // 检查当前登录状态
 * if (isTokenValid()) {
 *   console.log('用户已登录');
 * } else {
 *   console.log('用户未登录或token已过期');
 * }
 * 
 * @since 1.0.0
 */
export function isTokenValid(): boolean {
  const token = localStorage.getItem("token");
  const expireTime = localStorage.getItem("token_expire");
  
  if (!token || !expireTime) {
    return false;
  }
  
  const now = Date.now();
  const expire = parseInt(expireTime);
  
  return now < expire;
}
 
/**
 * 获取当前登录用户信息
 * 
 * @function getCurrentUser
 * @returns {Object|null} 用户信息对象或null
 * @description 从 localStorage 中获取并解析用户信息,如果数据损坏或不存在则返回null
 * 
 * @example
 * // 获取当前用户信息
 * const user = getCurrentUser();
 * if (user) {
 *   console.log(`当前用户: ${user.username}`);
 * } else {
 *   console.log('未获取到用户信息');
 * }
 * 
 * @throws {Error} 当JSON解析失败时记录错误日志
 * @since 1.0.0
 */
export function getCurrentUser() {
  try {
    const userInfo = localStorage.getItem("user_info");
    return userInfo ? JSON.parse(userInfo) : null;
  } catch (error) {
    console.error('解析用户信息失败:', error);
    return null;
  }
}
 
/**
 * 退出登录功能
 * 
 * @function logout
 * @returns {void}
 * @description 清理所有本地存储的用户登录信息,包括token、用户信息等,并重置表单状态
 * 
 * @example
 * // 用户点击退出按钮
 * logout();
 * // 登录信息已清理,表单已重置
 * 
 * @since 1.0.0
 */
export function logout() {
  localStorage.removeItem("token");
  localStorage.removeItem("token_expire");
  localStorage.removeItem("user_info");
  localStorage.removeItem("username");
  
  // 清空表单数据
  username.value = "";
  password.value = "";
  errorMsg.value = "";
}
 
/**
 * 用户登录功能
 * 
 * @async
 * @function login
 * @returns {Promise<boolean>} 登录是否成功
 * @description 处理用户登录请求,支持本地模拟模式和真实API调用两种方式
 * 
 * @example
 * // 在Vue组件中调用
 * const success = await login();
 * if (success) {
 *   router.push('/home');
 * } else {
 *   // 处理登录失败
 * }
 * 
 * @throws {Error} 当网络请求失败或服务器错误时抛出异常
 * 
 * @since 1.0.0
 * 
 * @see {@link isTokenValid} - 用于检查token有效性
 * @see {@link logout} - 用于退出登录
 */
export async function login(): Promise<boolean> {
  // 重置错误信息和加载状态
  errorMsg.value = "";
  loading.value = true;
 
  try {
    // 检查是否启用本地模拟模式
    if (FAKE_MODE) {
      // 模拟网络延迟,提供真实的用户体验
      await new Promise((resolve) => setTimeout(resolve, 500));

      // 硬编码的测试账户验证
      if (username.value === "admin" && password.value === "123456") {
        const fakeToken = "fake-jwt-token";
        const expireAt = Date.now() + TOKEN_EXPIRE_MS;

        // 保存模拟的token和过期时间
        localStorage.setItem("token", fakeToken);
        localStorage.setItem("token_expire", expireAt.toString());
        
        // 清空密码输入框(安全考虑)
        password.value = "";
        return true;
      } else {
        errorMsg.value = "用户名或密码错误";
        return false;
      }
    }
 
    // 真实API调用 - 调用模拟后台服务
    const res = await authAPI.login(username.value, password.value);
 
    // 检查API响应状态和数据格式
    if (res.status === 200 && res.data && res.data.token) {
      const expireAt = Date.now() + TOKEN_EXPIRE_MS;
 
      // 保存JWT token和过期时间
      localStorage.setItem("token", res.data.token);
      localStorage.setItem("token_expire", expireAt.toString());
      
      // 如果响应中包含用户信息,也保存起来
      if (res.data.user) {
        localStorage.setItem("user_info", JSON.stringify(res.data.user));
      }
      
      // 清空密码输入框(安全考虑)
      password.value = "";
      return true;
    } else {
      errorMsg.value = "用户名或密码错误";
      return false;
    }
 
  } catch (error) {
    console.error('登录请求失败:', error);
    
    // 根据错误类型显示不同的错误信息
    if (error.response) {
      // 服务器返回了错误状态码
      const status = error.response.status;
      const message = error.response.data?.message || "登录失败";
      
      if (status === 401) {
        errorMsg.value = "用户名或密码错误";
      } else if (status === 400) {
        errorMsg.value = message || "请求参数错误";
      } else {
        errorMsg.value = `服务器错误 (${status}): ${message}`;
      }
    } else if (error.request) {
      // 请求发出了但没有收到响应
      errorMsg.value = "无法连接到服务器,请检查网络连接";
    } else {
      // 请求配置出错
      errorMsg.value = "登录请求配置错误";
    }
    
    return false;
  } finally {
    // 无论成功还是失败,都要取消加载状态
    loading.value = false;
  }
}