From 80f8c88d0d07995c4fdba1035f069be3ed455511 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sat, 28 Sep 2024 11:57:05 +0800 Subject: [PATCH] feat: sort user search --- lib/http/search.dart | 6 +- lib/pages/search_panel/controller.dart | 6 +- .../search_panel/widgets/user_panel.dart | 186 +++++++++++++++++- lib/pages/video/detail/reply/view.dart | 8 +- 4 files changed, 195 insertions(+), 11 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index e070e9de..95e8de9c 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -76,6 +76,8 @@ class SearchHttp { String? order, int? duration, int? tids, + int? orderSort, + int? userType, }) async { var reqData = { 'search_type': searchType.type, @@ -83,9 +85,11 @@ class SearchHttp { // 'order_sort': 0, // 'user_type': 0, 'page': page, - if (order != null) 'order': order, + if (order != null && order.isNotEmpty) 'order': order, if (duration != null) 'duration': duration, if (tids != null) 'tids': tids, + if (orderSort != null) 'order_sort': orderSort, + if (userType != null) 'user_type': userType, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 04111c0e..dc98d32d 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -15,6 +15,8 @@ class SearchPanelController extends CommonController { // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; int? tids; + int? orderSort; + int? userType; @override void onInit() { @@ -68,8 +70,10 @@ class SearchPanelController extends CommonController { searchType: searchType!, keyword: keyword!, page: currentPage, - order: searchType!.type != 'video' ? null : order.value, + order: order.value, duration: searchType!.type != 'video' ? null : duration.value, tids: tids, + orderSort: orderSort, + userType: userType, ); } diff --git a/lib/pages/search_panel/widgets/user_panel.dart b/lib/pages/search_panel/widgets/user_panel.dart index 5db78c8c..867c0e52 100644 --- a/lib/pages/search_panel/widgets/user_panel.dart +++ b/lib/pages/search_panel/widgets/user_panel.dart @@ -1,4 +1,9 @@ +import 'package:PiliPalaX/pages/search/widgets/search_text.dart'; +import 'package:PiliPalaX/pages/search_panel/controller.dart'; +import 'package:PiliPalaX/pages/video/detail/reply/view.dart' + show MySliverPersistentHeaderDelegate; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; import 'package:PiliPalaX/utils/utils.dart'; @@ -6,13 +11,67 @@ import 'package:PiliPalaX/utils/utils.dart'; import '../../../common/constants.dart'; import '../../../utils/grid.dart'; -Widget searchUserPanel(BuildContext context, ctr, list) { +Widget searchUserPanel(BuildContext context, searchPanelCtr, list) { TextStyle style = TextStyle( fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, color: Theme.of(context).colorScheme.outline); + final ctr = Get.put(UserPanelController()); - return CustomScrollView(controller: ctr.scrollController, slivers: [ - SliverGrid( + return CustomScrollView( + controller: searchPanelCtr.scrollController, + slivers: [ + SliverPersistentHeader( + pinned: false, + floating: true, + delegate: MySliverPersistentHeaderDelegate( + child: Container( + height: 40, + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + Obx( + () => Text( + '排序: ${ctr.orderFiltersList[ctr.currentOrderFilterval.value]['label']}', + maxLines: 1, + style: + TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + const Spacer(), + Obx( + () => Text( + '用户类型: ${ctr.userTypeFiltersList[ctr.currentUserTypeFilterval.value]['label']}', + maxLines: 1, + style: + TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + const Spacer(), + SizedBox( + width: 32, + height: 32, + child: IconButton( + tooltip: '筛选', + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + ctr.onShowFilterDialog(context, searchPanelCtr); + }, + icon: Icon( + Icons.filter_list_outlined, + size: 18, + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ], + ), + ), + ), + ), + SliverGrid( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( mainAxisSpacing: StyleString.cardSpace, crossAxisSpacing: StyleString.safeSpace, @@ -77,6 +136,123 @@ Widget searchUserPanel(BuildContext context, ctr, list) { ); }, childCount: list!.length, - )) - ]); + ), + ) + ], + ); +} + +class UserPanelController extends GetxController { + List orderFiltersList = [ + {'label': '默认排序', 'value': 0, 'orderSort': 0, 'order': ''}, + {'label': '粉丝数由高到低', 'value': 1, 'orderSort': 0, 'order': 'fans'}, + {'label': '粉丝数由低到高', 'value': 2, 'orderSort': 1, 'order': 'fans'}, + {'label': 'Lv等级由高到低', 'value': 3, 'orderSort': 0, 'order': 'level'}, + {'label': 'Lv等级由低到高', 'value': 4, 'orderSort': 1, 'order': 'level'}, + ]; + List userTypeFiltersList = [ + {'label': '全部用户', 'value': 0, 'userType': 0}, + {'label': 'UP主', 'value': 1, 'userType': 1}, + {'label': '普通用户', 'value': 2, 'userType': 2}, + {'label': '认证用户', 'value': 3, 'userType': 3}, + ]; + RxInt currentOrderFilterval = 0.obs; + RxInt currentUserTypeFilterval = 0.obs; + + onShowFilterDialog( + BuildContext context, + SearchPanelController searchPanelCtr, + ) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (_) => SingleChildScrollView( + child: Container( + width: double.infinity, + padding: EdgeInsets.only( + top: 20, + left: 16, + right: 16, + bottom: 80 + MediaQuery.of(context).padding.bottom, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + const Text('用户粉丝数及等级排序顺序', style: TextStyle(fontSize: 16)), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: orderFiltersList + .map( + (item) => SearchText( + searchText: item['label'], + onSelect: (_) async { + Get.back(); + currentOrderFilterval.value = item['value']; + SmartDialog.dismiss(); + SmartDialog.showToast("「${item['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'bili_user${searchPanelCtr.keyword!}'); + ctr.orderSort = item['orderSort']; + ctr.order.value = item['order']; + SmartDialog.showLoading(msg: 'loading'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (_) {}, + bgColor: item['value'] == currentOrderFilterval.value + ? Theme.of(context).colorScheme.primaryContainer + : null, + textColor: item['value'] == currentOrderFilterval.value + ? Theme.of(context).colorScheme.onPrimaryContainer + : null, + ), + ) + .toList(), + ), + const SizedBox(height: 20), + const Text('用户分类', style: TextStyle(fontSize: 16)), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: userTypeFiltersList + .map( + (item) => SearchText( + searchText: item['label'], + onSelect: (_) async { + Get.back(); + currentUserTypeFilterval.value = item['value']; + SmartDialog.dismiss(); + SmartDialog.showToast("「${item['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'bili_user${searchPanelCtr.keyword!}'); + ctr.userType = item['userType']; + SmartDialog.showLoading(msg: 'loading'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (_) {}, + bgColor: item['value'] == currentUserTypeFilterval.value + ? Theme.of(context).colorScheme.primaryContainer + : null, + textColor: item['value'] == + currentUserTypeFilterval.value + ? Theme.of(context).colorScheme.onPrimaryContainer + : null, + ), + ) + .toList(), + ), + ], + ), + ), + ), + ); + } } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 41866cbc..16273704 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -133,7 +133,7 @@ class _VideoReplyPanelState extends State SliverPersistentHeader( pinned: false, floating: true, - delegate: _MySliverPersistentHeaderDelegate( + delegate: MySliverPersistentHeaderDelegate( child: Container( height: 40, padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), @@ -257,8 +257,8 @@ class _VideoReplyPanelState extends State } } -class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { - _MySliverPersistentHeaderDelegate({required this.child}); +class MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { + MySliverPersistentHeaderDelegate({required this.child}); final double _minExtent = 45; final double _maxExtent = 45; final Widget child; @@ -281,7 +281,7 @@ class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { double get minExtent => _minExtent; @override - bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) { + bool shouldRebuild(covariant MySliverPersistentHeaderDelegate oldDelegate) { return true; } }