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:
@@ -10,13 +10,10 @@ import '../pangolin_theme.dart';
|
||||
import '../state/app_providers.dart';
|
||||
import '../state/connection_provider.dart';
|
||||
import '../state/nodes_provider.dart';
|
||||
import '../state/quota_provider.dart';
|
||||
import '../widgets/ad_reward_dialog.dart';
|
||||
import '../widgets/app_top_bar.dart';
|
||||
import '../widgets/connect_button.dart';
|
||||
import '../widgets/country_code.dart';
|
||||
import '../widgets/pangolin_icons.dart';
|
||||
import '../widgets/quota_card.dart';
|
||||
|
||||
class ConnectPage extends ConsumerWidget {
|
||||
const ConnectPage({super.key, required this.isWide, required this.onOpenNodes});
|
||||
@@ -31,8 +28,6 @@ class ConnectPage extends ConsumerWidget {
|
||||
final conn = ref.watch(connectionProvider);
|
||||
final node = ref.watch(effectiveNodeProvider);
|
||||
final smart = ref.watch(isSmartSelectProvider);
|
||||
final isFree = ref.watch(isFreePlanProvider);
|
||||
final quota = ref.watch(quotaProvider);
|
||||
final stats = ref.watch(vpnStatsProvider).valueOrNull;
|
||||
// 账户首拉中 → 整页转圈,先拉到 me(决定免费/会员)再渲染,
|
||||
// 避免「先闪免费广告、me 到了再跳会员」。me 是常驻 Notifier,只首启时转一下。
|
||||
@@ -53,18 +48,11 @@ class ConnectPage extends ConsumerWidget {
|
||||
VpnPhase.on => t.capOn,
|
||||
};
|
||||
|
||||
// 免费额度耗尽:off 态连接键灰化不可点,点击弹加时(移动看广告 / 桌面升级)。
|
||||
final isDesktop = context.formFactor == FormFactor.desktop;
|
||||
final connectEnabled = !(isFree && quota.isExhausted && conn.phase == VpnPhase.off);
|
||||
void onQuotaBlockedTap() => showQuotaAdFlow(context, ref, isDesktop: isDesktop);
|
||||
|
||||
final button = ConnectButton(
|
||||
phase: conn.phase,
|
||||
elapsed: conn.elapsed,
|
||||
offLabel: t.connectNow,
|
||||
secureLabel: t.secure,
|
||||
enabled: connectEnabled,
|
||||
onDisabledTap: onQuotaBlockedTap,
|
||||
onTap: () => ref.read(connectionProvider.notifier).toggle(),
|
||||
);
|
||||
|
||||
@@ -82,14 +70,6 @@ class ConnectPage extends ConsumerWidget {
|
||||
]);
|
||||
|
||||
final infoChildren = <Widget>[
|
||||
if (isFree)
|
||||
QuotaCard(
|
||||
quota: quota,
|
||||
t: t,
|
||||
countdown: conn.freeCountdown,
|
||||
isDesktop: isDesktop,
|
||||
onWatchAd: onQuotaBlockedTap,
|
||||
),
|
||||
if (conn.phase == VpnPhase.on) _SpeedRow(t: t, down: down, up: up, latency: latencyValue),
|
||||
_CurrentNodeCard(t: t, node: node, smart: smart, pingLabel: pingLabel, showLabel: isWide, onTap: onOpenNodes),
|
||||
];
|
||||
@@ -114,8 +94,6 @@ class ConnectPage extends ConsumerWidget {
|
||||
offLabel: t.connectNow,
|
||||
secureLabel: t.secure,
|
||||
size: 176,
|
||||
enabled: connectEnabled,
|
||||
onDisabledTap: onQuotaBlockedTap,
|
||||
onTap: () => ref.read(connectionProvider.notifier).toggle(),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -132,19 +110,6 @@ class ConnectPage extends ConsumerWidget {
|
||||
),
|
||||
],
|
||||
_NodePill(t: t, node: node, smart: smart, pingLabel: pingLabel),
|
||||
if (isFree) ...[
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: 340,
|
||||
child: QuotaCard(
|
||||
quota: quota,
|
||||
t: t,
|
||||
countdown: conn.freeCountdown,
|
||||
isDesktop: isDesktop,
|
||||
onWatchAd: onQuotaBlockedTap,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (conn.phase == VpnPhase.on) ...[
|
||||
const SizedBox(height: 16),
|
||||
_SpeedRow(t: t, down: down, up: up, latency: latencyValue),
|
||||
|
||||
Reference in New Issue
Block a user