feat: 实现导航页基础功能

- 添加导航条目和分类数据模型
- 实现导航卡片网格展示
- 支持点击卡片打开链接
- 添加主题样式(渐变色、卡片阴影)
- 区分系统默认和自定义条目
- 添加分类统计信息"
This commit is contained in:
2026-09-19 23:17:36 +08:00
parent 1c98340fc5
commit 039ea70edf
26 changed files with 796 additions and 2583 deletions
+7 -17
View File
@@ -104,11 +104,7 @@ lib/
├── link_search_field.dart # 搜索框(含聚焦光晕)
├── link_section_view.dart # 分区标题 + 自适应网格
├── link_card.dart # 站点卡片
├── back_to_top_button.dart # 右下角圆形回到顶部
├── dialog_buttons.dart # 对话框的主/次按钮
├── confirm_dialog.dart # 通用确认框(删除前确认)
├── site_form_dialog.dart # 网站表单(新增 / 编辑共用)
└── ...
└── back_to_top_button.dart # 右下角圆形回到顶部
```
`test/` 展开(与 `lib/` 一一对应):
@@ -143,15 +139,15 @@ main.dart → app.dart
数据流:
```
后端 GET /api/nav ──▶ { site: { name, searchHint }, groups: [ { …, sites: [] } ] }
后端 GET /api/nav ──▶ { site: { name, searchHint, submitUrl }, groups: [ … ] }
│ ▲
│ │ POST /api/sites(提交)
│ │ PATCH /api/sites/{id}(编辑)
│ │ DELETE /api/sites/{id}(删除)
▼ │
▼ │ ← 这三个接口页面上暂时不用
HttpHomeRepository ──实现──▶ HomeRepository(读 / 提交 / 编辑 / 删除)
│ 把分组树映射成前端模型:分组 → 侧栏导航;有网站的组 → 内容分区;
│ 每个网站带 editable,决定卡片上有没有编辑/删除入口
│ 每个网站带 editable(页面暂时不据此渲染任何入口)
▼
HomePage State:_load() 拿到 HomeData,并在加载中 / 失败(可重试)/ 完成三种状态间切换
│ (页面持有交互状态:当前分区、侧栏折叠、已展开的分组)
@@ -383,7 +379,7 @@ CSS 的 `max-height: 0` 只是视觉上收起了子菜单,DOM 里那些链接
| 检查 | 命令 | 结果 |
| --- | --- | --- |
| 静态分析 | `flutter analyze` | 1 条告警(`sidebar_nav.dart` 里 `_NavRow` 的 `super.key` 从未被传入,见下方说明) |
| 单元与组件测试 | `flutter test` | 57 个用例通过,6 个失败(都是侧栏实现与既有用例不一致,见下方说明) |
| 单元与组件测试 | `flutter test` | 47 个用例通过,6 个失败(都是侧栏实现与既有用例不一致,见下方说明) |
| Web 构建 | `flutter build web --no-wasm-dry-run` | 构建成功 |
> 上面这两处不是本轮对接引入的:`e2cc3ac` 那次提交重写了侧栏(行内改用 `Container`、
@@ -401,18 +397,12 @@ CSS 的 `max-height: 0` 只是视觉上收起了子菜单,DOM 里那些链接
- 导航:高亮固定写在第一个条目上;点有对应分区的导航会滚过去且高亮不变;点 `#icon` 这类没有对应元素的导航滚动偏移不变。
- 搜索框:输入后仍是两个分区、五张卡片(不过滤)。
- 滚动:滚动主内容不会重建侧栏、也不会改动高亮;回到顶部把偏移归零。
- 其它:点击卡片把对应 URL 交给 `UrlOpener`;点「提交网站」没有任何反应;卡片渲染首字母、名称与描述,悬停上浮量等于 `AppSizes.cardHoverLift`。
- 其它:点击卡片把对应 URL 交给 `UrlOpener`;点「提交网站」跳到后端配的地址;卡片渲染首字母、名称与描述,悬停上浮量等于 `AppSizes.cardHoverLift`。
- 后端对接:请求打到 `/api/nav`;分组映射成侧栏导航树;只有挂着网站的分组进内容区;
子分组标题带父级前缀;网站顺序按后端给的排;非 200、非 JSON、缺 `groups`、
连不上后端分别给出对应提示;缺字段的单条数据被跳过而不是整页失败。
- 加载状态:数据没回来时显示「正在加载…」;失败时显示原因和「重试」;点重试会重新取数并在成功后渲染内容。
- 提交表单:空表单提交会逐项提示且不发请求;内网地址被拦下;合法内容按 camelCase
字段发给仓库;后端报错时留在表单里显示原话;提交中按钮变「提交中…」且不可再点;
模板里只列用户分组(内置分组不出现在建议里),点建议会填进输入框。
- 联调:点侧栏「提交网站」打开表单;提交成功后静默重新拉一次数据并弹提示。
- 编辑 / 删除:可修改的条目才有入口;编辑表单预填现有内容且不显示提交者字段;
删除要先确认,点取消什么都不发生;确认后调删除接口并静默刷新;内置条目没有这两个入口。
- 站点信息:站点名与搜索框提示来自后端 `site` 字段,前端不再写死。
- 站点信息:站点名、搜索框提示、提交入口地址都来自后端 `site` 字段,前端不再写死。
## 10. 后续演进建议