From 8703d9f5765d6ca6118c85afb86a505f84135a0d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 22 Aug 2023 09:49:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=AD=9B=E9=80=89v1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 11 +- lib/models/common/search_type.dart | 17 ++ lib/pages/search/view.dart | 3 +- lib/pages/searchPanel/controller.dart | 17 +- lib/pages/searchPanel/view.dart | 8 +- .../searchPanel/widgets/video_panel.dart | 154 ++++++++++++++++-- 6 files changed, 186 insertions(+), 24 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 4a1a4fdd..f413b6cf 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -46,14 +46,19 @@ class SearchHttp { required SearchType searchType, required String keyword, required page, + String? order, }) async { - var res = await Request().get(Api.searchByType, data: { + Map reqData = { 'search_type': searchType.type, 'keyword': keyword, // 'order_sort': 0, // 'user_type': 0, - 'page': page - }); + 'page': page, + }; + if (order != null && order != '') { + reqData['order'] = order; + } + var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { Object data; switch (searchType) { diff --git a/lib/models/common/search_type.dart b/lib/models/common/search_type.dart index b4caecc1..491ee7b4 100644 --- a/lib/models/common/search_type.dart +++ b/lib/models/common/search_type.dart @@ -27,3 +27,20 @@ extension SearchTypeExtension on SearchType { ['video', 'media_bangumi', 'live_room', 'bili_user'][index]; String get label => ['视频', '番剧', '直播间', '用户'][index]; } + +// 搜索类型为视频、专栏及相簿时 +enum ArchiveFilterType { + totalrank, + click, + pubdate, + dm, + stow, + scores, + // 专栏 + // attention, +} + +extension ArchiveFilterTypeExtension on ArchiveFilterType { + String get description => + ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index]; +} diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index 786500bf..9c446a45 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -45,8 +45,9 @@ class _SearchPageState extends State with RouteAware { return OpenContainer( closedElevation: 0, openElevation: 0, - onClosed: (_) { + onClosed: (_) async { // 在 openBuilder 关闭时触发的回调函数 + await Future.delayed(const Duration(milliseconds: 500)); _searchController.onClear(); }, openColor: Theme.of(context).colorScheme.background, diff --git a/lib/pages/searchPanel/controller.dart b/lib/pages/searchPanel/controller.dart index b8e4a166..67a15ba3 100644 --- a/lib/pages/searchPanel/controller.dart +++ b/lib/pages/searchPanel/controller.dart @@ -12,17 +12,22 @@ class SearchPanelController extends GetxController { SearchType? searchType; RxInt page = 1.obs; RxList resultList = [].obs; + RxString order = ''.obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( - searchType: searchType!, keyword: keyword!, page: page.value); + searchType: searchType!, + keyword: keyword!, + page: page.value, + order: searchType!.type != 'video' ? '' : order.value, + ); if (result['status']) { - if (type == 'init' || type == 'onLoad') { - page.value++; - resultList.addAll(result['data'].list); - } else if (type == 'onRefresh') { + if (type == 'onRefresh') { resultList.value = result['data'].list; + } else { + resultList.addAll(result['data'].list); } + page.value++; onPushDetail(keyword, resultList); } return result; @@ -30,7 +35,7 @@ class SearchPanelController extends GetxController { Future onRefresh() async { page.value = 1; - onSearch(type: 'onRefresh'); + await onSearch(type: 'onRefresh'); } // 返回顶部并刷新 diff --git a/lib/pages/searchPanel/view.dart b/lib/pages/searchPanel/view.dart index 23f9dd73..346e5048 100644 --- a/lib/pages/searchPanel/view.dart +++ b/lib/pages/searchPanel/view.dart @@ -28,7 +28,6 @@ class _SearchPanelState extends State with AutomaticKeepAliveClientMixin { late SearchPanelController _searchPanelController; - bool _isLoadingMore = false; late Future _futureBuilderFuture; late ScrollController scrollController; @@ -76,12 +75,15 @@ class _SearchPanelState extends State if (snapshot.connectionState == ConnectionState.done) { Map data = snapshot.data; var ctr = _searchPanelController; - List list = ctr.resultList; + RxList list = ctr.resultList; if (data['status']) { return Obx(() { switch (widget.searchType) { case SearchType.video: - return searchVideoPanel(context, ctr, list); + return SearchVideoPanel( + ctr: _searchPanelController, + list: list.value, + ); case SearchType.media_bangumi: return searchMbangumiPanel(context, ctr, list); case SearchType.bili_user: diff --git a/lib/pages/searchPanel/widgets/video_panel.dart b/lib/pages/searchPanel/widgets/video_panel.dart index ca6b09fb..1c115c90 100644 --- a/lib/pages/searchPanel/widgets/video_panel.dart +++ b/lib/pages/searchPanel/widgets/video_panel.dart @@ -1,15 +1,147 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; +import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/pages/searchPanel/index.dart'; -Widget searchVideoPanel(BuildContext context, ctr, list) { - return ListView.builder( - controller: ctr!.scrollController, - addAutomaticKeepAlives: false, - addRepaintBoundaries: false, - itemCount: list!.length, - itemBuilder: (context, index) { - var i = list![index]; - return VideoCardH(videoItem: i); - }, - ); +class SearchVideoPanel extends StatelessWidget { + SearchVideoPanel({ + this.ctr, + this.list, + Key? key, + }) : super(key: key); + + final SearchPanelController? ctr; + final List? list; + + final VideoPanelController controller = Get.put(VideoPanelController()); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.topCenter, + children: [ + Padding( + padding: const EdgeInsets.only(top: 34), + child: ListView.builder( + controller: ctr!.scrollController, + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: list!.length, + itemBuilder: (context, index) { + var i = list![index]; + return Padding( + padding: index == 0 + ? const EdgeInsets.only(top: 2) + : EdgeInsets.zero, + child: VideoCardH(videoItem: i), + ); + }, + ), + ), + // 分类筛选 + Container( + width: double.infinity, + height: 34, + padding: const EdgeInsets.only(left: 8, top: 0, right: 12), + // decoration: BoxDecoration( + // border: Border( + // bottom: BorderSide( + // color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + // ), + // ), + // ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Obx( + () => Wrap( + // spacing: , + children: [ + for (var i in controller.filterList) ...[ + CustomFilterChip( + label: i['label'], + type: i['type'], + selectedType: controller.selectedType.value, + callFn: (bool selected) async { + controller.selectedType.value = i['type']; + ctr!.order.value = i['type'].toString().split('.').last; + SmartDialog.showLoading(msg: 'loooad'); + await ctr!.onRefresh(); + SmartDialog.dismiss(); + }, + ), + ] + ], + ), + ), + ), + ), // 放置在ListView.builder()上方的组件 + ], + ); + } +} + +class CustomFilterChip extends StatelessWidget { + const CustomFilterChip({ + this.label, + this.type, + this.selectedType, + this.callFn, + Key? key, + }) : super(key: key); + + final String? label; + final ArchiveFilterType? type; + final ArchiveFilterType? selectedType; + final Function? callFn; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 32, + child: FilterChip( + padding: const EdgeInsets.only(left: 11, right: 11), + labelPadding: EdgeInsets.zero, + label: Text( + label!, + style: const TextStyle(fontSize: 13), + ), + labelStyle: TextStyle( + color: type == selectedType + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline), + selected: type == selectedType, + showCheckmark: false, + shape: ContinuousRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + selectedColor: Colors.transparent, + // backgroundColor: + // Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), + backgroundColor: Colors.transparent, + side: BorderSide.none, + onSelected: (bool selected) => callFn!(selected), + ), + ); + } +} + +class VideoPanelController extends GetxController { + RxList filterList = [{}].obs; + Rx selectedType = ArchiveFilterType.values.first.obs; + + @override + void onInit() { + List> list = ArchiveFilterType.values + .map((type) => { + 'label': type.description, + 'type': type, + }) + .toList(); + filterList.value = list; + super.onInit(); + } + + onSelect() {} }