docs(h5): 新增 H5 材料浏览端设计文档
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
ddc26c414a
commit
d550c73439
|
|
@ -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 4,history 模式
|
||||
├── 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` 存 token,key 为 `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]` 标签值布局;竞争优势、应用场景、重要等级、替代类型用 chip;4 项等级用星级可视化。长文本(优势说明、成本说明、应用说明)独立成段。
|
||||
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` 技能产出
|
||||
Loading…
Reference in New Issue