profile.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import 'package:chicken_farm/core/services/offline_storage_service.dart';
  2. import 'package:chicken_farm/core/services/sync_service.dart';
  3. import 'package:chicken_farm/core/utils/storage.dart';
  4. import 'package:chicken_farm/core/utils/toast.dart';
  5. import 'package:chicken_farm/stores/auth_store.dart';
  6. import 'package:chicken_farm/stores/config_store.dart';
  7. import 'package:chicken_farm/stores/dict_stroe.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter_riverpod/flutter_riverpod.dart';
  10. import 'package:chicken_farm/core/services/connectivity_service.dart';
  11. import 'package:chicken_farm/pages/account/config_dialog.dart';
  12. class ProfilePage extends ConsumerWidget {
  13. const ProfilePage({super.key});
  14. @override
  15. Widget build(BuildContext context, WidgetRef ref) {
  16. final authState = ref.watch(authStoreProvider);
  17. final authStore = ref.read(authStoreProvider.notifier);
  18. return Scaffold(
  19. body: Padding(
  20. padding: const EdgeInsets.all(16.0),
  21. child: Column(
  22. crossAxisAlignment: CrossAxisAlignment.start,
  23. children: [
  24. const SizedBox(height: 20),
  25. if (authState.state == AuthState.authenticated &&
  26. authState.user != null) ...[
  27. Expanded(
  28. child: SingleChildScrollView(
  29. child: Column(
  30. crossAxisAlignment: CrossAxisAlignment.start,
  31. children: [
  32. Card(
  33. child: Padding(
  34. padding: const EdgeInsets.all(16.0),
  35. child: Column(
  36. crossAxisAlignment: CrossAxisAlignment.start,
  37. children: [
  38. ListTile(
  39. leading: const CircleAvatar(
  40. radius: 30,
  41. child: Icon(Icons.person, size: 30),
  42. ),
  43. title: Text(
  44. authState.user!.nickName ?? '未设置昵称',
  45. style: const TextStyle(
  46. fontSize: 20,
  47. fontWeight: FontWeight.bold,
  48. ),
  49. ),
  50. subtitle: Text(
  51. '用户名: ${authState.user!.userName}',
  52. ),
  53. ),
  54. const Divider(),
  55. const Text(
  56. '基本信息',
  57. style: TextStyle(fontWeight: FontWeight.bold),
  58. ),
  59. const SizedBox(height: 10),
  60. _buildInfoRow(
  61. '手机号码',
  62. authState.user!.phonenumber ?? '未填写',
  63. ),
  64. _buildInfoRow(
  65. '邮箱地址',
  66. authState.user!.email ?? '未填写',
  67. ),
  68. _buildInfoRow(
  69. '部门',
  70. authState.user!.orgName ?? '未分配',
  71. ),
  72. _buildInfoRow(
  73. '角色',
  74. (authState.user!.roles
  75. ?.map((r) => r.roleName)
  76. .join(', ')) ??
  77. '未分配',
  78. ),
  79. ],
  80. ),
  81. ),
  82. ),
  83. const SizedBox(height: 20),
  84. // 显示待上传数据数量的按钮
  85. FutureBuilder<List<OfflineOperation>>(
  86. future: OfflineStorageService().getPendingOperations(),
  87. builder: (context, snapshot) {
  88. if (snapshot.connectionState ==
  89. ConnectionState.done &&
  90. snapshot.hasData) {
  91. final pendingCount = snapshot.data!.length;
  92. return SizedBox(
  93. width: double.infinity,
  94. child: ElevatedButton.icon(
  95. onPressed: pendingCount > 0
  96. ? () async {
  97. ToastUtil.show(
  98. '正在上传 $pendingCount 条数据...',
  99. );
  100. final syncService = SyncService();
  101. await syncService
  102. .syncPendingOperations();
  103. ToastUtil.success('数据上传完成');
  104. // 刷新按钮状态
  105. (context as Element).markNeedsBuild();
  106. }
  107. : null, // 禁用按钮
  108. icon: const Icon(Icons.upload),
  109. label: Text('上传数据 ($pendingCount条待上传)'),
  110. style: ElevatedButton.styleFrom(
  111. padding: const EdgeInsets.symmetric(
  112. horizontal: 30,
  113. vertical: 15,
  114. ),
  115. textStyle: const TextStyle(fontSize: 16),
  116. ),
  117. ),
  118. );
  119. }
  120. return const SizedBox(
  121. width: double.infinity,
  122. child: Center(child: CircularProgressIndicator()),
  123. );
  124. },
  125. ),
  126. const SizedBox(height: 20),
  127. SizedBox(
  128. width: double.infinity,
  129. child: ElevatedButton.icon(
  130. onPressed: () {
  131. ToastUtil.confirm('确定要清除所有缓存吗?', () async {
  132. await StorageUtils.removeWithPrefix("vb_");
  133. ConfigStore().clearAll();
  134. DictStore().clearAll();
  135. if (context.mounted) {
  136. ToastUtil.success('缓存已清除');
  137. }
  138. });
  139. },
  140. icon: const Icon(Icons.cleaning_services),
  141. label: const Text('清除缓存'),
  142. style: ElevatedButton.styleFrom(
  143. padding: const EdgeInsets.symmetric(
  144. horizontal: 30,
  145. vertical: 15,
  146. ),
  147. textStyle: const TextStyle(fontSize: 16),
  148. ),
  149. ),
  150. ),
  151. const SizedBox(height: 20),
  152. SizedBox(
  153. width: double.infinity,
  154. child: ElevatedButton.icon(
  155. onPressed: () async {
  156. final result = await showDialog(
  157. context: context,
  158. builder: (context) => const ConfigDialog(),
  159. );
  160. // 如果配置发生了变化,显示提示
  161. if (result == true && context.mounted) {
  162. ToastUtil.success('配置已保存');
  163. }
  164. },
  165. icon: const Icon(Icons.settings),
  166. label: const Text('服务配置'),
  167. style: ElevatedButton.styleFrom(
  168. padding: const EdgeInsets.symmetric(
  169. horizontal: 30,
  170. vertical: 15,
  171. ),
  172. textStyle: const TextStyle(fontSize: 16),
  173. ),
  174. ),
  175. ),
  176. ],
  177. ),
  178. ),
  179. ),
  180. // 网络状态感知的退出登录按钮
  181. Consumer(
  182. builder: (context, ref, child) {
  183. final isConnectedAsync = ref.watch(isConnectedProvider);
  184. return SizedBox(
  185. width: double.infinity,
  186. child: ElevatedButton.icon(
  187. onPressed: isConnectedAsync.when(
  188. data: (isConnected) {
  189. // 在线状态下允许退出登录,离线状态下禁用
  190. return isConnected
  191. ? () {
  192. ToastUtil.confirm(
  193. '确定要退出登录吗?',
  194. () => authStore.logout(),
  195. );
  196. }
  197. : null;
  198. },
  199. loading: () => null,
  200. error: (_, _) => null,
  201. ),
  202. icon: const Icon(Icons.logout),
  203. label: const Text('退出登录'),
  204. style: ElevatedButton.styleFrom(
  205. // 保持背景色不变,只改变文字颜色为红色
  206. foregroundColor: Colors.red,
  207. padding: const EdgeInsets.symmetric(
  208. horizontal: 30,
  209. vertical: 15,
  210. ),
  211. textStyle: const TextStyle(fontSize: 16),
  212. ),
  213. ),
  214. );
  215. },
  216. ),
  217. ] else ...[
  218. const Center(child: Text('加载中...')),
  219. ],
  220. ],
  221. ),
  222. ),
  223. );
  224. }
  225. Widget _buildInfoRow(String label, String value) {
  226. return Padding(
  227. padding: const EdgeInsets.symmetric(vertical: 4.0),
  228. child: Row(
  229. children: [
  230. SizedBox(
  231. width: 80,
  232. child: Text(
  233. '$label:',
  234. style: const TextStyle(fontWeight: FontWeight.w500),
  235. ),
  236. ),
  237. Expanded(child: Text(value)),
  238. ],
  239. ),
  240. );
  241. }
  242. }