123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- import 'dart:io';
- import 'package:auto_route/annotations.dart';
- import 'package:dio/dio.dart';
- import 'package:eitc_erm_dental_flutter/entity/clinic_info.dart';
- import 'package:eitc_erm_dental_flutter/funcs.dart';
- import 'package:eitc_erm_dental_flutter/global.dart';
- import 'package:eitc_erm_dental_flutter/http/gzkj_response.dart';
- import 'package:eitc_erm_dental_flutter/http/http.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_screenutil/flutter_screenutil.dart';
- import 'package:percent_indicator/percent_indicator.dart' as pi;
- import '../../entity/history_item_info.dart';
- ///上传页面
- @RoutePage(name: "uploadRoute")
- class UploadPage extends StatefulWidget {
- //医院信息
- final ClinicInfo clinicInfo;
- ///要上传的历史记录列表
- final List<HistoryItemInfo> uploadList;
- ///是否是从查看页面上传
- final bool isFromView;
- const UploadPage(
- {super.key,
- required this.uploadList,
- required this.clinicInfo,
- required this.isFromView});
- @override
- State<UploadPage> createState() => _UploadPageState();
- }
- class _UploadPageState extends State<UploadPage> {
- ///是否正在上传
- bool _isUploading = false;
- ///是否显示停止上传dialog
- bool _isStopDialogShow = false;
- ///上传进度
- double _uploadProgress = 0.0;
- CancelToken? cancelToken;
- ///上传索引
- int _uploadingIndex = 0;
- //上传数量
- int _uploadCount = 0;
- @override
- void initState() {
- super.initState();
- screenDisableRotate();
- WidgetsBinding.instance.addPostFrameCallback((_) => _startUpload());
- }
- @override
- void dispose() {
- super.dispose();
- cancelToken?.cancel();
- cancelToken = null;
- //如果是从查看页面上传的,就需要恢复屏幕旋转
- if (widget.isFromView) {
- screenEnableRotate();
- }
- }
- @override
- Widget build(BuildContext context) {
- return PopScope(
- canPop: !_isUploading,
- onPopInvokedWithResult: _onPop,
- child: Scaffold(
- resizeToAvoidBottomInset: false,
- appBar: _getAppBar(),
- body: SafeArea(
- child: Padding(
- padding: EdgeInsets.only(top: 52.h),
- child: _isUploading
- ? _UploadingView(
- progress: _uploadProgress,
- fileIndex: _uploadingIndex,
- fileCount: _uploadCount,
- )
- : SizedBox(),
- )),
- ));
- }
- ///获取appbar
- AppBar _getAppBar() {
- return AppBar(
- title: Text(getS().upload),
- );
- }
- ///返回控制
- void _onPop(bool didPop, dynamic result) async {
- if (didPop) {
- return;
- }
- _showStopDialog();
- }
- ///显示停止弹窗
- void _showStopDialog() async {
- _isStopDialogShow = true;
- bool? bo = await showDialog<bool>(
- context: context,
- barrierDismissible: false,
- builder: (ctx) => AlertDialog(
- title: Text(getS().hint),
- content: Text(getS().stopUploadAlert),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(ctx, false),
- child: Text(getS().cancel)),
- TextButton(
- onPressed: () => Navigator.pop(ctx, true),
- child: Text(getS().confirm)),
- ],
- ));
- _isStopDialogShow = false;
- if (bo == null || !bo) {
- return;
- }
- _stopUpload();
- }
- ///关闭退出弹窗
- void _dismissStopDialog() {
- if (!_isStopDialogShow) {
- return;
- }
- Navigator.pop(context, false);
- }
- ///开始上传
- void _startUpload() {
- _doUpload();
- }
- ///停止上传
- void _stopUpload() {
- _dismissStopDialog();
- setState(() {
- _isUploading = false;
- _uploadProgress = 0.0;
- cancelToken?.cancel();
- cancelToken = null;
- });
- }
- ///执行上传
- void _doUpload() async {
- List<HistoryItemInfo> toUploadList = [];
- Stream<HistoryItemInfo> stream = Stream.fromIterable(widget.uploadList);
- await for (HistoryItemInfo info in stream) {
- //检查文件是否存在
- if (await File(info.path).exists()) {
- toUploadList.add(info);
- }
- }
- //没有可上传的文件
- if (toUploadList.isEmpty) {
- _showNothingToUpload();
- return;
- }
- _uploadCount = toUploadList.length;
- logd("上传数量=$_uploadCount");
- stream = Stream.fromIterable(toUploadList);
- //刷新页面
- setState(() {
- _isUploading = true;
- });
- await for (HistoryItemInfo info in stream) {
- File file = File(info.path);
- String suffix = file.path.substring(file.path.lastIndexOf("."));
- String fileName =
- "HS${info.prefixInfo?.idCard ?? ""}_${info.time}$suffix";
- MultipartFile part =
- await MultipartFile.fromFile(file.path, filename: fileName);
- cancelToken = CancelToken();
- String deviceModel =
- info.prefixInfo?.wifi.replaceAll(deviceWifiPrefix, "") ?? "";
- Map<String, dynamic> map = {
- "deviceID": deviceModel,
- "applicationKey": uploadApplicationKey,
- "idCardNo": info.prefixInfo?.idCard ?? "",
- "fileType": uploadFileType,
- "fileMedium": suffix,
- "fileName": fileName,
- "chunkData": part,
- };
- FormData formData = FormData.fromMap(map);
- logd("上传数据=$map");
- try {
- GzkjResponse<dynamic> response = await Http.instance.uploadGzkj(
- widget.clinicInfo.clinicApiUrl ?? "",
- data: formData,
- cancelToken: cancelToken,
- onSendProgress: _onUploadProgress,
- fromJsonT: (_) => 0);
- if (!response.isSuccess) {
- _showUploadExceptionDialog(
- response.message ?? getS().uploadFailedPleaseRetry);
- logd("上传失败,msg=${response.message}");
- break;
- } else {
- //都上传完了
- if (_uploadingIndex >= _uploadCount - 1) {
- _showUploadSuccessDialog();
- break;
- } else {
- //刷新界面
- setState(() {
- _uploadProgress = 0.0;
- _uploadingIndex++;
- logd("上传下一个,index=$_uploadingIndex");
- });
- }
- }
- } catch (e) {
- loge("上传失败", error: e);
- _showUploadExceptionDialog(getS().uploadFailedPleaseRetry);
- break;
- }
- }
- }
- ///显示上传成功弹窗
- void _showUploadSuccessDialog() async {
- _dismissStopDialog();
- await showDialog(
- context: context,
- builder: (ctx) => AlertDialog(
- title: Text(getS().hint),
- content: Text(getS().uploadSuccess),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(ctx),
- child: Text(getS().confirm))
- ],
- ));
- if (mounted) {
- Navigator.pop(context);
- }
- }
- ///显示上传错误弹窗
- void _showUploadExceptionDialog(String str) async {
- if (!_isUploading) {
- return;
- }
- _dismissStopDialog();
- if (context.mounted) {
- await showDialog(
- context: context,
- builder: (ctx) {
- return AlertDialog(
- title: Text(getS().uploadFailed),
- content: Text(str),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(ctx),
- child: Text(getS().confirm))
- ],
- );
- });
- if (mounted) {
- Navigator.pop(context);
- }
- }
- }
- ///显示没有可上传的文件的提示框
- void _showNothingToUpload() async {
- _isUploading = false;
- if (context.mounted) {
- await showDialog(
- context: context,
- builder: (ctx) {
- return AlertDialog(
- title: Text(getS().hint),
- content: Text(getS().noFileCanUpload),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(ctx),
- child: Text(getS().close))
- ],
- );
- });
- if (mounted) {
- Navigator.pop(context);
- }
- }
- }
- //假的接口错误
- void _fakeInterfaceError() async {
- await Future.delayed(const Duration(seconds: 2));
- _showUploadExceptionDialog(getS().uploadFailedByInterfaceError);
- }
- ///上传进度
- void _onUploadProgress(int count, int total) {
- double progress = count.toDouble() / total.toDouble();
- /*logd(
- "上传进度,count=$count,total=$total,progress=$progress,uploadingIndex=$_uploadingIndex,uploadCount=$_uploadCount");*/
- setState(() {
- _uploadProgress = progress;
- });
- }
- }
- ///上传中视图
- class _UploadingView extends StatelessWidget {
- ///进度,范围0到1
- final double progress;
- final int fileIndex;
- final int fileCount;
- const _UploadingView(
- {required this.progress,
- required this.fileIndex,
- required this.fileCount});
- @override
- Widget build(BuildContext context) {
- return Padding(
- padding: EdgeInsets.symmetric(horizontal: 30.w),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- getS().uploadingHint,
- style: Theme.of(context).textTheme.titleMedium,
- ),
- SizedBox(
- height: 40.h,
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- pi.LinearPercentIndicator(
- percent: progress,
- lineHeight: 15.h,
- progressColor: Theme.of(context).colorScheme.primary,
- barRadius: Radius.circular(15.h),
- padding: EdgeInsets.zero,
- center: Text(
- "${(progress * 100.0).round()}%",
- style: TextStyle(fontSize: 12.sp, color: Colors.white),
- )),
- SizedBox(
- height: 5.h,
- ),
- Text("${fileIndex + 1}/$fileCount")
- ],
- ),
- ],
- ),
- );
- }
- }
|