history_page.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. import 'dart:io';
  2. import 'package:auto_route/auto_route.dart';
  3. import 'package:eitc_erm_dental_flutter/app_router.gr.dart';
  4. import 'package:eitc_erm_dental_flutter/entity/history_item_info.dart';
  5. import 'package:eitc_erm_dental_flutter/funcs.dart';
  6. import 'package:eitc_erm_dental_flutter/pages/history/vm/history_view_model.dart';
  7. import 'package:eitc_erm_dental_flutter/pages/history/widget/history_item_view.dart';
  8. import 'package:eitc_erm_dental_flutter/widget/custom_divider.dart';
  9. import 'package:eitc_erm_dental_flutter/widget/empty_widget.dart';
  10. import 'package:eitc_erm_dental_flutter/widget/loading_widget.dart';
  11. import 'package:eitc_erm_dental_flutter/widget/main_button.dart';
  12. import 'package:flutter/material.dart';
  13. import 'package:flutter_riverpod/flutter_riverpod.dart';
  14. import 'package:flutter_screenutil/flutter_screenutil.dart';
  15. import 'package:fluwx/fluwx.dart';
  16. import '../../global.dart';
  17. ///历史记录页面
  18. @RoutePage(name: "historyRoute")
  19. class HistoryPage extends ConsumerStatefulWidget {
  20. const HistoryPage({super.key});
  21. @override
  22. ConsumerState createState() => _HistoryPageState();
  23. }
  24. class _HistoryPageState extends ConsumerState<HistoryPage> {
  25. @override
  26. void initState() {
  27. super.initState();
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. return PopScope(
  32. canPop: false,
  33. onPopInvokedWithResult: _onPop,
  34. child: Scaffold(
  35. appBar: _getAppBar(context),
  36. body: Padding(
  37. padding: EdgeInsets.symmetric(horizontal: 16.w),
  38. child: Column(
  39. children: [
  40. _getConsultations(context),
  41. _getHistoryTitle(),
  42. _getSelectedCount(context),
  43. Expanded(child: _getListWidget(context)),
  44. _getBottomButtons(context),
  45. SizedBox(
  46. height: 5.h,
  47. )
  48. ],
  49. ),
  50. ),
  51. ));
  52. }
  53. ///pop拦截
  54. void _onPop(bool didPop, _) {
  55. if (didPop) {
  56. return;
  57. }
  58. if (ref.read(historySelectModeProvider)) {
  59. _setSelectMode(false);
  60. return;
  61. }
  62. Navigator.pop(context);
  63. }
  64. ///获取appbar
  65. AppBar _getAppBar(BuildContext context) {
  66. return AppBar(
  67. centerTitle: true,
  68. forceMaterialTransparency: true,
  69. title: Text(getS().serviceCenter),
  70. leading: IconButton(
  71. onPressed: () {
  72. Navigator.pop(context);
  73. },
  74. icon: Icon(
  75. Platform.isIOS ? Icons.arrow_back_ios_new : Icons.arrow_back)),
  76. );
  77. }
  78. ///获取咨询部分
  79. Widget _getConsultations(BuildContext context) {
  80. return Container(
  81. padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h),
  82. decoration: BoxDecoration(
  83. color: Theme.of(context).colorScheme.surfaceContainerHigh,
  84. borderRadius: BorderRadius.circular(15.r)),
  85. child: Column(
  86. crossAxisAlignment: CrossAxisAlignment.start,
  87. children: [
  88. Text(
  89. getS().freeConsultation,
  90. style: Theme.of(context)
  91. .textTheme
  92. .titleMedium
  93. ?.copyWith(fontWeight: FontWeight.bold),
  94. ),
  95. SizedBox(
  96. height: 10.h,
  97. ),
  98. Text(
  99. getS().electricityHospitalConsultationDesc,
  100. style: Theme.of(context).textTheme.bodySmall,
  101. ),
  102. SizedBox(
  103. height: 10.h,
  104. ),
  105. CustomDivider(
  106. height: 0,
  107. color: Colors.grey[300],
  108. ),
  109. SizedBox(
  110. height: 10.h,
  111. ),
  112. Align(
  113. alignment: Alignment.center,
  114. child: MainButton(
  115. text: getS().consultationNow,
  116. fitMinimumSize: true,
  117. buttonPadding:
  118. EdgeInsets.symmetric(horizontal: 20.w, vertical: 6.h),
  119. minimumSize: Size.zero,
  120. shape: RoundedRectangleBorder(
  121. borderRadius: BorderRadius.circular(8.r)),
  122. onPressed: _onOpenElectricityHospitalMiniProgram),
  123. ),
  124. ],
  125. ),
  126. );
  127. }
  128. ///获取历史记录标题
  129. Widget _getHistoryTitle() {
  130. return Column(
  131. children: [
  132. SizedBox(
  133. height: 10.h,
  134. ),
  135. Row(
  136. children: [
  137. Text(getS().history),
  138. Spacer(),
  139. Consumer(builder: (ctx, ref, _) {
  140. int type = ref.watch(historyViewTypeProvider);
  141. return PopupMenuButton(
  142. color: const Color(0xFF383838),
  143. //背景形状
  144. shape: RoundedRectangleBorder(
  145. borderRadius: BorderRadius.circular(8.r)),
  146. itemBuilder: (ctx) {
  147. return [
  148. PopupMenuItem(
  149. child: SizedBox(
  150. width: double.infinity,
  151. child: Text(
  152. getS().byTime,
  153. textAlign: TextAlign.center,
  154. style: const TextStyle(color: Colors.white),
  155. ),
  156. ),
  157. onTap: () => _updateListType(0),
  158. ),
  159. PopupMenuItem(
  160. child: SizedBox(
  161. width: double.infinity,
  162. child: Text(
  163. getS().byCategory,
  164. textAlign: TextAlign.center,
  165. style: const TextStyle(color: Colors.white),
  166. ),
  167. ),
  168. onTap: () => _updateListType(1),
  169. ),
  170. ];
  171. },
  172. child: Row(
  173. children: [
  174. Text(type == 0 ? getS().byTime : getS().byCategory),
  175. Icon(Icons.arrow_drop_down)
  176. ],
  177. ));
  178. }),
  179. ],
  180. ),
  181. Consumer(builder: (ctx, ref, _) {
  182. return ref.read(historySelectModeProvider)
  183. ? SizedBox()
  184. : SizedBox(
  185. height: 10.h,
  186. );
  187. })
  188. ],
  189. );
  190. }
  191. ///更新列表类型
  192. void _updateListType(int type) {
  193. if (ref.read(historyViewTypeProvider) == type) {
  194. return;
  195. }
  196. ref.read(historyViewTypeProvider.notifier).setType(type);
  197. }
  198. ///获取已选择数量widget
  199. Widget _getSelectedCount(BuildContext context) {
  200. return Consumer(builder: (ctx, ref, _) {
  201. bool isSelectedMode = ref.watch(historySelectModeProvider);
  202. Color primaryColor = Theme.of(context).colorScheme.primary;
  203. //如果不用Visibility的方式,当进入选择模式时,GridView会滚到最上面
  204. return Visibility(
  205. visible: isSelectedMode,
  206. child: Row(
  207. children: [
  208. Consumer(builder: (_, tref, __) {
  209. int count = tref.watch(historySelectedCountProvider);
  210. return Text(getS().hasSelectCountItem(count));
  211. }),
  212. SizedBox(
  213. width: 6.w,
  214. ),
  215. ElevatedButton(
  216. onPressed: () => _setSelectMode(false),
  217. style: ButtonStyle(
  218. minimumSize: const WidgetStatePropertyAll(Size.zero),
  219. padding: WidgetStatePropertyAll(EdgeInsets.symmetric(
  220. horizontal: 15.w, vertical: 4.h)),
  221. elevation: const WidgetStatePropertyAll(0.0),
  222. backgroundColor:
  223. const WidgetStatePropertyAll(Color(0xFFCEDCF0))),
  224. child: Row(
  225. children: [
  226. Text(
  227. getS().cancel,
  228. style: TextStyle(color: primaryColor, fontSize: 12.sp),
  229. ),
  230. SizedBox(
  231. width: 5.w,
  232. ),
  233. Icon(Icons.close, size: 18.r, color: primaryColor)
  234. ],
  235. )),
  236. const Spacer(),
  237. SizedBox(
  238. width: 25.w,
  239. child: Consumer(builder: (_, cref, __) {
  240. int count = cref.watch(historySelectedCountProvider);
  241. return Checkbox(
  242. value: count ==
  243. (cref
  244. .read(historyListProvider)
  245. .value
  246. ?.getMergedList(false)
  247. .length ??
  248. 0),
  249. shape: const CircleBorder(),
  250. onChanged: (bo) => cref
  251. .read(historyListProvider.notifier)
  252. .selectAll(bo ?? false));
  253. })),
  254. Text(getS().selectAll),
  255. SizedBox(
  256. width: 5.w,
  257. ),
  258. ],
  259. ));
  260. });
  261. }
  262. Widget _getListWidget(BuildContext context) {
  263. return Consumer(builder: (ctx, ref, _) {
  264. AsyncValue<HistoryListData> value = ref.watch(historyListProvider);
  265. int viewType = ref.watch(historyViewTypeProvider);
  266. return switch (value) {
  267. AsyncData<HistoryListData>(value: var data) => data.isEmpty
  268. ? EmptyWidget(
  269. text: getS().noHistories,
  270. )
  271. : (viewType == 0 ? _getTimeList(data) : _getTypeList(ctx, data)),
  272. _ => LoadingWidget(
  273. text: getS().loadingHistories,
  274. )
  275. };
  276. });
  277. }
  278. ///获取按时间列表
  279. Widget _getTimeList(HistoryListData data) {
  280. return _getGridView(data.getMergedList(true));
  281. }
  282. ///获取按类型列表
  283. Widget _getTypeList(BuildContext context, HistoryListData data) {
  284. return SingleChildScrollView(
  285. child: Column(
  286. crossAxisAlignment: CrossAxisAlignment.start,
  287. children: [
  288. Text(
  289. getS().photo,
  290. style: Theme.of(context).textTheme.titleMedium,
  291. ),
  292. SizedBox(
  293. height: 10.h,
  294. ),
  295. _getGridView(data.photoList, shinkWarp: true, scrollable: false),
  296. SizedBox(
  297. height: 10.h,
  298. ),
  299. Text(
  300. getS().video,
  301. style: Theme.of(context).textTheme.titleMedium,
  302. ),
  303. SizedBox(
  304. height: 10.h,
  305. ),
  306. _getGridView(data.videoList, shinkWarp: true, scrollable: false)
  307. ],
  308. ),
  309. );
  310. }
  311. ///获取GridView
  312. Widget _getGridView(List<HistoryItemInfo> list,
  313. {bool shinkWarp = false, bool scrollable = true}) {
  314. return GridView.builder(
  315. shrinkWrap: shinkWarp,
  316. physics: scrollable
  317. ? const ClampingScrollPhysics()
  318. : const NeverScrollableScrollPhysics(),
  319. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  320. crossAxisCount: 2,
  321. childAspectRatio: 164.0 / 120.0,
  322. mainAxisSpacing: 8.r,
  323. crossAxisSpacing: 15.r),
  324. itemBuilder: (ctx, index) {
  325. HistoryItemInfo item = list[index];
  326. return GestureDetector(
  327. child: HistoryItemView(
  328. index: index,
  329. name: item.name,
  330. path: item.path,
  331. type: item.type,
  332. ),
  333. onTap: () => _onTap(ctx, item),
  334. onLongPress: () => _onLongPress(ctx, item),
  335. );
  336. },
  337. itemCount: list.length,
  338. );
  339. }
  340. ///点击
  341. void _onTap(BuildContext context, HistoryItemInfo info) async {
  342. if (ref.read(historySelectModeProvider)) {
  343. ref.read(historyListProvider.notifier).select(info, !info.isSelected);
  344. return;
  345. }
  346. await context.pushRoute(info.type == 0
  347. ? PhotoViewRoute(info: info)
  348. : VideoPlayerRoute(info: info));
  349. }
  350. ///长按
  351. void _onLongPress(BuildContext context, HistoryItemInfo info) {
  352. if (ref.read(historySelectModeProvider)) {
  353. return;
  354. }
  355. _setSelectMode(true);
  356. ref.read(historyListProvider.notifier).select(info, true);
  357. }
  358. ///设置选择模式
  359. void _setSelectMode(bool isSelectMode) {
  360. ref.read(historySelectModeProvider.notifier).setSelectMode(isSelectMode);
  361. }
  362. ///获取选择的条目
  363. List<HistoryItemInfo> _getSelectedItems() {
  364. return ref.read(historyListProvider.notifier).getSelectedItems();
  365. }
  366. ///获取底部按钮
  367. Widget _getBottomButtons(BuildContext context) {
  368. bool isSelectedMode = ref.watch(historySelectModeProvider);
  369. //如果不用Visibility的方式,当进入选择模式时,GridView会滚到最上面
  370. return Visibility(
  371. visible: isSelectedMode,
  372. child: Row(
  373. children: [
  374. Expanded(
  375. child: MainButton(
  376. text: getS().upload, onPressed: () => _onUpload(context)),
  377. ),
  378. SizedBox(
  379. width: 15.w,
  380. ),
  381. Expanded(
  382. child: MainButton(
  383. text: getS().delete,
  384. onPressed: () => _onDelete(context),
  385. isOutlined: true,
  386. )),
  387. ],
  388. ));
  389. }
  390. ///上传
  391. void _onUpload(BuildContext context) async {
  392. if (!await checkInternetWifi()) {
  393. return;
  394. }
  395. List<HistoryItemInfo> list = _getSelectedItems();
  396. if (list.isEmpty) {
  397. showToast(text: getS().pleaseSelectToUpload);
  398. return;
  399. }
  400. if (context.mounted) {
  401. context.pushRoute(
  402. UploadSelectClinicRoute(uploadList: list, isFromView: false));
  403. }
  404. }
  405. ///删除
  406. void _onDelete(BuildContext context) async {
  407. List<HistoryItemInfo> list = _getSelectedItems();
  408. if (list.isEmpty) {
  409. showToast(text: getS().pleaseSelectToDelete);
  410. return;
  411. }
  412. bool? bo = await showDeleteAlertDialog(
  413. context, getS().deleteMultiHint(list.length));
  414. if (bo == null || !bo) {
  415. return;
  416. }
  417. bool result = await ref.read(historyListProvider.notifier).delete(list);
  418. showToast(text: result ? getS().deleteSuccess : getS().deleteFailed);
  419. }
  420. ///打开电力医院小程序
  421. void _onOpenElectricityHospitalMiniProgram() async {
  422. if (!await isWxInstalled()) {
  423. return;
  424. }
  425. fluwx.open(
  426. target: MiniProgram(
  427. username: hospitalMiniProgramId, path: hospitalMiniProgramPath));
  428. }
  429. }