feat: 历史记录多选删除

This commit is contained in:
guozhigq
2023-10-01 10:35:03 +08:00
parent 8f987e8352
commit 2ef3a8cd25
4 changed files with 321 additions and 89 deletions

View File

@ -79,6 +79,7 @@ class HisListItem {
this.kid, this.kid,
this.tagName, this.tagName,
this.liveStatus, this.liveStatus,
this.checked,
}); });
String? title; String? title;
@ -105,6 +106,7 @@ class HisListItem {
int? kid; int? kid;
String? tagName; String? tagName;
int? liveStatus; int? liveStatus;
bool? checked;
HisListItem.fromJson(Map<String, dynamic> json) { HisListItem.fromJson(Map<String, dynamic> json) {
title = json['title']; title = json['title'];
@ -131,6 +133,7 @@ class HisListItem {
kid = json['kid']; kid = json['kid'];
tagName = json['tag_name']; tagName = json['tag_name'];
liveStatus = json['live_status']; liveStatus = json['live_status'];
checked = false;
} }
} }

View File

@ -8,11 +8,13 @@ import 'package:pilipala/utils/storage.dart';
class HistoryController extends GetxController { class HistoryController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
RxList<HisListItem> historyList = [HisListItem()].obs; RxList<HisListItem> historyList = <HisListItem>[].obs;
RxBool isLoadingMore = false.obs; RxBool isLoadingMore = false.obs;
RxBool pauseStatus = false.obs; RxBool pauseStatus = false.obs;
Box localCache = GStrorage.localCache; Box localCache = GStrorage.localCache;
RxBool isLoading = false.obs; RxBool isLoading = false.obs;
RxBool enableMultiple = false.obs;
RxInt checkedCount = 0.obs;
@override @override
void onInit() { void onInit() {
@ -140,6 +142,7 @@ class HistoryController extends GetxController {
// 删除已看历史记录 // 删除已看历史记录
Future onDelHistory() async { Future onDelHistory() async {
/// TODO 优化
List<HisListItem> result = List<HisListItem> result =
historyList.where((e) => e.progress == -1).toList(); historyList.where((e) => e.progress == -1).toList();
for (HisListItem i in result) { for (HisListItem i in result) {
@ -149,4 +152,46 @@ class HistoryController extends GetxController {
} }
SmartDialog.showToast('操作完成'); SmartDialog.showToast('操作完成');
} }
// 删除选中的记录
Future onDelCheckedHistory() async {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('确认删除所选历史记录吗?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () async {
/// TODO 优化
await SmartDialog.dismiss();
SmartDialog.showLoading(msg: '请求中');
List<HisListItem> result =
historyList.where((e) => e.checked!).toList();
for (HisListItem i in result) {
String resKid = 'archive_${i.kid}';
await UserHttp.delHistory(resKid);
historyList.removeWhere((e) => e.kid == i.kid);
}
checkedCount.value = 0;
SmartDialog.dismiss();
},
child: const Text('确认'),
)
],
);
},
);
}
} }

View File

