feat: 医生端完整实现 + 双角色登录 + 健康日历 + 饮食记录重做 + 配色系统重构
后端: - User表加Role字段 + DoctorProfile表 + 废弃旧Doctor数据 - JWT加Role claim + 注册/登录拆分(/api/auth/register + /api/auth/login) - 医生端点全量加JWT鉴权+Role校验(15个端点) - 日历端点改造(用药/运动/随访按日期+星期匹配) - 饮食记录PUT端点 + 健康记录DELETE端点 - 用户Profile端点返回Role 前端: - 登录页重做(角色选择卡片 用户紫/医生蓝 + 审核码6666 + 注册返登录) - App路由分流(userRole→患者端/医生端) - 医生端: 工作台Dashboard + 患者管理(搜索分页) + 问诊/报告/随访CRUD - 报告审核: AI预分析+指标表格+严重程度4级+模板多选+评语 - 随访编辑: 患者选择器+日期时间+打通患者端 - 健康日历: 可点击日历+当日安排列表+用药/运动/随访颜色标记 - 饮食记录: 热量趋势7/30天+日历(一周)+当日胶囊详情+侧滑编辑删除 - 健康档案: 日期选择器+手术可添加多条+白底渐变保存按钮 - 配色系统: 清理25个未使用色+新增doctorBlue/drawerGradient/pageGrey - 患者端侧边栏: 新渐变+白底按钮+紫色汉堡图标 - AI对话: 用户气泡白底黑字+药管家改蓝色 - App启动闪屏: 防登录页闪烁 文档: color_design_system.md(20页面完整配色设计+图标底色对照表)
This commit is contained in:
@@ -3,17 +3,16 @@ import 'package:dio/dio.dart';
|
||||
import '../core/api_client.dart';
|
||||
import '../core/local_database.dart';
|
||||
|
||||
/// 用户简要信息
|
||||
class UserInfo {
|
||||
final String id;
|
||||
final String phone;
|
||||
final String role;
|
||||
final String? name;
|
||||
final String? avatarUrl;
|
||||
|
||||
UserInfo({required this.id, required this.phone, this.name, this.avatarUrl});
|
||||
UserInfo({required this.id, required this.phone, this.role = 'User', this.name, this.avatarUrl});
|
||||
}
|
||||
|
||||
/// 认证状态
|
||||
class AuthState {
|
||||
final UserInfo? user;
|
||||
final bool isLoggedIn;
|
||||
@@ -22,7 +21,6 @@ class AuthState {
|
||||
const AuthState({this.user, this.isLoggedIn = false, this.isLoading = true});
|
||||
}
|
||||
|
||||
/// 认证 Provider
|
||||
final authProvider = NotifierProvider<AuthNotifier, AuthState>(AuthNotifier.new);
|
||||
|
||||
final localDbProvider = Provider<LocalDatabase>((ref) => LocalDatabase.instance);
|
||||
@@ -53,10 +51,9 @@ class AuthNotifier extends Notifier<AuthState> {
|
||||
if (data != null) {
|
||||
await db.write('access_token', data['accessToken']);
|
||||
await db.write('refresh_token', data['refreshToken']);
|
||||
state = AuthState(
|
||||
isLoggedIn: true,
|
||||
isLoading: false,
|
||||
user: UserInfo(id: '', phone: '', name: data['user']?['name']),
|
||||
final u = data['user'] as Map<String, dynamic>?;
|
||||
state = AuthState(isLoggedIn: true, isLoading: false,
|
||||
user: UserInfo(id: '', phone: '', role: u?['role'] ?? 'User'),
|
||||
);
|
||||
_loadProfile();
|
||||
} else {
|
||||
@@ -73,35 +70,53 @@ class AuthNotifier extends Notifier<AuthState> {
|
||||
final response = await api.get('/api/user/profile');
|
||||
final user = response.data['data'];
|
||||
if (user != null) {
|
||||
state = AuthState(
|
||||
isLoggedIn: true,
|
||||
isLoading: false,
|
||||
state = AuthState(isLoggedIn: true, isLoading: false,
|
||||
user: UserInfo(
|
||||
id: user['id'] ?? '',
|
||||
phone: user['phone'] ?? '',
|
||||
name: user['name'],
|
||||
avatarUrl: user['avatarUrl'],
|
||||
id: user['id'] ?? '', phone: user['phone'] ?? '',
|
||||
role: user['role'] ?? state.user?.role ?? 'User',
|
||||
name: user['name'], avatarUrl: user['avatarUrl'],
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[Auth]加载个人信息失败: $e');
|
||||
print('[Auth] loadProfile: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送验证码,返回 (error, devCode)
|
||||
/// 发送验证码
|
||||
Future<({String? error, String? devCode})> sendSms(String phone) async {
|
||||
try {
|
||||
final api = ref.read(apiClientProvider);
|
||||
final response = await api.post('/api/auth/send-sms', data: {'phone': phone});
|
||||
final devCode = response.data['data']?['devCode'] as String?;
|
||||
return (error: null, devCode: devCode);
|
||||
return (error: null, devCode: response.data['data']?['devCode'] as String?);
|
||||
} catch (e) {
|
||||
return (error: '发送失败: $e', devCode: null);
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证码登录
|
||||
/// 注册(新用户,需选身份)
|
||||
Future<String?> register(String phone, String code, String role, {String? inviteCode}) async {
|
||||
try {
|
||||
final api = ref.read(apiClientProvider);
|
||||
final response = await api.post('/api/auth/register', data: {
|
||||
'phone': phone, 'smsCode': code, 'role': role,
|
||||
if (inviteCode != null) 'inviteCode': inviteCode,
|
||||
});
|
||||
final data = response.data['data'];
|
||||
if (data == null) return response.data['message'] ?? '注册失败';
|
||||
|
||||
await api.saveTokens(data['accessToken'], data['refreshToken']);
|
||||
final user = data['user'];
|
||||
state = AuthState(isLoggedIn: true, isLoading: false,
|
||||
user: UserInfo(id: user['id'] ?? '', phone: user['phone'] ?? '', role: role),
|
||||
);
|
||||
return null;
|
||||
} catch (e) {
|
||||
return '注册失败: $e';
|
||||
}
|
||||
}
|
||||
|
||||
/// 登录(已有账号)
|
||||
Future<String?> login(String phone, String code) async {
|
||||
try {
|
||||
final api = ref.read(apiClientProvider);
|
||||
@@ -111,13 +126,10 @@ class AuthNotifier extends Notifier<AuthState> {
|
||||
|
||||
await api.saveTokens(data['accessToken'], data['refreshToken']);
|
||||
final user = data['user'];
|
||||
state = AuthState(
|
||||
isLoggedIn: true,
|
||||
isLoading: false,
|
||||
state = AuthState(isLoggedIn: true, isLoading: false,
|
||||
user: UserInfo(
|
||||
id: user['id'] ?? '',
|
||||
phone: user['phone'] ?? '',
|
||||
name: user['name'],
|
||||
id: user['id'] ?? '', phone: user['phone'] ?? '',
|
||||
role: user['role'] ?? 'User', name: user['name'],
|
||||
avatarUrl: user['avatarUrl'],
|
||||
),
|
||||
);
|
||||
@@ -133,9 +145,14 @@ class AuthNotifier extends Notifier<AuthState> {
|
||||
final db = ref.read(localDbProvider);
|
||||
final refresh = await db.read('refresh_token');
|
||||
if (refresh != null) {
|
||||
try { await api.post('/api/auth/logout', data: {'refreshToken': refresh}); } catch (e) { print('[Auth]登出请求失败: $e'); }
|
||||
try { await api.post('/api/auth/logout', data: {'refreshToken': refresh}); } catch (e) { print('[Auth] logout: $e'); }
|
||||
}
|
||||
await api.clearTokens();
|
||||
state = const AuthState(isLoggedIn: false, isLoading: false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 便捷:当前用户角色
|
||||
final userRoleProvider = Provider<String>((ref) {
|
||||
return ref.watch(authProvider).user?.role ?? 'User';
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user