home_service_vm.dart 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. import 'package:cs_resources/generated/assets.dart';
  2. import 'package:cs_resources/theme/app_colors_theme.dart';
  3. import 'package:domain/entity/garage_sale_rent_entity.dart';
  4. import 'package:domain/entity/newsfeed_detail_entity.dart';
  5. import 'package:flutter/cupertino.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
  8. import 'package:plugin_platform/engine/toast/toast_engine.dart';
  9. import 'package:riverpod_annotation/riverpod_annotation.dart';
  10. import 'package:router/ext/auto_router_extensions.dart';
  11. import 'package:shared/utils/log_utils.dart';
  12. import 'package:widgets/dialog/app_custom_dialog.dart';
  13. import 'package:widgets/load_state_layout.dart';
  14. import 'package:widgets/my_checkbox_group.dart';
  15. import 'package:widgets/widget_export.dart';
  16. import '../../../respository/services_respository.dart';
  17. import '../../../router/page/services_page_router.dart';
  18. import 'home_service_state.dart';
  19. part 'home_service_vm.g.dart';
  20. @riverpod
  21. class HomeServiceVm extends _$HomeServiceVm {
  22. late ServicesRespository servicesRespositoryInstance;
  23. bool _needShowPlaceholder = false; //是否展示LoadingView
  24. int _page = 1; // 当前页
  25. int _limit = 10; // 每页数量
  26. int _count = 0; // 总条数
  27. bool _isSingleSelect = true;
  28. List<Map<String, dynamic>> _currentSelectedCategory = [];
  29. Map<String, dynamic> _queryParams = {
  30. 'category_id': null,
  31. 'keyword': null,
  32. 'is_liked': null,
  33. };
  34. // Refresh 控制器
  35. final EasyRefreshController refreshController = EasyRefreshController(
  36. controlFinishRefresh: true, //允许刷新
  37. controlFinishLoad: true, //允许加载
  38. );
  39. HomeServiceState initState() {
  40. return HomeServiceState(
  41. list: []
  42. );
  43. }
  44. @override
  45. HomeServiceState build(){
  46. // 引入数据仓库
  47. // servicesRespositoryInstance = ref.read(commonGarageRespositoryProvider);
  48. final state = initState();
  49. Log.d("--------------------------build---------------------");
  50. return state;
  51. }
  52. //刷新页面状态
  53. void changeLoadingState(LoadState loadState, String? errorMsg) {
  54. state = state.copyWith(
  55. loadingState: loadState,
  56. errorMessage: errorMsg
  57. );
  58. }
  59. // 初始化页面数据
  60. initPageData() {
  61. Log.d("----home_service_vm-----initPageData ${state.loadingState}");
  62. onRefresh();
  63. }
  64. // 上拉加载 更多
  65. Future loadMore() async {
  66. Log.d("----home_service_vm-----loadMore");
  67. _page++;
  68. getListData();
  69. }
  70. // 下拉刷新
  71. Future onRefresh() async {
  72. // 当前pageView 页面正处于显示状态
  73. Log.d("----forsale_vm-----onRefresh ");
  74. // await Future.delayed(const Duration(seconds: 2));
  75. _page = 1;
  76. getListData();
  77. }
  78. // 手动进行刷新
  79. Future triggerRefresh() async {
  80. Log.d("trggerRefresh");
  81. refreshController.callRefresh();
  82. }
  83. // 手动进行刷新
  84. Future directRefresh() async {
  85. state = state.copyWith(list:[]);
  86. // 注意:由于 nestedscrollview 嵌套easyfresh 组件 refreshController.callRefresh() 自动刷新只能滚动顶部但是不会触发下拉刷新,这里调用是 用到了将其滚动到顶部的作用,进而刷新操作主动掉接口
  87. // https://github.com/xuelongqy/flutter_easy_refresh/issues/692
  88. refreshController.callRefresh();
  89. refreshController.resetFooter();
  90. _page = 1;
  91. _needShowPlaceholder = true;
  92. getListData();
  93. }
  94. // 重试请求
  95. Future retryRequest() async {
  96. _page = 1;
  97. _needShowPlaceholder = true;
  98. getListData();
  99. }
  100. // 获取list 列表数据
  101. Future getListData<T>({bool? isLoadMore}) async {
  102. if (_needShowPlaceholder) {
  103. changeLoadingState(LoadState.State_Loading, null);
  104. }
  105. List<Map<String, dynamic>> list = [
  106. {
  107. 'id':1,
  108. 'service_type': 0, // 0 房屋保洁 1 空调保洁 2 维修
  109. 'cover_img': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
  110. 'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
  111. 'title': 'House Cleaning Services',
  112. 'price': 66,
  113. 'unit': '/hr',
  114. 'liked': true,
  115. 'likes_count': 12,
  116. 'company_name': 'HONG YE GROUP PTE LTD',
  117. },
  118. {
  119. 'id':2,
  120. 'service_type': 1, // 0 房屋保洁 1 空调保洁 2 维修
  121. 'cover_img': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
  122. 'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
  123. 'title': 'Repair air Conditioner Services',
  124. 'price': 88,
  125. 'unit': '/unit',
  126. 'liked': false,
  127. 'likes_count': 10,
  128. 'company_name': 'HONG YE GROUP PTE LTD',
  129. },
  130. {
  131. 'id':3,
  132. 'service_type': 2, // 0 房屋保洁 1 空调保洁 2 维修
  133. 'cover_img': 'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
  134. 'resources': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
  135. 'title': 'Air Conditioning Cleaning',
  136. 'price': 166,
  137. 'unit': '/hr',
  138. 'liked': true,
  139. 'likes_count': 1212,
  140. 'company_name': 'HONG YE GROUP PTE LTD',
  141. },
  142. ];
  143. handlerResultData(true, list:list);
  144. // try {
  145. // //请求网络
  146. // Map<String, dynamic> params = {
  147. // "type": 1, // 类型(1=Sale,2=Rent)
  148. // "category_id": _queryParams['category_id'],
  149. // "keyword": _queryParams['keyword'],
  150. // "page": _page,
  151. // "limit": _limit,
  152. // };
  153. // Log.d("请求参数------$params");
  154. // final result = await servicesRespositoryInstance.fetchGarageDataList(params);
  155. // //校验成功失败
  156. // if (result.isSuccess) {
  157. // // handlerResultList((result.data as GarageSaleRentEntity).list as List<GarageSaleRentList>, isLoadMore ?? false);
  158. // } else {
  159. // String errorMessage = result.errorMsg!;
  160. // changeLoadingState(LoadState.State_Error, errorMessage);
  161. // ToastEngine.show(result.errorMsg ?? "Network Load Error");
  162. // }
  163. // } catch (e) {
  164. // ToastEngine.show("Error: $e");
  165. // }
  166. // 最后赋值
  167. _needShowPlaceholder = false;
  168. }
  169. handlerResultData(bool isList, {List<Map<String, dynamic>>? list, dynamic? data}){
  170. Future.delayed(const Duration(seconds: 1)).then((value) {
  171. if(isList){
  172. // list 数据模式
  173. if(list != null && list.isNotEmpty){
  174. if(_page == 1){
  175. state.list.clear();
  176. state.list!.addAll(list);
  177. refreshController.finishRefresh();
  178. changeLoadingState(LoadState.State_Success, null);
  179. }else {
  180. final allList = state.list;
  181. allList!.addAll(list);
  182. state = state.copyWith(list: allList);
  183. refreshController.finishLoad();
  184. }
  185. }else {
  186. if(_page == 1){
  187. state.list.clear();
  188. changeLoadingState(LoadState.State_Empty, null);
  189. refreshController.finishRefresh();
  190. }else {
  191. refreshController.finishLoad(IndicatorResult.noMore);
  192. }
  193. }
  194. }else {
  195. // 单个数据模式
  196. if(data!=null){
  197. if(_page == 1){
  198. refreshController.finishRefresh();
  199. }else{
  200. refreshController.finishLoad();
  201. }
  202. changeLoadingState(LoadState.State_Success, null);
  203. }else {
  204. if(_page == 1){
  205. refreshController.finishRefresh();
  206. }else{
  207. refreshController.finishLoad();
  208. }
  209. changeLoadingState(LoadState.State_Empty, null);
  210. }
  211. }
  212. });
  213. }
  214. void handlerResultList(List<GarageSaleRentList>? list, bool isLoadMore) {
  215. if (list != null && list.isNotEmpty) {
  216. //有数据,判断是刷新还是加载更多的数据
  217. if (_page == 1) {
  218. //刷新的方式
  219. state.list!.clear();
  220. state.list!.addAll(list.map((item) => item.toJson()).toList());
  221. refreshController.finishRefresh();
  222. //更新展示的状态
  223. changeLoadingState(LoadState.State_Success, null);
  224. } else {
  225. //加载更多
  226. final allList = state.list;
  227. allList!.addAll(list.map((item) => item.toJson()).toList());
  228. state = state.copyWith(list: allList);
  229. refreshController.finishLoad();
  230. }
  231. } else {
  232. if (_page == 1) {
  233. //展示无数据的布局
  234. state.list!.clear();
  235. changeLoadingState(LoadState.State_Empty, null);
  236. refreshController.finishRefresh();
  237. } else {
  238. //展示加载完成,没有更多数据了
  239. if (_page == 1) {
  240. //展示无数据的布局
  241. state.list!.clear();
  242. changeLoadingState(LoadState.State_Empty, null);
  243. refreshController.finishRefresh();
  244. } else {
  245. //展示加载完成,没有更多数据了
  246. if(state.list!.length == 0){
  247. changeLoadingState(LoadState.State_Empty, null);
  248. refreshController.finishLoad();
  249. }else {
  250. if(_needShowPlaceholder){
  251. changeLoadingState(LoadState.State_Success, null);
  252. }
  253. }
  254. //更新展示的状态
  255. refreshController.finishLoad(IndicatorResult.noMore);
  256. }
  257. }
  258. }
  259. }
  260. // 获取 homeservice 的分类选项
  261. Future<List<Map<String, dynamic>>> getHomeServiceCategoryOptions() async{
  262. ToastEngine.show("Loading...");
  263. await Future.delayed(const Duration(seconds: 1));
  264. ToastEngine.dismiss();
  265. List<Map<String, dynamic>> serviceCategoryList = [
  266. {
  267. 'id': 1,
  268. 'name': 'Part-Time Cleaning',
  269. },
  270. {
  271. 'id': 2,
  272. 'name': 'Move in/Out Cleaning',
  273. },
  274. {
  275. 'id': 3,
  276. 'name': 'Laundry Anddry Cleaning',
  277. },
  278. {
  279. 'id': 4,
  280. 'name': 'Sofa Cleaning',
  281. },
  282. {
  283. 'id': 5,
  284. 'name': 'Find BBQ Essentials',
  285. },
  286. ];
  287. // 获取分类列表
  288. // try {
  289. // // 加入有缓存 就取缓存
  290. // List<Map<String, dynamic>>? StorageCategoryList = SPUtil.getObjectList(
  291. // AppConstant.storageGarageCategoryList)?.cast<Map<String, dynamic>>();
  292. //
  293. // if (StorageCategoryList != null && StorageCategoryList.isNotEmpty) {
  294. // Log.d("取StorageCategoryList 缓存: $StorageCategoryList ");
  295. // serviceCategoryList = StorageCategoryList;
  296. // } else {
  297. // Map<String, dynamic> params = {};
  298. // final result = await commonGarageRespositoryInstance
  299. // .fetchGarageCateGoryList(params);
  300. // if (result.isSuccess) {
  301. // final listJson = result.getListJson();
  302. // // 将 listJson 转换为 List<Map<String, dynamic>>
  303. // serviceCategoryList = (listJson as List?)
  304. // ?.map((item) => item as Map<String, dynamic>)
  305. // .toList() ?? [];
  306. // // 将 serviceCategoryList 存入缓存
  307. // Log.d("设置StorageCategoryList 缓存");
  308. // SPUtil.putObjectList(
  309. // AppConstant.storageGarageCategoryList, serviceCategoryList);
  310. // }
  311. // }
  312. // } catch(error){
  313. //
  314. // }
  315. return serviceCategoryList;
  316. }
  317. // 点击了filter icon
  318. handlerClickFilterIcon(BuildContext context) async{
  319. List<Map<String, dynamic>> serviceCategoryList = await getHomeServiceCategoryOptions();
  320. // 显示弹框
  321. handlerShowChooseGarageCategoryDialog(context, serviceCategoryList);
  322. }
  323. Future<void> handlerShowChooseGarageCategoryDialog(BuildContext context, List<Map<String, dynamic>> garageCategoryList) async{
  324. await DialogEngine.show(
  325. tag: "chooseGarageSaleCategory",
  326. position: DialogPosition.center,
  327. widget: AppCustomDialog(
  328. message: '',
  329. title: 'Choose a Category',
  330. dialogWidth: MediaQuery.of(context).size.width * 0.8,
  331. // contentBoxMaxHeight: 350,
  332. // contentBoxMinHeight: 300,
  333. isShowConfirmBtn: garageCategoryList!.length > 0 ? true: false,
  334. confirmTxt: "Ok",
  335. messageBuilder: (BuildContext context){
  336. return Container(
  337. color: context.appColors.textWhite,
  338. child: Column(
  339. mainAxisAlignment: MainAxisAlignment.start,
  340. crossAxisAlignment: CrossAxisAlignment.start,
  341. children: garageCategoryList!.length > 0 ? [
  342. MyCheckboxGroup(
  343. isSingleSelect: _isSingleSelect,
  344. labelStyle: const TextStyle(
  345. fontSize: 16,
  346. fontWeight: FontWeight.w500,
  347. ),
  348. items: garageCategoryList!,
  349. defaultSelectedItems: [],
  350. onChanged: (List<Map<String, dynamic>> selectedItems){
  351. Log.d("----MyCheckboxGroup onChanged $selectedItems");
  352. _currentSelectedCategory = selectedItems;
  353. }
  354. )
  355. ]: [
  356. Container(
  357. child: CircularProgressIndicator(
  358. strokeWidth: 3,
  359. valueColor: AlwaysStoppedAnimation(context.appColors.textDarkGray),
  360. ),
  361. )
  362. ],
  363. ),
  364. );
  365. },
  366. isShowCancelBtn:false,
  367. confirmAction: (){
  368. // 点击了确定
  369. Log.d("----点击了确定按钮");
  370. int? categoryId;
  371. if(_isSingleSelect){
  372. if(_currentSelectedCategory.length > 0){
  373. categoryId = _currentSelectedCategory[0]['id'];
  374. }
  375. }
  376. setCurrentQueryParams({
  377. "category_id": _queryParams?['categoryId'],
  378. });
  379. directRefresh();
  380. },
  381. )
  382. );
  383. }
  384. // 点击 收藏/取消收藏
  385. Future<bool?> handlerClickCollection(int id, bool? isCollection) async{
  386. try {
  387. //请求网络
  388. Map<String, dynamic> params = {
  389. "id": id,
  390. };
  391. Log.d("请求参数------$params");
  392. final result = await servicesRespositoryInstance.fetchGarageColleciton(params);
  393. //校验成功失败
  394. if (result.isSuccess) {
  395. // 修改 该id 的 liked 和 likes_count 字段
  396. state.list!.forEach((Map<String, dynamic> element) {
  397. GarageSaleRentList elementEntity = GarageSaleRentList.fromJson(element);
  398. if(elementEntity.id == id){
  399. elementEntity.liked = !elementEntity.liked!;
  400. elementEntity.likesCount = elementEntity.liked! ? (elementEntity.likesCount! + 1) : (elementEntity.likesCount! - 1);
  401. }
  402. element = elementEntity.toJson();
  403. });
  404. return true;
  405. } else {
  406. String errorMessage = result.errorMsg!;
  407. changeLoadingState(LoadState.State_Error, errorMessage);
  408. ToastEngine.show(result.errorMsg ?? "Network Load Error");
  409. }
  410. } catch (e) {
  411. Log.e("Error: $e");
  412. ToastEngine.show("Error: $e");
  413. }
  414. }
  415. // 设置当前的 _queryParams
  416. setCurrentQueryParams(Map<String, dynamic> params){
  417. _queryParams.addAll(params);
  418. }
  419. // 获取当前的 _queryParams
  420. Map<String, dynamic> getCurrentQueryParams(String? key){
  421. if(key!=null && key!.isNotEmpty){
  422. return _queryParams[key];
  423. }
  424. return _queryParams;
  425. }
  426. // 去详情页面
  427. void handlerGotoDetail({BuildContext? context, required int id, int serviceTypeCode= 0 }){
  428. Log.d("去详情页面 $id $serviceTypeCode");
  429. if(serviceTypeCode == 0 || serviceTypeCode == 1){
  430. // clean service 跳转到 clean 详情页
  431. appRouter.push(ServiceCleanDetailPageRoute(id: id, serviceTypeCode: serviceTypeCode));
  432. }else if(serviceTypeCode == 2){
  433. // repair service 跳转到 repair 详情页
  434. appRouter.push(ServiceRepairDetailPageRoute(id: id, serviceTypeCode: serviceTypeCode));
  435. }
  436. }
  437. }