feat: 专栏分类搜索
This commit is contained in:
@ -28,7 +28,7 @@ extension SearchTypeExtension on SearchType {
|
|||||||
String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index];
|
String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索类型为视频、专栏及相簿时
|
// 搜索类型为视频时
|
||||||
enum ArchiveFilterType {
|
enum ArchiveFilterType {
|
||||||
totalrank,
|
totalrank,
|
||||||
click,
|
click,
|
||||||
@ -44,3 +44,21 @@ extension ArchiveFilterTypeExtension on ArchiveFilterType {
|
|||||||
String get description =>
|
String get description =>
|
||||||
['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
|
['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索类型为专栏时
|
||||||
|
enum ArticleFilterType {
|
||||||
|
// 综合排序
|
||||||
|
totalrank,
|
||||||
|
// 最新发布
|
||||||
|
pubdate,
|
||||||
|
// 最多点击
|
||||||
|
click,
|
||||||
|
// 最多喜欢
|
||||||
|
attention,
|
||||||
|
// 最多评论
|
||||||
|
scores,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ArticleFilterTypeExtension on ArticleFilterType {
|
||||||
|
String get description => ['综合排序', '最新发布', '最多点击', '最多喜欢', '最多评论'][index];
|
||||||
|
}
|
||||||
|
@ -24,7 +24,9 @@ class SearchPanelController extends GetxController {
|
|||||||
searchType: searchType!,
|
searchType: searchType!,
|
||||||
keyword: keyword!,
|
keyword: keyword!,
|
||||||
page: page.value,
|
page: page.value,
|
||||||
order: searchType!.type != 'video' ? null : order.value,
|
order: !['video', 'article'].contains(searchType!.type)
|
||||||
|
? null
|
||||||
|
: (order.value == '' ? null : order.value),
|
||||||
duration: searchType!.type != 'video' ? null : duration.value,
|
duration: searchType!.type != 'video' ? null : duration.value,
|
||||||
tids: searchType!.type != 'video' ? null : tids.value,
|
tids: searchType!.type != 'video' ? null : tids.value,
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: invalid_use_of_protected_member
|
||||||
|
|
||||||
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';
|
||||||
@ -45,6 +47,11 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
),
|
),
|
||||||
tag: widget.searchType!.type + widget.keyword!,
|
tag: widget.searchType!.type + widget.keyword!,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// 专栏默认排序
|
||||||
|
if (widget.searchType == SearchType.article) {
|
||||||
|
_searchPanelController.order.value = 'totalrank';
|
||||||
|
}
|
||||||
scrollController = _searchPanelController.scrollController;
|
scrollController = _searchPanelController.scrollController;
|
||||||
scrollController.addListener(() async {
|
scrollController.addListener(() async {
|
||||||
if (scrollController.position.pixels >=
|
if (scrollController.position.pixels >=
|
||||||
@ -84,7 +91,6 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
return SearchVideoPanel(
|
return SearchVideoPanel(
|
||||||
ctr: _searchPanelController,
|
ctr: _searchPanelController,
|
||||||
// ignore: invalid_use_of_protected_member
|
|
||||||
list: list.value,
|
list: list.value,
|
||||||
);
|
);
|
||||||
case SearchType.media_bangumi:
|
case SearchType.media_bangumi:
|
||||||
@ -94,7 +100,10 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
case SearchType.live_room:
|
case SearchType.live_room:
|
||||||
return searchLivePanel(context, ctr, list);
|
return searchLivePanel(context, ctr, list);
|
||||||
case SearchType.article:
|
case SearchType.article:
|
||||||
return searchArticlePanel(context, ctr, list);
|
return SearchArticlePanel(
|
||||||
|
ctr: _searchPanelController,
|
||||||
|
list: list.value,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,83 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
|
import 'package:pilipala/pages/search_panel/index.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
|
class SearchArticlePanel extends StatelessWidget {
|
||||||
|
SearchArticlePanel({
|
||||||
|
required this.ctr,
|
||||||
|
this.list,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final SearchPanelController ctr;
|
||||||
|
final List? list;
|
||||||
|
|
||||||
|
final ArticlePanelController controller = Get.put(ArticlePanelController());
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
children: [
|
||||||
|
searchArticlePanel(context, ctr, list),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 36,
|
||||||
|
padding: const EdgeInsets.only(left: 8, top: 0, right: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Obx(
|
||||||
|
() => Wrap(
|
||||||
|
// spacing: ,
|
||||||
|
children: [
|
||||||
|
for (var i in controller.filterList) ...[
|
||||||
|
CustomFilterChip(
|
||||||
|
label: i['label'],
|
||||||
|
type: i['type'],
|
||||||
|
selectedType: controller.selectedType.value,
|
||||||
|
callFn: (bool selected) async {
|
||||||
|
controller.selectedType.value = i['type'];
|
||||||
|
ctr.order.value =
|
||||||
|
i['type'].toString().split('.').last;
|
||||||
|
SmartDialog.showLoading(msg: 'loading');
|
||||||
|
await ctr.onRefresh();
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget searchArticlePanel(BuildContext context, ctr, list) {
|
Widget searchArticlePanel(BuildContext context, ctr, list) {
|
||||||
TextStyle textStyle = TextStyle(
|
TextStyle textStyle = TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline);
|
color: Theme.of(context).colorScheme.outline);
|
||||||
return ListView.builder(
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 36),
|
||||||
|
child: list!.isNotEmpty
|
||||||
|
? ListView.builder(
|
||||||
controller: ctr!.scrollController,
|
controller: ctr!.scrollController,
|
||||||
|
addAutomaticKeepAlives: false,
|
||||||
|
addRepaintBoundaries: false,
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
@ -39,7 +107,8 @@ Widget searchArticlePanel(BuildContext context, ctr, list) {
|
|||||||
list[index].imageUrls.isNotEmpty)
|
list[index].imageUrls.isNotEmpty)
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(
|
||||||
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
return NetworkImgLayer(
|
return NetworkImgLayer(
|
||||||
@ -86,9 +155,11 @@ Widget searchArticlePanel(BuildContext context, ctr, list) {
|
|||||||
style: textStyle),
|
style: textStyle),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text('${list[index].view}浏览', style: textStyle),
|
Text('${list[index].view}浏览',
|
||||||
|
style: textStyle),
|
||||||
Text(' • ', style: textStyle),
|
Text(' • ', style: textStyle),
|
||||||
Text('${list[index].reply}评论', style: textStyle),
|
Text('${list[index].reply}评论',
|
||||||
|
style: textStyle),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -102,5 +173,77 @@ Widget searchArticlePanel(BuildContext context, ctr, list) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
HttpError(
|
||||||
|
errMsg: '没有数据',
|
||||||
|
isShowBtn: false,
|
||||||
|
fn: () => {},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CustomFilterChip extends StatelessWidget {
|
||||||
|
const CustomFilterChip({
|
||||||
|
this.label,
|
||||||
|
this.type,
|
||||||
|
this.selectedType,
|
||||||
|
this.callFn,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String? label;
|
||||||
|
final ArticleFilterType? type;
|
||||||
|
final ArticleFilterType? selectedType;
|
||||||
|
final Function? callFn;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 34,
|
||||||
|
child: FilterChip(
|
||||||
|
padding: const EdgeInsets.only(left: 11, right: 11),
|
||||||
|
labelPadding: EdgeInsets.zero,
|
||||||
|
label: Text(
|
||||||
|
label!,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: type == selectedType
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.outline),
|
||||||
|
selected: type == selectedType,
|
||||||
|
showCheckmark: false,
|
||||||
|
shape: ContinuousRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
selectedColor: Colors.transparent,
|
||||||
|
// backgroundColor:
|
||||||
|
// Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
side: BorderSide.none,
|
||||||
|
onSelected: (bool selected) => callFn!(selected),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArticlePanelController extends GetxController {
|
||||||
|
RxList<Map> filterList = [{}].obs;
|
||||||
|
Rx<ArticleFilterType> selectedType = ArticleFilterType.values.first.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
List<Map<String, dynamic>> list = ArticleFilterType.values
|
||||||
|
.map((type) => {
|
||||||
|
'label': type.description,
|
||||||
|
'type': type,
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
filterList.value = list;
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user