# 前端页面设计风格一致性分析报告

> **项目**: 会议核销 SaaS 系统 Frontend  
> **技术栈**: Vue 3 + Element Plus + Vite + TypeScript  
> **分析范围**: `frontend/src/views/` 下的 31 个页面组件 + 20 个子组件  
> **分析日期**: 2026-04-03

---

## 一、核心发现总览

```mermaid
pie title 风格不一致分布
    "内联样式泛滥" : 35
    "工具栏/搜索区布局混乱" : 20
    "弹窗/抽屉尺寸不统一" : 15
    "表格与按钮间距不一致" : 12
    "CSS 管理缺失" : 10
    "颜色/字号硬编码" : 8
```

> [!WARNING]
> 在 31 个页面组件中，**仅有 5 个** 使用了 `<style scoped>` 块，其余全部依赖内联 `style=""` 控制间距和布局。这严重影响后续维护和全局主题切换能力。

---

## 二、逐项分析

### 1. 页面容器结构 — `<el-card>` 使用方式不一致

所有 31 个功能页面都以 `<el-card>` 作为根容器，但细节差异如下：

| 类型 | 页面 | 使用方式 | 问题 |
|------|------|---------|------|
| ✅ 标准模式 | `UserPage`, `RolePage`, `MenuPage`, `PermissionPage` 等 25 个 | `<el-card>` （默认 shadow） | 一致 |
| ⚠️ 嵌套卡片 | `DataPermissionPage` | 外层 `<el-card>` + 内层 `<el-card shadow="never">` | 嵌套层级不统一 |
| ⚠️ 多卡片网格 | `OperationsDashboardPage` | 外层 `<el-card>` + 9 个内嵌 `<el-card>` | 独特网格布局，无复用 |
| ⚠️ 弹窗嵌套 | `MeetingAuditProgressDialog` | `<el-card shadow="never">` 在 dialog 内 | 扁平卡片仅此一处 |
| 🔴 登录页独立 | `PlatformLoginPage`, `TenantLoginPage` | `<el-card class="login-card">` | 独立设计，与后台页面完全脱离 |

> [!IMPORTANT]
> **建议**: 创建统一的 `<PageContainer>` 包裹组件，标准化 shadow 规则和 padding 规范。

---

### 2. 工具栏/搜索区域 — 布局方案碎片化

搜索栏和操作区的布局方式至少有 **4 种不同范式**：

````carousel
#### 方案 A: `<el-space wrap>` (7 个页面)
```
UserPage, RolePage, DataPermissionPage,
NotificationPolicyPage, TemplatePage,
PlatformDictionaryPage 等
```
使用 `<el-space wrap>` 包裹按钮组

---

#### 方案 B: `<el-form :inline="true">` (8 个页面)
```
ExpertPage, FinancePage, ObservabilityPage,
PlatformSessionPage, MeetingQueryToolbar 等
```
使用内联表单包裹搜索字段和按钮

---

#### 方案 C: 裸按钮 (3 个页面)
```
PermissionPage, OperationsDashboardPage,
AuditQueryToolbar
```
直接将 `<el-button>` 放在卡片内，无任何包裹容器

---

#### 方案 D: 自定义 header-row (1 个页面)
```
InAppNotificationPage
```
使用自定义 CSS class `.header-row` 实现 flex 布局
在 card header slot 中排列控件
````

> [!IMPORTANT]
> **建议**: 统一使用方案 A（`<el-space wrap>`）或方案 B（`<el-form :inline="true">`）二选一，并封装为 `<QueryToolbar>` 组件。

---

### 3. 表格与上方控件的间距 — 值不统一

当搜索栏与表格之间需要间距时，各页面的做法五花八门：

| 间距方式 | 页面 | 间距值 |
|---------|------|--------|
| `style="margin-top: 12px"` | `RolePage`, `PermissionPage`, `PlatformPermissionPage`, `DataPermissionPage` | 12px |
| `style="margin-top: 8px"` | `ObservabilityPage` (×3 处) | 8px |
| 无间距（紧贴搜索栏） | `UserPage`, `ExpertPage`, `MeetingPage`, `FinancePage` | 0 |
| `style="margin-bottom: 12px"` (反向) | `OperationsDashboardPage` | 12px (apply on button instead) |
| `<el-divider />` | `MeetingQueryToolbar`, `FinancePage`, `ObservabilityPage` | Element 默认 |

