Files
jiu/client/lib/screens/license/license_screen.dart
T

194 lines
6.8 KiB
Dart

// screens/license/license_screen.dart — 授权管理独立一级屏(原型 license.html /
// m-license.html),三 tab:授权信息 / 在线购买续费 / 订单管理。
// 桌面 `/license`、移动 `/me/license` 复用同一 Widget,内部按 context.isMobile
// 切换两种形态(桌面:头部+DsSeg+卡片区;移动:通栏 DsSeg+滚动内容)。
//
// tab 可见性:
// - 「在线购买续费」iOS App Store 合规态(hideExternalPurchaseUi)整 tab 隐藏;
// - 「订单管理」GET /license/purchases 仅管理员可用(后端判权),非管理员隐藏该 tab。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../core/auth/auth_state.dart';
import '../../core/config/store_compliance.dart';
import '../../core/responsive/responsive.dart';
import '../../core/theme/app_dims.g.dart';
import '../../core/theme/context_tokens.dart';
import '../../core/utils/money.dart';
import '../../core/utils/web_launch.dart';
import '../../providers/license_purchase_provider.dart';
import '../../widgets/ds/ds_atoms.dart';
import 'license_orders_tab.dart';
import '../settings/license_panel.dart';
import '../settings/purchase_card.dart';
import '../settings/settings_card.dart';
enum _LicSection { info, buy, orders }
class LicenseScreen extends ConsumerStatefulWidget {
const LicenseScreen({super.key});
@override
ConsumerState<LicenseScreen> createState() => _LicenseScreenState();
}
class _LicenseScreenState extends ConsumerState<LicenseScreen> {
_LicSection _section = _LicSection.info;
void _goInfo() => setState(() => _section = _LicSection.info);
void _goBuy() => setState(() => _section = _LicSection.buy);
@override
Widget build(BuildContext context) {
final t = context.tokens;
final role = ref.watch(authStateProvider.select((s) => s.user?.role));
final isAdmin = role == 'admin' || role == 'superadmin';
final showBuy = !hideExternalPurchaseUi;
final showOrders = isAdmin;
// 当前 tab 若因权限/合规态变得不可见,回退到「授权信息」。
if ((_section == _LicSection.buy && !showBuy) ||
(_section == _LicSection.orders && !showOrders)) {
_section = _LicSection.info;
}
final sections = [
_LicSection.info,
if (showBuy) _LicSection.buy,
if (showOrders) _LicSection.orders,
];
final mobile = context.isMobile;
final segLabels = {
_LicSection.info: '授权信息',
_LicSection.buy: mobile ? '购买 / 续费' : '在线购买 / 续费',
_LicSection.orders: '订单管理',
};
Widget seg = DsSeg(
items: [for (final s in sections) segLabels[s]!],
index: sections.indexOf(_section).clamp(0, sections.length - 1),
onChanged: (i) => setState(() => _section = sections[i]),
);
Widget body = switch (_section) {
_LicSection.info => _infoBody(mobile),
_LicSection.buy => _buyBody(mobile, isAdmin),
_LicSection.orders => LicenseOrdersTab(
onGoInfo: _goInfo,
onGoBuy: showBuy ? _goBuy : null,
),
};
if (mobile) {
return Container(
color: t.bg,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(14, 12, 14, 0),
child: Align(alignment: Alignment.centerLeft, child: seg),
),
const SizedBox(height: 12),
Expanded(child: body),
],
),
);
}
return Container(
color: t.bg,
padding: const EdgeInsets.fromLTRB(26, 22, 26, 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildHeader(context, ref, t),
const SizedBox(height: 18),
Align(alignment: Alignment.centerLeft, child: seg),
const SizedBox(height: 18),
Expanded(child: body),
],
),
);
}
Widget _buildHeader(BuildContext context, WidgetRef ref, dynamic t) {
final sub = switch (_section) {
_LicSection.info => '授权状态 · 兑换券 · 降级规则',
_LicSection.buy => '选择套餐支付宝支付 · 到账自动续期',
_LicSection.orders => () {
final result = ref.watch(purchaseListProvider).valueOrNull;
if (result == null) return '订单流水';
return '${result.total} 笔 · '
'累计 ¥${yuanFromMinor(result.summary.paidTotalMinor)}';
}(),
};
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('授权管理',
style: TextStyle(
fontSize: AppDims.fsH1,
fontWeight: FontWeight.w700,
color: t.heading)),
const SizedBox(width: AppDims.sp3),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(sub, style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
),
const Spacer(),
if (_section == _LicSection.orders)
DsButton('刷新',
icon: LucideIcons.refreshCw,
onPressed: () =>
ref.read(purchaseListProvider.notifier).reload()),
],
);
}
Widget _infoBody(bool mobile) {
final child = mobile
? const LicensePanel()
: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 720),
child: const LicensePanel(),
);
return SingleChildScrollView(
padding: mobile ? const EdgeInsets.fromLTRB(14, 0, 14, 14) : EdgeInsets.zero,
child: child,
);
}
Widget _buyBody(bool mobile, bool isAdmin) {
final card = SettingsCard(
title: '在线购买 / 续费',
desc: isAdmin ? '选择套餐支付宝支付,到账后授权自动续期' : '在线购买仅门店管理员可操作',
child: isAdmin
? const PurchaseCard()
: Builder(builder: (ctx) {
final t = ctx.tokens;
return Row(children: [
Text('在线购买请联系门店管理员 · ',
style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
InkWell(
onTap: () => launchAuthedWebPath(ref, '/#pricing'),
child: Text('查看套餐价格 →',
style: TextStyle(
fontSize: AppDims.fsSm,
fontWeight: FontWeight.w600,
color: t.primary)),
),
]);
}),
);
final child = mobile
? card
: ConstrainedBox(constraints: const BoxConstraints(maxWidth: 560), child: card);
return SingleChildScrollView(
padding: mobile ? const EdgeInsets.fromLTRB(14, 0, 14, 14) : EdgeInsets.zero,
child: child,
);
}
}