mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-30 23:58:13 +08:00
feat: search all
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:PiliPlus/models/search/result.dart';
|
|||||||
import 'package:PiliPlus/utils/page_utils.dart';
|
import 'package:PiliPlus/utils/page_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import '../../http/search.dart';
|
import '../../http/search.dart';
|
||||||
import '../../utils/utils.dart';
|
import '../../utils/utils.dart';
|
||||||
import '../constants.dart';
|
import '../constants.dart';
|
||||||
@@ -77,6 +78,17 @@ class VideoCardH extends StatelessWidget {
|
|||||||
if (type == 'ketang') {
|
if (type == 'ketang') {
|
||||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||||
return;
|
return;
|
||||||
|
} else if (type == 'live_room') {
|
||||||
|
if (videoItem is SearchVideoItemModel) {
|
||||||
|
int? roomId = (videoItem as SearchVideoItemModel).id;
|
||||||
|
if (roomId != null) {
|
||||||
|
Get.toNamed('/liveRoom?roomid=$roomId');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(
|
||||||
|
'err: live_room : ${videoItem.runtimeType}');
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if ((videoItem is HotVideoItemModel) &&
|
if ((videoItem is HotVideoItemModel) &&
|
||||||
(videoItem as HotVideoItemModel).redirectUrl?.isNotEmpty ==
|
(videoItem as HotVideoItemModel).redirectUrl?.isNotEmpty ==
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ class SearchHttp {
|
|||||||
case SearchType.article:
|
case SearchType.article:
|
||||||
data = SearchArticleModel.fromJson(res.data['data']);
|
data = SearchArticleModel.fromJson(res.data['data']);
|
||||||
break;
|
break;
|
||||||
// case SearchType.all:
|
case SearchType.all:
|
||||||
// break;
|
break;
|
||||||
}
|
}
|
||||||
return LoadingState.success(data);
|
return LoadingState.success(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -115,7 +115,7 @@ class SearchHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LoadingState> searchAll({
|
static Future<LoadingState<SearchAllModel>> searchAll({
|
||||||
required String keyword,
|
required String keyword,
|
||||||
required page,
|
required page,
|
||||||
String? order,
|
String? order,
|
||||||
@@ -140,17 +140,15 @@ class SearchHttp {
|
|||||||
if (pubEnd != null) 'pubtime_end_s': pubEnd,
|
if (pubEnd != null) 'pubtime_end_s': pubEnd,
|
||||||
};
|
};
|
||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.searchByType,
|
Api.searchAll,
|
||||||
queryParameters: params,
|
queryParameters: params,
|
||||||
);
|
);
|
||||||
if (res.data is! Map) {
|
if (res.data is! Map) {
|
||||||
return LoadingState.error('没有相关数据');
|
return LoadingState.error('没有相关数据');
|
||||||
}
|
}
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
dynamic data;
|
|
||||||
try {
|
try {
|
||||||
// TODO
|
return LoadingState.success(SearchAllModel.fromJson(res.data['data']));
|
||||||
return LoadingState.success(data);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint(err.toString());
|
debugPrint(err.toString());
|
||||||
return LoadingState.error(err.toString());
|
return LoadingState.error(err.toString());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// ignore_for_file: constant_identifier_names
|
// ignore_for_file: constant_identifier_names
|
||||||
enum SearchType {
|
enum SearchType {
|
||||||
// all,
|
all,
|
||||||
// 视频:video
|
// 视频:video
|
||||||
video,
|
video,
|
||||||
// 番剧:media_bangumi,
|
// 番剧:media_bangumi,
|
||||||
@@ -25,7 +25,7 @@ enum SearchType {
|
|||||||
|
|
||||||
extension SearchTypeExtension on SearchType {
|
extension SearchTypeExtension on SearchType {
|
||||||
String get label => [
|
String get label => [
|
||||||
// '综合',
|
'综合',
|
||||||
'视频',
|
'视频',
|
||||||
'番剧',
|
'番剧',
|
||||||
'影视',
|
'影视',
|
||||||
|
|||||||
@@ -14,6 +14,46 @@ abstract class SearchNumData<T> {
|
|||||||
List<T>? list;
|
List<T>? list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SearchAllModel extends SearchNumData {
|
||||||
|
SearchAllModel({
|
||||||
|
super.numResults,
|
||||||
|
super.list,
|
||||||
|
});
|
||||||
|
|
||||||
|
SearchAllModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
numResults = (json['numResults'] as num?)?.toInt();
|
||||||
|
if ((json['result'] as List?)?.isNotEmpty == true) {
|
||||||
|
final isRefresh = json['page'] == 1;
|
||||||
|
list = [];
|
||||||
|
for (final item in json['result']) {
|
||||||
|
if ((item['data'] as List?)?.isNotEmpty == true) {
|
||||||
|
switch (item['result_type']) {
|
||||||
|
case 'media_bangumi' || 'media_bangumi':
|
||||||
|
if (isRefresh) {
|
||||||
|
list!.add((item['data'] as List)
|
||||||
|
.map((e) => SearchMBangumiItemModel.fromJson(e))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'bili_user':
|
||||||
|
if (isRefresh) {
|
||||||
|
list!.addAll((item['data'] as List)
|
||||||
|
.map((e) => SearchUserItemModel.fromJson(e))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
list!.addAll((item['data'] as List)
|
||||||
|
.map((e) => SearchVideoItemModel.fromJson(e))
|
||||||
|
.toList());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SearchVideoModel extends SearchNumData<SearchVideoItemModel> {
|
class SearchVideoModel extends SearchNumData<SearchVideoItemModel> {
|
||||||
SearchVideoModel({
|
SearchVideoModel({
|
||||||
super.numResults,
|
super.numResults,
|
||||||
|
|||||||
@@ -141,8 +141,12 @@ class _IntroDetailState extends CommonCollapseSlidePageState<IntroDetail> {
|
|||||||
(item) => SearchText(
|
(item) => SearchText(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
text: item['tag_name'],
|
text: item['tag_name'],
|
||||||
onTap: (_) => Get.toNamed('/searchResult',
|
onTap: (_) => Get.toNamed(
|
||||||
parameters: {'keyword': item['tag_name']}),
|
'/searchResult',
|
||||||
|
parameters: {
|
||||||
|
'keyword': item['tag_name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
onLongPress: (_) => Utils.copyText(item['tag_name']),
|
onLongPress: (_) => Utils.copyText(item['tag_name']),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
87
lib/pages/bangumi/widgets/bangumi_card_v_search.dart
Normal file
87
lib/pages/bangumi/widgets/bangumi_card_v_search.dart
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||||
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
|
import 'package:PiliPlus/utils/page_utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
|
// 视频卡片 - 垂直布局
|
||||||
|
class BangumiCardVSearch extends StatelessWidget {
|
||||||
|
const BangumiCardVSearch({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SearchMBangumiItemModel item;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: InkWell(
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context: context,
|
||||||
|
title: item.title?.map((e) => e['text']).join(),
|
||||||
|
cover: item.cover,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
PageUtils.viewBangumi(seasonId: item.seasonId);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: StyleString.mdRadius,
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 0.75,
|
||||||
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
src: item.cover,
|
||||||
|
width: maxWidth,
|
||||||
|
height: maxHeight,
|
||||||
|
),
|
||||||
|
PBadge(
|
||||||
|
text: item.seasonTypeName,
|
||||||
|
right: 6,
|
||||||
|
top: 6,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bagumiContent(context)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget bagumiContent(context) {
|
||||||
|
return Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.title!.map((e) => e['text']).join(),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: const TextStyle(
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,7 +117,10 @@ class SSearchController extends GetxController {
|
|||||||
parameters: {
|
parameters: {
|
||||||
'keyword': controller.text,
|
'keyword': controller.text,
|
||||||
},
|
},
|
||||||
arguments: initIndex,
|
arguments: {
|
||||||
|
'initIndex': initIndex,
|
||||||
|
'fromSearch': true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
searchFocusNode.requestFocus();
|
searchFocusNode.requestFocus();
|
||||||
}
|
}
|
||||||
|
|||||||
77
lib/pages/search_panel/all/controller.dart
Normal file
77
lib/pages/search_panel/all/controller.dart
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/search.dart';
|
||||||
|
import 'package:PiliPlus/models/common/search_type.dart';
|
||||||
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||||
|
|
||||||
|
class SearchAllController
|
||||||
|
extends SearchPanelController<SearchAllModel, dynamic> {
|
||||||
|
SearchAllController({
|
||||||
|
required super.keyword,
|
||||||
|
required super.searchType,
|
||||||
|
required super.tag,
|
||||||
|
});
|
||||||
|
|
||||||
|
bool? hasJump2Video;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
jump2Video();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List? getDataList(response) {
|
||||||
|
return response.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool customHandleResponse(bool isRefresh, Success response) {
|
||||||
|
searchResultController?.count[searchType.index] =
|
||||||
|
response.response.numResults ?? 0;
|
||||||
|
if (searchType == SearchType.video && hasJump2Video != true && isRefresh) {
|
||||||
|
hasJump2Video = true;
|
||||||
|
onPushDetail(response.response.list);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LoadingState<SearchAllModel>> customGetData() => SearchHttp.searchAll(
|
||||||
|
keyword: keyword,
|
||||||
|
page: currentPage,
|
||||||
|
order: order.value,
|
||||||
|
duration: searchType == SearchType.video ? duration.value : null,
|
||||||
|
tids: tids,
|
||||||
|
orderSort: orderSort,
|
||||||
|
userType: userType,
|
||||||
|
categoryId: categoryId,
|
||||||
|
pubBegin: pubBegin,
|
||||||
|
pubEnd: pubEnd,
|
||||||
|
);
|
||||||
|
|
||||||
|
void onPushDetail(resultList) async {
|
||||||
|
try {
|
||||||
|
int? aid = int.tryParse(keyword);
|
||||||
|
if (aid != null && resultList.first.aid == aid) {
|
||||||
|
PiliScheme.videoPush(aid, null, showDialog: false);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jump2Video() {
|
||||||
|
if (RegExp(r'^av\d+$', caseSensitive: false).hasMatch(keyword)) {
|
||||||
|
hasJump2Video = true;
|
||||||
|
PiliScheme.videoPush(
|
||||||
|
int.parse(keyword.substring(2)),
|
||||||
|
null,
|
||||||
|
showDialog: false,
|
||||||
|
);
|
||||||
|
} else if (RegExp(r'^bv[a-z\d]{10}$', caseSensitive: false)
|
||||||
|
.hasMatch(keyword)) {
|
||||||
|
hasJump2Video = true;
|
||||||
|
PiliScheme.videoPush(null, keyword, showDialog: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
lib/pages/search_panel/all/view.dart
Normal file
117
lib/pages/search_panel/all/view.dart
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/video_card_h.dart';
|
||||||
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
|
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_search.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/all/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/pgc/widgets/item.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/user/widgets/item.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/view.dart';
|
||||||
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:waterfall_flow/waterfall_flow.dart';
|
||||||
|
|
||||||
|
class SearchAllPanel extends CommonSearchPanel {
|
||||||
|
const SearchAllPanel({
|
||||||
|
super.key,
|
||||||
|
required super.keyword,
|
||||||
|
required super.tag,
|
||||||
|
required super.searchType,
|
||||||
|
super.hasHeader = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SearchAllPanel> createState() => _SearchAllPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchAllPanelState
|
||||||
|
extends CommonSearchPanelState<SearchAllPanel, SearchAllModel, dynamic> {
|
||||||
|
@override
|
||||||
|
late final SearchAllController controller = Get.put(
|
||||||
|
SearchAllController(
|
||||||
|
keyword: widget.keyword,
|
||||||
|
searchType: widget.searchType,
|
||||||
|
tag: widget.tag,
|
||||||
|
),
|
||||||
|
tag: widget.searchType.name + widget.tag,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final TextStyle pgcStyle = TextStyle(fontSize: 13);
|
||||||
|
late TextStyle userStyle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
userStyle = TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildList(List<dynamic> list) {
|
||||||
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 80),
|
||||||
|
sliver: SliverWaterfallFlow.extent(
|
||||||
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
|
lastChildLayoutTypeBuilder: (index) {
|
||||||
|
if (index == list.length - 1) {
|
||||||
|
controller.onLoadMore();
|
||||||
|
}
|
||||||
|
return index == list.length
|
||||||
|
? LastChildLayoutType.foot
|
||||||
|
: LastChildLayoutType.none;
|
||||||
|
},
|
||||||
|
children: list
|
||||||
|
.map(
|
||||||
|
(item) => switch (item) {
|
||||||
|
SearchVideoItemModel() => SizedBox(
|
||||||
|
height: 120,
|
||||||
|
child: VideoCardH(
|
||||||
|
videoItem: item,
|
||||||
|
showPubdate: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
List<SearchMBangumiItemModel>() => item.length == 1
|
||||||
|
? SizedBox(
|
||||||
|
height: 160,
|
||||||
|
child: SearchPgcItem(style: pgcStyle, item: item.first),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
height: Grid.smallCardWidth / 2 / 0.75 +
|
||||||
|
MediaQuery.textScalerOf(context).scale(60),
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.only(bottom: 7),
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: item.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return Container(
|
||||||
|
width: Grid.smallCardWidth / 2,
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
left: StyleString.safeSpace,
|
||||||
|
right: index == item.length - 1
|
||||||
|
? StyleString.safeSpace
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
|
child: BangumiCardVSearch(item: item[index]),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SearchUserItemModel() => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 5),
|
||||||
|
child: SearchUserItem(
|
||||||
|
style: userStyle,
|
||||||
|
item: item,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_ => const SizedBox.shrink(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/image_save.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:PiliPlus/models/search/result.dart';
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/pgc/widgets/item.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/view.dart';
|
import 'package:PiliPlus/pages/search_panel/view.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:PiliPlus/utils/page_utils.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
@@ -51,105 +46,7 @@ class _SearchPgcPanelState extends CommonSearchPanelState<SearchPgcPanel,
|
|||||||
if (index == list.length - 1) {
|
if (index == list.length - 1) {
|
||||||
controller.onLoadMore();
|
controller.onLoadMore();
|
||||||
}
|
}
|
||||||
final i = list[index];
|
return SearchPgcItem(style: style, item: list[index]);
|
||||||
return InkWell(
|
|
||||||
onTap: () {
|
|
||||||
PageUtils.viewBangumi(seasonId: i.seasonId);
|
|
||||||
},
|
|
||||||
onLongPress: () => imageSaveDialog(
|
|
||||||
context: context,
|
|
||||||
title: i.title?.map((item) => item['text']).join() ?? '',
|
|
||||||
cover: i.cover,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: StyleString.safeSpace,
|
|
||||||
vertical: StyleString.cardSpace,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
width: 111,
|
|
||||||
height: 148,
|
|
||||||
src: i.cover,
|
|
||||||
),
|
|
||||||
PBadge(
|
|
||||||
text: i.seasonTypeName,
|
|
||||||
top: 6.0,
|
|
||||||
right: 4.0,
|
|
||||||
bottom: null,
|
|
||||||
left: null,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.onSurface),
|
|
||||||
children: [
|
|
||||||
for (var i in i.title!) ...[
|
|
||||||
TextSpan(
|
|
||||||
text: i['text'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.titleSmall!
|
|
||||||
.fontSize!,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: i['type'] == 'em'
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary
|
|
||||||
: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onSurface,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Text('评分:${i.mediaScore?['score']}', style: style),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
if (i.areas?.isNotEmpty == true)
|
|
||||||
Text(i.areas!, style: style),
|
|
||||||
const SizedBox(width: 3),
|
|
||||||
const Text('·'),
|
|
||||||
const SizedBox(width: 3),
|
|
||||||
Text(Utils.dateFormat(i.pubtime).toString(),
|
|
||||||
style: style),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
if (i.styles?.isNotEmpty == true)
|
|
||||||
Text(i.styles!, style: style),
|
|
||||||
const SizedBox(width: 3),
|
|
||||||
const Text('·'),
|
|
||||||
const SizedBox(width: 3),
|
|
||||||
if (i.indexShow?.isNotEmpty == true)
|
|
||||||
Text(i.indexShow!, style: style),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
childCount: list.length,
|
childCount: list.length,
|
||||||
),
|
),
|
||||||
|
|||||||
118
lib/pages/search_panel/pgc/widgets/item.dart
Normal file
118
lib/pages/search_panel/pgc/widgets/item.dart
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
|
import 'package:PiliPlus/utils/page_utils.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SearchPgcItem extends StatelessWidget {
|
||||||
|
const SearchPgcItem({
|
||||||
|
super.key,
|
||||||
|
required this.style,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TextStyle style;
|
||||||
|
final SearchMBangumiItemModel item;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
PageUtils.viewBangumi(seasonId: item.seasonId);
|
||||||
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
context: context,
|
||||||
|
title: item.title?.map((item) => item['text']).join() ?? '',
|
||||||
|
cover: item.cover,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: StyleString.safeSpace,
|
||||||
|
vertical: StyleString.cardSpace,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: 111,
|
||||||
|
height: 148,
|
||||||
|
src: item.cover,
|
||||||
|
),
|
||||||
|
PBadge(
|
||||||
|
text: item.seasonTypeName,
|
||||||
|
top: 6.0,
|
||||||
|
right: 4.0,
|
||||||
|
bottom: null,
|
||||||
|
left: null,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface),
|
||||||
|
children: [
|
||||||
|
for (var i in item.title!) ...[
|
||||||
|
TextSpan(
|
||||||
|
text: i['text'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleSmall!
|
||||||
|
.fontSize!,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: i['type'] == 'em'
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text('评分:${item.mediaScore?['score']}', style: style),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (item.areas?.isNotEmpty == true)
|
||||||
|
Text(item.areas!, style: style),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
const Text('·'),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
Text(
|
||||||
|
Utils.dateFormat(item.pubtime).toString(),
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (item.styles?.isNotEmpty == true)
|
||||||
|
Text(item.styles!, style: style),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
const Text('·'),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
if (item.indexShow?.isNotEmpty == true)
|
||||||
|
Text(item.indexShow!, style: style),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/search/result.dart';
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/user/controller.dart';
|
import 'package:PiliPlus/pages/search_panel/user/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/user/widgets/item.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/view.dart';
|
import 'package:PiliPlus/pages/search_panel/view.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
@@ -118,80 +117,9 @@ class _SearchUserPanelState extends CommonSearchPanelState<SearchUserPanel,
|
|||||||
if (index == list.length - 1) {
|
if (index == list.length - 1) {
|
||||||
controller.onLoadMore();
|
controller.onLoadMore();
|
||||||
}
|
}
|
||||||
final item = list[index];
|
return SearchUserItem(
|
||||||
String heroTag = Utils.makeHeroTag(item.mid);
|
style: style,
|
||||||
return InkWell(
|
item: list[index],
|
||||||
onTap: () => Get.toNamed('/member?mid=${item.mid}',
|
|
||||||
arguments: {'heroTag': heroTag, 'face': item.upic}),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Stack(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
width: 42,
|
|
||||||
height: 42,
|
|
||||||
src: item.upic,
|
|
||||||
type: 'avatar',
|
|
||||||
),
|
|
||||||
if (item.officialVerify?['type'] == 0 ||
|
|
||||||
item.officialVerify?['type'] == 1)
|
|
||||||
Positioned(
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.offline_bolt,
|
|
||||||
color: item.officialVerify?['type'] == 0
|
|
||||||
? Colors.yellow
|
|
||||||
: Colors.lightBlueAccent,
|
|
||||||
size: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item.uname!,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Image.asset(
|
|
||||||
'assets/images/lv/lv${item.isSeniorMember == 1 ? '6_s' : item.level}.png',
|
|
||||||
height: 11,
|
|
||||||
semanticLabel: '等级${item.level}',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'粉丝:${Utils.numFormat(item.fans)} 视频:${Utils.numFormat(item.videos)}',
|
|
||||||
style: style,
|
|
||||||
),
|
|
||||||
if (item.officialVerify?['desc'] != null &&
|
|
||||||
item.officialVerify?['desc'] != '')
|
|
||||||
Text(
|
|
||||||
item.officialVerify?['desc'],
|
|
||||||
style: style,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: list.length,
|
childCount: list.length,
|
||||||
|
|||||||
96
lib/pages/search_panel/user/widgets/item.dart
Normal file
96
lib/pages/search_panel/user/widgets/item.dart
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:PiliPlus/models/search/result.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class SearchUserItem extends StatelessWidget {
|
||||||
|
const SearchUserItem({
|
||||||
|
super.key,
|
||||||
|
required this.style,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TextStyle style;
|
||||||
|
final SearchUserItemModel item;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
String heroTag = Utils.makeHeroTag(item.mid);
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => Get.toNamed(
|
||||||
|
'/member?mid=${item.mid}',
|
||||||
|
arguments: {'heroTag': heroTag, 'face': item.upic},
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: 42,
|
||||||
|
height: 42,
|
||||||
|
src: item.upic,
|
||||||
|
type: 'avatar',
|
||||||
|
),
|
||||||
|
if (item.officialVerify?['type'] == 0 ||
|
||||||
|
item.officialVerify?['type'] == 1)
|
||||||
|
Positioned(
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.offline_bolt,
|
||||||
|
color: item.officialVerify?['type'] == 0
|
||||||
|
? Colors.yellow
|
||||||
|
: Colors.lightBlueAccent,
|
||||||
|
size: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.uname!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/lv/lv${item.isSeniorMember == 1 ? '6_s' : item.level}.png',
|
||||||
|
height: 11,
|
||||||
|
semanticLabel: '等级${item.level}',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'粉丝:${Utils.numFormat(item.fans)} 视频:${Utils.numFormat(item.videos)}',
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
if (item.officialVerify?['desc'] != null &&
|
||||||
|
item.officialVerify?['desc'] != '')
|
||||||
|
Text(
|
||||||
|
item.officialVerify?['desc'],
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:PiliPlus/pages/search/controller.dart';
|
import 'package:PiliPlus/pages/search/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/search_panel/all/view.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/article/view.dart';
|
import 'package:PiliPlus/pages/search_panel/article/view.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/live/view.dart';
|
import 'package:PiliPlus/pages/search_panel/live/view.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/pgc/view.dart';
|
import 'package:PiliPlus/pages/search_panel/pgc/view.dart';
|
||||||
@@ -23,6 +24,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
late SearchResultController _searchResultController;
|
late SearchResultController _searchResultController;
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
final String _tag = DateTime.now().millisecondsSinceEpoch.toString();
|
final String _tag = DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
|
final bool? _isFromSearch = Get.arguments?['fromSearch'];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -34,7 +36,9 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
|
|
||||||
_tabController = TabController(
|
_tabController = TabController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
initialIndex: Get.arguments is int ? Get.arguments : 0,
|
initialIndex: Get.arguments?['initIndex'] != null
|
||||||
|
? (Get.arguments?['initIndex'] as int)
|
||||||
|
: 0,
|
||||||
length: SearchType.values.length,
|
length: SearchType.values.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -68,7 +72,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
),
|
),
|
||||||
title: GestureDetector(
|
title: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (Get.previousRoute.startsWith('/search?')) {
|
if (_isFromSearch == true) {
|
||||||
Get.back();
|
Get.back();
|
||||||
} else {
|
} else {
|
||||||
Get.offNamed(
|
Get.offNamed(
|
||||||
@@ -150,10 +154,11 @@ class _SearchResultPageState extends State<SearchResultPage>
|
|||||||
children: SearchType.values
|
children: SearchType.values
|
||||||
.map(
|
.map(
|
||||||
(item) => switch (item) {
|
(item) => switch (item) {
|
||||||
// SearchType.all => SearchVideoPanel(
|
SearchType.all => SearchAllPanel(
|
||||||
// keyword: _searchResultController.keyword,
|
tag: _tag,
|
||||||
// tag: _tag,
|
searchType: item,
|
||||||
// ),
|
keyword: _searchResultController.keyword,
|
||||||
|
),
|
||||||
SearchType.video => SearchVideoPanel(
|
SearchType.video => SearchVideoPanel(
|
||||||
tag: _tag,
|
tag: _tag,
|
||||||
searchType: item,
|
searchType: item,
|
||||||
|
|||||||
@@ -788,10 +788,10 @@ class _VideoInfoState extends State<VideoInfo> {
|
|||||||
(item) => SearchText(
|
(item) => SearchText(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
text: item['tag_name'],
|
text: item['tag_name'],
|
||||||
onTap: (_) => Get.toNamed('/searchResult',
|
onTap: (_) => Get.toNamed(
|
||||||
parameters: {
|
'/searchResult',
|
||||||
'keyword': item['tag_name']
|
parameters: {'keyword': item['tag_name']},
|
||||||
}),
|
),
|
||||||
onLongPress: (_) =>
|
onLongPress: (_) =>
|
||||||
Utils.copyText(item['tag_name']),
|
Utils.copyText(item['tag_name']),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -922,8 +922,10 @@ class ReplyItemGrpc extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (appUrlSchema.startsWith('bilibili://search')) {
|
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||||
Get.toNamed('/searchResult',
|
Get.toNamed(
|
||||||
parameters: {'keyword': title});
|
'/searchResult',
|
||||||
|
parameters: {'keyword': title},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
PageUtils.handleWebview(matchStr);
|
PageUtils.handleWebview(matchStr);
|
||||||
}
|
}
|
||||||
@@ -947,8 +949,10 @@ class ReplyItemGrpc extends StatelessWidget {
|
|||||||
..onTap = () {
|
..onTap = () {
|
||||||
final String topic =
|
final String topic =
|
||||||
matchStr.substring(1, matchStr.length - 1);
|
matchStr.substring(1, matchStr.length - 1);
|
||||||
Get.toNamed('/searchResult',
|
Get.toNamed(
|
||||||
parameters: {'keyword': topic});
|
'/searchResult',
|
||||||
|
parameters: {'keyword': topic},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user