22e07613a1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
194 lines
6.8 KiB
Dart
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,
|
|
);
|
|
}
|
|
}
|