upload_page.dart 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import 'package:chicken_farm/core/services/offline_storage_service.dart';
  2. import 'package:chicken_farm/core/services/upload_service.dart';
  3. import 'package:chicken_farm/core/utils/logger.dart';
  4. import 'package:chicken_farm/core/utils/service_checker.dart';
  5. import 'package:chicken_farm/routes/app_routes.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:go_router/go_router.dart';
  8. class UploadPage extends StatefulWidget {
  9. const UploadPage({super.key});
  10. @override
  11. State<UploadPage> createState() => _UploadPageState();
  12. }
  13. // 定义状态常量
  14. class UploadStatus {
  15. static const int preparing = 0; // 准备上传
  16. static const int uploading = 1; // 正在上传
  17. static const int completed = 2; // 上传完成
  18. static const int error = 3; // 上传出错
  19. static const int exception = 4; // 上传异常
  20. }
  21. class _UploadPageState extends State<UploadPage> with WidgetsBindingObserver {
  22. late UploadService _uploadService;
  23. int _totalOperations = 0;
  24. int _uploadedOperations = 0;
  25. int _status = UploadStatus.preparing; // 改为int类型
  26. bool _isUploading = false;
  27. bool _uploadCompleted = false;
  28. @override
  29. void initState() {
  30. super.initState();
  31. _uploadService = UploadService();
  32. logger.d('UploadPage initState');
  33. WidgetsBinding.instance.addObserver(this);
  34. // 初始化时就开始上传
  35. WidgetsBinding.instance.addPostFrameCallback((_) {
  36. _startUpload();
  37. });
  38. }
  39. @override
  40. void dispose() {
  41. WidgetsBinding.instance.removeObserver(this);
  42. super.dispose();
  43. }
  44. @override
  45. void didChangeAppLifecycleState(AppLifecycleState state) async {
  46. super.didChangeAppLifecycleState(state);
  47. //
  48. final pendingOps = await OfflineStorageService().getPendingOperations();
  49. final pendingCount = pendingOps.length;
  50. if (pendingCount > 0) {
  51. logger.d('有$pendingCount个待上传数据');
  52. _registerUploadCallbacks();
  53. }
  54. }
  55. // 根据状态获取对应的文本
  56. String _getStatusText() {
  57. switch (_status) {
  58. case UploadStatus.preparing:
  59. return '准备上传...';
  60. case UploadStatus.uploading:
  61. return '数据上传中...';
  62. case UploadStatus.completed:
  63. return '上传完成';
  64. case UploadStatus.error:
  65. return '上传出错,请稍后再试';
  66. case UploadStatus.exception:
  67. return '上传异常,请稍后再试';
  68. default:
  69. return '未知状态';
  70. }
  71. }
  72. // 根据状态获取对应的文本样式
  73. TextStyle _getStatusTextStyle(BuildContext context) {
  74. switch (_status) {
  75. case UploadStatus.completed:
  76. return Theme.of(
  77. context,
  78. ).textTheme.headlineSmall!.copyWith(color: Colors.green);
  79. case UploadStatus.error:
  80. case UploadStatus.exception:
  81. return Theme.of(
  82. context,
  83. ).textTheme.headlineSmall!.copyWith(color: Colors.red);
  84. default:
  85. return Theme.of(context).textTheme.headlineSmall!;
  86. }
  87. }
  88. Future<void> _startUpload() async {
  89. logger.d('准备上传');
  90. // 检查网络
  91. if (!await ServiceChecker().checkService()) {
  92. setState(() {
  93. _isUploading = false;
  94. _status = UploadStatus.error; // 使用状态常量
  95. });
  96. return;
  97. }
  98. // 开始上传
  99. try {
  100. _uploadService.showUpload();
  101. _registerUploadCallbacks();
  102. await _uploadService.startUpload();
  103. } catch (e) {
  104. logger.e(e);
  105. setState(() {
  106. _isUploading = false;
  107. _status = UploadStatus.exception; // 使用状态常量
  108. });
  109. }
  110. }
  111. void _registerUploadCallbacks() {
  112. _uploadService.showUpload();
  113. setState(() {
  114. _isUploading = true;
  115. _status = UploadStatus.uploading; // 使用状态常量
  116. _uploadedOperations = 0;
  117. _totalOperations = 0;
  118. _uploadCompleted = false;
  119. });
  120. int t = DateTime.now().millisecondsSinceEpoch;
  121. logger.d('开始上传,$t');
  122. // 注册回调
  123. _uploadService.registerCallbacks(
  124. onProgress: (uploaded, total, status) {
  125. if (mounted) {
  126. setState(() {
  127. _uploadedOperations = uploaded;
  128. _totalOperations = total;
  129. });
  130. }
  131. logger.d('上传进度 $t:$uploaded/$total');
  132. if (status != '上传中...') {
  133. ScaffoldMessenger.of(context).showSnackBar(
  134. SnackBar(content: Text(status), backgroundColor: Colors.lightBlue),
  135. );
  136. }
  137. },
  138. onComplete: () {
  139. if (mounted) {
  140. setState(() {
  141. _isUploading = false;
  142. _uploadCompleted = true;
  143. _status = UploadStatus.completed; // 使用状态常量
  144. });
  145. }
  146. logger.d('上传完成');
  147. ScaffoldMessenger.of(context).showSnackBar(
  148. SnackBar(content: Text('上传完成'), backgroundColor: Colors.green),
  149. );
  150. },
  151. onError: (message, {isAuthError = false}) {
  152. logger.e('上传出错:$message');
  153. if (isAuthError == true) {
  154. // 认证错误,跳转到登录页面
  155. context.pushNamed(AppRouteNames.login);
  156. }
  157. if (mounted) {
  158. setState(() {
  159. _isUploading = false;
  160. _status = UploadStatus.error; // 使用状态常量
  161. });
  162. }
  163. },
  164. );
  165. }
  166. @override
  167. Widget build(BuildContext context) {
  168. return PopScope(
  169. canPop: false, // 禁用返回键
  170. onPopInvokedWithResult: (bool didPop, Object? result) {
  171. // 返回键被触发时的回调,但因为 canPop 为 false,所以不会真正返回
  172. },
  173. child: Scaffold(
  174. appBar: AppBar(
  175. title: const Text('数据上传'),
  176. centerTitle: true,
  177. automaticallyImplyLeading: false, // 禁用默认的返回图标
  178. ),
  179. body: LayoutBuilder(
  180. builder: (context, constraints) {
  181. return SingleChildScrollView(
  182. child: ConstrainedBox(
  183. constraints: BoxConstraints(minHeight: constraints.maxHeight),
  184. child: IntrinsicHeight(
  185. child: Center(
  186. child: Padding(
  187. padding: const EdgeInsets.all(16.0),
  188. child: Column(
  189. mainAxisAlignment: MainAxisAlignment.center,
  190. crossAxisAlignment: CrossAxisAlignment.center,
  191. children: [
  192. Text(
  193. _getStatusText(), // 使用方法获取状态文本
  194. style: _getStatusTextStyle(context), // 使用方法获取样式
  195. textAlign: TextAlign.center,
  196. ),
  197. const SizedBox(height: 30),
  198. if (_totalOperations > 0) ...[
  199. LinearProgressIndicator(
  200. value: _uploadedOperations / _totalOperations,
  201. minHeight: 10,
  202. ),
  203. const SizedBox(height: 10),
  204. Text('$_uploadedOperations / $_totalOperations'),
  205. const SizedBox(height: 30),
  206. ],
  207. if (_isUploading) ...[
  208. const SizedBox(
  209. width: 50,
  210. height: 50,
  211. child: CircularProgressIndicator(),
  212. ),
  213. const SizedBox(height: 20),
  214. SizedBox(
  215. width: 120,
  216. child: ElevatedButton(
  217. onPressed: () {
  218. _uploadService.hideUpload();
  219. context.pop(2); // 后台上传
  220. },
  221. child: const Text('后台上传'),
  222. ),
  223. ),
  224. ] else if (_uploadCompleted) ...[
  225. SizedBox(
  226. width: 120,
  227. child: ElevatedButton(
  228. onPressed: () {
  229. context.pop(1); // 上传成功
  230. },
  231. child: const Text('返回'),
  232. ),
  233. ),
  234. ] else ...[
  235. SizedBox(
  236. width: 120,
  237. child: ElevatedButton(
  238. onPressed: () {
  239. context.pop(0); // 取消上传
  240. },
  241. child: const Text('退出'),
  242. ),
  243. ),
  244. ],
  245. ],
  246. ),
  247. ),
  248. ),
  249. ),
  250. ),
  251. );
  252. },
  253. ),
  254. ),
  255. );
  256. }
  257. }