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
@@ -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 DServers:padding 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;
|
||||
|
||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 72 KiB |
@@ -116,7 +116,7 @@
|
||||
.rate-chip .u{ font-size:10px; color:var(--fg3); }
|
||||
|
||||
/* ---- 节点视图 ---- */
|
||||
.nodes-view{ padding:var(--space-5) var(--space-6) var(--space-6); }
|
||||
/* .nodes-view 宽度/内边距由 view-body 原子承担(见 atoms.css),此处不再重复声明 */
|
||||
.searchbar{ display:flex; align-items:center; gap:9px; background:var(--bg-subtle);
|
||||
border-radius:var(--radius-md); padding:9px 13px; margin-bottom:var(--space-4); max-width:360px; }
|
||||
.searchbar svg{ width:17px; height:17px; color:var(--fg3); }
|
||||
@@ -474,7 +474,7 @@
|
||||
|
||||
<!-- 节点视图 -->
|
||||
<section class="view" data-view="nodes">
|
||||
<div class="nodes-view">
|
||||
<div class="view-body nodes-view">
|
||||
<div class="searchbar"><svg><use href="#i-search"/></svg><input data-i18n-ph="nodes.search" placeholder="搜索加速线路…"></div>
|
||||
<div class="card card-flush">
|
||||
<div class="server-row is-smart is-selected" data-node="smart">
|
||||
|
||||