feat(client): App 内在线购买/续费授权(管理员支付宝下单+到账轮询)
- 新增套餐目录 license_plans.dart(标准/高级 × 月付/年付,与官网 checkout 一致) - license 模型/仓库新增 PurchaseOrder/PurchaseStatusInfo 与下单、查单接口 - 系统设置授权面板:管理员显示「在线购买 / 续费」按钮,弹窗选套餐→ 外部浏览器打开支付宝收银台→3s 轮询到账→自动刷新授权并展示新到期日 - 用户手册(docs.md + user-manual.html)同步补充在线购买入口说明 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -32,6 +32,7 @@ import '../../widgets/ds/ds_atoms.dart';
|
||||
import '../../widgets/ds/ds_menu.dart';
|
||||
import '../../widgets/ds/ds_table.dart';
|
||||
import '../../widgets/write_guard.dart';
|
||||
import 'purchase_dialog.dart';
|
||||
import 'users_screen.dart' show userRoleBadge, userStatusBadge;
|
||||
import '../../core/theme/app_fonts.dart';
|
||||
import '../../widgets/ds/ds_toast.dart';
|
||||
@@ -685,6 +686,8 @@ class _LicensePanelState extends ConsumerState<_LicensePanel> {
|
||||
Widget build(BuildContext context) {
|
||||
final t = context.tokens;
|
||||
final lic = ref.watch(licenseProvider).valueOrNull;
|
||||
final role = ref.watch(authStateProvider.select((s) => s.user?.role));
|
||||
final isAdmin = role == 'admin' || role == 'superadmin';
|
||||
final expires = lic?.expiresAt != null
|
||||
? DateFormat('yyyy-MM-dd').format(lic!.expiresAt!)
|
||||
: '—';
|
||||
@@ -759,14 +762,21 @@ class _LicensePanelState extends ConsumerState<_LicensePanel> {
|
||||
Row(children: [
|
||||
Text('没有兑换券?',
|
||||
style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
|
||||
InkWell(
|
||||
onTap: () => launchUrl(Uri.parse('${AppInfo.website}/#pricing')),
|
||||
child: Text('购买授权时长 →',
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsSm,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: t.primary)),
|
||||
),
|
||||
// 在线购买仅管理员(后端 purchase 接口 admin only);其他角色引导看官网价格页
|
||||
if (isAdmin)
|
||||
DsButton('在线购买 / 续费',
|
||||
variant: DsBtnVariant.accent,
|
||||
onPressed: () => showPurchaseDialog(context))
|
||||
else
|
||||
InkWell(
|
||||
onTap: () =>
|
||||
launchUrl(Uri.parse('${AppInfo.website}/#pricing')),
|
||||
child: Text('查看套餐价格 →',
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsSm,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: t.primary)),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
// 过期降级说明(现有能力保留)
|
||||
|
||||
Reference in New Issue
Block a user