# 登录页面搬迁文档

> **搬迁源**：`D:\code\company\SCTMES_MES_V5\vue-app`  
> **搬迁目标**：`d:\code\mes\mes-ui`  
> **状态**：✅ 已完成（2026-05-26）

---

## 一、文件清单与依赖关系

```
登录页面 index.vue
    │
    ├── ① 页面自身
    ├── ② 依赖的 Store 模块
    ├── ③ 依赖的 API 文件
    ├── ④ 依赖的静态资源（图片）
    ├── ⑤ 依赖的 i18n 语言包
    ├── ⑥ 依赖的 CSS 公共样式（% placeholder）
    ├── ⑦ 依赖的工具库
    └── ⑧ 路由中已有框架（无需搬迁本文件）
```

---

## 二、需要搬迁的文件

### 2.1 页面核心文件

| 文件 | 旧路径 | 新目标路径 | 说明 |
|------|--------|-----------|------|
| 登录页面 | `src/views/system_settings/system_monitoring/system/login/index.vue` | **覆盖** `src/views/system/login/index.vue` | 新项目已有一个简易占位页面，需用旧项目的完整页面替换 |

> 旧路径嵌套很深：`system_settings/system_monitoring/system/login/`。  
> 新项目按对照表命名为简化的 `system/login/`，保持一致的命名风格。

---

### 2.2 Store 模块（Vuex）

| 文件 | 旧路径 | 新目标路径 | 说明 |
|------|--------|-----------|------|
| account | `src/store/modules/d2admin/modules/account.js` | `src/store/modules/d2admin/modules/account.js` | **核心**：含 `login` 和 `logout` action |
| user | `src/store/modules/d2admin/modules/user.js` | `src/store/modules/d2admin/modules/user.js` | account.login 中调用 `d2admin/user/set`，**新项目已存在** |

> `account.js` 的 `login` action 依赖链：  
> `login()` → `loginAdminUser()` API → 写 cookie(token/uuid) → `dispatch(d2admin/user/set)` → `dispatch(load)`  
> `load()` → 依次加载 user/theme/transition/page/menu/size/color

**account.js 依赖的其它模块（新项目已存在，不需搬迁）**：

| 模块 | 路径 | 状态 |
|------|------|:--:|
| user | `src/store/modules/d2admin/modules/user.js` | ✅ 已有 |
| theme | `src/store/modules/d2admin/modules/theme.js` | ✅ 已有 |
| transition | `src/store/modules/d2admin/modules/transition.js` | ✅ 已有 |
| page | `src/store/modules/d2admin/modules/page.js` | ✅ 已有 |
| menu | `src/store/modules/d2admin/modules/menu.js` | ✅ 已有 |
| size | `src/store/modules/d2admin/modules/size.js` | ✅ 已有 |
| color | `src/store/modules/d2admin/modules/color.js` | ✅ 已有 |
| db | `src/store/modules/d2admin/modules/db.js` | ✅ 已有 |

---

### 2.3 API 文件

| 文件 | 旧路径 | 新目标路径 | 说明 |
|------|--------|-----------|------|
| 登录/注销 API | `src/api/login.js` | `src/api/auth.js` | 含 `loginAdminUser()` 和 `logoutAdminUser()` |

> 注意：旧项目 `login.js` 中 import 了 `@/plugin/axios/request`，这是旧项目的 axios 封装。  
> 新项目使用 `@/api/_service` 中的 `request`，搬迁时需替换 import 路径。

---

### 2.4 静态资源（图片）

| 文件 | 旧路径 | 新目标路径 | 说明 |
|------|--------|-----------|------|
| 登录页 Logo | `public/image/logo/sc_logo.png` | `public/image/logo/sc_logo.png` | 登录表单上方的 Logo |

> 旧项目 `public/image/logo/` 目录下共有 9 个文件，但登录页面只用 `sc_logo.png`。  
> 根据需要可按需搬其他 logo 文件。新项目原有 `src/views/system/login/image/logo@2x.png`，搬迁后可删除。

---

### 2.5 i18n 语言包

**i18n key 前缀**：`page.system_administration.system_monitoring.login`

| key | 中文 | 英文 |
|-----|------|------|
| `time_is_most_precious` | 时间是一切财富中最宝贵的财富 | Time is the most precious of all wealth |
| `username` | 用户名 | Username |
| `password` | 密码 | Password |
| `login` | 登录 | Login |
| `quick_select_user` | 快速选择用户 | Quick Select User |
| `please_enter_username` | 请输入用户名 | Please enter username |
| `please_enter_password` | 请输入密码 | Please enter password |
| `please_enter_code` | 请输入验证码 | Please enter captcha |
| `dev_version` | 开发版本 | Development Version |
| `test_version` | 测试版本 | Test Version |
| `form_validation_failed` | 表单校验失败，请检查 | Form validation failed |

