This commit is contained in:
dzc committed 2026-09-09 17:45:45 +08:00
1 parent 60d9380a65
commit fe6d988fa3
46 files changed
+5738 -1013

No files matched your search

@@ -1,6 +1,7 @@
import 'package:common_utils/common_utils.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:forui/forui.dart';
import 'package:get/get.dart';
@@ -55,3 +56,197 @@ Future<bool> settingPasswordDialog(BuildContext context) async {
);
return flag ?? false;
}
/// 终端顶栏管理操作(进入设置、退出登录、唤醒终端)共用的二次验证。
///
/// 校验来源与主菜单的设置入口一致:当前登录账号的登录密码。
Future<bool> terminalAdminPasswordDialog(
BuildContext context, {
required String actionLabel,
}) async {
final appUserSetting = await AppSettingTool.getUserSetting();
final result = await showAppDialog<bool>(
context: context,
barrierDismissible: false,
canPop: false,
builder: (_) => _TerminalAdminPasswordDialog(
actionLabel: actionLabel,
expectedPassword: appUserSetting.password,
),
);
return result ?? false;
}
class _TerminalAdminPasswordDialog extends StatefulWidget {
const _TerminalAdminPasswordDialog({
required this.actionLabel,
required this.expectedPassword,
});
final String actionLabel;
final String? expectedPassword;
@override
State<_TerminalAdminPasswordDialog> createState() =>
_TerminalAdminPasswordDialogState();
}
class _TerminalAdminPasswordDialogState
extends State<_TerminalAdminPasswordDialog> {
late final TextEditingController _controller;
bool _obscureText = true;
bool _closing = false;
String? _errorText;
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _submit() {
if (_closing) return;
final password = _controller.text.trim();
if (password.isEmpty) {
setState(() => _errorText = '请输入管理密码');
return;
}
if (password != widget.expectedPassword) {
setState(() => _errorText = '管理密码不正确,请重新输入');
HapticFeedback.mediumImpact();
return;
}
_close(true);
}
void _close(bool result) {
if (_closing) return;
_closing = true;
// 先释放输入法焦点,再由路由守卫等待弹窗退场后切换页面,避免 Overlay 与根路由同时拆除。
FocusManager.instance.primaryFocus?.unfocus();
Navigator.of(context).pop(result);
}
@override
Widget build(BuildContext context) {
final theme = FTheme.of(context);
return AppDialog(
title: Text(
'终端操作验证',
style: theme.typography.xl2.copyWith(
color: XfjDesignTokens.ink,
fontWeight: FontWeight.w800,
height: 1,
),
),
leading: Container(
width: 48.w,
height: 48.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: XfjDesignTokens.primarySoft,
borderRadius: BorderRadius.circular(XfjDesignTokens.controlRadius),
),
child: FIcon(
FAssets.icons.shieldCheck,
color: XfjDesignTokens.primary,
size: 24.sp,
),
),
width: 680.w,
maxWidth: 680.w,
contentPadding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 36.h),
onClose: () => _close(false),
semanticLabel: '${widget.actionLabel}管理密码验证弹框',
content: SizedBox(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${widget.actionLabel}前需要验证管理密码。',
style: theme.typography.base.copyWith(
color: XfjDesignTokens.muted,
),
),
SizedBox(height: 16.h),
FTextField.password(
controller: _controller,
autofocus: true,
label: const Text('管理密码'),
hint: '请输入管理密码',
maxLines: 1,
obscureText: _obscureText,
keyboardType: TextInputType.number,
textInputAction: TextInputAction.done,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
onChange: (_) {
if (_errorText != null) setState(() => _errorText = null);
},
onSubmit: (_) => _submit(),
suffixBuilder: (_, __, ___) => InkWell(
onTap: () => setState(() => _obscureText = !_obscureText),
borderRadius: BorderRadius.circular(
XfjDesignTokens.controlRadius,
),
child: SizedBox(
width: 48.w,
height: 48.h,
child: FIcon(
_obscureText ? FAssets.icons.eye : FAssets.icons.eyeOff,
color: XfjDesignTokens.muted,
size: 22.sp,
).center(),
),
),
),
if (_errorText != null) ...[
SizedBox(height: 8.h),
Row(
children: [
FIcon(
FAssets.icons.circleAlert,
color: XfjDesignTokens.danger,
size: 18.sp,
),
SizedBox(width: 8.w),
Text(
_errorText!,
style: theme.typography.sm.copyWith(
color: XfjDesignTokens.danger,
fontWeight: FontWeight.w600,
),
),
],
),
],
],
),
),
actions: [
FButton(
style: FButtonStyle.outline,
label: Text('取消', style: theme.typography.base),
onPress: () => _close(false),
),
FButton(
label: Text(
'验证并继续',
style: theme.typography.base.copyWith(color: Colors.white),
),
onPress: _submit,
),
],
);
}
}