feat(client): pay v2 支付渠道 switch + 订单列表/详情端到端

购买/支付(Phase B):
- 新 SegSwitch 段控 widget(镜像 .segswitch 原子)
- 购买页重构:顶部支付渠道 switch(默认 USDT→加密货币 / 人民币→支付宝),套餐价随渠道分币种显示,删底部弹窗选方式;点购买直达支付页
- 支付页加订单信息卡(套餐/渠道/金额按币种)
- models:PayChannel + PayCatalogItem.priceUsdtMicro/priceLabel(ch)/perMonthLabel(ch)

订单(P0,全新):
- payment_api.listOrders() + orders_provider(列表 + 详情 family)
- OrdersScreen 列表→详情(状态 pill / 可复制订单号 / 继续支付·重新购买)
- 导航:NavView.orders + desktop_shell 分支 + account 入口

配套:
- l10n +19 getter(支付渠道 4 + 订单 15)×6 语言,l10n 闸绿
- 图标单源迁移 lucide_icons_flutter + pangolin_icons.dart(codegen)
- 联系渠道订正(Telegram/邮件 support@yanmeiai.com/官网) + 前端去广告&时长
- 测试:购买 switch 分币种 + 订单列表/空态/详情 + 假 API listOrders;golden 重录
- flutter analyze 0 error,flutter test 226 全绿

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-12 00:07:15 +08:00
parent f6ce5267d5
commit 272eca04ce
66 changed files with 1398 additions and 262 deletions
+12 -28
View File
@@ -24,19 +24,11 @@ class ConnectButton extends StatefulWidget {
required this.secureLabel,
this.elapsed = Duration.zero,
this.size = 208,
this.enabled = true,
this.onDisabledTap,
});
final VpnPhase phase;
final VoidCallback onTap;
/// 是否可点。免费额度耗尽时置 false → 灰化不可连,点击走 onDisabledTap。
final bool enabled;
/// 灰化态被点击的回调(如弹看广告加时/升级)。enabled=false 时生效。
final VoidCallback? onDisabledTap;
/// off 态圆内文字(如「点击连接」/「CONNECT」)。
final String offLabel;
@@ -63,26 +55,18 @@ class _ConnectButtonState extends State<ConnectButton> with SingleTickerProvider
Widget build(BuildContext context) {
final c = context.pangolin;
final s = widget.phase;
// 免费额度耗尽:off 态灰化不可连(锁图标 + 柔和阴影),点击走 onDisabledTap。
final disabled = !widget.enabled && s == VpnPhase.off;
final Color fill = disabled
? c.bgSubtle
: switch (s) {
VpnPhase.off => c.bgSubtle,
VpnPhase.connecting => c.accent,
VpnPhase.on => c.success,
};
final Color fg = disabled
? c.fg3
: (s == VpnPhase.off ? c.accent : PangolinColors.white);
final IconData icon = disabled
? PangolinIcons.lock
: switch (s) {
VpnPhase.off => PangolinIcons.power,
VpnPhase.connecting => PangolinIcons.loader,
VpnPhase.on => PangolinIcons.shieldCheck,
};
final Color fill = switch (s) {
VpnPhase.off => c.bgSubtle,
VpnPhase.connecting => c.accent,
VpnPhase.on => c.success,
};
final Color fg = s == VpnPhase.off ? c.accent : PangolinColors.white;
final IconData icon = switch (s) {
VpnPhase.off => PangolinIcons.power,
VpnPhase.connecting => PangolinIcons.loaderCircle,
VpnPhase.on => PangolinIcons.shieldCheck,
};
// off 用柔和阴影;connecting/on 增加同色光晕环(box-shadow,不动背景计算)。
final List<BoxShadow> glow = s == VpnPhase.off
@@ -100,7 +84,7 @@ class _ConnectButtonState extends State<ConnectButton> with SingleTickerProvider
button: true,
label: widget.offLabel,
child: GestureDetector(
onTap: disabled ? widget.onDisabledTap : widget.onTap,
onTap: widget.onTap,
child: AnimatedContainer(
duration: PangolinMotion.slow,
curve: PangolinMotion.easeOut,