@ -39,6 +39,20 @@ class _HistoryPageState extends State<HistoryPage> {
); );
} }
// 选中
onChoose(index) {
_historyController.historyList[index].checked =
!_historyController.historyList[index].checked!;
_historyController.checkedCount.value =
_historyController.historyList.where((item) => item.checked!).length;
_historyController.historyList.refresh();
}
// 更新多选状态
onUpdateMultiple() {
setState(() {});
}
@override @override
void dispose() { void dispose() {
scrollController.removeListener(() {}); scrollController.removeListener(() {});
@ -48,51 +62,115 @@ class _HistoryPageState extends State<HistoryPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBarWidget(
titleSpacing: 0, visible: _historyController.enableMultiple.value,
centerTitle: false, child1: AppBar(
title: Text( titleSpacing: 0,
'观看记录', centerTitle: false,
style: Theme.of(context).textTheme.titleMedium, leading: IconButton(
), onPressed: () => Get.back(),
actions: [ icon: const Icon(Icons.arrow_back_outlined),
PopupMenuButton<String>(
onSelected: (String type) {
// 处理菜单项选择的逻辑
switch (type) {
case 'pause':
_historyController.onPauseHistory();
break;
case 'clear':
_historyController.onClearHistory();
break;
case 'del':
_historyController.onDelHistory();
break;
default:
}
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'pause',
child: Obx(
() => Text(!_historyController.pauseStatus.value
? '暂停观看记录'
: '恢复观看记录'),
),
),
const PopupMenuItem<String>(
value: 'clear',
child: Text('清空观看记录'),
),
const PopupMenuItem<String>(
value: 'del',
child: Text('删除已看记录'),
),
],
), ),
const SizedBox(width: 6), title: Text(
], '观看记录',
style: Theme.of(context).textTheme.titleMedium,
),
actions: [
// TextButton(
// onPressed: () {
// _historyController.enableMultiple.value = true;
// setState(() {});
// },
// child: const Text('多选'),
// ),
PopupMenuButton<String>(
onSelected: (String type) {
// 处理菜单项选择的逻辑
switch (type) {
case 'pause':
_historyController.onPauseHistory();
break;
case 'clear':
_historyController.onClearHistory();
break;
case 'del':
_historyController.onDelHistory();
break;
case 'multiple':
_historyController.enableMultiple.value = true;
setState(() {});
break;
default:
}
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'pause',
child: Obx(
() => Text(!_historyController.pauseStatus.value
? '暂停观看记录'
: '恢复观看记录'),
),
),
const PopupMenuItem<String>(
value: 'clear',
child: Text('清空观看记录'),
),
const PopupMenuItem<String>(
value: 'del',
child: Text('删除已看记录'),
),
const PopupMenuItem<String>(
value: 'multiple',
child: Text('多选删除'),
),
],
),
const SizedBox(width: 6),
],
),
child2: AppBar(
titleSpacing: 0,
centerTitle: false,
leading: IconButton(
onPressed: () {
_historyController.enableMultiple.value = false;
for (var item in _historyController.historyList) {
item.checked = false;
}
_historyController.checkedCount.value = 0;
setState(() {});
},
icon: const Icon(Icons.close_outlined),
),
title: Obx(
() => Text(
'已选择${_historyController.checkedCount.value}',
style: Theme.of(context).textTheme.titleMedium,
),
),
actions: [
TextButton(
onPressed: () {
for (var item in _historyController.historyList) {
item.checked = true;
}
_historyController.checkedCount.value =
_historyController.historyList.length;
_historyController.historyList.refresh();
},
child: const Text('全选'),
),
TextButton(
onPressed: () => _historyController.onDelCheckedHistory(),
child: Text(
'删除',
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
),
const SizedBox(width: 6),
],
),
), ),
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
@ -120,6 +198,8 @@ class _HistoryPageState extends State<HistoryPage> {
videoItem: videoItem:
_historyController.historyList[index], _historyController.historyList[index],
ctr: _historyController, ctr: _historyController,
onChoose: () => onChoose(index),
onUpdateMultiple: () => onUpdateMultiple(),
); );
}, },
childCount: childCount:
@ -155,6 +235,36 @@ class _HistoryPageState extends State<HistoryPage> {
], ],
), ),
), ),
// bottomNavigationBar: BottomAppBar(),
);
}
}
class AppBarWidget extends StatelessWidget implements PreferredSizeWidget {
const AppBarWidget({
required this.child1,
required this.child2,
required this.visible,
Key? key,
}) : super(key: key);
final PreferredSizeWidget child1;
final PreferredSizeWidget child2;
final bool visible;
@override
Size get preferredSize => child1.preferredSize;
@override
Widget build(BuildContext context) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: child,
);
},
child: !visible ? child1 : child2,
); );
} }
} }

View File