**搬迁操作**：

1. 在 `src/locales/zh-chs.json` 的 `page.system_administration.system_monitoring` 下新增：
```json
"login": {
  "time_is_most_precious": "时间是一切财富中最宝贵的财富",
  "username": "用户名",
  "password": "密码",
  "login": "登录",
  "please_enter_username": "请输入用户名",
  "please_enter_password": "请输入密码",
  "dev_version": "开发版本",
  "test_version": "测试版本",
  "form_validation_failed": "表单校验失败，请检查"
}
```

2. 在 `src/locales/en.json` 中对应添加英文翻译。

---

### 2.6 CSS 公共样式（依赖但不在页面内）

页面中使用了 SCSS 的 `%placeholder` 选择器：

| placeholder | 作用 | 定义位置 |
|-------------|------|---------|
| `%unable-select` | 禁止用户选中 + 鼠标变手形 | `src/assets/style/public.scss` |
| `%full` | 绝对定位填满父元素 | `src/assets/style/public.scss` |
| `%flex-center-col` | flex 垂直水平居中 | `src/assets/style/public.scss` |
| `$color-text-normal` | 文字颜色变量 | `src/assets/style/unit/color.scss` |
| `$color-primary` | 主色变量 | `src/assets/style/unit/color.scss` |
| `$color-bg` | 背景色变量 | `src/assets/style/unit/color.scss` |

> ✅ 这些在新项目中已存在（通过 `additionalData` 全局注入），不需要额外搬迁。

---

### 2.7 工具/插件依赖（新项目已有，不需搬迁）

| 依赖 | 旧路径 | 新项目状态 |
|------|--------|:--:|
| `util.cookies` | `src/libs/util.cookies.js` → `src/libs/util.js` | ✅ 已有 |
| `localeMixin` | `src/locales/mixin.js` | ✅ 已有 |
| `dayjs` | npm 包 | ✅ 已有（`package.json` 中） |
| `$baseUrl` | `Vue.prototype.$baseUrl = process.env.BASE_URL` | ✅ 已在 `plugin/d2admin/index.js` 注册 |
| `vuex` `mapActions` | npm 包 | ✅ 已有 |

---

### 2.8 路由（新项目已配置，不需搬迁）

旧路由：
```js
{
  path: '/login',
  name: 'login',
  component: _import('system_settings/system_monitoring/system/login')
}
```

新项目路由已在 `routes.js` 的 `frameOut` 中配置：
```js
{
  path: '/login',
  name: 'login',
  component: _import('system/login')
}
```

> ✅ 新路由已指向 `system/login`，对应的 `src/views/system/login/index.vue` 需要替换为旧页面内容。

---

## 三、搬迁执行步骤

| 步骤 | 操作 | 文件 |
|:--:|------|------|
| 1 | 拷贝登录 API（替换 import 为 `@/api/_service`） | `src/api/login.js` → `src/api/auth.js` |
| 2 | 拷贝 account Store（替换 API 引用路径） | `src/store/modules/d2admin/modules/account.js` |
| 3 | 拷贝 Logo 图片 | `public/image/logo/sc_logo.png` |
| 4 | 更新登录页面（替换 import 路径 + i18n key + logo 路径） | `src/views/system/login/index.vue` |
| 5 | 添加 i18n 语言包 | `src/locales/zh-chs.json` / `en.json` |
| 6 | 验证：`pnpm serve` 启动 → 访问 `/login` 测试登录流程 |

---

## 四、页面中需要修改的内容

登录页面 `index.vue` 从旧项目拷贝后，需改以下几处：

### 4.1 import 路径修正

```diff
- import { mapActions } from 'vuex'
- import util from '@/libs/util'
- import localeMixin from '@/locales/mixin.js'
+ // 这些在新项目中路径一致，无需修改
+ // util.cookies、localeMixin、mapActions 均同上
```

> 注意：旧页面 mixins 中引用了 `localeMixin`（切换语言），新项目中此 mixin 在 `src/locales/mixin.js` 已存在，不需动。

### 4.2 Logo 图片路径

```diff
- <img class="page-login--logo" :src="`${$baseUrl}image/logo/sc_logo.png`">
+ <img class="page-login--logo" src="/image/logo/sc_logo.png">
```

