mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +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:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../http/search.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import '../constants.dart';
|
||||
@@ -77,6 +78,17 @@ class VideoCardH extends StatelessWidget {
|
||||
if (type == 'ketang') {
|
||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||
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) &&
|
||||
(videoItem as HotVideoItemModel).redirectUrl?.isNotEmpty ==
|
||||
|
||||
@@ -102,8 +102,8 @@ class SearchHttp {
|
||||
case SearchType.article:
|
||||
data = SearchArticleModel.fromJson(res.data['data']);
|
||||
break;
|
||||
// case SearchType.all:
|
||||
// break;
|
||||
case SearchType.all:
|
||||
break;
|
||||
}
|
||||
return LoadingState.success(data);
|
||||
} catch (err) {
|
||||
@@ -115,7 +115,7 @@ class SearchHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> searchAll({
|
||||
static Future<LoadingState<SearchAllModel>> searchAll({
|
||||
required String keyword,
|
||||
required page,
|
||||
String? order,
|
||||
@@ -140,17 +140,15 @@ class SearchHttp {
|
||||
if (pubEnd != null) 'pubtime_end_s': pubEnd,
|
||||
};
|
||||
var res = await Request().get(
|
||||
Api.searchByType,
|
||||
Api.searchAll,
|
||||
queryParameters: params,
|
||||
);
|
||||
if (res.data is! Map) {
|
||||
return LoadingState.error('没有相关数据');
|
||||
}
|
||||
if (res.data['code'] == 0) {
|
||||
dynamic data;
|
||||
try {
|
||||
// TODO
|
||||
return LoadingState.success(data);
|
||||
return LoadingState.success(SearchAllModel.fromJson(res.data['data']));
|
||||
} catch (err) {
|
||||
debugPrint(err.toString());
|
||||
return LoadingState.error(err.toString());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
enum SearchType {
|
||||
// all,
|
||||
all,
|
||||
// 视频:video
|
||||
video,
|
||||
// 番剧:media_bangumi,
|
||||
@@ -25,7 +25,7 @@ enum SearchType {
|
||||
|
||||
extension SearchTypeExtension on SearchType {
|
||||
String get label => [
|
||||
// '综合',
|
||||
'综合',
|
||||
'视频',
|
||||
'番剧',
|
||||
'影视',
|
||||
|
||||
@@ -14,6 +14,46 @@ abstract class SearchNumData<T> {
|
||||
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> {
|
||||
SearchVideoModel({
|
||||
super.numResults,
|
||||
|
||||
@@ -141,8 +141,12 @@ class _IntroDetailState extends CommonCollapseSlidePageState<IntroDetail> {
|
||||
(item) => SearchText(
|
||||
fontSize: 13,
|
||||
text: item['tag_name'],
|
||||
onTap: (_) => Get.toNamed('/searchResult',
|
||||
parameters: {'keyword': item['tag_name']}),
|
||||
onTap: (_) => Get.toNamed(
|
||||
'/searchResult',
|
||||
parameters: {
|
||||
'keyword': 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: {
|
||||
'keyword': controller.text,
|
||||
},
|
||||
arguments: initIndex,
|
||||
arguments: {
|
||||
'initIndex': initIndex,
|
||||
'fromSearch': true,
|
||||
},
|
||||
);
|
||||
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/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/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -51,105 +46,7 @@ class _SearchPgcPanelState extends CommonSearchPanelState<SearchPgcPanel,
|
||||
if (index == list.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
final i = 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),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
return SearchPgcItem(style: style, item: list[index]);
|
||||
},
|
||||
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/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/search/result.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/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -118,80 +117,9 @@ class _SearchUserPanelState extends CommonSearchPanelState<SearchUserPanel,
|
||||
if (index == list.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
final item = list[index];
|
||||
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,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
return SearchUserItem(
|
||||
style: style,
|
||||
item: list[index],
|
||||
);
|
||||
},
|
||||
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_panel/all/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/pgc/view.dart';
|
||||
@@ -23,6 +24,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
||||
late SearchResultController _searchResultController;
|
||||
late TabController _tabController;
|
||||
final String _tag = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
final bool? _isFromSearch = Get.arguments?['fromSearch'];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -34,7 +36,9 @@ class _SearchResultPageState extends State<SearchResultPage>
|
||||
|
||||
_tabController = TabController(
|
||||
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,
|
||||
);
|
||||
|
||||
@@ -68,7 +72,7 @@ class _SearchResultPageState extends State<SearchResultPage>
|
||||
),
|
||||
title: GestureDetector(
|
||||
onTap: () {
|
||||
if (Get.previousRoute.startsWith('/search?')) {
|
||||
if (_isFromSearch == true) {
|
||||
Get.back();
|
||||
} else {
|
||||
Get.offNamed(
|
||||
@@ -150,10 +154,11 @@ class _SearchResultPageState extends State<SearchResultPage>
|
||||
children: SearchType.values
|
||||
.map(
|
||||
(item) => switch (item) {
|
||||
// SearchType.all => SearchVideoPanel(
|
||||
// keyword: _searchResultController.keyword,
|
||||
// tag: _tag,
|
||||
// ),
|
||||
SearchType.all => SearchAllPanel(
|
||||
tag: _tag,
|
||||
searchType: item,
|
||||
keyword: _searchResultController.keyword,
|
||||
),
|
||||
SearchType.video => SearchVideoPanel(
|
||||
tag: _tag,
|
||||
searchType: item,
|
||||
|
||||
@@ -788,10 +788,10 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
(item) => SearchText(
|
||||
fontSize: 13,
|
||||
text: item['tag_name'],
|
||||
onTap: (_) => Get.toNamed('/searchResult',
|
||||
parameters: {
|
||||
'keyword': item['tag_name']
|
||||
}),
|
||||
onTap: (_) => Get.toNamed(
|
||||
'/searchResult',
|
||||
parameters: {'keyword': item['tag_name']},
|
||||
),
|
||||
onLongPress: (_) =>
|
||||
Utils.copyText(item['tag_name']),
|
||||
),
|
||||
|
||||
@@ -922,8 +922,10 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
}
|
||||
} else {
|
||||
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||
Get.toNamed('/searchResult',
|
||||
parameters: {'keyword': title});
|
||||
Get.toNamed(
|
||||
'/searchResult',
|
||||
parameters: {'keyword': title},
|
||||
);
|
||||
} else {
|
||||
PageUtils.handleWebview(matchStr);
|
||||
}
|
||||
@@ -947,8 +949,10 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
..onTap = () {
|
||||
final String topic =
|
||||
matchStr.substring(1, matchStr.length - 1);
|
||||
Get.toNamed('/searchResult',
|
||||
parameters: {'keyword': topic});
|
||||
Get.toNamed(
|
||||
'/searchResult',
|
||||
parameters: {'keyword': topic},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user