> [!NOTE]
> 同一个系统内出现 `0px`, `8px`, `12px` 以及 `<el-divider>` 四种不同间距方案来处理同一类场景。

---

### 4. 弹窗 / 抽屉尺寸 — 宽度规格混乱

```mermaid
graph LR
    subgraph Drawer尺寸
        D1["520px — RolePage"]
        D2["560px — UserPage, ExpertPage ×2"]
        D3["680px — DataPermissionPage, NotificationPolicyPage"]
        D4["80% — ExpertPage (银行卡列表)"]
    end
    subgraph Dialog宽度
        W1["500px — DataPermissionPage (分配角色)"]
        W2["520px — ExpertPage (合并)"]
        W3["560px — ObservabilityPage, ExpertPage (OCR)"]
        W4["620px — ExpertPage (身份证OCR)"]
        W5["680px — RolePage (权限绑定)"]
        W6["700px — NotificationPolicyPage (触发发送)"]
        W7["720px — UserPage (历史), NotificationPolicyPage (编辑)"]
        W8["860px — UserPage (代理授权)"]
    end
```

> **统计**: 抽屉有 4 种宽度规格，对话框有 8 种宽度规格，总计 **12 种不同尺寸**，严重缺乏统一的尺寸档位体系。

> [!IMPORTANT]
> **建议**: 定义 3 个标准 Drawer 尺寸 (`small: 480px`, `medium: 640px`, `large: 80%`)  
> 定义 3 个标准 Dialog 尺寸 (`small: 520px`, `medium: 680px`, `large: 860px`)

---

### 5. 表单控件宽度 — 硬编码且无统一标准

在内联 `style` 中给 `<el-select>`, `<el-input>` 等设置的宽度值收集如下：

| 控件场景 | 出现的宽度值 |
|---------|------------|
| 搜索栏 Select | `90px`, `110px`, `140px`, `150px`, `160px`, `180px` |
| 表单 Select | `220px`, `240px`, `260px`, `280px` |
| 搜索栏 Input | `180px`, `190px`, `200px`, `260px` |
| 全宽 Select | `100%` |
| 弹窗内 Select | `100%`, `220px` |

> **统计**: 光 `<el-select>` 的宽度就出现了 **10 种** 不同的硬编码值。

---

### 6. CSS 管理 — `<style scoped>` 严重缺失

| 分类 | 页面数 | 比例 |
|------|--------|------|
| ✅ 使用 `<style scoped>` | 5 个 (`PlatformLoginPage`, `TenantLoginPage`, `NotificationPolicyPage`, `InAppNotificationPage`, `AppLayout`) | 16% |
| ✅ 子组件使用 `<style scoped>` | 6 个 (`MeetingListTable`, `MeetingMaterialDrawer`, `MeetingDocPreviewDialog`, `MeetingCreatePlatformExpertDialog`, `MeetingBindExpertDialog`, `MaterialPictureCardFileItem`) | — |
| 🔴 完全无 `<style>` 块 | 26 个页面 | **84%** |

> [!CAUTION]
> 84% 的页面组件没有任何 CSS 管理方案，全靠内联 `style=""` 支撑，极度不利于：
> - 全局主题切换 / 暗色模式适配
> - 设计 Token 标准化
> - 多人协作维护

---

### 7. 颜色与字体硬编码

在模板中直接内联的颜色和字号值：

| 硬编码值 | 出处 | 场景 |
|---------|------|------|
| `color: #606266` | `TenantLoginPage`, `AuditQueryToolbar`, `AppLayout` | 提示文字颜色 |
| `color: #909399` | `ExpertPage` ×4 | 辅助说明 |
| `font-weight: 600` | `RolePage`, `PlatformRolePage`, `FinancePage`, `ObservabilityPage`, `NotificationPolicyPage` | 小标题 |
| `font-size: 12px` | `NotificationPolicyPage` | 提示文字 |
| `font-size: 13px` | `AppLayout` | 登录信息 |
| `line-height: 1.8` / `1.9` | `ExpertPage`, `DataPermissionPage`, `NotificationPolicyPage` | 详情 |
| `background: #f8f8f8` | `NotificationPolicyPage`, `AppLayout` | 预览盒 / 主内容区 |
| `border: 1px solid #ebeef5` | `NotificationPolicyPage`, `AppLayout` | 边框 |

> [!NOTE]
> 这些颜色值实际上都对应 Element Plus 的设计变量（`--el-text-color-regular`, `--el-text-color-secondary` 等），应该改用 CSS 变量引用。

