Files
pangolin/client/lib/shell/desktop_shell.dart
T
wangjia 272eca04ce 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
2026-07-12 00:07:15 +08:00

115 lines
4.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// desktop_shell.dart — 桌面外壳(左侧栏 + 顶栏 + 内容区)
//
// 对照 ui_kits/desktop/dapp.jsx920×600 窗口、204 侧栏(6 项)、52 顶栏、内容区。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../pangolin_theme.dart';
import '../screens/account_page.dart';
import '../screens/connect_page.dart';
import '../screens/contact_page.dart';
import '../screens/nodes_page.dart';
import '../screens/orders_page.dart';
import '../screens/payment_page.dart';
import '../screens/purchase_page.dart';
import '../screens/settings_page.dart';
import '../screens/stats_page.dart';
import '../state/app_providers.dart';
import '../state/navigation_provider.dart';
import '../widgets/account_screens.dart';
import '../widgets/content_top_bar.dart';
import '../widgets/nav_sidebar.dart';
import '../widgets/pangolin_icons.dart';
class DesktopShell extends ConsumerWidget {
const DesktopShell({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final c = context.pangolin;
final t = ref.watch(appTextProvider);
final view = ref.watch(navViewProvider);
// 「我的」移到侧栏顶部品牌按钮(见 NavSidebar);联系放设置下面。
final items = <NavSidebarItem>[
(icon: PangolinIcons.power, label: t.tabConnect, view: NavView.connect),
(icon: PangolinIcons.globe, label: t.tabServers, view: NavView.servers),
(icon: PangolinIcons.chartNoAxesColumn, label: t.tabStats, view: NavView.stats),
(icon: PangolinIcons.settings, label: t.settingsTitle, view: NavView.settings),
(icon: PangolinIcons.messageCircle, label: t.contactTitle, view: NavView.contact),
];
final titles = <NavView, String>{
NavView.connect: t.tabConnect,
NavView.servers: t.tabServers,
NavView.stats: t.tabStats,
NavView.account: t.tabMe,
NavView.contact: t.contactTitle,
NavView.settings: t.settingsTitle,
NavView.plans: t.choosePlan,
NavView.redeem: t.redeemTitle,
NavView.devices: t.myDevices,
NavView.purchase: t.purchaseTitle,
NavView.payment: t.paymentTitle,
NavView.orders: t.ordersTitle,
};
void go(NavView v) => ref.read(navViewProvider.notifier).state = v;
Widget content() {
switch (view) {
case NavView.connect:
return ConnectPage(isWide: true, onOpenNodes: () => go(NavView.servers));
case NavView.servers:
return NodesPage(isWide: true, onPicked: () => go(NavView.connect));
case NavView.stats:
return const StatsPage(isWide: true);
case NavView.account:
return const AccountPage(isWide: true);
case NavView.contact:
return const ContactPage();
case NavView.settings:
return const SettingsPage();
// 账户下钻子页(内容区,顶栏带返回)
case NavView.plans:
// 只有 pro 走 pay v2 购买通道;team 等仍走既有兑换/联系人工流程。
return PlansScreen(t: t, embedded: true, onChoose: (code) => go(code == 'pro' ? NavView.purchase : NavView.redeem));
case NavView.redeem:
return RedeemScreen(t: t, embedded: true);
case NavView.devices:
return DevicesScreen(t: t, embedded: true);
case NavView.purchase:
return PurchaseScreen(t: t, embedded: true, onOrderCreated: () => go(NavView.payment));
case NavView.payment:
return PaymentScreen(t: t, embedded: true, onDone: () => go(NavView.account));
case NavView.orders:
return OrdersScreen(t: t, embedded: true);
}
}
// 子页返回目标:payment → purchase(换方式/失败重试仍在购买语境内);
// 其余下钻子页(含 purchase 本身)→ account。
VoidCallback? backTarget() {
if (view == NavView.payment) return () => go(NavView.purchase);
if (kAccountSubViews.contains(view)) return () => go(NavView.account);
return null;
}
return ColoredBox(
color: c.bg,
child: Row(children: [
NavSidebar(items: items),
Expanded(
child: Column(children: [
ContentTopBar(
title: titles[view] ?? t.brand,
onBack: backTarget(),
),
Expanded(child: content()),
]),
),
]),
);
}
}