From f79e4765c2c43f8a4261f58ff494bdcca0aad0df Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 23 Sep 2023 00:48:22 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20UP=E4=B8=BB=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/archive/controller.dart | 20 ++++++- lib/pages/member/archive/view.dart | 72 +++++++++++++++++++++++- pubspec.lock | 8 +-- pubspec.yaml | 2 +- 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/lib/pages/member/archive/controller.dart b/lib/pages/member/archive/controller.dart index e893a07d..9a15d4fc 100644 --- a/lib/pages/member/archive/controller.dart +++ b/lib/pages/member/archive/controller.dart @@ -5,20 +5,38 @@ class ArchiveController extends GetxController { int? mid; int pn = 1; int count = 0; + RxMap currentOrder = {}.obs; + List> orderList = [ + {'type': 'pubdate', 'label': '最新发布'}, + {'type': 'click', 'label': '最多播放'}, + {'type': 'stow', 'label': '最多收藏'}, + ]; @override void onInit() { super.onInit(); mid = int.parse(Get.parameters['mid']!); + currentOrder.value = orderList.first; } // 获取用户投稿 Future getMemberArchive() async { - var res = await MemberHttp.memberArchive(mid: mid, pn: pn); + var res = await MemberHttp.memberArchive( + mid: mid, pn: pn, order: currentOrder['type']!); if (res['status']) { count = res['data'].page['count']; pn += 1; } return res; } + + toggleSort() async { + pn = 1; + int index = orderList.indexOf(currentOrder.value); + if (index == orderList.length - 1) { + currentOrder.value = orderList.first; + } else { + currentOrder.value = orderList[index + 1]; + } + } } diff --git a/lib/pages/member/archive/view.dart b/lib/pages/member/archive/view.dart index 430f5ede..f387a557 100644 --- a/lib/pages/member/archive/view.dart +++ b/lib/pages/member/archive/view.dart @@ -18,6 +18,8 @@ class _ArchivePanelState extends State with AutomaticKeepAliveClientMixin { DateTime lastRefreshTime = DateTime.now(); late final LoadMoreListSource source = LoadMoreListSource(); + final ArchiveController _archiveController = + Get.put(ArchiveController(), tag: Get.arguments['heroTag']); @override bool get wantKeepAlive => true; @@ -40,14 +42,63 @@ class _ArchivePanelState extends State // return PullToRefreshHeader(info, lastRefreshTime); // }, // ), - const SizedBox(height: 4), + Padding( + padding: + const EdgeInsets.only(left: 14, top: 8, bottom: 8, right: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('排序方式'), + SizedBox( + height: 35, + width: 85, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + // _archiveController.order = 'click'; + // _archiveController.pn = 1; + _archiveController.toggleSort(); + source.refresh(true); + // LoadMoreListSource().loadData(); + }, + child: Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Text( + _archiveController.currentOrder['label']!, + key: ValueKey( + _archiveController.currentOrder['label']!), + ), + ), + ), + ), + ), + ], + ), + ), Expanded( child: LoadingMoreList( ListConfig( sourceList: source, itemBuilder: (BuildContext c, VListItemModel item, int index) { - return VideoCardH(videoItem: item); + if (index == 0) { + return Column( + children: [ + const SizedBox(height: 6), + VideoCardH(videoItem: item) + ], + ); + } else { + return VideoCardH(videoItem: item); + } }, indicatorBuilder: _buildIndicator, ), @@ -144,12 +195,16 @@ class _ArchivePanelState extends State class LoadMoreListSource extends LoadingMoreBase { final ArchiveController _archiveController = Get.put(ArchiveController(), tag: Get.arguments['heroTag']); + bool forceRefresh = false; @override Future loadData([bool isloadMoreAction = false]) async { bool isSuccess = false; var res = await _archiveController.getMemberArchive(); if (res['status']) { + if (_archiveController.pn == 2) { + clear(); + } addAll(res['data'].list.vlist); } if (length < res['data'].page['count']) { @@ -159,4 +214,17 @@ class LoadMoreListSource extends LoadingMoreBase { } return isSuccess; } + + @override + Future refresh([bool clearBeforeRequest = false]) async { + // _hasMore = true; + // pageindex = 1; + // //force to refresh list when you don't want clear list before request + // //for the case, if your list already has 20 items. + forceRefresh = !clearBeforeRequest; + var result = await super.refresh(clearBeforeRequest); + + forceRefresh = false; + return result; + } } diff --git a/pubspec.lock b/pubspec.lock index a80f40cb..c1af42ef 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -721,18 +721,18 @@ packages: dependency: "direct main" description: name: loading_more_list - sha256: aa680edc81cf024c394dccfa7ba1db701a5efb23ec1b8c657308428ce9da11d1 + sha256: "6b49eb935345d6cf291e0367d3c238ef0a525a08b671ee41e09ee67d41888a7a" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "6.0.0" loading_more_list_library: dependency: transitive description: name: loading_more_list_library - sha256: "31348925a98748ffe04f661e4b47df37103fabad39442064fcf59148a5fee2dd" + sha256: de6b57edbab83022180f053ec3f598dd5e1192cfd6a285882b8155e3cb5dc581 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" logging: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 22d91b26..e6497778 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,7 +70,7 @@ dependencies: # 解决sliver滑动不同步 extended_nested_scroll_view: ^6.1.2 # 上拉加载 - loading_more_list: ^5.0.3 + loading_more_list: ^6.0.0 # 下拉刷新 pull_to_refresh_notification: ^3.0.1 # 图标 From 7ad6b25abe6a7dc9b14cb8b482511b030b2ab5fb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Sep 2023 00:34:20 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20UP=E4=B8=BB=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/member.dart | 27 +++- lib/pages/member/view.dart | 5 + lib/pages/member_search/controller.dart | 90 +++++++++++ lib/pages/member_search/index.dart | 4 + lib/pages/member_search/view.dart | 195 ++++++++++++++++++++++++ lib/router/app_pages.dart | 2 + 7 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 lib/pages/member_search/controller.dart create mode 100644 lib/pages/member_search/index.dart create mode 100644 lib/pages/member_search/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index ef9fe94a..288f0b2b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -245,6 +245,9 @@ class Api { // wts=1689767832 static const String memberArchive = '/x/space/wbi/arc/search'; + // 用户动态搜索 + static const String memberDynamicSearch = '/x/space/dynamic/search'; + // 用户动态 static const String memberDynamic = '/x/polymer/web-dynamic/v1/feed/space'; diff --git a/lib/http/member.dart b/lib/http/member.dart index 5cf3f1ee..a9c158da 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -66,7 +66,7 @@ class MemberHttp { int ps = 30, int tid = 0, int? pn, - String keyword = '', + String? keyword, String order = 'pubdate', bool orderAvoided = true, }) async { @@ -75,7 +75,7 @@ class MemberHttp { 'ps': ps, 'tid': tid, 'pn': pn, - 'keyword': keyword, + 'keyword': keyword ?? '', 'order': order, 'platform': 'web', 'web_location': 1550101, @@ -121,4 +121,27 @@ class MemberHttp { }; } } + + // 搜索用户动态 + static Future memberDynamicSearch({int? pn, int? ps, int? mid}) async { + var res = await Request().get(Api.memberDynamic, data: { + 'keyword': '海拔', + 'mid': mid, + 'pn': pn, + 'ps': ps, + 'platform': 'web' + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': DynamicsDataModel.fromJson(res.data['data']), + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index a29bd545..66db7ec8 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -109,6 +109,11 @@ class _MemberPageState extends State }, ), actions: [ + IconButton( + onPressed: () => Get.toNamed( + '/memberSearch?mid=${Get.parameters['mid']}&uname=${_memberController.memberInfo.value.name!}'), + icon: const Icon(Icons.search_outlined), + ), PopupMenuButton( icon: const Icon(Icons.more_vert), itemBuilder: (BuildContext context) => [ diff --git a/lib/pages/member_search/controller.dart b/lib/pages/member_search/controller.dart new file mode 100644 index 00000000..be4f5b1a --- /dev/null +++ b/lib/pages/member_search/controller.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/member.dart'; +import 'package:pilipala/models/member/archive.dart'; + +class MemberSearchController extends GetxController { + final ScrollController scrollController = ScrollController(); + Rx controller = TextEditingController().obs; + final FocusNode searchFocusNode = FocusNode(); + RxString searchKeyWord = ''.obs; + String hintText = '搜索'; + RxString loadingStatus = 'init'.obs; + RxString loadingText = '加载中...'.obs; + bool hasRequest = false; + late int mid; + RxString uname = ''.obs; + int archivePn = 1; + int archiveCount = 0; + RxList archiveList = [].obs; + int dynamic_pn = 1; + RxList dynamicList = [].obs; + + int ps = 30; + + @override + void onInit() { + super.onInit(); + mid = int.parse(Get.parameters['mid']!); + uname.value = Get.parameters['uname']!; + } + + // 清空搜索 + void onClear() { + if (searchKeyWord.value.isNotEmpty && controller.value.text != '') { + controller.value.clear(); + searchKeyWord.value = ''; + } else { + Get.back(); + } + } + + void onChange(value) { + searchKeyWord.value = value; + } + + // 提交搜索内容 + void submit() { + loadingStatus.value = 'loading'; + if (hasRequest) { + archivePn = 1; + searchArchives(); + } + } + + // 搜索视频 + Future searchArchives({type = 'init'}) async { + if (type == 'onLoad' && loadingText.value == '没有更多了') { + return; + } + var res = await MemberHttp.memberArchive( + mid: mid, + pn: archivePn, + keyword: controller.value.text, + order: 'pubdate', + ); + if (res['status']) { + if (type == 'init' || archivePn == 1) { + archiveList.value = res['data'].list.vlist; + } else { + archiveList.addAll(res['data'].list.vlist); + } + archiveCount = res['data'].page['count']; + if (archiveList.length == archiveCount) { + loadingText.value = '没有更多了'; + } + archivePn += 1; + hasRequest = true; + } + // loadingStatus.value = 'finish'; + return res; + } + + // 搜索动态 + Future searchDynamic() async {} + + // + onLoad() { + searchArchives(type: 'onLoad'); + } +} diff --git a/lib/pages/member_search/index.dart b/lib/pages/member_search/index.dart new file mode 100644 index 00000000..4f10617b --- /dev/null +++ b/lib/pages/member_search/index.dart @@ -0,0 +1,4 @@ +library member_search; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/member_search/view.dart b/lib/pages/member_search/view.dart new file mode 100644 index 00000000..ff0553a2 --- /dev/null +++ b/lib/pages/member_search/view.dart @@ -0,0 +1,195 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; +import 'package:pilipala/common/widgets/video_card_h.dart'; + +import 'controller.dart'; + +class MemberSearchPage extends StatefulWidget { + const MemberSearchPage({super.key}); + + @override + State createState() => _MemberSearchPageState(); +} + +class _MemberSearchPageState extends State + with SingleTickerProviderStateMixin { + final MemberSearchController _memberSearchCtr = + Get.put(MemberSearchController()); + late ScrollController scrollController; + + @override + void initState() { + super.initState(); + scrollController = _memberSearchCtr.scrollController; + scrollController.addListener( + () { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 300) { + EasyThrottle.throttle('history', const Duration(seconds: 1), () { + _memberSearchCtr.onLoad(); + }); + } + }, + ); + // _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + // _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + titleSpacing: 0, + actions: [ + IconButton( + onPressed: () => _memberSearchCtr.submit(), + icon: const Icon(CupertinoIcons.search, size: 22)), + const SizedBox(width: 10) + ], + title: Obx( + () => TextField( + autofocus: true, + focusNode: _memberSearchCtr.searchFocusNode, + controller: _memberSearchCtr.controller.value, + textInputAction: TextInputAction.search, + onChanged: (value) => _memberSearchCtr.onChange(value), + decoration: InputDecoration( + hintText: _memberSearchCtr.hintText, + border: InputBorder.none, + suffixIcon: IconButton( + icon: Icon( + Icons.clear, + size: 22, + color: Theme.of(context).colorScheme.outline, + ), + onPressed: () => _memberSearchCtr.onClear(), + ), + ), + onSubmitted: (String value) => _memberSearchCtr.submit(), + ), + ), + ), + body: Obx( + () => Column( + children: _memberSearchCtr.loadingStatus.value == 'init' + ? [ + Expanded( + child: Center( + child: Text('搜索「${_memberSearchCtr.uname.value}」的动态、视频'), + ), + ), + ] + : [ + // TabBar( + // controller: _tabController, + // tabs: const [ + // Tab(text: "视频"), + // Tab(text: "动态"), + // ], + // ), + Expanded( + child: + // TabBarView( + // controller: _tabController, + // children: [ + FutureBuilder( + future: _memberSearchCtr.searchArchives(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () => _memberSearchCtr.archiveList.isNotEmpty + ? ListView.builder( + controller: scrollController, + itemCount: + _memberSearchCtr.archiveList.length + + 1, + itemBuilder: (context, index) { + if (index == + _memberSearchCtr + .archiveList.length) { + return Container( + height: MediaQuery.of(context) + .padding + .bottom + + 60, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context) + .padding + .bottom), + child: Center( + child: Obx( + () => Text( + _memberSearchCtr + .loadingText.value, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .outline, + fontSize: 13), + ), + ), + ), + ); + } else { + return VideoCardH( + videoItem: _memberSearchCtr + .archiveList[index]); + } + }, + ) + : _memberSearchCtr.loadingStatus.value == + 'loading' + ? ListView.builder( + itemCount: 10, + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + ) + : const CustomScrollView( + slivers: [ + NoData(), + ], + ), + ); + } else { + return CustomScrollView( + slivers: [ + HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ) + ], + ); + } + } else { + // 骨架屏 + return ListView.builder( + itemCount: 10, + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + ); + } + }, + ), + // ], + // ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index fd6db614..4b90fcd1 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -18,6 +18,7 @@ import 'package:pilipala/pages/html/index.dart'; import 'package:pilipala/pages/later/index.dart'; import 'package:pilipala/pages/liveRoom/view.dart'; import 'package:pilipala/pages/member/index.dart'; +import 'package:pilipala/pages/member_search/index.dart'; import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/searchResult/index.dart'; @@ -86,6 +87,7 @@ class Routes { CustomGetPage(name: '/liveRoom', page: () => const LiveRoomPage()), // 用户中心 CustomGetPage(name: '/member', page: () => const MemberPage()), + CustomGetPage(name: '/memberSearch', page: () => const MemberSearchPage()), // 二级回复 CustomGetPage( name: '/replyReply', page: () => const VideoReplyReplyPanel()),