---

## 三、已有组件拆分的一致性

| 模块 | 是否拆分子组件 | 子组件数 | 评价 |
|------|--------------|---------|------|
| MeetingPage | ✅ 已拆分 | 17 个 | 拆分合理，但子组件之间也存在细微风格差异 |
| AuditPage | ✅ 已拆分 | 3 个 | 基本一致 |
| ProjectPage | ⚠️ 部分拆分 | 1 个 (ProjectEditDrawer) | 主页面和抽屉风格有差异 |
| 其余 28 个页面 | 🔴 未拆分 | 0 个 | 全部为单文件巨型组件 |

> 最大的单文件组件 `MeetingPage.vue` 达 **156KB / ~4000+ 行**，虽然已拆分子组件但主文件仍然庞大。

---

## 四、表单验证方式不一致

| 方式 | 页面 | 问题 |
|------|------|------|
| 手动 `if` 检查 | `ExpertPage` (submitExpert, submitCard) | 6 个连续 `if + ElMessage.warning` 全手工 |
| `ElMessageBox.prompt` | `UserPage`, `RolePage` | 用于输入框式验证 |
| 无验证 | `DataPermissionPage`, `OperationsDashboardPage` | 直接提交，无需验证 |
| ❌ 未使用 `el-form` 的 `rules` 和 `ref.validate()` | **全部页面** | 没有一个页面使用 Element Plus 原生的表单验证 |

---

## 五、TODO 清单（按优先级排列）

### P0 — 基础设施（先行完成，为后续改造铺路）

- [ ] **[P0-01]** 创建全局 CSS 变量文件 `src/styles/variables.css`  
  定义间距 Token (`--spacing-xs: 4px`, `--spacing-sm: 8px`, `--spacing-md: 12px`, `--spacing-lg: 16px`, `--spacing-xl: 24px`)

- [ ] **[P0-02]** 创建全局工具类文件 `src/styles/utilities.css`  
  定义 `.mt-sm`, `.mt-md`, `.mb-md`, `.gap-sm` 等常用间距类

- [ ] **[P0-03]** 定义弹窗/抽屉尺寸常量 `src/constants/ui.ts`  
  ```ts
  export const DRAWER_SIZE = { sm: '480px', md: '640px', lg: '80%' }
  export const DIALOG_WIDTH = { sm: '520px', md: '680px', lg: '860px' }
  ```

- [ ] **[P0-04]** 定义表单控件标准宽度常量  
  ```ts
  export const INPUT_WIDTH = { xs: '100px', sm: '160px', md: '220px', lg: '280px', full: '100%' }
  ```

---

### P1 — 公共组件封装

- [ ] **[P1-01]** 封装 `<PageContainer>` 组件  
  统一 `<el-card>` + header slot + 标准 padding/shadow 行为

- [ ] **[P1-02]** 封装 `<QueryToolbar>` 组件  
  统一搜索栏布局、按钮排列方式、与下方表格的间距

- [ ] **[P1-03]** 封装 `<ActionButtons>` 组件  
  统一操作列按钮的间距和视觉风格

- [ ] **[P1-04]** 封装 `<SectionTitle>` 组件  
  替换 `<div style="font-weight: 600; margin-bottom: 8px">` 这类重复标题

---

### P2 — 各页面样式迁移（消灭内联 style）

- [ ] **[P2-01]** `UserPage.vue` — 移除 4 处内联 style，改用工具类；select/input 改用标准宽度
- [ ] **[P2-02]** `RolePage.vue` — 移除 5 处内联 style（margin-top, margin-right, margin-bottom）
- [ ] **[P2-03]** `ExpertPage.vue` — 移除 8 处内联 style（color, margin-left, width, border-radius）
- [ ] **[P2-04]** `DataPermissionPage.vue` — 统一嵌套 card shadow 规则、移除内联 margin/line-height
- [ ] **[P2-05]** `FinancePage.vue` — 移除 4 处内联 style（width, font-weight, margin）
- [ ] **[P2-06]** `ObservabilityPage.vue` — 移除 8 处内联 style（margin-top, width, font-weight）
- [ ] **[P2-07]** `NotificationPolicyPage.vue` — 移除 10+ 处内联 style，`.preview-box` 迁移到全局
- [ ] **[P2-08]** `PlatformSessionPage.vue` — 统一 4 个 select/input 的宽度规格
- [ ] **[P2-09]** `PlatformRolePage.vue` — 移除 4 处内联 style（margin-right, margin-bottom, font-weight）
- [ ] **[P2-10]** `PlatformMenuPage.vue` — 移除 3 处内联 style（margin-top, margin-right）
- [ ] **[P2-11]** `OperationsDashboardPage.vue` — 移除 1 处内联 style（margin-bottom）
- [ ] **[P2-12]** `PermissionPage.vue` / `PlatformPermissionPage.vue` — 统一 margin-top 值
- [ ] **[P2-13]** `TemplatePage.vue` — 移除 7 处内联 style（width, margin-top, margin-left）
- [ ] **[P2-14]** `TenantPage.vue` — 移除 3 处内联 style（object-fit, margin-bottom, color）
- [ ] **[P2-15]** `InAppNotificationPage.vue` — 移除 1 处内联 style（margin-bottom）
- [ ] **[P2-16]** `AuditQueryToolbar.vue` — 移除 3 处内联 style（margin-left, margin-bottom, color）

