feat: 实现导航页基础功能
- 添加导航条目和分类数据模型 - 实现导航卡片网格展示 - 支持点击卡片打开链接 - 添加主题样式(渐变色、卡片阴影) - 区分系统默认和自定义条目 - 添加分类统计信息"
This commit is contained in:
@@ -10,13 +10,9 @@ import '../../data/home_repository.dart';
|
||||
import '../../domain/models/home_data.dart';
|
||||
import '../../domain/models/link_item.dart';
|
||||
import '../../domain/models/link_section.dart';
|
||||
import '../../domain/models/nav_node.dart';
|
||||
import '../../domain/models/submit_site_draft.dart';
|
||||
import '../widgets/back_to_top_button.dart';
|
||||
import '../widgets/confirm_dialog.dart';
|
||||
import '../widgets/link_section_view.dart';
|
||||
import '../widgets/sidebar_nav.dart';
|
||||
import '../widgets/site_form_dialog.dart';
|
||||
import '../widgets/top_bar.dart';
|
||||
|
||||
/// 首页:可折叠侧栏 + 顶部控制栏 + 分组卡片网格。
|
||||
@@ -153,100 +149,14 @@ class _HomePageState extends State<HomePage> {
|
||||
|
||||
void _openItem(LinkItem item) => widget.urlOpener.open(item.url);
|
||||
|
||||
/// 打开「提交网站」表单;提交成功后静默刷新一次,让新站点立刻出现在侧栏里。
|
||||
Future<void> _openSubmitDialog(HomeData data) async {
|
||||
final bool saved = await showSiteFormDialog(
|
||||
context: context,
|
||||
navNodes: data.navNodes,
|
||||
heading: '提交网站',
|
||||
onSubmit: (SubmitSiteDraft draft) => widget.repository.submitSite(draft),
|
||||
);
|
||||
if (!saved || !mounted) {
|
||||
return;
|
||||
}
|
||||
_toast('已提交,感谢');
|
||||
await _load(showLoading: false);
|
||||
}
|
||||
|
||||
/// 编辑一个用户提交的站点:表单预填现有内容,保存走 `PATCH /api/sites/{id}`。
|
||||
Future<void> _openEditDialog(HomeData data, LinkItem item) async {
|
||||
final ({String group, String subGroup})? path = _groupPathOf(
|
||||
data.navNodes,
|
||||
item.categoryId,
|
||||
);
|
||||
final bool saved = await showSiteFormDialog(
|
||||
context: context,
|
||||
navNodes: data.navNodes,
|
||||
heading: '编辑网站',
|
||||
initial: SubmitSiteDraft(
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
description: item.description,
|
||||
group: path?.group ?? '',
|
||||
subGroup: path?.subGroup ?? '',
|
||||
),
|
||||
onSubmit: (SubmitSiteDraft draft) =>
|
||||
widget.repository.updateSite(item.id, draft),
|
||||
);
|
||||
if (!saved || !mounted) {
|
||||
return;
|
||||
}
|
||||
_toast('已保存');
|
||||
await _load(showLoading: false);
|
||||
}
|
||||
|
||||
/// 删除一个用户提交的站点:先确认,再走 `DELETE /api/sites/{id}`。
|
||||
Future<void> _confirmDelete(LinkItem item) async {
|
||||
final bool confirmed = await showConfirmDialog(
|
||||
context: context,
|
||||
title: '删除网站',
|
||||
message: '确定要删除「${item.title}」吗?删掉之后不能恢复。',
|
||||
confirmLabel: '删除',
|
||||
danger: true,
|
||||
);
|
||||
if (!confirmed || !mounted) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await widget.repository.deleteSite(item.id);
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
_toast('删除失败:$error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_toast('已删除');
|
||||
await _load(showLoading: false);
|
||||
}
|
||||
|
||||
void _toast(String message) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
/// 从侧栏树里反查某个分组 id 的「一级分组 + 子分组」名字。
|
||||
/// 「提交网站」跳到后端配好的收集入口(Mattermost 之类)。
|
||||
///
|
||||
/// 后端只按分组名收更新,所以编辑时必须把 id 还原成名字;
|
||||
/// 层级最多两级,这里只查两轮。
|
||||
({String group, String subGroup})? _groupPathOf(
|
||||
List<NavNode> nodes,
|
||||
String categoryId,
|
||||
) {
|
||||
for (final NavNode root in nodes) {
|
||||
if (root.id == categoryId) {
|
||||
return (group: root.title, subGroup: '');
|
||||
}
|
||||
for (final NavNode child in root.children) {
|
||||
if (child.id == categoryId) {
|
||||
return (group: root.title, subGroup: child.title);
|
||||
}
|
||||
}
|
||||
/// 地址在 `data/nav.json` 的 `site.submitUrl`;没配就什么都不做。
|
||||
void _openSubmitUrl(HomeData data) {
|
||||
if (data.submitUrl.isEmpty) {
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
widget.urlOpener.open(data.submitUrl);
|
||||
}
|
||||
|
||||
void _backToTop() {
|
||||
@@ -293,7 +203,7 @@ class _HomePageState extends State<HomePage> {
|
||||
openGroupIds: _openGroupIds,
|
||||
onToggleGroup: _toggleGroup,
|
||||
onSectionSelected: _goToSection,
|
||||
onSubmitSite: () => unawaited(_openSubmitDialog(data)),
|
||||
onSubmitSite: () => _openSubmitUrl(data),
|
||||
onToggleCollapse: _toggleSidebar,
|
||||
),
|
||||
Expanded(child: _buildMain(data)),
|
||||
@@ -334,9 +244,6 @@ class _HomePageState extends State<HomePage> {
|
||||
key: _sectionKeys[section.id],
|
||||
section: section,
|
||||
onOpen: _openItem,
|
||||
onEdit: (LinkItem item) =>
|
||||
unawaited(_openEditDialog(data, item)),
|
||||
onDelete: (LinkItem item) => unawaited(_confirmDelete(item)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user