From 7ad6b25abe6a7dc9b14cb8b482511b030b2ab5fb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Sep 2023 00:34:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20UP=E4=B8=BB=E6=8A=95=E7=A8=BF=E6=90=9C?= =?UTF-8?q?=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()),