---

### P3 — 弹窗/抽屉标准化

- [ ] **[P3-01]** 全部 Drawer 改用 `DRAWER_SIZE` 常量（统一归档到 sm/md/lg）
- [ ] **[P3-02]** 全部 Dialog 改用 `DIALOG_WIDTH` 常量（统一归档到 sm/md/lg）
- [ ] **[P3-03]** 统一 Drawer 的 `destroy-on-close` 行为（目前部分有、部分无）
- [ ] **[P3-04]** 统一 footer 按钮排列方式（目前 [取消 + 确定] vs [关闭 + 保存] 命名不统一）

---

### P4 — 颜色与硬编码值替换

- [ ] **[P4-01]** `#606266` → `var(--el-text-color-regular)` （5 处）
- [ ] **[P4-02]** `#909399` → `var(--el-text-color-secondary)` （4 处）
- [ ] **[P4-03]** `#f8f8f8` → `var(--el-fill-color-lighter)` （2 处）
- [ ] **[P4-04]** `#ebeef5` → `var(--el-border-color-lighter)` （2 处）
- [ ] **[P4-05]** `#c0c4cc` → `var(--el-text-color-placeholder)` （AppLayout scrollbar）

---

### P5 — 表单验证标准化

- [ ] **[P5-01]** `ExpertPage.vue` — 用 `el-form` 的 `rules` + `formRef.validate()` 替换手动 if 校验
- [ ] **[P5-02]** `UserPage.vue` — 添加 `rules` 验证（姓名、手机号必填等）
- [ ] **[P5-03]** `NotificationPolicyPage.vue` — 添加表单 rules
- [ ] **[P5-04]** 所有有 required 标记的表单字段实际加上 `rules` 规则

---

### P6 — 长期优化

- [ ] **[P6-01]** `MeetingPage.vue` (156KB) 进一步拆分，将 dialog/drawer 独立为子组件
- [ ] **[P6-02]** 建立 ESLint 规则禁止新增内联 `style=""`
- [ ] **[P6-03]** 统一 `<el-form>` 的 `label-width` 值（当前 `70px / 90px / 100px / 110px` 四种）
- [ ] **[P6-04]** 抽取公共的 `statusFormatter` / `toZhStatus` 为 composable
- [ ] **[P6-05]** 考虑引入全局暗色模式支持

---

## 六、改造优先级路线图

```mermaid
gantt
    title 前端风格统一改造路线图
    dateFormat  YYYY-MM-DD
    section P0 基础设施
    CSS 变量 & 工具类         :p0, 2026-04-07, 2d
    尺寸/宽度常量             :after p0, 1d
    section P1 公共组件
    PageContainer              :p1a, after p0, 2d
    QueryToolbar               :p1b, after p1a, 2d
    SectionTitle & ActionButtons :after p1b, 1d
    section P2-P4 样式迁移
    高频页面 (User/Role/Expert)  :p2, after p1b, 3d
    中频页面 (Finance/Audit/Obs) :after p2, 3d
    低频页面 (Platform/Template) :after p2, 3d
    颜色硬编码替换              :after p2, 1d
    section P5-P6 深度优化
    表单验证标准化              :p5, after p2, 3d
    MeetingPage 拆分            :after p5, 3d
    ESLint 规则 & 暗色模式      :after p5, 2d
```

> [!TIP]
> 建议按 **P0 → P1 → P2（高频页面优先）→ P3 → P4 → P5 → P6** 的顺序执行，预计总工作量约 **15-20 人日**。
