refactor: 移除前端 Mock API,新增共享组件,优化认证流程

- 删除 mock_api_client、mock_calendar_service、mock_history_service
- 新增 fixed_length_code_input、link_button、message_composer 共享组件
- 优化登录/注册/密码重置页面使用新组件
- 简化 injection.dart 移除 mock 分支
- 更新 env.dart 配置(BACKEND_URL 替换 API_URL)
- 后端 agentscope 工具和测试更新
- 重构 AGENTS.md 文档结构
- 新增 deploy/ 目录和 protocol 文档
This commit is contained in:
qzl
2026-03-12 16:41:45 +08:00
parent d7fbb74bf8
commit 01c36eb32e
70 changed files with 5138 additions and 5829 deletions
@@ -8,6 +8,8 @@ import '../../../../core/theme/design_tokens.dart';
import '../../../../core/di/injection.dart';
import '../../../../shared/widgets/app_button.dart';
import '../../../../shared/widgets/banner/app_banner.dart';
import '../../../../shared/widgets/fixed_length_code_input.dart';
import '../../../../shared/widgets/link_button.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../presentation/cubits/register_cubit.dart';
@@ -34,6 +36,41 @@ class RegisterView extends StatefulWidget {
}
class _RegisterViewState extends State<RegisterView> {
static const _inviteCodeLength = 4;
static const _inviteAllowedChars = <String>{
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'J',
'K',
'M',
'N',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
};
final _nicknameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@@ -51,10 +88,15 @@ class _RegisterViewState extends State<RegisterView> {
Future<void> _handleNext() async {
final cubit = context.read<RegisterCubit>();
final inviteCode = _inviteCodeController.text.trim().toUpperCase();
final normalizedInviteCode = inviteCode.length == _inviteCodeLength
? inviteCode
: '';
cubit.usernameChanged(_nicknameController.text);
cubit.emailChanged(_emailController.text);
cubit.passwordChanged(_passwordController.text);
cubit.inviteCodeChanged(_inviteCodeController.text);
cubit.inviteCodeChanged(normalizedInviteCode);
if (!cubit.state.isStep1Valid || cubit.state.isSending) {
String? errorMsg;
@@ -71,6 +113,14 @@ class _RegisterViewState extends State<RegisterView> {
return;
}
if (inviteCode.isNotEmpty && normalizedInviteCode.isEmpty && mounted) {
Toast.show(
context,
'邀请码需为 4 位,且仅支持 A-H/J-N/P-Z 与 2-9;已按无邀请码继续注册',
type: ToastType.warning,
);
}
if (mounted) {
context.push('/register/verification', extra: cubit);
}
@@ -147,7 +197,7 @@ class _RegisterViewState extends State<RegisterView> {
const SizedBox(height: 12),
_buildPasswordInput(),
const SizedBox(height: 12),
_buildInput('邀请码(选填)', '请输入邀请码', _inviteCodeController),
_buildInviteCodeInput(),
const SizedBox(height: 12),
_buildStepIndicator(),
if (state.errorMessage != null)
@@ -174,6 +224,42 @@ class _RegisterViewState extends State<RegisterView> {
);
}
Widget _buildInviteCodeInput() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'邀请码(选填)',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate600,
),
),
const SizedBox(height: 6),
FixedLengthCodeInput(
controller: _inviteCodeController,
length: _inviteCodeLength,
semanticLabel: '邀请码输入框',
uppercase: true,
allowedCharacters: _inviteAllowedChars,
onChanged: (value) {
context.read<RegisterCubit>().inviteCodeChanged(value);
},
),
const SizedBox(height: 6),
const Text(
'4 位邀请码,支持 A-H/J-N/P-Z 与 2-9',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.slate500,
),
),
],
);
}
Widget _buildInput(
String label,
String hint,
@@ -262,16 +348,6 @@ class _RegisterViewState extends State<RegisterView> {
}
Widget _buildFooter() {
return GestureDetector(
onTap: () => context.pop(),
child: const Text(
'已有账号?去登录',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
),
),
);
return LinkButton(text: '已有账号?去登录', onTap: () => context.pop());
}
}