docs(h5): 新增 H5 材料浏览端设计文档

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-24 15:31:43 +08:00
parent ddc26c414a
commit d550c73439
1 changed files with 212 additions and 0 deletions

View File

@ -0,0 +1,212 @@
# H5 材料浏览端 设计文档
- 日期2026-04-24
- 作者caoqianming
- 状态:草案
## 1 · 背景与目标
在现有 mat3 项目PC 端材料管理 + Django 后端)基础上,新增面向手机浏览器的 **H5 材料查看端**
核心诉求:**只读浏览**——登录后按 "大类 → 种类 → 子类 → 材料 → 详情" 的层级快速找到材料并查看详细信息。
不包含新增 / 编辑 / 导入 / 审批等写操作。
## 2 · 项目结构与技术栈
### 2.1 组织方式
新建独立项目 `frontend-h5/`,与现有 `frontend/` 平级;独立 Vite、独立依赖、独立打包。
```
frontend-h5/
├── index.html
├── vite.config.js # base: '/m/'dev proxy → 后端
├── package.json
├── tailwind.config.js
├── postcss.config.js
└── src/
├── main.js
├── App.vue
├── router/ # vue-router 4history 模式
├── store/ # pinia
├── api/ # 参照 frontend/src/api 复制同名文件,保持一致
├── composables/ # useAuth / useInfiniteScroll / useToast ...
├── styles/ # tailwind.css、全局变量
├── components/ # MaterialCard / CategoryCard / StarLevel / Chip / NavBar / Toast ...
└── views/
├── Login.vue
├── Home.vue
├── CategoryDetail.vue
└── MaterialDetail.vue
```
### 2.2 技术栈
- Vue 3 + vue-router 4 + Pinia
- Axios同 PC 端一致的拦截器:附 token、统一错误处理、401 跳登录)
- **Tailwind CSS**(自定义设计令牌),不使用 Vant/NutUI
- 少量自研组件Toast、NavBar、Tab、InfiniteList、StarLevel、Chip、Skeleton
### 2.3 部署
- 构建产物部署到 Nginx 子路径 `/m/`(与 PC 端同域名)
- `vite.config.js``base: '/m/'`
- 开发期 `vite` devServer 走本地端口(如 5174通过 `server.proxy``/api` 转发到后端
### 2.4 登录态
- `localStorage` 存 tokenkey 为 `h5_token`(与 PC 端 `token` 隔离,避免互相污染)
- Pinia `authStore` 暴露 `token` / `user` / `login()` / `logout()`
- Axios request 拦截器:存在 `h5_token` 则附 `Authorization`
- Axios response 拦截器401 清 token 并跳 `/login?redirect=<current>`
## 3 · 路由与页面流
| 路径 | 页面 | 说明 |
| --- | --- | --- |
| `/login` | Login | 账号密码登录 |
| `/` | Home | 大类卡片 + 选中后展开种类区 |
| `/category/:major/:category` | CategoryDetail | 子类 Tab + 材料列表 |
| `/material/:id` | MaterialDetail | 材料详情(三块) |
**路由守卫**:非 `/login` 页进入前校验 token无 token → 跳 `/login?redirect=<原路径>`;登录成功后回跳。
**过渡**:页面切换用右推左 slide + fade CSS 过渡。
**状态保留**
- Pinia `uiStore` 记录 Home 选中的大类、CategoryDetail 选中的子类 Tab、列表滚动位置
- 从详情返回时恢复选中态和滚动位置
- 使用 `<keep-alive>` 缓存 Home 与 CategoryDetail
## 4 · 数据接口
### 4.1 复用现有接口
| 用途 | 接口 |
| --- | --- |
| 登录 | `POST /auth/login/` |
| 当前用户 | `GET /auth/user/` |
| 材料列表 | `GET /material/?major_category=&material_category=&material_subcategory=&search=&page=&page_size=` |
| 材料详情 | `GET /material/{id}/` |
| 选项字典 | `GET /material/choices/` |
### 4.2 新增接口
由于 `MaterialCategory``Material.major_category` 没有 FK 关系,"大类下的种类""种类下的子类" 需要从 `Material` 表按大类 distinct 计算。两个新接口都作为 `MaterialViewSet``@action` 提供。
**接口 1**`GET /material/categories-by-major/?major_category=architecture`
- 从 `Material` 表取 `major_category=X` 的记录,按 `material_category` 分组 count
- 响应:
```json
[
{ "value": "地砖", "count": 12 },
{ "value": "涂料", "count": 8 }
]
```
**接口 2**`GET /material/subcategories-by-category/?major_category=&material_category=`
- 同上,按 `material_subcategory` 分组 count过滤空值
- 响应:
```json
[
{ "value": "釉面砖", "count": 5 },
{ "value": "通体砖", "count": 3 }
]
```
"全部"分类不从后端返回,由前端在 Tab 第一项注入,点击时不传 `material_subcategory` 参数。
### 4.3 列表卡片使用字段
- `name`(材料名称)
- `cost_compare`(成本对比,百分数,负数表示更便宜)
- `advantage_display`(竞争优势,取前 2 个 chip 显示)
- `importance_level`(重要等级 chip
- `score_level`综合评分1-3 星)
- `factory_short_name`(供应商简称)
### 4.4 分页与加载
- `page_size = 20`
- 列表支持下拉刷新 + 上拉无限加载(基于 IntersectionObserver 的 `useInfiniteScroll`
## 5 · 视觉与交互设计
### 5.1 设计语言
**克制的卡片式 · 暖中性色** + **墨绿品牌色**
- 背景:白 `#FFFFFF` + 极浅灰分层 `#FAFAFA` / `#F5F4F2`
- 主色:`#2F4F3F`(墨绿),用于按钮、重点数值、选中态
- 辅助色:红 `#D2584A`(核心)、蓝 `#5A7FB8`(优先)、灰 `#8A8A8A`(一般)
- 卡片圆角 `16-20px`;阴影 `0 1px 2px rgba(0,0,0,.04)`;不用重描边
- 字体:系统栈 (`-apple-system, "PingFang SC", ...`);数值字段启用 `font-feature-settings: "tnum"` 等宽
- 点按反馈:`active:scale-[0.98]` + 背景变深
以上颜色 / 圆角 / 间距在 `tailwind.config.js` 定义为 theme tokens后续组件直接用工具类。
### 5.2 关键页面
**Login**
- 上 60% 留白 + 品牌标识
- 下 40% 表单:用户名、密码、提交按钮
- 失败信息由 Toast 从顶部下滑显示2s 自动消失
**Home**
- 顶部栏:欢迎语 + 用户名 + 退出图标
- 主体2×2 大类卡片网格(建筑/景观/设备/装修),每卡显示大类名 + 材料总数
- 选中大类后在下方 slide-down + fade 出现种类卡片区2 列网格,卡带材料数角标)
- 未选中时下方显示引导文案
- 选中的大类 ID 写入 `uiStore`,从详情返回恢复展开状态
**CategoryDetail**
- 顶部 NavBar返回按钮 + 种类名标题
- 吸顶的子类 Tab 栏(横向滚动,"全部" 为第一项,选中下划线 + 文字加重)
- 材料卡列表:通栏,左信息区(名称 / 供应商 / 优势 chip+ 右数值区(成本 / 评分 / 重要等级)
- 下拉刷新、上拉无限加载
- 空态:图标 + "暂无材料"
**MaterialDetail**
- 顶部 NavBar
- 可选顶部 banner`brochure` 宣传图16:9懒加载点击可全屏预览
- 三个 section每个 section 是一张圆角卡:
1. **材料信息**:基础 / 应用 / 优势 / 成本 / 评分,`grid-cols-[auto_1fr]` 标签值布局;竞争优势、应用场景、重要等级、替代类型用 chip4 项等级用星级可视化。长文本(优势说明、成本说明、应用说明)独立成段。
2. **品牌与供应商**:品牌、供应商简称/全称、合作模式、省-市、对接人、对接人联系方式。电话字段带 `tel:` 链接可一键拨打。
3. **案例信息**:落地项目、案例(保留换行)、经办人、备注。
### 5.3 通用交互
- Loading顶部 NProgress 细线进度条;列表用骨架屏
- 错误:非 401 错误 Toast 提示;页面级错误态带"重试"按钮
- 401清 token + 跳登录
## 6 · 后端改动清单
`apps/material/views.py`
- `MaterialViewSet` 新增两个 `@action(detail=False, methods=['get'])`
- `categories_by_major`
- `subcategories_by_category`
- 两者权限与现有列表一致(登录用户可访问)
- 考虑给 `material_category` / `material_subcategory` 字段的"查询计数"加缓存(低优,后续再说)
无 model migration。
## 7 · 验收标准
- [ ] 手机浏览器iOS Safari / 微信内置 / Android Chrome访问 `/m/`,可完成 登录 → 大类 → 种类 → 子类 → 材料详情 的完整浏览
- [ ] 详情页三块布局、字段映射与本文档 §5.2 一致
- [ ] 从详情返回上级页面,滚动位置与选中 Tab 保持
- [ ] 401 时自动跳登录且登录后回跳原路径
- [ ] 包体积:首屏 gzip 后 < 150 KB不含首图
- [ ] 单手可达性:关键按钮在屏幕下半区
- [ ] PC 端 `/` 功能不受影响
## 8 · 非目标
- 新增、编辑、导入、导出、审批
- 离线缓存 / PWA
- 多语言
- 手机号验证码登录(后续视需要再开 Spec
## 9 · 风险与开放项
- **微信内置浏览器差异**:目前未要求适配微信分享/JSSDK仅确保页面能正常打开
- **H5 与 PC 登录态隔离**:使用不同 localStorage key后端 token 为同一份 JWT无服务端改动成本
- **种类 distinct 性能**:当前数据量小可直接 SQL `GROUP BY`;后续若过万条可再加索引或 Redis 缓存
- **视觉最终稿**:本文档只定方向,具体 mockup 由后续 `frontend-design` 技能产出