history_page.dart 14 KB

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