fix(client): 审计修复——device_limit 相对时间走 relativeTime 单源+裸 Icons 换 PangolinIcons+Payment onDone 三处接通+内联双语文案收进 AppText
This commit is contained in:
@@ -565,6 +565,9 @@ abstract class AppText {
|
||||
String get switchPayMethod; // 换一种支付方式 / Switch payment method
|
||||
String get cancelOrder; // 取消订单 / Cancel order
|
||||
String get qrNotSupported;
|
||||
String get orderCreateFailed; // 下单失败,请稍后重试 / Failed to create order, please retry(errorZh/En 为空时兜底)
|
||||
String get payUnsupportedRenderType; // 暂不支持,请换支付方式 / Unsupported, switch payment method
|
||||
String get payNoActiveOrder; // 暂无进行中的订单 / No active order
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
String get payChannel;
|
||||
|
||||
@@ -427,6 +427,12 @@ class StringsEn extends AppText {
|
||||
String get cancelOrder => 'Cancel order';
|
||||
@override
|
||||
String get qrNotSupported => 'Copy and open in Alipay';
|
||||
@override
|
||||
String get orderCreateFailed => 'Failed to create order, please retry';
|
||||
@override
|
||||
String get payUnsupportedRenderType => 'Unsupported, switch payment method';
|
||||
@override
|
||||
String get payNoActiveOrder => 'No active order';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
|
||||
@@ -428,6 +428,12 @@ class StringsEs extends AppText {
|
||||
String get cancelOrder => 'Cancelar pedido';
|
||||
@override
|
||||
String get qrNotSupported => 'Copia el contenido y ábrelo en Alipay';
|
||||
@override
|
||||
String get orderCreateFailed => 'Error al crear el pedido, inténtalo de nuevo más tarde';
|
||||
@override
|
||||
String get payUnsupportedRenderType => 'No compatible, cambia el método de pago';
|
||||
@override
|
||||
String get payNoActiveOrder => 'No hay pedidos activos';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
|
||||
@@ -428,6 +428,12 @@ class StringsJa extends AppText {
|
||||
String get cancelOrder => '注文をキャンセル';
|
||||
@override
|
||||
String get qrNotSupported => '内容をコピーしてAlipayで開いてください';
|
||||
@override
|
||||
String get orderCreateFailed => '注文の作成に失敗しました。しばらくして再試行してください';
|
||||
@override
|
||||
String get payUnsupportedRenderType => '未対応です。支払い方法を変更してください';
|
||||
@override
|
||||
String get payNoActiveOrder => '進行中の注文はありません';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
|
||||
@@ -428,6 +428,12 @@ class StringsKo extends AppText {
|
||||
String get cancelOrder => '주문 취소';
|
||||
@override
|
||||
String get qrNotSupported => '내용을 복사해 Alipay에서 여세요';
|
||||
@override
|
||||
String get orderCreateFailed => '주문 생성에 실패했습니다. 잠시 후 다시 시도하세요';
|
||||
@override
|
||||
String get payUnsupportedRenderType => '지원되지 않습니다. 결제 방법을 변경하세요';
|
||||
@override
|
||||
String get payNoActiveOrder => '진행 중인 주문이 없습니다';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
|
||||
@@ -428,6 +428,12 @@ class StringsRu extends AppText {
|
||||
String get cancelOrder => 'Отменить заказ';
|
||||
@override
|
||||
String get qrNotSupported => 'Скопируйте и откройте в Alipay';
|
||||
@override
|
||||
String get orderCreateFailed => 'Не удалось создать заказ, повторите попытку позже';
|
||||
@override
|
||||
String get payUnsupportedRenderType => 'Не поддерживается, смените способ оплаты';
|
||||
@override
|
||||
String get payNoActiveOrder => 'Нет активных заказов';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
|
||||
@@ -421,6 +421,12 @@ class StringsZh extends AppText {
|
||||
String get cancelOrder => '取消订单';
|
||||
@override
|
||||
String get qrNotSupported => '请复制内容后在支付宝内打开';
|
||||
@override
|
||||
String get orderCreateFailed => '下单失败,请稍后重试';
|
||||
@override
|
||||
String get payUnsupportedRenderType => '暂不支持,请换支付方式';
|
||||
@override
|
||||
String get payNoActiveOrder => '暂无进行中的订单';
|
||||
|
||||
// ── 支付渠道 switch + 订单 ──
|
||||
@override
|
||||
|
||||
@@ -64,8 +64,8 @@ class AccountPage extends ConsumerWidget {
|
||||
NavView.purchase,
|
||||
PurchaseScreen(
|
||||
t: t,
|
||||
onOrderCreated: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => PaymentScreen(t: t))),
|
||||
onOrderCreated: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => PaymentScreen(t: t, onDone: () => Navigator.of(context).pop()))),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -87,8 +87,9 @@ class AccountPage extends ConsumerWidget {
|
||||
NavView.purchase,
|
||||
PurchaseScreen(
|
||||
t: t,
|
||||
onOrderCreated: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => PaymentScreen(t: t))),
|
||||
onOrderCreated: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) =>
|
||||
PaymentScreen(t: t, onDone: () => Navigator.of(context).pop()))),
|
||||
),
|
||||
)),
|
||||
Divider(height: 1, color: c.border),
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../services/auth_api.dart';
|
||||
import '../services/device_identity.dart';
|
||||
import '../state/account_providers.dart';
|
||||
import '../state/auth_provider.dart';
|
||||
import '../widgets/pangolin_icons.dart';
|
||||
|
||||
class DeviceLimitScreen extends ConsumerStatefulWidget {
|
||||
const DeviceLimitScreen({super.key, required this.t, required this.limit});
|
||||
@@ -84,7 +85,7 @@ class _DeviceLimitScreenState extends ConsumerState<DeviceLimitScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 32),
|
||||
Icon(Icons.devices_other_rounded, size: 40, color: c.accent),
|
||||
Icon(PangolinIcons.monitorSmartphone, size: 40, color: c.accent),
|
||||
const SizedBox(height: 14),
|
||||
Text(t.deviceLimitTitle,
|
||||
textAlign: TextAlign.center,
|
||||
@@ -97,7 +98,7 @@ class _DeviceLimitScreenState extends ConsumerState<DeviceLimitScreen> {
|
||||
if (removableCount > 0)
|
||||
OutlinedButton.icon(
|
||||
onPressed: _busyUuid != null ? null : () => _removeOldest(devices, currentId),
|
||||
icon: const Icon(Icons.history_rounded, size: 18),
|
||||
icon: const Icon(PangolinIcons.clock, size: 18),
|
||||
label: Text(t.deviceLimitRemoveOldest),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: c.accent,
|
||||
@@ -136,7 +137,9 @@ class _DeviceLimitScreenState extends ConsumerState<DeviceLimitScreen> {
|
||||
final busy = _busyUuid == d.uuid;
|
||||
final sub = d.online
|
||||
? t.devOnline
|
||||
: (d.lastSeen != null ? '${t.devLastOnline} ${_rel(d.lastSeen!)}' : t.devOffline);
|
||||
: (d.lastSeen != null
|
||||
? '${t.devLastOnline} ${t.lang.relativeTime(DateTime.now().difference(d.lastSeen!.toLocal()))}'
|
||||
: t.devOffline);
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
@@ -184,18 +187,9 @@ class _DeviceLimitScreenState extends ConsumerState<DeviceLimitScreen> {
|
||||
}
|
||||
|
||||
static IconData _platformIcon(DevicePlatform p) => switch (p) {
|
||||
DevicePlatform.ios || DevicePlatform.android => Icons.smartphone_rounded,
|
||||
DevicePlatform.windows || DevicePlatform.linux => Icons.computer_rounded,
|
||||
DevicePlatform.macos => Icons.laptop_mac_rounded,
|
||||
DevicePlatform.unknown => Icons.devices_other_rounded,
|
||||
DevicePlatform.ios || DevicePlatform.android => PangolinIcons.smartphone,
|
||||
DevicePlatform.windows || DevicePlatform.linux => PangolinIcons.monitor,
|
||||
DevicePlatform.macos => PangolinIcons.laptop,
|
||||
DevicePlatform.unknown => PangolinIcons.monitorSmartphone,
|
||||
};
|
||||
|
||||
// 相对时间(粗粒度):x 分钟/小时/天前。
|
||||
static String _rel(DateTime t) {
|
||||
final d = DateTime.now().toUtc().difference(t.toUtc());
|
||||
if (d.inMinutes < 1) return '刚刚';
|
||||
if (d.inMinutes < 60) return '${d.inMinutes} 分钟前';
|
||||
if (d.inHours < 24) return '${d.inHours} 小时前';
|
||||
return '${d.inDays} 天前';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,17 @@ class _OrderDetailView extends ConsumerWidget {
|
||||
builder: (_) => PurchaseScreen(
|
||||
t: t,
|
||||
onOrderCreated: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => PaymentScreen(t: t)),
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PaymentScreen(
|
||||
t: t,
|
||||
onDone: () {
|
||||
Navigator.of(context).pop();
|
||||
// 支付页「完成/取消」回到订单详情:失效列表 + 本单详情,拉新状态。
|
||||
ref.invalidate(ordersProvider);
|
||||
ref.invalidate(orderDetailProvider(order.orderNo));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
@@ -209,8 +209,9 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
|
||||
Text(t.qrNotSupported, style: PangolinText.caption.copyWith(color: c.fg3)),
|
||||
]));
|
||||
default:
|
||||
renderBody = _card(c, child: Text('${session.renderType}: ${_zh ? "暂不支持,请换支付方式" : "Unsupported, switch method"}',
|
||||
style: PangolinText.body.copyWith(color: c.fg2)));
|
||||
renderBody = _card(c,
|
||||
child: Text('${session.renderType}: ${t.payUnsupportedRenderType}',
|
||||
style: PangolinText.body.copyWith(color: c.fg2)));
|
||||
}
|
||||
|
||||
return ListView(padding: const EdgeInsets.fromLTRB(20, 14, 20, 24), children: [
|
||||
@@ -306,7 +307,7 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
|
||||
PaymentPhase.succeeded => _succeeded(context, s),
|
||||
PaymentPhase.failed => _failed(context, ref, s),
|
||||
PaymentPhase.idle => Center(
|
||||
child: Text(_zh ? '暂无进行中的订单' : 'No active order',
|
||||
child: Text(t.payNoActiveOrder,
|
||||
style: PangolinText.body.copyWith(color: c.fg3))),
|
||||
};
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class PurchaseScreen extends ConsumerStatefulWidget {
|
||||
|
||||
class _PurchaseScreenState extends ConsumerState<PurchaseScreen> {
|
||||
AppText get t => widget.t;
|
||||
bool get _zh => t.lang == AppLang.zh;
|
||||
|
||||
// 支付渠道:默认 USDT·加密货币。切换即刷新套餐卡显价币种。
|
||||
PayChannel _channel = PayChannel.usdt;
|
||||
@@ -59,9 +60,7 @@ class _PurchaseScreenState extends ConsumerState<PurchaseScreen> {
|
||||
// 下单失败给可见 toast,别让用户以为「点了没反应」。
|
||||
showPangolinToast(
|
||||
context,
|
||||
t.lang == AppLang.zh
|
||||
? (st.errorZh ?? '下单失败,请稍后重试')
|
||||
: (st.errorEn ?? 'Failed to create order, please retry'),
|
||||
(_zh ? st.errorZh : st.errorEn) ?? t.orderCreateFailed,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -77,8 +76,7 @@ class _PurchaseScreenState extends ConsumerState<PurchaseScreen> {
|
||||
error: (_, __) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(40),
|
||||
child: Text(t.lang == AppLang.zh ? '加载失败,请重试' : 'Failed to load, retry',
|
||||
style: PangolinText.body.copyWith(color: c.fg3)),
|
||||
child: Text(t.lang.loadFailedRetry, style: PangolinText.body.copyWith(color: c.fg3)),
|
||||
),
|
||||
),
|
||||
data: (items) {
|
||||
|
||||
@@ -126,7 +126,7 @@ class UpdateBanner extends ConsumerWidget {
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
ref.read(dismissedUpdateVersionProvider.notifier).state = info.latestVersion,
|
||||
icon: Icon(Icons.close, size: 18, color: c.fg3),
|
||||
icon: Icon(PangolinIcons.x, size: 18, color: c.fg3),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(minWidth: 36, minHeight: 36),
|
||||
tooltip: t.updateLater,
|
||||
|
||||
Reference in New Issue
Block a user