// server_tile.dart — 节点列表行(国家码块 · 名称 · 延迟 · 信号条 · 选中态) import 'package:flutter/material.dart'; import '../l10n/app_text.dart'; import '../models/node.dart'; import '../pangolin_theme.dart'; import 'country_code.dart'; import 'pangolin_icons.dart'; class ServerInfo { const ServerInfo({ required this.code, required this.name, required this.sub, required this.ping, this.nodeId = '', }); final String code, name, sub; final int ping; /// 控制面节点 ID,对应 POST /v1/nodes/:nodeId/connect 路径参数。 /// 留空时降级为使用 [code] 小写值(仅限 mock 联调)。 final String nodeId; String get effectiveNodeId => nodeId.isEmpty ? code.toLowerCase() : nodeId; } class ServerTile extends StatelessWidget { const ServerTile({ super.key, required this.node, required this.lang, this.active = false, this.onTap, }); final Node node; final AppLang lang; final bool active; final VoidCallback? onTap; @override Widget build(BuildContext context) { final c = context.pangolin; return InkWell( onTap: onTap, child: Container( color: active ? c.accentSubtle : Colors.transparent, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), child: Row( children: [ CountryCode(code: node.code, active: active), const SizedBox(width: 13), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(node.localizedName(lang), style: PangolinText.sm .copyWith(color: c.fg1, fontWeight: FontWeight.w600, fontSize: 15)), const SizedBox(height: 2), Text(node.localizedSub(lang), style: PangolinText.caption.copyWith(color: c.fg3, fontWeight: FontWeight.w400)), ], ), ), Text(node.pingLabel, style: PangolinText.mono.copyWith(color: c.fg2, fontSize: 12)), const SizedBox(width: 8), SignalBars(ping: node.ping), if (active) ...[const SizedBox(width: 10), Icon(PangolinIcons.check, size: 18, color: c.accent)], ], ), ), ); } }