### **主题：用户认证模块 - 登录页面技术设计方案 (v2.0)**

**1. 总体目标 (Overall Objective)**

构建一个安全、健壮、响应式且符合 Material Design 3 (MD3) 规范的用户登录页面。该页面需完全遵循项目既定的 `vue3-typescript-stye.md` 架构规范，并实现客户端密码加密、全局状态管理和统一的API错误处理逻辑。

**2. 页面级组件设计 (`Login.vue`)**

* **文件位置:** `src/pages/Login.vue`
* **技术栈:** Vue 3 (`<script setup lang="ts">`), Vuetify 3
* **布局与样式:**
    * **居中布局:** 必须使用 Vuetify 的栅格系统 (`v-container`, `v-row`, `v-col`) 实现页面内容在所有视口下（移动端、平板、桌面端）的垂直与水平居中。
    * **登录卡片:** 主体容器使用 `v-card` 组件，并设置 `elevation` 属性以符合 MD3 的视觉深度。
    * **标题栏:** 卡片顶部使用 `v-toolbar` 组件，颜色绑定为 `color="primary"`，内容为项目图标（使用 `v-icon`）与 "ProjectTonyStack" 文本。
    * **主题化:** 所有组件颜色、背景、文本颜色严禁硬编码。必须使用 Vuetify 主题变量（如 `primary`, `error`, `surface`），确保在明亮/黑暗模式下无缝切换。
* **表单设计 (`v-form`):**
    * **表单实例:** 使用 `v-form` 组件包裹所有输入域，并通过 `ref` 引用实例以调用其 `validate` 方法。
    * **输入字段 (`v-text-field`):**
        * **用户名:** `label` 为 "用户名"，`prepend-inner-icon` 设置为 `mdi-account-outline`。
        * **密码:** `label` 为 "密码"，`prepend-inner-icon` 设置为 `mdi-lock-outline`。
        * **密码可见性:** 字段类型 (`type`) 动态绑定为 `'text'` 或 `'password'`。使用 `append-inner-icon` 属性，并根据状态动态切换 `mdi-eye` 和 `mdi-eye-off` 图标，点击该图标可切换密码的明文/密文显示。
    * **校验规则:** 用户名和密码均为必填项。必须使用 Vuetify 的 `:rules` 属性进行客户端校验，提供明确的错误提示信息（如："用户名为必填项"）。
* **用户交互:**
    * **登录按钮 (`v-btn`):** 按钮应为块级元素 (`block`)，颜色为 `primary`。必须绑定 `:loading` 属性到组件的布尔状态，在API请求期间显示加载指示器，并禁用按钮。
    * **错误提示:** 在表单内、登录按钮上方，使用 `v-alert` 组件。通过 `v-if` 指令控制其显示。`type` 属性绑定为 `error`，使其颜色与主题的错误色保持一致。Alert 中显示的错误文本来自组件状态。

**3. 密码加密传输方案**

* **选型:** 鉴于 "密钥不会外泄" 的假设及对简化实现的追求，我们采用**对称加密算法 AES (CBC模式, PKCS7填充)**。
* **实现:**
    * **依赖库:** 项目引入 `crypto-js` 库进行加密操作。
    * **工具模块:** 在 `src/utils/crypto.ts` 中封装一个 `encryptAES` 函数。此函数接收明文密码，使用预设的16字节密钥（KEY）和16字节初始化向量（IV）进行加密，并返回 Base64 编码的密文字符串。
    * **数据流:**
        1.  前端：在发起登录请求前，**仅对密码字段**调用 `encryptAES` 函数。
        2.  传输：向后端 `POST /api/user/login` 接口发送 JSON 体，其中 `username` 为明文，`password` 为加密后的密文。
        3.  后端：使用相同的 KEY 和 IV 解密密码，然后进行哈希比对验证。

**4. 状态管理 (Pinia)**

* **模块位置:** `src/store/auth.ts`
* **职责:** 统一管理用户认证状态（Token）及相关业务逻辑（登录、登出），作为全应用认证信息的唯一可信源。
* **State:** 定义 `AuthState` 接口，包含 `token: string | null`。初始状态尝试从 `localStorage` 读取 `token` 以支持持久化登录。
* **Actions:**
    * `login(credentials: UserCredentials): Promise<void>`:
        1.  接收包含明文密码的 `credentials` 对象。
        2.  调用 `src/utils/crypto.ts` 中的 `encryptAES` 加密密码。
        3.  调用 `src/api/modules/user.ts` 中的 `loginApi` 方法发起请求。
        4.  **成功时 (`code: 0`):** 从 API 响应中提取 `token`，调用内部 `setToken` action。
        5.  **失败时:** 捕获 API 层抛出的错误，调用 `clearToken` action，然后将错误继续向上抛出，由UI层处理显示。
    * `setToken(token: string)`: 将 `token` 同时存入 Pinia state 和 `localStorage`。
    * `logout()`: 清理 state 和 `localStorage` 中的 `token`，并可在此触发路由跳转至登录页。
* **Getters:**
    * `isAuthenticated: (state) => boolean`: 根据 `state.token` 是否存在返回一个布尔值，便于在应用各处快速判断登录状态。

**5. API 层设计与全局错误处理**

* **API 模块:**
    * **文件位置:** `src/api/modules/user.ts`
    * **函数定义:** 导出一个 `loginApi(data: LoginPayload): Promise<LoginResponse>` 函数。参数和返回类型必须从 `@/types/user.d.ts` 导入，确保类型安全。
* **类型定义:**
    * **文件位置:** `@/types/user.d.ts`
    * **接口:** 定义 `UserCredentials` (明文)、`LoginPayload` (加密后)、`LoginResponse` (包含 `token` 字段) 等精确的接口。
* **统一拦截器 (Critical Refinement):**
    * **文件位置:** `@/api/interceptors.ts`
    * **请求拦截器:** 自动从 `authStore` 获取 `token`，如果存在，则附加到每个请求的 `Authorization` Header 中 (`Bearer ${token}`)。
    * **响应拦截器:**
        1.  **业务成功 (`code === 0`):** 解析响应体，直接 `resolve(response.data.data)`。
        2.  **业务失败 (`code !== 0`):** `reject(new Error(response.data.message))`。错误信息将由页面级的 `catch` 块捕获并显示在 `v-alert` 中。
        3.  **未授权处理 (`code === 10003`):** 这是全局的强制下线逻辑。拦截器必须执行以下原子操作：
            * 通过全局通知组件（如 Snackbar）向用户显示 "登录状态已过期，请重新登录" 的提示。
            * 调用 `authStore.logout()` 清理所有本地认证凭据。
            * 延迟2秒后，使用 `router.push('/login')` 强制将页面跳转到登录页。**此逻辑严禁在任何页面组件中单独实现**。