mat/docs/superpowers/specs/2026-04-24-h5-material-brow...

213 lines
8.8 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.

# 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` 技能产出