diff --git a/marking_app/lib/common/model/job/gesture_recording.dart b/marking_app/lib/common/model/job/gesture_recording.dart index 5e36be8..f021a6e 100644 --- a/marking_app/lib/common/model/job/gesture_recording.dart +++ b/marking_app/lib/common/model/job/gesture_recording.dart @@ -41,3 +41,11 @@ class GestureHandwritingRecording { required this.intervalTime, }); } + +/** + * 笔记还原页面 查看原稿 + */ +class ShowStudentMmanuscript { + bool showManuscript; // 查看原稿 + ShowStudentMmanuscript(this.showManuscript); +} diff --git a/marking_app/lib/pages/homework_correction/components/trajectoryView.dart b/marking_app/lib/pages/homework_correction/components/trajectoryView.dart index c940af3..2ce7669 100644 --- a/marking_app/lib/pages/homework_correction/components/trajectoryView.dart +++ b/marking_app/lib/pages/homework_correction/components/trajectoryView.dart @@ -224,7 +224,7 @@ class TrajectoryViewState extends ConsumerState { var zhixinCall = () async { if (mounted) { - print('执行添加笔画${i},${j}'); + // print('执行添加笔画${i},${j}'); trajectorys = List.from(trajectorys)..add(theRecording); ref.read(jobDrawingTrajectoryProvider.notifier).setVal(trajectorys); } diff --git a/marking_app/lib/pages/homework_correction/job_home.dart b/marking_app/lib/pages/homework_correction/job_home.dart index 9e067a7..9492963 100644 --- a/marking_app/lib/pages/homework_correction/job_home.dart +++ b/marking_app/lib/pages/homework_correction/job_home.dart @@ -1,19 +1,568 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter/services.dart'; +// import 'package:flutter_hooks/flutter_hooks.dart'; +// import 'package:flutter_screenutil/flutter_screenutil.dart'; +// import 'package:functional_widget_annotation/functional_widget_annotation.dart'; +// import 'package:marking_app/common/mixin/common.dart'; +// import 'package:marking_app/common/model/event_bus/job_home_refresh_bus.dart'; +// import 'package:marking_app/common/model/marking/marking_list_params.dart'; +// import 'package:marking_app/pages/common/event_bus_mixin.dart'; +// import 'package:marking_app/routes/RouterManager.dart'; +// import 'package:marking_app/utils/index.dart'; +// import 'package:marking_app/utils/my_text.dart'; +// import 'package:flutter_easyrefresh/easy_refresh.dart'; + +// import 'package:badges/badges.dart' as badges; + +// part 'job_home.g.dart'; + +// class JobHome extends StatefulWidget { +// const JobHome({super.key}); + +// @override +// State createState() => _JobHomeState(); +// } + +// class _JobHomeState extends State with CommonMixin, EventBusMixin, AutomaticKeepAliveClientMixin { +// @override +// bool get wantKeepAlive => true; + +// late LinkHeaderNotifier _linkNotifier; +// late ValueNotifier _secondFloorOpen; + +// @override +// void initState() { +// getData(); +// eventOn(callback: (JobHomeRefreshBus item) => getData()); +// _linkNotifier = LinkHeaderNotifier(); +// _secondFloorOpen = ValueNotifier(false); +// super.initState(); +// } + +// @override +// void dispose() { +// eventCancel(); +// _linkNotifier.dispose(); +// _secondFloorOpen.dispose(); +// super.dispose(); +// } + +// Future getData() async { +// try { +// var _client = await getClient(); +// var _result = await _client.getJobsByPage(MarkingListParams( +// isFinish: false, +// page: 1, +// limit: 1, +// pageType: 0, +// )); +// var data = _result.data?.total ?? 0; +// eventFire(model: QuantityToBeReviewedData(data)); +// return data; +// } catch (e) { +// return 0; +// } +// } + +// @override +// Widget build(BuildContext context) { +// super.build(context); + +// return AnnotatedRegion( +// value: const SystemUiOverlayStyle( +// systemNavigationBarColor: Color(0xFF000000), +// systemNavigationBarDividerColor: null, +// statusBarColor: Colors.white, +// systemNavigationBarIconBrightness: Brightness.light, +// statusBarIconBrightness: Brightness.dark, +// statusBarBrightness: Brightness.light, +// ), +// child: SizedBox( +// height: ScreenUtil().screenHeight, +// width: ScreenUtil().screenWidth, +// child: Column( +// children: [ +// // 二楼 +// SecondFloorWidget(_linkNotifier, _secondFloorOpen, refreshCall: () => eventFire(model: JobHomeRefreshBus())), +// Expanded( +// child: EasyRefresh.custom( +// header: LinkHeader( +// _linkNotifier, +// extent: 70.0, +// triggerDistance: 70.0, +// completeDuration: Duration(milliseconds: 500), +// ), +// onRefresh: () async { +// if (_secondFloorOpen.value) return; +// // await Future.delayed(Duration(seconds: 2), () { +// // if (mounted) { +// // setState(() { +// // _count = 20; +// // }); +// // } +// // }); +// }, +// onLoad: () async { +// // await Future.delayed(Duration(seconds: 2), () { +// // if (mounted) { +// // setState(() { +// // _count += 20; +// // }); +// // } +// // }); +// }, +// slivers: [ +// SliverAppBar( +// expandedHeight: 300.h, +// pinned: true, +// floating: true, +// backgroundColor: Colors.red, +// flexibleSpace: FlexibleSpaceBar( +// centerTitle: false, +// title: Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// SlidingData([ +// EntranceModel( +// title: '作业批阅', image: 'assets/images/job_home_marking.png', navigationUrl: RouterManager.jobMainListPagePath), +// EntranceModel( +// title: '学生历史作业', +// image: 'assets/images/job_home_history.png', +// navigationUrl: '${RouterManager.jobStudentGroupPath}?page=history', +// ), +// EntranceModel( +// title: '知识点点掌握', +// image: 'assets/images/job_home_knowledge.png', +// navigationUrl: RouterManager.jobKnowledgePointsPath) +// ]), +// $TermRow([ +// EntranceModel( +// title: '答题轨迹', +// image: 'assets/images/job_home_answer_record.png', +// navigationUrl: RouterManager.answerTrajectoryPath), +// EntranceModel( +// title: '优先批阅设定', +// image: 'assets/images/job_home_youxian.png', +// navigationUrl: '${RouterManager.jobStudentGroupPath}?page=set', +// ) +// ], 0), +// ], +// )), +// ), +// SliverList( +// delegate: SliverChildBuilderDelegate( +// (context, index) { +// return Container( +// height: 40.h, +// color: Colors.amber, +// width: ScreenUtil().screenWidth, +// ); +// }, +// childCount: 10, +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// )); +// } +// } + +// class EntranceModel extends Object { +// String title; +// String image; +// String navigationUrl; +// EntranceModel({required this.title, required this.image, required this.navigationUrl}); +// } + +// class QuantityToBeReviewedData extends Object { +// int num; +// QuantityToBeReviewedData(this.num); +// } + +// @swidget +// Widget $termRow(BuildContext context, List items, int data) { +// var leng = items.length; +// Widget childWidget; +// switch (leng) { +// case 1: +// childWidget = Row(children: [Expanded(child: $TermItem(items[0], data))]); +// break; +// case 2: +// childWidget = Row(children: [ +// Expanded(flex: 9, child: $TermItem(items[0], data)), +// Expanded(flex: 1, child: SizedBox()), +// Expanded(flex: 9, child: $TermItem(items[1], data)), +// ]); +// break; +// case 3: +// double _theHeight = ScreenUtil().screenWidth / 19 + 54.h * 2; +// childWidget = Row( +// children: [ +// Expanded(child: $TermItem(items[0], data, theHeight: _theHeight)), +// SizedBox(width: ScreenUtil().screenWidth / 19), +// Expanded( +// child: SizedBox( +// height: _theHeight, +// child: Column( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// $TermItem(items[1], data), +// $TermItem(items[2], data), +// ], +// ), +// ), +// ), +// ], +// ); +// break; +// default: +// childWidget = Container(); +// } + +// return Container(padding: EdgeInsets.symmetric(horizontal: 14.w), child: childWidget); +// } + +// @swidget +// Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHeight}) { +// bool isJob = e.title == '作业批阅'; + +// return Material( +// color: Colors.white, +// elevation: 3.r, +// shadowColor: const Color.fromRGBO(231, 231, 231, 1), +// borderRadius: BorderRadius.all(Radius.circular(8.r)), +// child: InkWell( +// onTap: () => easyThrottle('GO_TO_JOB_HOME_NAVIGATION', () { +// RouterManager.router.navigateTo(context, e.navigationUrl, transition: getTransition()); +// }), + +// // splashColor: splashColor, +// borderRadius: BorderRadius.all(Radius.circular(8.r)), +// child: badges.Badge( +// showBadge: isJob && data > 0, +// ignorePointer: false, +// badgeContent: quickText(data, color: Colors.white, size: 10.sp), +// badgeAnimation: badges.BadgeAnimation.rotation( +// animationDuration: Duration(seconds: 1), +// colorChangeAnimationDuration: Duration(seconds: 1), +// loopAnimation: false, +// curve: Curves.fastOutSlowIn, +// colorChangeAnimationCurve: Curves.easeInCubic, +// ), +// badgeStyle: badges.BadgeStyle( +// badgeColor: Color.fromRGBO(255, 105, 105, 1), +// shape: badges.BadgeShape.square, +// borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(8.5.r), bottomRight: Radius.circular(8.5.r)), +// // borderSide: BorderSide(color: Colors.white, width: 2), +// elevation: 1, +// padding: EdgeInsets.symmetric(horizontal: isPad() ? 11.w : 16.w, vertical: 2.h), +// ), +// position: badges.BadgePosition.topEnd(top: 10.r, end: 10.r), +// child: Container( +// height: theHeight, +// padding: EdgeInsets.symmetric(vertical: 12.h), +// decoration: BoxDecoration( +// borderRadius: BorderRadius.all(Radius.circular(8.r)), +// // boxShadow: [ +// // BoxShadow( +// // color: const Color.fromRGBO(231, 231, 231, 1), +// // offset: Offset(4.w, 6.h), //阴影y轴偏移量 +// // blurRadius: 8, //阴影模糊程度 +// // spreadRadius: 0.2, //阴影扩散程度 +// // ) +// // ], +// // border: Border.all(width: 0.5.w, color: Color.fromARGB(255, 219, 226, 250)), +// ), +// alignment: Alignment.center, +// child: isJob +// ? Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), +// SizedBox(height: 6.r), +// quickText(e.title, size: 12.sp, color: Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), +// ], +// ) +// : Row( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), +// SizedBox(width: 6.r), +// quickText(e.title, size: 12.sp, color: Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), +// ], +// ), +// ), +// )), +// ); +// } + +// class SlidingData extends HookWidget with EventBusMixin { +// final List items; +// SlidingData(this.items); + +// @override +// Widget build(BuildContext context) { +// var dataNumber = useState(null); + +// useEffect(() { +// eventOn(callback: (QuantityToBeReviewedData data) => (dataNumber.value = data)); +// return () { +// eventCancel(); +// }; +// }, []); + +// return $TermRow(items, dataNumber.value?.num ?? 0); +// } +// } + +// /// 二楼视图 +// class SecondFloorWidget extends StatefulWidget { +// // Header连接通知器 +// final LinkHeaderNotifier linkNotifier; +// // 二楼开启状态 +// final ValueNotifier secondFloorOpen; +// final Function refreshCall; + +// const SecondFloorWidget(this.linkNotifier, this.secondFloorOpen, {required this.refreshCall, Key? key}) : super(key: key); + +// @override +// State createState() => SecondFloorWidgetState(); +// } + +// class SecondFloorWidgetState extends State { +// // 触发二楼高度 +// final double _openSecondFloorExtent = 100.0; +// // 指示器值 +// double? _indicatorValue = 0.0; + +// // 二楼高度 +// double _secondFloor = 0.0; +// // 显示展开收起动画 +// bool _toggleAnimation = false; +// Duration _toggleAnimationDuration = Duration(milliseconds: 300); +// // 二楼是否打开 +// bool _isOpen = false; + +// RefreshMode get _refreshState => widget.linkNotifier.refreshState; +// double get _pulledExtent => widget.linkNotifier.pulledExtent; + +// @override +// void initState() { +// widget.linkNotifier.addListener(onLinkNotify); +// super.initState(); +// } + +// void onLinkNotify() { +// setState(() { +// if (_refreshState == RefreshMode.armed || _refreshState == RefreshMode.refresh) { +// _indicatorValue = null; +// // 判断是否到展开二楼 +// if (widget.secondFloorOpen.value && !_toggleAnimation) { +// _isOpen = true; +// _secondFloor = MediaQuery.of(context).size.height - 60.h; +// _toggleAnimation = true; +// Future.delayed(_toggleAnimationDuration, () { +// if (mounted) { +// setState(() { +// _toggleAnimation = false; +// }); +// } +// }); +// } +// } else if (_refreshState == RefreshMode.refreshed || _refreshState == RefreshMode.done) { +// _indicatorValue = 1.0; +// } else { +// if (_refreshState == RefreshMode.inactive) { +// _indicatorValue = 0.0; +// _toggleAnimation = true; +// Future.delayed(_toggleAnimationDuration, () { +// if (mounted) { +// setState(() { +// _toggleAnimation = false; +// }); +// } +// }); +// } else { +// double indicatorValue = _pulledExtent / 70.0 * 0.8; +// _indicatorValue = indicatorValue < 0.8 ? indicatorValue : 0.8; +// // 判断是否到达打开二楼高度 +// if (_refreshState == RefreshMode.drag) { +// if (_pulledExtent >= _openSecondFloorExtent) { +// widget.secondFloorOpen.value = true; +// } else { +// widget.secondFloorOpen.value = false; +// } +// } +// } +// } +// }); +// } + +// @override +// Widget build(BuildContext context) { +// // var spaceWidth = SizedBox(height: ScreenUtil().screenWidth / 19); +// // return RefreshIndicator( +// // onRefresh: () async => widget.refreshCall(), +// // child: ListView( +// // children: [ +// // Container( +// // constraints: BoxConstraints( +// // minHeight: 200.h, +// // maxWidth: double.infinity, +// // ), +// // child: Image.asset('assets/images/job_home_top_bgm.png', fit: BoxFit.fitWidth), +// // ), +// // SizedBox(height: 30.h), +// // SlidingData([ +// // EntranceModel(title: '作业批阅', image: 'assets/images/job_home_marking.png', navigationUrl: RouterManager.jobMainListPagePath), +// // EntranceModel( +// // title: '学生历史作业', +// // image: 'assets/images/job_home_history.png', +// // navigationUrl: '${RouterManager.jobStudentGroupPath}?page=history', +// // ), +// // EntranceModel(title: '知识点点掌握', image: 'assets/images/job_home_knowledge.png', navigationUrl: RouterManager.jobKnowledgePointsPath) +// // ]), +// // spaceWidth, +// // $TermRow([ +// // EntranceModel(title: '答题轨迹', image: 'assets/images/job_home_answer_record.png', navigationUrl: RouterManager.answerTrajectoryPath), +// // EntranceModel( +// // title: '优先批阅设定', +// // image: 'assets/images/job_home_youxian.png', +// // navigationUrl: '${RouterManager.jobStudentGroupPath}?page=set', +// // ) +// // ], 0), +// // // spaceWidth, +// // // $TermRow([EntranceModel(title: '批阅设置', image: 'assets/images/job_home_marking_set.png', navigationUrl: '')], 0), +// // ], +// // ), +// // ); +// var heightVal = _isOpen +// ? _secondFloor +// : _refreshState == RefreshMode.inactive +// ? 0.0 +// : _pulledExtent; +// return AnnotatedRegion( +// value: const SystemUiOverlayStyle( +// systemNavigationBarColor: Color(0xFF000000), +// systemNavigationBarDividerColor: null, +// statusBarColor: Colors.white, +// systemNavigationBarIconBrightness: Brightness.light, +// statusBarIconBrightness: Brightness.dark, +// statusBarBrightness: Brightness.light, +// ), +// child: InkWell( +// onTap: () { +// if (_isOpen) { +// setState(() { +// _isOpen = false; +// _toggleAnimation = true; +// Future.delayed(_toggleAnimationDuration, () { +// if (mounted) { +// setState(() { +// _toggleAnimation = false; +// }); +// } +// }); +// }); +// } +// }, +// child: AnimatedContainer( +// padding: EdgeInsets.zero, +// height: heightVal, +// color: Colors.white, +// duration: _toggleAnimation ? _toggleAnimationDuration : Duration(milliseconds: 1), +// child: Stack( +// children: [ +// Positioned( +// bottom: 0.0, +// left: 0.0, +// right: 0.0, +// child: Container( +// height: MediaQuery.of(context).size.height, +// width: double.infinity, +// child: Image.asset( +// 'assets/images/job_home_top_bgm.png', +// fit: BoxFit.fitHeight, +// ), +// ), +// ), +// Positioned( +// bottom: 0.0, +// left: 0.0, +// right: 0.0, +// child: AnimatedCrossFade( +// firstChild: Center( +// child: Container( +// alignment: Alignment.center, +// margin: EdgeInsets.only( +// bottom: 20.0, +// top: 10.0, +// ), +// width: 24.0, +// height: 24.0, +// child: Offstage( +// offstage: widget.secondFloorOpen.value, +// child: CircularProgressIndicator( +// value: _indicatorValue, +// valueColor: AlwaysStoppedAnimation(Colors.white), +// strokeWidth: 2.4, +// ), +// ), +// ), +// ), +// secondChild: Center( +// child: Container( +// alignment: Alignment.center, +// margin: EdgeInsets.only( +// bottom: 20.0, +// top: 10.0, +// ), +// child: Offstage( +// offstage: !widget.secondFloorOpen.value, +// child: Text( +// '欢迎来到二楼', +// style: TextStyle(fontSize: 18.0, color: Colors.white), +// ), +// ), +// ), +// ), +// crossFadeState: widget.secondFloorOpen.value ? CrossFadeState.showSecond : CrossFadeState.showFirst, +// duration: Duration(milliseconds: 300), +// ), +// ), +// ], +// ), +// ), +// ), +// ); +// } +// } + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_page_data.dart'; import 'package:marking_app/common/model/event_bus/job_home_refresh_bus.dart'; +import 'package:marking_app/common/model/job/job_task_item.dart'; import 'package:marking_app/common/model/marking/marking_list_params.dart'; import 'package:marking_app/pages/common/event_bus_mixin.dart'; import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/easy_refresh/mixin/refresh_data_handle.dart'; import 'package:marking_app/utils/index.dart'; import 'package:marking_app/utils/my_text.dart'; import 'package:badges/badges.dart' as badges; +import 'package:marking_app/utils/request/rest_client.dart'; -import '../../utils/my_future_builder.dart'; +import 'components/new_version_of_homework/homework_tasks_view_item.dart'; part 'job_home.g.dart'; @@ -24,37 +573,51 @@ class JobHome extends StatefulWidget { State createState() => _JobHomeState(); } -class _JobHomeState extends State with CommonMixin, EventBusMixin, AutomaticKeepAliveClientMixin { +class _JobHomeState extends State + with CommonMixin, EventBusMixin, RefreshDataHandle, AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; + var param = MarkingListParams(isFinish: false, page: 1, limit: 1, pageType: 0); + + int totalJobNumber = 0; + List jobDatas = []; + late final EasyRefreshController _refreshController; + @override void initState() { - getData(); - eventOn(callback: (JobHomeRefreshBus item) => getData()); + _refreshController = EasyRefreshController(); super.initState(); } @override void dispose() { + _refreshController.dispose(); eventCancel(); super.dispose(); } - Future getData() async { - try { - var _client = await getClient(); - var _result = await _client.getJobsByPage(MarkingListParams( - isFinish: false, - page: 1, - limit: 1, - pageType: 0, - )); - var data = _result.data?.total ?? 0; - eventFire(model: QuantityToBeReviewedData(data)); - return data; - } catch (e) { - return 0; + /* 发起请求 => 作业 */ + Future toGetPageData({bool isReFresh = false}) async { + if (!isReFresh) { + param.page++; + } + RestClient client = await getClient(); + BasePageData? results = await toRefreshData( + _refreshController, + api: client.getJobsByPage, + params: param, + isReFresh: isReFresh, + context: context, + ); + if (results != null) { + Future.delayed(Duration(seconds: 1), () => eventFire(model: QuantityToBeReviewedData(results.total))); + if (isReFresh) { + jobDatas.clear(); + jobDatas = results.items; + } else + jobDatas.addAll(results.items); + setState(() {}); } } @@ -71,24 +634,31 @@ class _JobHomeState extends State with CommonMixin, EventBusMixin, Auto statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, ), - child: RefreshIndicator( - onRefresh: () async => eventFire(model: JobHomeRefreshBus()), + child: EasyRefresh( + firstRefresh: true, + taskIndependence: true, + enableControlFinishLoad: true, + enableControlFinishRefresh: true, + emptyWidget: jobDatas.isEmpty ? const MyEmptyWidget() : null, + controller: _refreshController, + header: MaterialHeader(), + footer: TaurusFooter(), child: ListView( children: [ - Container( - constraints: BoxConstraints( - minHeight: 200.h, - maxWidth: double.infinity, - ), - // decoration: BoxDecoration( - // image: DecorationImage( - // image: AssetImage('assets/images/job_home_top_bgm.png'), - // fit: BoxFit.fitWidth, // 完全填充 - // ), - // ), - child: Image.asset('assets/images/job_home_top_bgm.png', fit: BoxFit.fitWidth), - ), - SizedBox(height: 30.h), + // Container( + // constraints: BoxConstraints( + // minHeight: 200.h, + // maxWidth: double.infinity, + // ), + // // decoration: BoxDecoration( + // // image: DecorationImage( + // // image: AssetImage('assets/images/job_home_top_bgm.png'), + // // fit: BoxFit.fitWidth, // 完全填充 + // // ), + // // ), + // child: Image.asset('assets/images/job_home_top_bgm.png', fit: BoxFit.fitWidth), + // ), + SizedBox(height: MediaQuery.of(context).padding.top + 20.h), SlidingData([ EntranceModel(title: '作业批阅', image: 'assets/images/job_home_marking.png', navigationUrl: RouterManager.jobMainListPagePath), EntranceModel( @@ -109,8 +679,19 @@ class _JobHomeState extends State with CommonMixin, EventBusMixin, Auto ], 0), // spaceWidth, // $TermRow([EntranceModel(title: '批阅设置', image: 'assets/images/job_home_marking_set.png', navigationUrl: '')], 0), + spaceWidth, + Container( + padding: EdgeInsets.symmetric(horizontal: 12.w), + child: Column( + children: jobDatas + .map((e) => HomeworkTasksViewItem(completed: false, jobTaskItem: e, call: () => _refreshController.callRefresh())) + .toList(), + ), + ) ], ), + onRefresh: () => toGetPageData(isReFresh: true), + onLoad: () => toGetPageData(isReFresh: false), ), ); } diff --git a/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart b/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart index 30f40bf..f03b826 100644 --- a/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart +++ b/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart @@ -16,3 +16,15 @@ class JobHandwritingDrawingTrajectoryProviderHandle extends StateNotifier( + (ref) => JobHandwritingStudentManuscriptHandle(ShowStudentMmanuscript(false))); + +class JobHandwritingStudentManuscriptHandle extends StateNotifier { + JobHandwritingStudentManuscriptHandle(ShowStudentMmanuscript progress) : super(progress); + + setVal(ShowStudentMmanuscript val) { + state = val; + } +} diff --git a/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart b/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart index e3d77e6..34cb1e4 100644 --- a/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart +++ b/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart @@ -375,19 +375,35 @@ class _HandwritingDrawBoxState extends ConsumerState with Ev late ImageStreamListener theImageStreamListener; late ValueNotifier> _vnHandWritings; + late ValueNotifier> _vnPrimaryHandWritings; late RemoveListener _jobHandwritingDrawingTrajectoryListener; // 批注关闭监听 + late RemoveListener _jobHandwritingDrawingTrajectoryListener1; // 查看原稿 List> _packagedHandwritingDatas = []; - List _packagedHandwritingDataAll = []; + List _packagedHandwritingDataAll = []; // 总数据 List pendingData = []; // 待执行数据 List timers = []; int handwritingTime = 0; int handwritingDuration = 0; - double speed = 1; // 播放速度 + double speed = 2.0; // 播放速度 + @override void initState() { super.initState(); _vnHandWritings = ValueNotifier>([]); + _vnPrimaryHandWritings = ValueNotifier>([]); + _jobHandwritingDrawingTrajectoryListener1 = ref.read(jobHandwritingStudentManuscriptProvider.notifier).addListener((state) { + print('点击进入了'); + // 查看原稿控制 + if (state.showManuscript) { + // 查看原稿 + eventFire(model: JobHandwritingPlaybarBus(false)); // 暂停 + _vnPrimaryHandWritings.value = [..._packagedHandwritingDataAll]; + } else { + // 清空原稿数据 + _vnPrimaryHandWritings.value = []; + } + }, fireImmediately: false); _jobHandwritingDrawingTrajectoryListener = ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).addListener((state) { _vnHandWritings.value = state; }, fireImmediately: false); @@ -447,7 +463,9 @@ class _HandwritingDrawBoxState extends ConsumerState with Ev if (e.isActive) e.cancel(); }); _jobHandwritingDrawingTrajectoryListener(); + _jobHandwritingDrawingTrajectoryListener1(); _vnHandWritings.dispose(); + _vnPrimaryHandWritings.dispose(); try { imageStream?.removeListener(theImageStreamListener); eventCancel(); @@ -537,7 +555,7 @@ class _HandwritingDrawBoxState extends ConsumerState with Ev pendingData.addAll(_packagedHandwritingDataAll); ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); } - + ref.read(jobHandwritingStudentManuscriptProvider.notifier).setVal(ShowStudentMmanuscript(false)); executableData.forEach((e) { var ter = Timer(Duration(milliseconds: e.intervalTime ~/ speed), () => zhixinCall(e)); timers.add(ter); @@ -579,14 +597,12 @@ class _HandwritingDrawBoxState extends ConsumerState with Ev @override Widget build(BuildContext context) { + var showManuscript = ref.watch(jobHandwritingStudentManuscriptProvider).showManuscript; return Container( alignment: Alignment.center, child: RepaintBoundary( child: CustomPaint( - willChange: true, - isComplex: true, - foregroundPainter: HandWritingDrawingPainter(ctrl: _vnHandWritings), - // size: Size(ScreenUtil().screenWidth - 60.r, widget.boxHeight), + foregroundPainter: HandWritingDrawingPainter(ctrl: showManuscript ? _vnPrimaryHandWritings : _vnHandWritings), child: RepaintBoundary( child: $TheCachedNetworkImage( imageUrl: widget.image, @@ -609,6 +625,47 @@ class _HandwritingDrawBoxState extends ConsumerState with Ev } } +class HandWritingDrawingPainter1 extends CustomPainter { + final ValueNotifier> ctrl; + HandWritingDrawingPainter1({required this.ctrl}) : super(repaint: ctrl); + //[定义画笔] + final Paint paintBrush = Paint() + //画笔颜色 + ..color = Colors.black + //画笔笔触类型 + ..strokeCap = StrokeCap.round + //是否启动抗锯齿 + ..isAntiAlias = true + //绘画风格,默认为填充 + // ..style = PaintingStyle.fill + //画笔的宽度 + ..style = PaintingStyle.stroke + ..strokeWidth = 0.5.r; + + @override + void paint(Canvas canvas, Size size) { + // canvas.drawPoints(PointMode.points, thePoints, paintBrush); + + var points = ctrl.value; + var _length = points.length; + for (int i = 0; i < _length; i++) { + GestureHandwritingRecording item = points[i]; + GestureHandwritingRecording? nextItem = i + 1 < _length ? points[i + 1] : null; + + Offset offsetData = item.data; + Offset? nextOffsetData = nextItem?.data; + if (nextOffsetData != null && item.stroke == nextItem?.stroke) { + canvas.drawLine(offsetData, nextOffsetData, paintBrush); + } + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; // 如果 oldDelegate 不是 MyCustomPainter 的实例,则总是重绘 + } +} + class HandWritingDrawingPainter extends CustomPainter { final ValueNotifier> ctrl; HandWritingDrawingPainter({required this.ctrl}) : super(repaint: ctrl); @@ -746,148 +803,174 @@ Widget $bottomPlaybar(BuildContext context, int timeConsuming, int pauseCount, L }, []); return Container( - height: 60.h, + height: 62.h, padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), alignment: Alignment.center, color: Color.fromRGBO(0, 0, 0, 0.4), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - if (usePlaybar.handWritingReady.value) - InkWell( - onTap: () => easyThrottle('job_handwriting_play_pause', () { - if (usePlaybar.handwritingDuration.value == 0) return ToastUtils.showInfo('没有笔迹'); + Container(alignment: Alignment.centerRight, child: StudentManuscriptBtn()), // 查看原稿按钮 + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (usePlaybar.handWritingReady.value) + InkWell( + onTap: () => easyThrottle('job_handwriting_play_pause', () { + if (usePlaybar.handwritingDuration.value == 0) return ToastUtils.showInfo('没有笔迹'); - usePlaybar.playPause.value = !usePlaybar.playPause.value; - usePlaybar.eventFire(model: JobHandwritingPlaybarBus(usePlaybar.playPause.value)); - }), - child: Icon( - !usePlaybar.playPause.value ? Icons.play_circle_outline : Icons.pause_circle_outline, - color: Colors.white, - size: 28.r, - ), - ) - else - SpinKitPouringHourGlassRefined(size: 40.sp, color: Colors.white), - SizedBox(width: 6.w), - Expanded( - child: LayoutBuilder(builder: (context, constraints) { - final double containerWidth = constraints.maxWidth; // 展示区域总宽度 - var unitScale = containerWidth / timeConsuming; // 单位刻度 - var pauseIntervalsLength = pauseIntervals.length; - - List pauseTickMarks = pauseIntervals.asMap().keys.map((e) { - bool isLast = e == pauseIntervalsLength - 1; - bool isFirst = e == 0; - var item = pauseIntervals[e]; - return Positioned( - top: 0, - left: unitScale * item.startTime, - child: Container( - width: unitScale * (item.apart ?? 0), - height: 8.h, - decoration: BoxDecoration( - color: Color.fromRGBO(202, 201, 201, 1), - borderRadius: isFirst - ? BorderRadius.only(topLeft: Radius.circular(8.r), bottomLeft: Radius.circular(10.r)) - : (isLast ? BorderRadius.only(topRight: Radius.circular(8.r), bottomRight: Radius.circular(10.r)) : null), - ), + usePlaybar.playPause.value = !usePlaybar.playPause.value; + usePlaybar.eventFire(model: JobHandwritingPlaybarBus(usePlaybar.playPause.value)); + }), + child: Icon( + !usePlaybar.playPause.value ? Icons.play_circle_outline : Icons.pause_circle_outline, + color: Colors.white, + size: 28.r, ), - ); - }).toList(); + ) + else + SpinKitPouringHourGlassRefined(size: 40.sp, color: Colors.white), + SizedBox(width: 6.w), + Expanded( + child: LayoutBuilder(builder: (context, constraints) { + final double containerWidth = constraints.maxWidth; // 展示区域总宽度 + var unitScale = containerWidth / timeConsuming; // 单位刻度 + var pauseIntervalsLength = pauseIntervals.length; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - children: [ - Container( + List pauseTickMarks = pauseIntervals.asMap().keys.map((e) { + bool isLast = e == pauseIntervalsLength - 1; + bool isFirst = e == 0; + var item = pauseIntervals[e]; + return Positioned( + top: 0, + left: unitScale * item.startTime, + child: Container( + width: unitScale * (item.apart ?? 0), height: 8.h, - width: containerWidth, decoration: BoxDecoration( - // color: Color.fromRGBO(146, 146, 146, 1), - color: Colors.white, - borderRadius: BorderRadius.circular(50.r), + color: Color.fromRGBO(202, 201, 201, 1), + borderRadius: isFirst + ? BorderRadius.only(topLeft: Radius.circular(8.r), bottomLeft: Radius.circular(10.r)) + : (isLast ? BorderRadius.only(topRight: Radius.circular(8.r), bottomRight: Radius.circular(10.r)) : null), ), ), - ...pauseTickMarks, - Container( - height: 8.h, + ); + }).toList(); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + children: [ + Container( + height: 8.h, + width: containerWidth, + decoration: BoxDecoration( + // color: Color.fromRGBO(146, 146, 146, 1), + color: Colors.white, + borderRadius: BorderRadius.circular(50.r), + ), + ), + ...pauseTickMarks, + Container( + height: 8.h, + width: containerWidth, + // color: Theme.of(context).primaryColor, + child: SliderTheme( + data: SliderTheme.of(context).copyWith( + trackHeight: 8.h, // 轨道高度 + trackShape: RoundedRectSliderTrackShape(), // 轨道形状,可以自定义 + activeTrackColor: Theme.of(context).primaryColor, // 激活的轨道颜色 + inactiveTrackColor: Colors.transparent, // 未激活的轨道颜色 + thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0, disabledThumbRadius: 0), + thumbColor: Colors.white, // 滑块颜色 + overlayShape: RoundSliderOverlayShape(overlayRadius: 0), + overlayColor: Colors.black54, // 滑块外圈颜色 + // valueIndicatorShape: PaddleSliderValueIndicatorShape(), // 标签形状,可以自定义 + ), + child: Slider( + value: (usePlaybar.handwritingDuration.value - usePlaybar.useTime.value).toDouble(), + min: 0.0, + max: usePlaybar.handwritingDuration.value.toDouble(), + inactiveColor: Colors.transparent, + onChangeEnd: (value) { + if (!usePlaybar.handWritingReady.value) return; + usePlaybar.playTimingSuspend(); // 暂停计时器得暂停 + usePlaybar.eventFire(model: JobHandwritingDragProgressBarBus(value.toInt(), usePlaybar.handwritingDuration.value)); + usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt(); + }, + onChanged: (double value) { + if (!usePlaybar.handWritingReady.value) return; + usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt(); + }, + ), + ), + ), + ], + ), + SizedBox(height: 4.h), + SizedBox( width: containerWidth, - // color: Theme.of(context).primaryColor, - child: SliderTheme( - data: SliderTheme.of(context).copyWith( - trackHeight: 8.h, // 轨道高度 - trackShape: RoundedRectSliderTrackShape(), // 轨道形状,可以自定义 - activeTrackColor: Theme.of(context).primaryColor, // 激活的轨道颜色 - inactiveTrackColor: Colors.transparent, // 未激活的轨道颜色 - thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0, disabledThumbRadius: 0), - thumbColor: Colors.white, // 滑块颜色 - overlayShape: RoundSliderOverlayShape(overlayRadius: 0), - overlayColor: Colors.black54, // 滑块外圈颜色 - // valueIndicatorShape: PaddleSliderValueIndicatorShape(), // 标签形状,可以自定义 - ), - child: Slider( - value: (usePlaybar.handwritingDuration.value - usePlaybar.useTime.value).toDouble(), - min: 0.0, - max: usePlaybar.handwritingDuration.value.toDouble(), - inactiveColor: Colors.transparent, - onChangeEnd: (value) { - if (!usePlaybar.handWritingReady.value) return; - usePlaybar.playTimingSuspend(); // 暂停计时器得暂停 - usePlaybar.eventFire(model: JobHandwritingDragProgressBarBus(value.toInt(), usePlaybar.handwritingDuration.value)); - usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt(); - }, - onChanged: (double value) { - if (!usePlaybar.handWritingReady.value) return; - usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt(); - }, - ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + quickText('累计停顿:$pauseCount次', color: Colors.white, size: 7.sp), + quickText(convertSeconds(usePlaybar.useTime.value)?.toString() ?? '', color: Colors.white, size: 7.sp), + ], ), - ), + ) ], - ), - SizedBox(height: 4.h), - SizedBox( - width: containerWidth, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - quickText('累计停顿:$pauseCount次', color: Colors.white, size: 7.sp), - quickText(convertSeconds(usePlaybar.useTime.value)?.toString() ?? '', color: Colors.white, size: 7.sp), - ], - ), - ) - ], - ); - }), - ), - SizedBox(width: 16.w), - InkWell( - onTap: () => easyThrottle('job_handwriting_speed', () { - var theIndex = PlaybackSpeed.values.indexOf(usePlaybar.constantFastSpeed.value); - if (theIndex == PlaybackSpeed.values.length - 1) { - theIndex = -1; - } - usePlaybar.constantFastSpeed.value = PlaybackSpeed.values[theIndex + 1]; - }, duration: Duration(milliseconds: 500)), - child: Container( - // alignment: Alignment., - padding: EdgeInsets.symmetric(horizontal: 3.w, vertical: 1.5.h), - decoration: BoxDecoration(color: Color.fromRGBO(182, 197, 250, 1), borderRadius: BorderRadius.circular(4.r)), - child: quickText( - '${usePlaybar.constantFastSpeed.value.name}', - color: Color.fromRGBO(79, 114, 244, 1), - size: 8.sp, - align: TextAlign.center, + ); + }), ), - ), - ), + SizedBox(width: 16.w), + InkWell( + onTap: () => easyThrottle('job_handwriting_speed', () { + var theIndex = PlaybackSpeed.values.indexOf(usePlaybar.constantFastSpeed.value); + if (theIndex == PlaybackSpeed.values.length - 1) { + theIndex = -1; + } + usePlaybar.constantFastSpeed.value = PlaybackSpeed.values[theIndex + 1]; + }, duration: Duration(milliseconds: 500)), + child: Container( + // alignment: Alignment., + padding: EdgeInsets.symmetric(horizontal: 3.w, vertical: 1.5.h), + decoration: BoxDecoration(color: Color.fromRGBO(182, 197, 250, 1), borderRadius: BorderRadius.circular(4.r)), + child: quickText( + '${usePlaybar.constantFastSpeed.value.name}', + color: Color.fromRGBO(79, 114, 244, 1), + size: 8.sp, + align: TextAlign.center, + ), + ), + ), + ], + ) ], ), ); } +// 学生原稿按钮视图 +class StudentManuscriptBtn extends ConsumerWidget { + const StudentManuscriptBtn({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return InkWell( + onTap: () => easyThrottle('job_handwriting_udent_manuscript', () { + var showManuscript = ref.read(jobHandwritingStudentManuscriptProvider).showManuscript; + ref.read(jobHandwritingStudentManuscriptProvider.notifier).setVal(ShowStudentMmanuscript(!showManuscript)); + }), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 2.w, vertical: 1.h), + decoration: BoxDecoration(color: Colors.grey, borderRadius: BorderRadius.circular(4.r)), + child: quickText('学生原稿', color: Colors.white, size: 8.sp), + ), + ); + } +} + class SysjTime extends StatefulWidget { const SysjTime({super.key}); @@ -921,7 +1004,7 @@ class _SysjTimeState extends State with EventBusMixin handwritingDuration; // 笔迹总时长 final ValueNotifier playPause; // 播放暂停 - final ValueNotifier constantFastSpeed; // 原速、快速 默认原速 + final ValueNotifier constantFastSpeed; // 播放速度 final ValueNotifier handWritingReady; final ValueNotifier useTime; // 耗时 单位:(秒) @@ -942,7 +1025,7 @@ class UseBottomPlaybar with EventBusMixin { if ((milliseconds % 1000) > 500) handwritingDuration += 1; return UseBottomPlaybar._( playPause: useState(false), - constantFastSpeed: useState(PlaybackSpeed.ORIGINAL_SPEED), + constantFastSpeed: useState(PlaybackSpeed.DOUBLE_SPEED), // 默认两倍速 useTime: useState(handwritingDuration), timer: useState(null), handwritingDuration: useState(handwritingDuration),