@ -12,13 +12,22 @@ import 'package:pilipala/models/common/business_type.dart';
import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/pages/history/index.dart'; import 'package:pilipala/pages/history/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
class HistoryItem extends StatelessWidget { class HistoryItem extends StatelessWidget {
final dynamic videoItem; final dynamic videoItem;
final HistoryController? ctr; final HistoryController? ctr;
const HistoryItem({super.key, required this.videoItem, this.ctr}); final Function? onChoose;
final Function? onUpdateMultiple;
const HistoryItem({
super.key,
required this.videoItem,
this.ctr,
this.onChoose,
this.onUpdateMultiple,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -27,6 +36,11 @@ class HistoryItem extends StatelessWidget {
String heroTag = Utils.makeHeroTag(aid); String heroTag = Utils.makeHeroTag(aid);
return InkWell( return InkWell(
onTap: () async { onTap: () async {
if (ctr!.enableMultiple.value) {
feedBack();
onChoose!();
return;
}
if (videoItem.history.business.contains('article')) { if (videoItem.history.business.contains('article')) {
int cid = videoItem.history.cid ?? int cid = videoItem.history.cid ??
// videoItem.history.oid ?? // videoItem.history.oid ??
@ -117,6 +131,14 @@ class HistoryItem extends StatelessWidget {
arguments: {'heroTag': heroTag, 'pic': videoItem.cover}); arguments: {'heroTag': heroTag, 'pic': videoItem.cover});
} }
}, },
onLongPress: () {
if (!ctr!.enableMultiple.value) {
feedBack();
ctr!.enableMultiple.value = true;
onChoose!();
onUpdateMultiple!();
}
},
child: Column( child: Column(
children: [ children: [
Padding( Padding(
@ -132,51 +154,103 @@ class HistoryItem extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AspectRatio( Stack(
aspectRatio: StyleString.aspectRatio, children: [
child: LayoutBuilder( AspectRatio(
builder: (context, boxConstraints) { aspectRatio: StyleString.aspectRatio,
double maxWidth = boxConstraints.maxWidth; child: LayoutBuilder(
double maxHeight = boxConstraints.maxHeight; builder: (context, boxConstraints) {
return Stack( double maxWidth = boxConstraints.maxWidth;
children: [ double maxHeight = boxConstraints.maxHeight;
Hero( return Stack(
tag: heroTag, children: [
child: NetworkImgLayer( Hero(
src: (videoItem.cover != '' tag: heroTag,
? videoItem.cover child: NetworkImgLayer(
: videoItem.covers.first), src: (videoItem.cover != ''
width: maxWidth, ? videoItem.cover
height: maxHeight, : videoItem.covers.first),
width: maxWidth,
height: maxHeight,
),
),
if (!BusinessType
.hiddenDurationType.hiddenDurationType
.contains(videoItem.history.business))
PBadge(
text: videoItem.progress == -1
? '已看完'
: '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}',
right: 6.0,
bottom: 6.0,
type: 'gray',
),
// 右上角
if (BusinessType.showBadge.showBadge
.contains(
videoItem.history.business) ||
videoItem.history.business ==
BusinessType.live.type)
PBadge(
text: videoItem.badge,
top: 6.0,
right: 6.0,
bottom: null,
left: null,
),
],
);
},
),
),
Obx(
() => Positioned.fill(
child: AnimatedOpacity(
opacity: ctr!.enableMultiple.value ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.black.withOpacity(0.6),
),
child: Center(
child: SizedBox(
width: 34,
height: 34,
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith(
(states) {
return Colors.white
.withOpacity(0.8);
},
),
),
onPressed: () {
feedBack();
onChoose!();
},
icon: Icon(
Icons.done_all_outlined,
color: videoItem.checked
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.outline,
),
),
),
), ),
), ),
if (!BusinessType ),
.hiddenDurationType.hiddenDurationType ),
.contains(videoItem.history.business)) ),
PBadge( ],
text: videoItem.progress == -1
? '已看完'
: '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}',
right: 6.0,
bottom: 6.0,
type: 'gray',
),
// 右上角
if (BusinessType.showBadge.showBadge
.contains(videoItem.history.business) ||
videoItem.history.business ==
BusinessType.live.type)
PBadge(
text: videoItem.badge,
top: 6.0,
right: 6.0,
bottom: null,
left: null,
),
],
);
},
),
), ),
VideoContent(videoItem: videoItem, ctr: ctr) VideoContent(videoItem: videoItem, ctr: ctr)
], ],