fix(nodes): 桌面 Nodes 页统一 600 宽度(原型挂 view-body + Flutter PageBody)+ 双列网格改单列镜像原型

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
wangjia
2026-07-13 10:13:48 +08:00
parent a61641b53b
commit 3d24451835
6 changed files with 23 additions and 21 deletions
+21 -19
View File
@@ -13,6 +13,7 @@ import '../state/nodes_provider.dart';
import '../widgets/app_top_bar.dart';
import '../widgets/country_code.dart';
import '../widgets/pangolin_field.dart';
import '../widgets/page_body.dart';
import '../widgets/pangolin_icons.dart';
import '../widgets/server_tile.dart';
import '../widgets/smart_select_card.dart';
@@ -128,31 +129,32 @@ class _NodesPageState extends ConsumerState<NodesPage> {
]);
if (widget.isWide) {
// 对照 dapp.jsx DServerspadding 4/28、网格 gap 10。智能卡置顶(CLAUDE.md §5 规范,三端一致)。
// 桌面 Nodes 页宽度统一为 view-body(600 居中)、单列 server-row 行列表——
// 镜像原型 design/prototype/screens/ui-desktop.html 的 .nodes-view(真相源)。
// 智能卡置顶(CLAUDE.md §5 规范,三端一致)。
return RefreshIndicator(
color: c.accent,
backgroundColor: c.surface,
onRefresh: _refresh,
child: ListView(
padding: const EdgeInsets.fromLTRB(28, 4, 28, 24),
children: [
smartCard,
const SizedBox(height: 14),
ConstrainedBox(constraints: const BoxConstraints(maxWidth: 440), child: searchRow),
const SizedBox(height: 14),
GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 4.4,
child: PageBody(
child: ListView(
padding: const EdgeInsets.fromLTRB(28, 4, 28, 24),
children: [
for (final n in nodes)
_NodeGridTile(node: n, lang: t.lang, active: !smart && n.code == selected, onTap: () => _onTapNode(n)),
smartCard,
const SizedBox(height: 14),
ConstrainedBox(constraints: const BoxConstraints(maxWidth: 440), child: searchRow),
const SizedBox(height: 14),
for (var i = 0; i < nodes.length; i++) ...[
_NodeGridTile(
node: nodes[i],
lang: t.lang,
active: !smart && nodes[i].code == selected,
onTap: () => _onTapNode(nodes[i]),
),
if (i < nodes.length - 1) const SizedBox(height: 10),
],
],
),
],
),
);
}
@@ -226,7 +228,6 @@ class _SearchBox extends StatelessWidget {
}
}
/// 宽屏双列网格中的节点块。
/// 刷新按钮(搜索框右侧):点击刷新节点列表(受 10s 限流);刷新中显示 spinner。
class _RefreshButton extends StatelessWidget {
const _RefreshButton({required this.refreshing, required this.onTap});
@@ -256,6 +257,7 @@ class _RefreshButton extends StatelessWidget {
}
}
/// 宽屏 Nodes 页单列节点行(镜像原型 .server-row);自然高度撑高,不依赖网格 aspect ratio。
class _NodeGridTile extends StatelessWidget {
const _NodeGridTile({required this.node, required this.lang, required this.active, required this.onTap});
final Node node;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 72 KiB