writeOff/frontend_style_analysis.md
haomingming 815aa04fe8 first
2026-05-20 18:21:39 +08:00

339 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端页面设计风格一致性分析报告
> **项目**: 会议核销 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 处内联 stylemargin-top, margin-right, margin-bottom
- [ ] **[P2-03]** `ExpertPage.vue` — 移除 8 处内联 stylecolor, margin-left, width, border-radius
- [ ] **[P2-04]** `DataPermissionPage.vue` — 统一嵌套 card shadow 规则、移除内联 margin/line-height
- [ ] **[P2-05]** `FinancePage.vue` — 移除 4 处内联 stylewidth, font-weight, margin
- [ ] **[P2-06]** `ObservabilityPage.vue` — 移除 8 处内联 stylemargin-top, width, font-weight
- [ ] **[P2-07]** `NotificationPolicyPage.vue` — 移除 10+ 处内联 style`.preview-box` 迁移到全局
- [ ] **[P2-08]** `PlatformSessionPage.vue` — 统一 4 个 select/input 的宽度规格
- [ ] **[P2-09]** `PlatformRolePage.vue` — 移除 4 处内联 stylemargin-right, margin-bottom, font-weight
- [ ] **[P2-10]** `PlatformMenuPage.vue` — 移除 3 处内联 stylemargin-top, margin-right
- [ ] **[P2-11]** `OperationsDashboardPage.vue` — 移除 1 处内联 stylemargin-bottom
- [ ] **[P2-12]** `PermissionPage.vue` / `PlatformPermissionPage.vue` — 统一 margin-top 值
- [ ] **[P2-13]** `TemplatePage.vue` — 移除 7 处内联 stylewidth, margin-top, margin-left
- [ ] **[P2-14]** `TenantPage.vue` — 移除 3 处内联 styleobject-fit, margin-bottom, color
- [ ] **[P2-15]** `InAppNotificationPage.vue` — 移除 1 处内联 stylemargin-bottom
- [ ] **[P2-16]** `AuditQueryToolbar.vue` — 移除 3 处内联 stylemargin-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 人日**。