feat: 添加视觉设计语言系统并重构认证页面UI

- 新增 visual_design_language.md 设计规范文档
- 新增 auth 设计 tokens (authBackground, authCard, authInput, feedback 系列等)
- 重构登录/注册/验证码/重置密码页面为新设计系统
- 新增 AuthHeroHeader, AuthSurfaceCard, AuthSection, AuthField, PasswordField 组件
- 重构 AppBanner 和 Toast 支持多类型配置 (info/success/warning/error)
- 后端 AgentScope: 重整 schemas/prompts/tools 作用域, 新增协议文档
- 更新 AGENTS.md 集成视觉设计语言约束
This commit is contained in:
qzl
2026-03-13 14:10:13 +08:00
parent fb3c649db7
commit a10a2db27a
100 changed files with 6333 additions and 4800 deletions
@@ -60,7 +60,9 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
void _onFocusChanged() {
if (_isFocused != _focusNode.hasFocus) {
_isFocused = _focusNode.hasFocus;
setState(() {
_isFocused = _focusNode.hasFocus;
});
}
}
@@ -98,56 +100,80 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
@override
Widget build(BuildContext context) {
final chars = widget.controller.text.split('');
final slotHeight = AppSpacing.xl * 2;
final slotHeight = AppSpacing.xl * 2 + AppSpacing.sm;
final slotSpacing = AppSpacing.sm;
final isComplete = chars.length == widget.length;
return Semantics(
label: widget.semanticLabel,
child: GestureDetector(
onTap: () => _focusNode.requestFocus(),
behavior: HitTestBehavior.opaque,
child: SizedBox(
height: slotHeight,
child: Stack(
alignment: Alignment.center,
children: [
Opacity(
opacity: 0,
child: SizedBox(
width: double.infinity,
height: slotHeight,
child: TextField(
controller: widget.controller,
focusNode: _focusNode,
keyboardType: widget.keyboardType,
inputFormatters: [
LengthLimitingTextInputFormatter(widget.length),
],
onChanged: _handleRawChanged,
autofillHints: const [AutofillHints.oneTimeCode],
child: AnimatedContainer(
duration: const Duration(milliseconds: 180),
padding: const EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: AppColors.authSectionBackground,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(
color: _isFocused
? AppColors.authInputFocus
: AppColors.authSectionBorder,
),
boxShadow: _isFocused
? [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.28),
blurRadius: 18,
offset: const Offset(0, 8),
),
]
: const [],
),
child: SizedBox(
height: slotHeight,
child: Stack(
alignment: Alignment.center,
children: [
Opacity(
opacity: 0,
child: SizedBox(
width: double.infinity,
height: slotHeight,
child: TextField(
controller: widget.controller,
focusNode: _focusNode,
keyboardType: widget.keyboardType,
inputFormatters: [
LengthLimitingTextInputFormatter(widget.length),
],
onChanged: _handleRawChanged,
autofillHints: const [AutofillHints.oneTimeCode],
),
),
),
),
IgnorePointer(
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
for (var index = 0; index < widget.length; index++) ...[
Expanded(
child: _buildCodeCell(
index: index,
chars: chars,
slotHeight: slotHeight,
IgnorePointer(
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
for (var index = 0; index < widget.length; index++) ...[
Expanded(
child: _buildCodeCell(
index: index,
chars: chars,
slotHeight: slotHeight,
isComplete: isComplete,
),
),
),
if (index != widget.length - 1)
SizedBox(width: slotSpacing),
if (index != widget.length - 1)
SizedBox(width: slotSpacing),
],
],
],
),
),
),
],
],
),
),
),
),
@@ -158,6 +184,7 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
required int index,
required List<String> chars,
required double slotHeight,
required bool isComplete,
}) {
final hasChar = index < chars.length;
final isActive =
@@ -168,18 +195,31 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
height: slotHeight,
alignment: Alignment.center,
decoration: BoxDecoration(
color: AppColors.white,
borderRadius: BorderRadius.circular(AppRadius.sm),
color: hasChar ? AppColors.white : AppColors.authInputBackground,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: isActive ? AppColors.primary : AppColors.slate300,
color: isActive
? AppColors.authPrimaryButton
: isComplete
? AppColors.authSecondaryButtonBorder
: AppColors.authInputBorder,
),
boxShadow: isActive
? [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.32),
blurRadius: 14,
offset: const Offset(0, 6),
),
]
: const [],
),
child: Text(
hasChar ? chars[index] : '',
style: const TextStyle(
style: TextStyle(
fontSize: AppSpacing.xl,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
color: hasChar ? AppColors.slate900 : AppColors.authLinkMuted,
),
),
);