refactor(client): 授权信息卡从「关于我们」迁至 设置→授权管理,替换授权到期时间卡
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
@@ -6,7 +6,6 @@
|
|||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
@@ -16,11 +15,9 @@ import '../../core/responsive/responsive.dart';
|
|||||||
import '../../core/theme/app_dims.g.dart';
|
import '../../core/theme/app_dims.g.dart';
|
||||||
import '../../core/theme/context_tokens.dart';
|
import '../../core/theme/context_tokens.dart';
|
||||||
import '../../core/update/app_updater.dart';
|
import '../../core/update/app_updater.dart';
|
||||||
import '../../core/utils/clock.dart';
|
|
||||||
import '../../core/utils/dialog_util.dart';
|
import '../../core/utils/dialog_util.dart';
|
||||||
import '../../providers/changelog_provider.dart';
|
import '../../providers/changelog_provider.dart';
|
||||||
import '../../providers/feedback_provider.dart';
|
import '../../providers/feedback_provider.dart';
|
||||||
import '../../providers/license_provider.dart';
|
|
||||||
import '../../providers/update_provider.dart';
|
import '../../providers/update_provider.dart';
|
||||||
import '../../core/theme/app_fonts.dart';
|
import '../../core/theme/app_fonts.dart';
|
||||||
import '../../widgets/brand_mark.dart';
|
import '../../widgets/brand_mark.dart';
|
||||||
@@ -55,7 +52,6 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
|
|||||||
children: [
|
children: [
|
||||||
_heroCard(t),
|
_heroCard(t),
|
||||||
_productCard(t),
|
_productCard(t),
|
||||||
_licenseCard(t),
|
|
||||||
_changelogCard(t),
|
_changelogCard(t),
|
||||||
// 页脚
|
// 页脚
|
||||||
Padding(
|
Padding(
|
||||||
@@ -372,72 +368,8 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 卡3:授权信息(3 cell)─────────────────────────────────
|
// ── 卡3:更新日志(timeline,默认 2 条 + 展开)───────────────
|
||||||
Widget _licenseCard(dynamic t) {
|
// (原授权信息卡已迁至 设置 → 授权管理)
|
||||||
final lic = ref.watch(licenseProvider).valueOrNull;
|
|
||||||
final expires = lic?.expiresAt != null
|
|
||||||
? DateFormat('yyyy-MM-dd').format(lic!.expiresAt!)
|
|
||||||
: '—';
|
|
||||||
// 状态/剩余天数走可注入时钟(golden 确定化)
|
|
||||||
final active = lic?.isActive == true &&
|
|
||||||
(lic?.expiresAt == null || lic!.expiresAt!.isAfter(appNow()));
|
|
||||||
final days = lic?.expiresAt != null
|
|
||||||
? lic!.expiresAt!.difference(appNow()).inDays.clamp(0, 99999)
|
|
||||||
: null;
|
|
||||||
final mobile = context.isMobile;
|
|
||||||
final cells = [
|
|
||||||
_licCell(t, '授权状态', active ? '已授权' : '未授权',
|
|
||||||
color: active ? t.success : t.warn),
|
|
||||||
_licCell(t, '到期日', expires),
|
|
||||||
_licCell(t, '剩余天数', days != null ? '$days 天' : '—'),
|
|
||||||
];
|
|
||||||
return _card(
|
|
||||||
t,
|
|
||||||
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
|
||||||
_cardH(t, LucideIcons.shieldCheck, '授权信息'),
|
|
||||||
if (mobile)
|
|
||||||
Column(children: [
|
|
||||||
for (final c in cells)
|
|
||||||
Padding(padding: const EdgeInsets.only(bottom: 10), child: c),
|
|
||||||
])
|
|
||||||
else
|
|
||||||
Row(children: [
|
|
||||||
for (var i = 0; i < cells.length; i++) ...[
|
|
||||||
if (i > 0) const SizedBox(width: 14),
|
|
||||||
Expanded(child: cells[i]),
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 原型 .lic-cell:lk 11 muted + lv 17/700/mono。
|
|
||||||
Widget _licCell(dynamic t, String label, String value, {Color? color}) =>
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: t.bg,
|
|
||||||
border: Border.all(color: t.borderSubtle),
|
|
||||||
borderRadius: BorderRadius.circular(AppDims.rMd),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(label,
|
|
||||||
style: TextStyle(fontSize: AppDims.fsXs, color: t.muted)),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Text(value,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: AppDims.fsH2,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
fontFamily: AppFonts.mono,
|
|
||||||
fontFamilyFallback: AppFonts.monoFallback,
|
|
||||||
color: color ?? t.heading)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// ── 卡4:更新日志(timeline,默认 2 条 + 展开)───────────────
|
|
||||||
Widget _changelogCard(dynamic t) {
|
Widget _changelogCard(dynamic t) {
|
||||||
final appVersion = ref.watch(appVersionProvider).valueOrNull ?? '';
|
final appVersion = ref.watch(appVersionProvider).valueOrNull ?? '';
|
||||||
final entries = ref.watch(changelogProvider).valueOrNull ?? const [];
|
final entries = ref.watch(changelogProvider).valueOrNull ?? const [];
|
||||||
|
|||||||
@@ -691,42 +691,38 @@ class _LicensePanelState extends ConsumerState<_LicensePanel> {
|
|||||||
final expires = lic?.expiresAt != null
|
final expires = lic?.expiresAt != null
|
||||||
? DateFormat('yyyy-MM-dd').format(lic!.expiresAt!)
|
? DateFormat('yyyy-MM-dd').format(lic!.expiresAt!)
|
||||||
: '—';
|
: '—';
|
||||||
// 剩余天数走可注入时钟(golden 确定化),不用 model 内的 DateTime.now()
|
// 状态/剩余天数走可注入时钟(golden 确定化),不用 model 内的 DateTime.now()
|
||||||
|
final active = lic?.isActive == true &&
|
||||||
|
(lic?.expiresAt == null || lic!.expiresAt!.isAfter(appNow()));
|
||||||
final days = lic?.expiresAt != null
|
final days = lic?.expiresAt != null
|
||||||
? lic!.expiresAt!.difference(appNow()).inDays.clamp(0, 99999)
|
? lic!.expiresAt!.difference(appNow()).inDays.clamp(0, 99999)
|
||||||
: null;
|
: null;
|
||||||
|
final licCells = [
|
||||||
|
_licCell(t, '授权状态', active ? '已授权' : '未授权',
|
||||||
|
color: active ? t.success : t.warn),
|
||||||
|
_licCell(t, '到期日', expires),
|
||||||
|
_licCell(t, '剩余天数', days != null ? '$days 天' : '—'),
|
||||||
|
];
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
// ── 卡1:授权到期时间(原型 .lic 统计条:bg 底 / border / r-md)──
|
// ── 卡1:授权信息(3 cell,自「关于我们」迁入)──
|
||||||
_SettingsCard(
|
_SettingsCard(
|
||||||
title: '授权到期时间',
|
title: '授权信息',
|
||||||
desc: '当前门店的授权有效期,续期时长在现有到期时间上叠加',
|
desc: '当前门店的授权状态与有效期,续期时长在现有到期时间上叠加',
|
||||||
child: Container(
|
child: context.isMobile
|
||||||
padding: const EdgeInsets.fromLTRB(20, 18, 20, 18),
|
? Column(children: [
|
||||||
decoration: BoxDecoration(
|
for (final c in licCells)
|
||||||
color: t.bg,
|
Padding(
|
||||||
border: Border.all(color: t.border),
|
padding: const EdgeInsets.only(bottom: 10), child: c),
|
||||||
borderRadius: BorderRadius.circular(AppDims.rMd),
|
])
|
||||||
),
|
: Row(children: [
|
||||||
child: Row(children: [
|
for (var i = 0; i < licCells.length; i++) ...[
|
||||||
_stat(t, '授权到期日', expires),
|
if (i > 0) const SizedBox(width: 14),
|
||||||
const SizedBox(width: 24),
|
Expanded(child: licCells[i]),
|
||||||
_stat(t, '剩余天数', days != null ? '剩余 $days 天' : '—', warn: true),
|
],
|
||||||
const Spacer(),
|
]),
|
||||||
Container(
|
|
||||||
width: 46,
|
|
||||||
height: 46,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: t.okSoft,
|
|
||||||
borderRadius: BorderRadius.circular(AppDims.rMd),
|
|
||||||
),
|
|
||||||
child:
|
|
||||||
Icon(LucideIcons.shieldCheck, size: 22, color: t.success),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
// ── 卡2:在线购买 / 续费(后端 purchase 接口 admin only,
|
// ── 卡2:在线购买 / 续费(后端 purchase 接口 admin only,
|
||||||
@@ -811,20 +807,30 @@ class _LicensePanelState extends ConsumerState<_LicensePanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _stat(dynamic t, String label, String value, {bool warn = false}) =>
|
/// 原型 .lic-cell(与「关于我们」同款):lk 11 muted + lv 17/700/mono。
|
||||||
Column(
|
Widget _licCell(dynamic t, String label, String value, {Color? color}) =>
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Container(
|
||||||
children: [
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||||||
Text(label, style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
|
decoration: BoxDecoration(
|
||||||
const SizedBox(height: 4),
|
color: t.bg,
|
||||||
Text(value,
|
border: Border.all(color: t.borderSubtle),
|
||||||
style: TextStyle(
|
borderRadius: BorderRadius.circular(AppDims.rMd),
|
||||||
fontSize: 24,
|
),
|
||||||
fontWeight: FontWeight.w700,
|
child: Column(
|
||||||
fontFamily: AppFonts.mono,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
fontFamilyFallback: AppFonts.monoFallback,
|
children: [
|
||||||
color: warn ? t.warn : t.heading)),
|
Text(label,
|
||||||
],
|
style: TextStyle(fontSize: AppDims.fsXs, color: t.muted)),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppDims.fsH2,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontFamily: AppFonts.mono,
|
||||||
|
fontFamilyFallback: AppFonts.monoFallback,
|
||||||
|
color: color ?? t.heading)),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 433 KiB After Width: | Height: | Size: 477 KiB |
|
Before Width: | Height: | Size: 435 KiB After Width: | Height: | Size: 480 KiB |
|
Before Width: | Height: | Size: 481 KiB After Width: | Height: | Size: 526 KiB |
|
Before Width: | Height: | Size: 343 KiB After Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 379 KiB |
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 408 KiB |