> 新项目中 `$baseUrl` 同样已注册，两种写法均可。建议用绝对路径 `/image/logo/sc_logo.png` 更直观。

### 4.3 i18n key 修正

旧页面使用的 key 前缀为 `page.system_settings.system_monitoring.system.login.xxx`，在新项目中统一简化为 `page.system.login.xxx`，或按新项目目录结构调整。

建议调整为更简洁的 key：

| 旧 key（太长） | 新 key（建议） |
|---------------|---------------|
| `page.system_settings.system_monitoring.system.login.username` | `page.system.login.username` |
| `page.system_settings.system_monitoring.system.login.password` | `page.system.login.password` |

---

## 五、不涉及的依赖（记录说明）

| 依赖 | 原因 |
|------|------|
| `@/libs/websocket.js` | 登录页面本身不 import websocket，但 `account.js` 的 `logout` action 中引用。新项目如不需 websocket，注释掉即可 |
| `@/api/modules/sys.user.api.js` | 旧 `account.login` 只调 `login.js` API，不涉及此文件 |
| `public/image/logo/` 其他 png | 登录页只用 `sc_logo.png`，其余 logo 按需搬迁 |

---

## 登录流程完整流程图

### 正向流程：用户登录

```mermaid
sequenceDiagram
    participant User as 用户
    participant LoginPage as Login 页面
    participant VUEX as Store account
    participant API as 后端 POST /login
    participant Cookie as Cookie
    participant Router as router.beforeEach
    participant Home as 首页

    User->>LoginPage: 输入用户名密码，点击登录
    LoginPage->>LoginPage: el-form.validate 校验表单
    alt 校验失败
        LoginPage-->>User: message.error 表单校验失败
    else 校验通过
        LoginPage->>VUEX: dispatch login { username, password }
        VUEX->>API: loginAdminUser
        alt 登录失败 401 或密码错误
            API-->>VUEX: throw Error
            VUEX->>VUEX: remove token 和 uuid
            VUEX-->>LoginPage: Promise rejected
            LoginPage-->>User: message.error
        else 登录成功
            API-->>VUEX: token 和 userInfo
            VUEX->>Cookie: 写入 token / uuid 有效期365天
            VUEX->>VUEX: localStorage.setItem user_id
            VUEX->>VUEX: dispatch user/set
            VUEX->>VUEX: dispatch load 加载持久化配置
            Note over VUEX: load按序加载: user theme transition page menu size color sourceData
            VUEX-->>LoginPage: Promise resolved
            LoginPage->>Router: router.replace redirect or /
            Router->>Router: beforeEach 检查 token
            Router-->>Home: 鉴权通过，跳转目标页
        end
    end
```

### 反向流程：用户注销

```mermaid
sequenceDiagram
    participant User as 用户
    participant Header as 顶栏用户菜单
    participant VUEX as Store account
    participant API as 后端 POST /logout
    participant Cookie as Cookie
    participant WS as WebSocket
    participant LoginPage as Login 页面

    User->>Header: 点击注销
    Header->>VUEX: dispatch logout { confirm: true }
    VUEX->>User: confirm 确定要注销吗
    alt 取消
        User-->>VUEX: 取消
    else 确认
        VUEX->>VUEX: Loading 遮罩
        VUEX->>WS: closeSock 断开 WebSocket
        VUEX->>API: logoutAdminUser
        API-->>VUEX: 服务端清除 session
        VUEX->>VUEX: dispatch db/set 清空 sourceData
        VUEX->>VUEX: dispatch user/set 清空用户信息
        VUEX->>Cookie: remove token / uuid / set block true
        VUEX->>VUEX: Loading 关闭
        VUEX->>LoginPage: router.push { name: login }
        LoginPage-->>User: 返回登录页面
    end
```

### 路由守卫鉴权流程（分三种情况）

#### 情况 A：无 Token → 跳转登录页

```mermaid
flowchart TD
    A["用户访问 URL"] --> B{"路由需要 auth?"}
    B -->|是| C["Cookie.get(token)"]
    C -->|"token 为空"| D["重定向到 /login?redirect=原地址"]
    D --> E["用户在登录页输入账号密码"]
    E --> F["登录成功"]
    F --> G["router.replace 回到目标页"]
    G --> H["进入首页"]
```

#### 情况 B：有 Token → 恢复会话

