feat: 饮食记录逻辑重构 + 通知管理分隔线修复 + 多页面 UI 调整
- 饮食: 新增 DietCommentaryPolicy + diet_record_logic, 饮食记录逻辑抽取复用 - 通知管理: 分隔线改为仿通知中心样式, 跳过图标区域只留文字下方 - 侧边栏: 对话记录操作栏浮层修复 + 常用功能间距收紧 - 智能体: 欢迎卡片去 400ms 延迟 + 胶囊去阴影 - 后端: AI 会话仓库新增方法 + 饮食评论策略 + 健康记录规则调整 - 其他: api_client IP 适配 + 多页面 UI 微调 + 新增测试
This commit is contained in:
@@ -4,6 +4,7 @@ import '../../core/app_colors.dart';
|
||||
import '../../core/navigation_provider.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
import '../../providers/data_providers.dart';
|
||||
import '../../widgets/app_gradient_widgets.dart';
|
||||
|
||||
class LoginPage extends ConsumerStatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
@@ -13,6 +14,11 @@ class LoginPage extends ConsumerStatefulWidget {
|
||||
|
||||
class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
static const _loginBg = 'assets/branding/login_background_v1.png';
|
||||
static const _loginActionGradient = AppColors.primaryGradient;
|
||||
|
||||
void _openStaticText(String type) {
|
||||
pushRoute(ref, 'staticText', params: {'type': type});
|
||||
}
|
||||
|
||||
final _phoneCtrl = TextEditingController();
|
||||
final _codeCtrl = TextEditingController();
|
||||
@@ -174,23 +180,51 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
const SizedBox(height: 18),
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: const TextSpan(
|
||||
style: TextStyle(
|
||||
text: TextSpan(
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
height: 1.55,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: '请阅读并同意'),
|
||||
TextSpan(
|
||||
text: '《服务协议》',
|
||||
style: TextStyle(color: AppColors.primary),
|
||||
const TextSpan(text: '请阅读并同意'),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.baseline,
|
||||
baseline: TextBaseline.alphabetic,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(ctx, false);
|
||||
_openStaticText('terms');
|
||||
},
|
||||
child: AppGradientText(
|
||||
'《服务协议》',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
height: 1.55,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(text: '和'),
|
||||
TextSpan(
|
||||
text: '《隐私政策》',
|
||||
style: TextStyle(color: AppColors.primary),
|
||||
const TextSpan(text: '和'),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.baseline,
|
||||
baseline: TextBaseline.alphabetic,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(ctx, false);
|
||||
_openStaticText('privacy');
|
||||
},
|
||||
child: AppGradientText(
|
||||
'《隐私政策》',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
height: 1.55,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -199,26 +233,9 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 50,
|
||||
child: OutlinedButton(
|
||||
onPressed: () => Navigator.pop(ctx, false),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primary,
|
||||
side: BorderSide(
|
||||
color: AppColors.primary.withValues(alpha: 0.62),
|
||||
width: 1.2,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
child: const Text('不同意'),
|
||||
),
|
||||
child: AppGradientOutlineButton(
|
||||
label: '不同意',
|
||||
onTap: () => Navigator.pop(ctx, false),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
@@ -229,7 +246,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
gradient: AppColors.doctorGradient,
|
||||
gradient: _loginActionGradient,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
boxShadow: AppColors.buttonShadow,
|
||||
),
|
||||
@@ -328,9 +345,9 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
),
|
||||
),
|
||||
trailing: _selectedDoctorId == d['id']?.toString()
|
||||
? const Icon(
|
||||
Icons.check_circle,
|
||||
color: AppColors.primary,
|
||||
? const AppGradientIcon(
|
||||
icon: Icons.check_circle,
|
||||
size: 22,
|
||||
)
|
||||
: null,
|
||||
onTap: () {
|
||||
@@ -368,6 +385,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: Colors.white,
|
||||
body: Stack(
|
||||
children: [
|
||||
@@ -475,6 +493,9 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
agreed: _agreed,
|
||||
onTap: () =>
|
||||
setState(() => _agreed = !_agreed),
|
||||
onTermsTap: () => _openStaticText('terms'),
|
||||
onPrivacyTap: () =>
|
||||
_openStaticText('privacy'),
|
||||
),
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
@@ -496,13 +517,13 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
_error = null;
|
||||
_successMsg = null;
|
||||
}),
|
||||
child: Text(
|
||||
_isLogin ? '没有账号?去注册' : '已有账号?去登录',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: AppColors.primary,
|
||||
child: Center(
|
||||
child: AppGradientText(
|
||||
_isLogin ? '没有账号?去注册' : '已有账号?去登录',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -645,7 +666,7 @@ class _TextField extends StatelessWidget {
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderSide: const BorderSide(color: AppColors.primary, width: 1.2),
|
||||
borderSide: const BorderSide(color: AppColors.auraIndigo, width: 1.2),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -657,10 +678,10 @@ class _GradientPrefixIcon extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShaderMask(
|
||||
shaderCallback: AppColors.doctorGradient.createShader,
|
||||
blendMode: BlendMode.srcIn,
|
||||
child: Icon(icon, size: 23, color: Colors.white),
|
||||
return AppGradientIcon(
|
||||
icon: icon,
|
||||
size: 23,
|
||||
gradient: _LoginPageState._loginActionGradient,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -730,7 +751,7 @@ class _SmsButton extends StatelessWidget {
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
gradient: disabled ? null : AppColors.doctorGradient,
|
||||
gradient: disabled ? null : _LoginPageState._loginActionGradient,
|
||||
color: disabled ? AppColors.cardInner : null,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(
|
||||
@@ -757,64 +778,67 @@ class _SmsButton extends StatelessWidget {
|
||||
class _Agreement extends StatelessWidget {
|
||||
final bool agreed;
|
||||
final VoidCallback onTap;
|
||||
const _Agreement({required this.agreed, required this.onTap});
|
||||
final VoidCallback onTermsTap;
|
||||
final VoidCallback onPrivacyTap;
|
||||
const _Agreement({
|
||||
required this.agreed,
|
||||
required this.onTap,
|
||||
required this.onTermsTap,
|
||||
required this.onPrivacyTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
const textStyle = TextStyle(fontSize: 13, color: AppColors.textHint);
|
||||
const linkStyle = TextStyle(fontSize: 13, fontWeight: FontWeight.w700);
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
margin: const EdgeInsets.only(right: 7, top: 1),
|
||||
margin: const EdgeInsets.only(right: 7),
|
||||
decoration: BoxDecoration(
|
||||
color: agreed ? AppColors.primary : Colors.white,
|
||||
gradient: agreed ? _LoginPageState._loginActionGradient : null,
|
||||
color: agreed ? null : Colors.white,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(
|
||||
color: agreed ? AppColors.primary : AppColors.border,
|
||||
color: agreed ? Colors.transparent : AppColors.border,
|
||||
),
|
||||
),
|
||||
child: agreed
|
||||
? const Icon(Icons.check, size: 13, color: Colors.white)
|
||||
: null,
|
||||
),
|
||||
Flexible(
|
||||
child: RichText(
|
||||
text: const TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '已阅读并同意',
|
||||
style: TextStyle(fontSize: 12, color: AppColors.textHint),
|
||||
),
|
||||
TextSpan(
|
||||
text: '《服务协议》',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '和',
|
||||
style: TextStyle(fontSize: 12, color: AppColors.textHint),
|
||||
),
|
||||
TextSpan(
|
||||
text: '《隐私政策》',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const Text('已阅读并同意', style: textStyle),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: onTermsTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const AppGradientText('《服务协议》', style: linkStyle),
|
||||
),
|
||||
const Text('和', style: textStyle),
|
||||
GestureDetector(
|
||||
onTap: onPrivacyTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const AppGradientText('《隐私政策》', style: linkStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -882,7 +906,7 @@ class _PrimaryButton extends StatelessWidget {
|
||||
height: 52,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
gradient: AppColors.doctorGradient,
|
||||
gradient: _LoginPageState._loginActionGradient,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
boxShadow: AppColors.buttonShadow,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user