mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
refactor: search panel
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:PiliPalaX/http/loading_state.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import '../models/bangumi/info.dart';
|
||||
import '../models/common/search_type.dart';
|
||||
@@ -68,7 +69,7 @@ class SearchHttp {
|
||||
}
|
||||
|
||||
// 分类搜索
|
||||
static Future searchByType({
|
||||
static Future<LoadingState> searchByType({
|
||||
required SearchType searchType,
|
||||
required String keyword,
|
||||
required page,
|
||||
@@ -86,14 +87,12 @@ class SearchHttp {
|
||||
};
|
||||
var res = await Request().get(Api.searchByType, data: reqData);
|
||||
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
|
||||
Object data;
|
||||
dynamic data;
|
||||
try {
|
||||
switch (searchType) {
|
||||
case SearchType.video:
|
||||
List<int> blackMidsList = localCache
|
||||
.get(LocalCacheKey.blackMidsList, defaultValue: [-1])
|
||||
.map<int>((i) => i as int)
|
||||
.toList();
|
||||
.get(LocalCacheKey.blackMidsList, defaultValue: <int>[]);
|
||||
for (var i in res.data['data']['result']) {
|
||||
// 屏蔽推广和拉黑用户
|
||||
i['available'] = !blackMidsList.contains(i['mid']);
|
||||
@@ -113,21 +112,20 @@ class SearchHttp {
|
||||
data = SearchArticleModel.fromJson(res.data['data']);
|
||||
break;
|
||||
}
|
||||
return {
|
||||
'status': true,
|
||||
'data': data,
|
||||
};
|
||||
if (data.list.isNotEmpty) {
|
||||
return LoadingState.success(data.list);
|
||||
} else {
|
||||
return LoadingState.empty();
|
||||
}
|
||||
} catch (err) {
|
||||
print(err);
|
||||
return LoadingState.error(err.toString());
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
'data': [],
|
||||
'msg': res.data['data'] != null && res.data['data']['numPages'] == 0
|
||||
? '没有相关数据'
|
||||
: res.data['message'],
|
||||
};
|
||||
return LoadingState.error(
|
||||
res.data['data'] != null && res.data['data']['numPages'] == 0
|
||||
? '没有相关数据'
|
||||
: res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,53 +1,32 @@
|
||||
import 'package:PiliPalaX/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:PiliPalaX/http/loading_state.dart';
|
||||
import 'package:PiliPalaX/pages/common/common_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPalaX/http/search.dart';
|
||||
import 'package:PiliPalaX/models/common/search_type.dart';
|
||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
||||
import 'package:PiliPalaX/utils/utils.dart';
|
||||
|
||||
class SearchPanelController extends GetxController {
|
||||
class SearchPanelController extends CommonController {
|
||||
SearchPanelController({this.keyword, this.searchType});
|
||||
ScrollController scrollController = ScrollController();
|
||||
String? keyword;
|
||||
SearchType? searchType;
|
||||
RxInt page = 1.obs;
|
||||
RxList resultList = [].obs;
|
||||
// 结果排序方式 搜索类型为视频、专栏及相簿时
|
||||
RxString order = ''.obs;
|
||||
// 视频时长筛选 仅用于搜索视频
|
||||
RxInt duration = 0.obs;
|
||||
|
||||
Future onSearch({type = 'init'}) async {
|
||||
var result = await SearchHttp.searchByType(
|
||||
searchType: searchType!,
|
||||
keyword: keyword!,
|
||||
page: page.value,
|
||||
order: searchType!.type != 'video' ? null : order.value,
|
||||
duration: searchType!.type != 'video' ? null : duration.value);
|
||||
if (result['status']) {
|
||||
if (type == 'onRefresh') {
|
||||
resultList.value = result['data'].list;
|
||||
} else {
|
||||
resultList.addAll(result['data'].list);
|
||||
}
|
||||
page.value++;
|
||||
onPushDetail(keyword, resultList);
|
||||
}
|
||||
return result;
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
Future onRefresh() async {
|
||||
page.value = 1;
|
||||
await onSearch(type: 'onRefresh');
|
||||
@override
|
||||
void handleSuccess(List currentList, List dataList) {
|
||||
onPushDetail(dataList);
|
||||
}
|
||||
|
||||
// 返回顶部并刷新
|
||||
void animateToTop() {
|
||||
scrollController.animToTop();
|
||||
}
|
||||
|
||||
void onPushDetail(keyword, resultList) async {
|
||||
void onPushDetail(resultList) async {
|
||||
// 匹配输入内容,如果是AV、BV号且有结果 直接跳转详情页
|
||||
Map matchRes = IdUtils.matchAvorBv(input: keyword);
|
||||
List matchKeys = matchRes.keys.toList();
|
||||
@@ -82,4 +61,13 @@ class SearchPanelController extends GetxController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => SearchHttp.searchByType(
|
||||
searchType: searchType!,
|
||||
keyword: keyword!,
|
||||
page: currentPage,
|
||||
order: searchType!.type != 'video' ? null : order.value,
|
||||
duration: searchType!.type != 'video' ? null : duration.value,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPalaX/http/loading_state.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -31,8 +32,6 @@ class _SearchPanelState extends State<SearchPanel>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late SearchPanelController _searchPanelController;
|
||||
|
||||
late Future _futureBuilderFuture;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@@ -51,17 +50,15 @@ class _SearchPanelState extends State<SearchPanel>
|
||||
_searchPanelController.scrollController.position.maxScrollExtent -
|
||||
100) {
|
||||
EasyThrottle.throttle('history', const Duration(seconds: 1), () {
|
||||
_searchPanelController.onSearch(type: 'onLoad');
|
||||
_searchPanelController.onLoadMore();
|
||||
});
|
||||
}
|
||||
});
|
||||
_futureBuilderFuture = _searchPanelController.onSearch();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchPanelController.scrollController.removeListener(() {});
|
||||
_searchPanelController.scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -72,101 +69,86 @@ class _SearchPanelState extends State<SearchPanel>
|
||||
onRefresh: () async {
|
||||
await _searchPanelController.onRefresh();
|
||||
},
|
||||
child: FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data != null) {
|
||||
Map data = snapshot.data;
|
||||
var ctr = _searchPanelController;
|
||||
RxList list = ctr.resultList;
|
||||
if (data['status']) {
|
||||
return Obx(() {
|
||||
list.length;
|
||||
switch (widget.searchType) {
|
||||
case SearchType.video:
|
||||
return SearchVideoPanel(
|
||||
ctr: _searchPanelController,
|
||||
// ignore: invalid_use_of_protected_member
|
||||
list: list.value,
|
||||
);
|
||||
case SearchType.media_bangumi:
|
||||
return searchBangumiPanel(context, ctr, list);
|
||||
case SearchType.bili_user:
|
||||
return searchUserPanel(context, ctr, list);
|
||||
case SearchType.live_room:
|
||||
return searchLivePanel(context, ctr, list);
|
||||
case SearchType.article:
|
||||
return searchArticlePanel(context, ctr, list);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return CustomScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () {
|
||||
setState(() {
|
||||
_futureBuilderFuture =
|
||||
_searchPanelController.onSearch();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return CustomScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: '没有相关数据',
|
||||
fn: () {
|
||||
setState(() {
|
||||
_futureBuilderFuture =
|
||||
_searchPanelController.onSearch();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||
childAspectRatio: StyleString.aspectRatio * 2.4,
|
||||
mainAxisExtent: 0),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
switch (widget.searchType) {
|
||||
case SearchType.video:
|
||||
return const VideoCardHSkeleton();
|
||||
case SearchType.media_bangumi:
|
||||
return const MediaBangumiSkeleton();
|
||||
case SearchType.bili_user:
|
||||
return const VideoCardHSkeleton();
|
||||
case SearchType.live_room:
|
||||
return const VideoCardHSkeleton();
|
||||
default:
|
||||
return const VideoCardHSkeleton();
|
||||
}
|
||||
},
|
||||
childCount: 15,
|
||||
))
|
||||
]);
|
||||
}
|
||||
},
|
||||
),
|
||||
child: Obx(() => _buildBody(_searchPanelController.loadingState.value)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
if (loadingState is Success) {
|
||||
switch (widget.searchType) {
|
||||
case SearchType.video:
|
||||
return SearchVideoPanel(
|
||||
ctr: _searchPanelController,
|
||||
list: loadingState.response,
|
||||
);
|
||||
case SearchType.media_bangumi:
|
||||
return searchBangumiPanel(
|
||||
context,
|
||||
_searchPanelController,
|
||||
loadingState.response,
|
||||
);
|
||||
case SearchType.bili_user:
|
||||
return searchUserPanel(
|
||||
context,
|
||||
_searchPanelController,
|
||||
loadingState.response,
|
||||
);
|
||||
case SearchType.live_room:
|
||||
return searchLivePanel(
|
||||
context,
|
||||
_searchPanelController,
|
||||
loadingState.response,
|
||||
);
|
||||
case SearchType.article:
|
||||
return searchArticlePanel(
|
||||
context,
|
||||
_searchPanelController,
|
||||
loadingState.response,
|
||||
);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
} else if (loadingState is Loading) {
|
||||
return CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||
childAspectRatio: StyleString.aspectRatio * 2.4,
|
||||
mainAxisExtent: 0),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
switch (widget.searchType) {
|
||||
case SearchType.video:
|
||||
return const VideoCardHSkeleton();
|
||||
case SearchType.media_bangumi:
|
||||
return const MediaBangumiSkeleton();
|
||||
case SearchType.bili_user:
|
||||
return const VideoCardHSkeleton();
|
||||
case SearchType.live_room:
|
||||
return const VideoCardHSkeleton();
|
||||
default:
|
||||
return const VideoCardHSkeleton();
|
||||
}
|
||||
},
|
||||
childCount: 15,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return CustomScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: loadingState is Error ? loadingState.errMsg : '没有相关数据',
|
||||
fn: _searchPanelController.onReload,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user