baodan/docs/前端移动端适配指南.md

467 lines
13 KiB
Markdown
Raw 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.

# 前端移动端适配指南
## 一、现状评估
### 1.1 项目技术栈
| 项目 | 当前状态 |
|------|----------|
| UI 框架 | Element Plus自带 `el-row`/`el-col` 响应式栅格) |
| CSS 框架 | **无**(无 Tailwind、无 Bootstrap |
| 响应式工具 | 手写媒体查询,仅 4/22 个 Vue 文件包含 `@media` |
| 移动端检测 | `App.vue``RecommendForm.vue` 各自实现了 `window.innerWidth < 768` 逻辑,未复用 |
| 断点体系 | **仅一个断点**`max-width: 767px`(手机),仅 `App.vue` 有平板断点7681024px |
### 1.2 适配完成度
| 等级 | 文件数 | 占比 |
|------|:------:|:----:|
| ✅ 已适配 | 4 | 18% |
| ⚠️ 部分适配 | 3 | 14% |
| ❌ 未适配 | 15 | 68% |
**已适配**`App.vue`、`RecommendForm.vue`、`ChatCopyButton.vue`、`ChatSuggestions.vue`
**部分适配**`ChatPage.vue`、`ChatSidebar.vue`、`ChatEmbed.vue`
**未适配**:其余 15 个页面/组件
---
## 二、问题清单(按严重程度排序)
### 🔴 严重 — 移动端完全不可用
| 文件 | 问题 | 影响 |
|------|------|------|
| `RegisterPage.vue` | `.register-card` 硬编码 `width: 400px`,无媒体查询 | <420px 屏幕水平溢出 |
| `RecommendDetailView.vue` | `el-descriptions` 强制 3 4 plan 表格标题栏 3 个按钮 | 布局严重错乱 |
| `RecommendHistory.vue` | 筛选栏最小宽度 ~560px表格总宽 ~920px | 完全溢出 |
| `DashboardPage.vue` | KPI 卡片 `el-col :span="6"` `:xs` 断点快捷卡片 `:span="8"` | 4 /3 列在手机上窄至 ~80px |
| `PermissionsPage.vue` | `el-col :span="8"` `:span="16"` 无响应式断点 | 角色列表/权限面板在手机上不可用 |
| `DataSourcePage.vue` | 同步日志弹窗 `width="700px"` | 所有手机屏幕溢出 |
### 🟠 高 — 移动端严重降级
| 文件 | 问题 |
|------|------|
| `ChatFilters.vue` | flex wrap筛选栏溢出`.filter-label` 设了 `white-space: nowrap` |
| `RecommendResult.vue`组件 | 7 列表格无横向滚动替换弹窗 `width="600px"` |
| `UsersPage.vue` | 筛选栏溢出固定 160px × 2 + 按钮表格 ~870px弹窗 480px |
| `StatsPage.vue` | `el-col :span="12"`/`:span="8"` 无响应式筛选器固定宽度 |
| `TemplatePage.vue` | 编辑弹窗 `width="50%"`手机 ~180pxplaceholder 行总宽 420px |
| `KBManagePage.vue` | 筛选栏总宽 ~580px状态弹窗 500px |
### 🟡 中 — 移动端可感知问题
| 文件 | 问题 |
|------|------|
| `ChatPage.vue` | 侧边栏固定 240px无平板适配 |
| `ChatEmbed.vue` | 纠错弹窗 `width="500px"`消息列表/输入区 padding 不缩 |
| `PromptPage.vue` | 创建弹窗 `width="70%"`手机 ~252px过窄 |
| `NotificationPage.vue` | 弹窗 `width="50%"`手机 ~180px不可用 |
| `DepartmentPage.vue` | 弹窗 `width="500px"` |
| `LoginLogsPage.vue` | 筛选项固定宽度150px/120px/280px |
| `ChatLogsPage.vue` | 筛选项固定宽度200px/150px/280px |
### 🟢 低 — 细节问题
| 文件 | 问题 |
|------|------|
| `LoginPage.vue` | 背景装饰 `.bg-decoration` 可能在极窄屏幕造成溢出 |
| `ChatSidebar.vue` | 移动端 `max-height: 200px` 过小 ~3 个会话删除按钮仅 hover 可见触屏不可发现 |
---
## 三、根因分析
### 3.1 六大共性问题
1. **无响应式框架** 全靠手写 media query覆盖率极低
2. **inline style 主导** 大量页面用 `style="width: 160px"` 等内联样式无法通过媒体查询覆盖
3. **弹窗宽度硬编码** `el-dialog` 统一用固定像素480700px或固定百分比50%70%无移动端 override
4. **表格无响应式策略** 多列表格总宽 870920px无横向滚动包裹无列隐藏无卡片视图
5. **断点体系缺失** `max-width: 767px` 一个断点无平板/大屏手机/横屏适配
6. **移动端检测重复** `App.vue` `RecommendForm.vue` 各自实现 `isMobile` 逻辑
---
## 四、适配方案
### 4.1 断点体系(统一标准)
```css
/* 手机竖屏 */
@media (max-width: 575px) { /* ... */ }
/* 手机横屏 / 大屏手机 */
@media (min-width: 576px) and (max-width: 767px) { /* ... */ }
/* 平板竖屏 */
@media (min-width: 768px) and (max-width: 1023px) { /* ... */ }
/* 桌面 */
@media (min-width: 1024px) { /* ... */ }
```
Element Plus 断点保持一致`xs`(<768px)、`sm`(≥768px)、`md`(≥992px)、`lg`(≥1200px)。
### 4.2 全局 CSS 变量(建议新建 `src/styles/responsive.css`
```css
:root {
/* 间距 */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
/* 弹窗宽度 */
--dialog-width-sm: 90vw;
--dialog-width-md: 80vw;
--dialog-width-lg: 600px;
/* 表格 */
--table-scroll-max-width: calc(100vw - 48px);
}
@media (min-width: 768px) {
:root {
--dialog-width-sm: 500px;
--dialog-width-md: 600px;
--dialog-width-lg: 700px;
--table-scroll-max-width: 100%;
}
}
```
### 4.3 共享 Composable新建 `src/composables/useMobile.ts`
```typescript
import { ref, onMounted, onUnmounted } from 'vue'
export function useMobile(breakpoint = 768) {
const isMobile = ref(window.innerWidth < breakpoint)
const isTablet = ref(window.innerWidth >= 768 && window.innerWidth < 1024)
const update = () => {
isMobile.value = window.innerWidth < breakpoint
isTablet.value = window.innerWidth >= 768 && window.innerWidth < 1024
}
onMounted(() => window.addEventListener('resize', update))
onUnmounted(() => window.removeEventListener('resize', update))
return { isMobile, isTablet }
}
```
---
## 五、逐页面修复方案
### 5.1 注册页 `RegisterPage.vue`
**问题**卡片固定 400px 宽度
**修复**
```css
.register-card {
width: 400px;
}
@media (max-width: 575px) {
.register-card {
width: 92vw;
max-width: 400px;
}
}
```
`el-form` `label-width` 改为响应式`:label-width="isMobile ? '0px' : '80px'"`手机用 top label)。
### 5.2 推荐详情页 `RecommendDetailView.vue`
**问题**3 列描述4 列表格标题栏 3 按钮
**修复**
- `el-descriptions`: `:column="isMobile ? 1 : 3"`
- 标题栏按钮`flex-wrap: wrap` 或手机端折叠为下拉菜单
- plan 表格外层加 `<div style="overflow-x: auto">`手机端隐藏"推荐理由"
### 5.3 推荐历史页 `RecommendHistory.vue`
**问题**筛选栏溢出表格过宽
**修复**
- 筛选栏`flex-wrap: wrap`筛选项用 `min-width: 120px; flex: 1` 替代固定宽度
- 表格外层 `<div style="overflow-x: auto">`手机端隐藏低优先级列"保单号"、"创建时间"
- 或使用 Element Plus `el-table` `scrollbar-always-on` + 设置列 `min-width`
### 5.4 仪表盘 `DashboardPage.vue`
**问题**KPI 4 快捷 3 列无响应式
**修复**
```html
<!-- KPI 卡片 -->
<el-col :xs="12" :sm="12" :md="6" v-for="item in kpiCards">
<!-- 快捷卡片 -->
<el-col :xs="12" :sm="8" :md="8" v-for="item in shortcutCards">
```
### 5.5 权限页 `PermissionsPage.vue`
**问题**角色列表 8/权限 16 分割无响应式
**修复**
```html
<el-col :xs="24" :sm="8"> <!-- 角色列表 -->
<el-col :xs="24" :sm="16"> <!-- 权限面板 -->
```
手机端改为上下堆叠布局
### 5.6 数据源页 `DataSourcePage.vue`
**问题**同步日志弹窗 700px
**修复**改为 `width="var(--dialog-width-lg)"` `width="min(700px, 90vw)"`
### 5.7 聊天筛选 `ChatFilters.vue`
**问题**flex wrap标签 nowrap
**修复**
```css
.chat-filters {
flex-wrap: wrap;
}
.filter-label {
white-space: normal; /* 允许换行 */
}
```
### 5.8 推荐结果组件 `RecommendResult.vue`
**问题**7 列表格无滚动
**修复**表格包裹 `<div class="table-responsive">`
```css
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
```
替换弹窗改为 `width="min(600px, 90vw)"`
### 5.9 用户管理 `UsersPage.vue`
**问题**筛选栏溢出表格过宽弹窗固定
**修复**
- 筛选栏`flex-wrap: wrap`筛选项 `flex: 1; min-width: 140px`
- 表格`overflow-x: auto` 包裹手机端隐藏部分列
- 弹窗`width="min(480px, 90vw)"`
### 5.10 统计页 `StatsPage.vue`
**问题**`el-col` 无响应式筛选器固定宽度
**修复**
```html
<el-col :xs="24" :sm="12" :md="8">
```
筛选器宽度改为 `width: 100%`手机端 `flex: 1; min-width: 120px`
### 5.11 模板页 `TemplatePage.vue`
**问题**弹窗 50% 太窄placeholder 行溢出
**修复**
- 弹窗改为 `width="min(500px, 90vw)"`
- placeholder `flex-wrap: wrap`每项 `min-width: 120px`
### 5.12 提示词页 `PromptPage.vue`
**问题**弹窗 70% 手机端过窄
**修复**改为 `width="min(600px, 90vw)"`
### 5.13 通知页 `NotificationPage.vue`
**问题**弹窗 50% 不可用
**修复**改为 `width="min(500px, 90vw)"`
### 5.14 部门页 `DepartmentPage.vue`
**问题**弹窗 500px
**修复**改为 `width="min(500px, 90vw)"`
### 5.15 知识库管理 `KBManagePage.vue`
**问题**筛选栏溢出弹窗固定
**修复**
- 筛选栏`flex-wrap: wrap`筛选项 `flex: 1; min-width: 130px`
- 弹窗`width="min(500px, 90vw)"`
### 5.16 登录日志 `LoginLogsPage.vue` / 聊天日志 `ChatLogsPage.vue`
**问题**筛选项固定宽度
**修复**筛选项改为 `width: 100%`手机端 `flex: 1; min-width: 140px`
### 5.17 聊天嵌入 `ChatEmbed.vue`
**问题**纠错弹窗 500pxpadding 不适配
**修复**
- 弹窗`width="min(500px, 90vw)"`
- `.message-list`手机端 padding 缩小为 `8px`
- `.input-area`手机端 padding 缩小为 `8px 12px`
### 5.18 聊天侧边栏 `ChatSidebar.vue`
**问题**手机端 max-height 过小删除按钮 hover 不可发现
**修复**
- `max-height` `200px` 调整为 `240px` `40vh`
- 删除按钮手机端改为 `opacity: 1`始终可见或增加长按触发
### 5.19 登录页 `LoginPage.vue`
**问题**背景装饰可能溢出
**修复**`.bg-decoration` `display: none` 在手机端
```css
@media (max-width: 575px) {
.bg-decoration {
display: none;
}
}
```
---
## 六、弹窗适配规则(全局)
| 原写法 | 推荐写法 | 说明 |
|--------|----------|------|
| `width="700px"` | `width="min(700px, 90vw)"` | 大弹窗 |
| `width="600px"` | `width="min(600px, 90vw)"` | 中弹窗 |
| `width="500px"` | `width="min(500px, 90vw)"` | 小弹窗 |
| `width="480px"` | `width="min(480px, 90vw)"` | 表单弹窗 |
| `width="70%"` | `width="min(600px, 90vw)"` | 百分比在手机上过窄 |
| `width="50%"` | `width="min(500px, 90vw)"` | 百分比在手机上不可用 |
| `width="90%"` | 可以保留 | 已是响应式写法 |
> **规则**:所有 `el-dialog` 的 `width` 使用 `min(固定值, 90vw)` 模式,确保手机端不超过 90% 视口宽度。
---
## 七、表格适配规则(全局)
### 7.1 横向滚动(最简单)
所有 `el-table` 外层包裹
```html
<div class="table-scroll-wrapper">
<el-table ...>
<!-- columns -->
</el-table>
</div>
```
```css
.table-scroll-wrapper {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
```
### 7.2 列优先级隐藏
```css
@media (max-width: 767px) {
/* 隐藏低优先级列 — 通过 el-table 的 v-if 或 class 控制 */
.hide-on-mobile {
display: none;
}
}
```
或使用 Element Plus `el-table-column` `show-overflow-tooltip` + `min-width` 替代 `width`
### 7.3 推荐的列宽策略
| 场景 | 策略 |
|------|------|
| 23 列表格 | 无需特殊处理 |
| 45 列表格 | `overflow-x: auto` 包裹 |
| 6+ 列表格 | `overflow-x: auto` + 手机端隐藏低优先级列 |
| 筛选栏 | `flex-wrap: wrap` + 筛选项 `flex: 1; min-width: 120px` |
---
## 八、筛选栏适配模板
所有筛选栏统一模式
```css
.filter-bar {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.filter-item {
flex: 1;
min-width: 120px;
}
@media (max-width: 575px) {
.filter-bar {
gap: 8px;
}
.filter-item {
min-width: 100%;
}
}
```
---
## 九、实施优先级
### Phase 1 — 核心页面(用户直接使用)
1. `ChatPage.vue` + `ChatEmbed.vue` + `ChatSidebar.vue` + `ChatFilters.vue`
2. `LoginPage.vue` + `RegisterPage.vue`
3. `RecommendForm.vue` + `RecommendResult.vue` + `RecommendDetailView.vue` + `RecommendHistory.vue`
### Phase 2 — 管理后台
4. `DashboardPage.vue`
5. `UsersPage.vue` + `PermissionsPage.vue`
6. 其余 admin 页面`PromptPage`、`TemplatePage`、`StatsPage`
### Phase 3 — 全局优化
7. 创建 `src/composables/useMobile.ts`替换所有 `isMobile` 重复逻辑
8. 创建 `src/styles/responsive.css`统一 CSS 变量
9. 创建 `src/styles/table.css`统一表格滚动样式
10. 创建 `src/styles/dialog.css`统一弹窗响应式
---
## 十、验证清单
每个页面适配后用以下方式验证
- [ ] Chrome DevTools 切换到 iPhone SE (375px) 查看
- [ ] Chrome DevTools 切换到 iPhone 14 Pro (393px) 查看
- [ ] Chrome DevTools 切换到 iPad (768px) 查看
- [ ] 检查无水平滚动条除非是表格横向滚动
- [ ] 弹窗在手机端可正常显示和操作
- [ ] 筛选栏在手机端可正常换行
- [ ] 表格在手机端可横向滚动
- [ ] 触屏设备上无 hover-only 的交互死角