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 'dart:convert';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:PiliPalaX/http/loading_state.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import '../models/bangumi/info.dart';
|
import '../models/bangumi/info.dart';
|
||||||
import '../models/common/search_type.dart';
|
import '../models/common/search_type.dart';
|
||||||
@@ -68,7 +69,7 @@ class SearchHttp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 分类搜索
|
// 分类搜索
|
||||||
static Future searchByType({
|
static Future<LoadingState> searchByType({
|
||||||
required SearchType searchType,
|
required SearchType searchType,
|
||||||
required String keyword,
|
required String keyword,
|
||||||
required page,
|
required page,
|
||||||
@@ -86,14 +87,12 @@ class SearchHttp {
|
|||||||
};
|
};
|
||||||
var res = await Request().get(Api.searchByType, data: reqData);
|
var res = await Request().get(Api.searchByType, data: reqData);
|
||||||
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
|
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
|
||||||
Object data;
|
dynamic data;
|
||||||
try {
|
try {
|
||||||
switch (searchType) {
|
switch (searchType) {
|
||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
List<int> blackMidsList = localCache
|
List<int> blackMidsList = localCache
|
||||||
.get(LocalCacheKey.blackMidsList, defaultValue: [-1])
|
.get(LocalCacheKey.blackMidsList, defaultValue: <int>[]);
|
||||||
.map<int>((i) => i as int)
|
|
||||||
.toList();
|
|
||||||
for (var i in res.data['data']['result']) {
|
for (var i in res.data['data']['result']) {
|
||||||
// 屏蔽推广和拉黑用户
|
// 屏蔽推广和拉黑用户
|
||||||
i['available'] = !blackMidsList.contains(i['mid']);
|
i['available'] = !blackMidsList.contains(i['mid']);
|
||||||
@@ -113,21 +112,20 @@ class SearchHttp {
|
|||||||
data = SearchArticleModel.fromJson(res.data['data']);
|
data = SearchArticleModel.fromJson(res.data['data']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return {
|
if (data.list.isNotEmpty) {
|
||||||
'status': true,
|
return LoadingState.success(data.list);
|
||||||
'data': data,
|
} else {
|
||||||
};
|
return LoadingState.empty();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
print(err);
|
||||||
|
return LoadingState.error(err.toString());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return LoadingState.error(
|
||||||
'status': false,
|
res.data['data'] != null && res.data['data']['numPages'] == 0
|
||||||
'data': [],
|
? '没有相关数据'
|
||||||
'msg': res.data['data'] != null && res.data['data']['numPages'] == 0
|
: res.data['message']);
|
||||||
? '没有相关数据'
|
|
||||||
: res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +1,32 @@
|
|||||||
import 'package:PiliPalaX/utils/extension.dart';
|
import 'package:PiliPalaX/http/loading_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:PiliPalaX/pages/common/common_controller.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPalaX/http/search.dart';
|
import 'package:PiliPalaX/http/search.dart';
|
||||||
import 'package:PiliPalaX/models/common/search_type.dart';
|
import 'package:PiliPalaX/models/common/search_type.dart';
|
||||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
import 'package:PiliPalaX/utils/id_utils.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
|
|
||||||
class SearchPanelController extends GetxController {
|
class SearchPanelController extends CommonController {
|
||||||
SearchPanelController({this.keyword, this.searchType});
|
SearchPanelController({this.keyword, this.searchType});
|
||||||
ScrollController scrollController = ScrollController();
|
|
||||||
String? keyword;
|
String? keyword;
|
||||||
SearchType? searchType;
|
SearchType? searchType;
|
||||||
RxInt page = 1.obs;
|
|
||||||
RxList resultList = [].obs;
|
|
||||||
// 结果排序方式 搜索类型为视频、专栏及相簿时
|
// 结果排序方式 搜索类型为视频、专栏及相簿时
|
||||||
RxString order = ''.obs;
|
RxString order = ''.obs;
|
||||||
// 视频时长筛选 仅用于搜索视频
|
// 视频时长筛选 仅用于搜索视频
|
||||||
RxInt duration = 0.obs;
|
RxInt duration = 0.obs;
|
||||||
|
|
||||||
Future onSearch({type = 'init'}) async {
|
@override
|
||||||
var result = await SearchHttp.searchByType(
|
void onInit() {
|
||||||
searchType: searchType!,
|
super.onInit();
|
||||||
keyword: keyword!,
|
queryData();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future onRefresh() async {
|
@override
|
||||||
page.value = 1;
|
void handleSuccess(List currentList, List dataList) {
|
||||||
await onSearch(type: 'onRefresh');
|
onPushDetail(dataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回顶部并刷新
|
void onPushDetail(resultList) async {
|
||||||
void animateToTop() {
|
|
||||||
scrollController.animToTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onPushDetail(keyword, resultList) async {
|
|
||||||
// 匹配输入内容,如果是AV、BV号且有结果 直接跳转详情页
|
// 匹配输入内容,如果是AV、BV号且有结果 直接跳转详情页
|
||||||
Map matchRes = IdUtils.matchAvorBv(input: keyword);
|
Map matchRes = IdUtils.matchAvorBv(input: keyword);
|
||||||
List matchKeys = matchRes.keys.toList();
|
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:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -31,8 +32,6 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
late SearchPanelController _searchPanelController;
|
late SearchPanelController _searchPanelController;
|
||||||
|
|
||||||
late Future _futureBuilderFuture;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
@@ -51,17 +50,15 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
_searchPanelController.scrollController.position.maxScrollExtent -
|
_searchPanelController.scrollController.position.maxScrollExtent -
|
||||||
100) {
|
100) {
|
||||||
EasyThrottle.throttle('history', const Duration(seconds: 1), () {
|
EasyThrottle.throttle('history', const Duration(seconds: 1), () {
|
||||||
_searchPanelController.onSearch(type: 'onLoad');
|
_searchPanelController.onLoadMore();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_futureBuilderFuture = _searchPanelController.onSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_searchPanelController.scrollController.removeListener(() {});
|
_searchPanelController.scrollController.removeListener(() {});
|
||||||
_searchPanelController.scrollController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,101 +69,86 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await _searchPanelController.onRefresh();
|
await _searchPanelController.onRefresh();
|
||||||
},
|
},
|
||||||
child: FutureBuilder(
|
child: Obx(() => _buildBody(_searchPanelController.loadingState.value)),
|
||||||
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,
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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