```mermaid
flowchart TD
    A["用户访问 URL，Cookie 中已有 token"] --> B{"路由需要 auth?"}
    B -->|是| C["Cookie.get(token)"]
    C -->|"token 有效"| D["next 放行"]
    D --> E["框架渲染 header-aside 布局"]
    E --> F["d2admin/page/isLoaded"]
    F --> G["d2admin/size/isLoaded"]
    G --> H["页面正常加载"]

    style C fill:#f9f,stroke:#333
    style D fill:#9f9,stroke:#333
    style H fill:#9f9,stroke:#333
```

#### 情况 C：Token 有效但后端已过期 → API 层拦截

```mermaid
flowchart TD
    A["页面加载后调用业务 API"] --> B["axios 请求携带 Authorization header"]
    B --> C{"后端 code === 401?"}
    C -->|否| D["正常返回数据"]
    C -->|"是 401"| E["axios 响应拦截器 catch 401"]
    E --> F["清除 Cookie(token / uuid)"]
    F --> G["Message.error 请重新登录"]
    G --> H["router.push /login"]

    style C fill:#f96,stroke:#333
    style H fill:#f96,stroke:#333
```

### 已有 Token 时的完整恢复时序

```mermaid
sequenceDiagram
    participant User as 用户
    participant Browser as 浏览器
    participant Router as router.beforeEach
    participant Cookie as Cookie
    participant Store as Vuex Store
    participant DB as Local DB lowdb
    participant Layout as Layout
    participant Page as 目标页面

    User->>Browser: 打开网页
    Browser->>Router: beforeEnter 触发
    Router->>Store: dispatch page/isLoaded
    Router->>Store: dispatch size/isLoaded
    Router->>Cookie: get token
    Cookie-->>Router: token = eyJ...
    Router->>Router: next 放行
    Router->>Layout: 渲染 header-aside
    Layout->>Store: menu 加载侧边栏
    Store->>DB: get sys.menu.asideCollapse
    DB-->>Store: 上次展开收起状态
    Layout->>Store: theme 加载主题
    Store->>DB: get sys.theme.activeName
    DB-->>Store: 上次选择的主题
    Layout->>Store: page 加载多标签页
    Store->>DB: get sys.page.opened
    DB-->>Store: 上次打开的标签页列表
    Layout->>Page: 目标页面渲染完成
    Page-->>User: 首页正常显示，主题侧栏标签页全部恢复
```

### 对比：首次登录 vs 已有 Token

| 维度 | 首次登录（无 Token） | 已有 Token 恢复 |
|------|-------------------|----------------|
| 入口 | `/login` 登录页 | 任意页面 URL |
| 路由守卫 | 拦下 → 重定向 /login | 放行 |
| 用户操作 | 输入用户名密码 | 无需操作 |
| `account/login` | ✅ 触发 | ❌ 不触发 |
| `account/load()` | ✅ 触发（写入 DB） | ❌ 不触发 |
| `page/isLoaded` | 触发（首次无缓存） | 触发（从 DB 恢复） |
| `size/isLoaded` | 触发 | 触发 |
| 主题/侧栏/标签页 | 从登录后 load() 写入 | 从 lowdb 读取上次状态 |
| Cookie | 新建 token / uuid | 复用已有 token |

---

### 整体状态流转

```mermaid
stateDiagram-v2
    [*] --> 未登录: 首次访问
    [*] --> 已登录: 已有 token，恢复会话
    未登录 --> 登录页面: 路由守卫拦下
    登录页面 --> 提交中: 输入账号密码，点击登录
    提交中 --> 登录成功: API 返回 token
    提交中 --> 登录失败: API 报错 / 401
    登录失败 --> 登录页面: 清除 cookie，允许重试
    登录成功 --> 已登录: 写入 cookie + load() 加载配置
    已登录 --> 已登录: 页面刷新（token 仍有效，Router + DB 恢复）
    已登录 --> 未登录: API 返回 401，被强制退出
    已登录 --> 注销中: 点击注销
    注销中 --> 已登录: 取消确认
    注销中 --> 未登录: 确认 → logout API → 清 cookie → 跳 /login
```

---

## 六、搬迁后验证清单

- [ ] `pnpm lint` 无报错
- [ ] `pnpm serve` 启动成功
- [ ] 访问 `http://localhost:8081/login` 能看到完整登录页面
- [ ] 输入正确的用户名密码能登录并跳转首页
- [ ] 中文/英文切换按钮文字正确
- [ ] 登录失败有表单校验提示
- [ ] 浏览器 Cookie 中有 `token` 和 `uuid`
