opt: multiSelect (#935)

This commit is contained in:
My-Responsitories
2025-08-04 12:57:37 +08:00
committed by GitHub
parent d246462535
commit 7b51f15753
23 changed files with 488 additions and 457 deletions

View File

@@ -0,0 +1,91 @@
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class AppBarWidget extends StatelessWidget implements PreferredSizeWidget {
const AppBarWidget({
required this.child1,
required this.child2,
required this.visible,
super.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
? KeyedSubtree.wrap(child1, 0)
: KeyedSubtree.wrap(child2, 1),
);
}
}
class MultiSelectAppBarWidget extends StatelessWidget
implements PreferredSizeWidget {
final MultiSelectMixin ctr;
final bool? visible;
final AppBar child;
final List<Widget>? children;
const MultiSelectAppBarWidget({
super.key,
required this.ctr,
this.visible,
this.children,
required this.child,
});
@override
Widget build(BuildContext context) {
return AppBarWidget(
visible: visible ?? ctr.enableMultiSelect.value,
child1: child,
child2: AppBar(
bottom: child.bottom,
leading: IconButton(
tooltip: '取消',
onPressed: ctr.handleSelect,
icon: const Icon(Icons.close_outlined),
),
title: Obx(() => Text('已选: ${ctr.checkedCount}')),
actions: [
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () => ctr.handleSelect(true),
child: const Text('全选'),
),
...?children,
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: ctr.onConfirm,
child: Text(
'移除',
style: TextStyle(color: Get.theme.colorScheme.error),
),
),
const SizedBox(width: 6),
],
),
);
}
@override
Size get preferredSize => child.preferredSize;
}

View File

@@ -312,7 +312,7 @@ class FavHttp {
static Future delNote({ static Future delNote({
required bool isPublish, required bool isPublish,
required List noteIds, required Iterable noteIds,
}) async { }) async {
final res = await Request().post( final res = await Request().post(
isPublish ? Api.delPublishNote : Api.delNote, isPublish ? Api.delPublishNote : Api.delNote,
@@ -637,13 +637,13 @@ class FavHttp {
} }
} }
static Future copyOrMoveFav({ static Future<LoadingState> copyOrMoveFav({
required bool isCopy, required bool isCopy,
required bool isFav, required bool isFav,
required dynamic srcMediaId, required dynamic srcMediaId,
required dynamic tarMediaId, required dynamic tarMediaId,
dynamic mid, dynamic mid,
required List resources, required Iterable resources,
}) async { }) async {
var res = await Request().post( var res = await Request().post(
isFav isFav
@@ -664,21 +664,21 @@ class FavHttp {
options: Options(contentType: Headers.formUrlEncodedContentType), options: Options(contentType: Headers.formUrlEncodedContentType),
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return {'status': true}; return const Success(null);
} else { } else {
return {'status': false, 'msg': res.data['message']}; return Error(res.data['message']);
} }
} }
static Future allFavFolders(mid) async { static Future<LoadingState<FavFolderData>> allFavFolders(Object mid) async {
var res = await Request().get( var res = await Request().get(
Api.favFolder, Api.favFolder,
queryParameters: {'up_mid': mid}, queryParameters: {'up_mid': mid},
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return {'status': true, 'data': FavFolderData.fromJson(res.data['data'])}; return Success(FavFolderData.fromJson(res.data['data']));
} else { } else {
return {'status': false, 'msg': res.data['message']}; return Error(res.data['message']);
} }
} }

View File

@@ -151,7 +151,7 @@ class UserHttp {
} }
// 移除已观看 // 移除已观看
static Future toViewDel({required List<int?> aids}) async { static Future toViewDel({required Iterable<int> aids}) async {
final Map<String, dynamic> params = { final Map<String, dynamic> params = {
'csrf': Accounts.main.csrf, 'csrf': Accounts.main.csrf,
'resources': aids.join(','), 'resources': aids.join(','),
@@ -204,7 +204,7 @@ class UserHttp {
} }
// 删除历史记录 // 删除历史记录
static Future delHistory(List<String> kidList) async { static Future delHistory(Iterable<String> kidList) async {
var res = await Request().post( var res = await Request().post(
Api.delHistory, Api.delHistory,
data: { data: {

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -5,35 +6,60 @@ mixin MultiSelectData {
bool? checked; bool? checked;
} }
abstract class MultiSelectController<R, T extends MultiSelectData> mixin MultiSelectMixin<T> {
extends CommonListController<R, T> {
late final RxBool enableMultiSelect = false.obs; late final RxBool enableMultiSelect = false.obs;
late final RxInt checkedCount = 0.obs;
late final allSelected = false.obs; late final allSelected = false.obs;
int get checkedCount;
void onSelect(T item, [bool disableSelect = true]);
void handleSelect([bool checked = false, bool disableSelect = true]);
void onConfirm();
}
abstract class MultiSelectController<R, T extends MultiSelectData>
extends CommonListController<R, T>
with MultiSelectMixin<T>, CommonMultiSelectMixin, DeleteItemMixin {}
mixin CommonMultiSelectMixin<T extends MultiSelectData> on MultiSelectMixin<T> {
Rx<LoadingState<List<T>?>> get loadingState;
late final RxInt rxCount = 0.obs;
@override
int get checkedCount => rxCount.value;
Iterable<T> get allChecked =>
loadingState.value.data!.where((v) => v.checked == true);
@override
void onSelect(T item, [bool disableSelect = true]) { void onSelect(T item, [bool disableSelect = true]) {
List<T> list = loadingState.value.data!; List<T> list = loadingState.value.data!;
item.checked = !(item.checked ?? false); item.checked = !(item.checked ?? false);
checkedCount.value = list.where((item) => item.checked == true).length; if (item.checked!) {
rxCount.value++;
} else {
rxCount.value--;
}
loadingState.refresh(); loadingState.refresh();
if (disableSelect) { if (disableSelect) {
if (checkedCount.value == 0) { if (checkedCount == 0) {
enableMultiSelect.value = false; enableMultiSelect.value = false;
} }
} else { } else {
allSelected.value = checkedCount.value == list.length; allSelected.value = checkedCount == list.length;
} }
} }
@override
void handleSelect([bool checked = false, bool disableSelect = true]) { void handleSelect([bool checked = false, bool disableSelect = true]) {
if (loadingState.value.isSuccess) { if (loadingState.value.isSuccess) {
List<T>? list = loadingState.value.data; final list = loadingState.value.data;
if (list?.isNotEmpty == true) { if (list?.isNotEmpty == true) {
for (T item in list!) { for (var item in list!) {
item.checked = checked; item.checked = checked;
} }
loadingState.refresh(); loadingState.refresh();
checkedCount.value = checked ? list.length : 0; rxCount.value = checked ? list.length : 0;
} }
} }
if (disableSelect && !checked) { if (disableSelect && !checked) {
@@ -41,3 +67,75 @@ abstract class MultiSelectController<R, T extends MultiSelectData>
} }
} }
} }
mixin DeleteItemMixin<R, T extends MultiSelectData>
on CommonListController<R, T>, CommonMultiSelectMixin<T> {
Future<void> afterDelete(Set<T> result) async {
// TODO: result require hash
final remainList = loadingState.value.data!;
if (result.length == 1) {
remainList.remove(result.single);
} else {
remainList.removeWhere(result.contains);
}
if (remainList.isNotEmpty) {
loadingState.refresh();
} else if (!isEnd) {
onReload();
}
if (enableMultiSelect.value) {
rxCount.value = 0;
enableMultiSelect.value = false;
}
}
}
// abstract class SetMultiSelectController<R, T, I>
// extends CommonListController<R, T>
// with MultiSelectMixin<T>, SetCommonMultiSelectMixin<T, I> {}
// mixin SetCommonMultiSelectMixin<T, R> on MultiSelectMixin<T> {
// Rx<LoadingState<List<T>?>> get loadingState;
// RxSet<R> get selected;
// @override
// int get checkedCount => selected.length;
// R getId(T item);
// @override
// void onSelect(T item, [bool disableSelect = true]) {
// final id = getId(item);
// if (selected.contains(id)) {
// selected.remove(id);
// } else {
// selected.add(id);
// }
// loadingState.refresh();
// if (disableSelect) {
// if (checkedCount == 0) {
// enableMultiSelect.value = false;
// }
// } else {
// allSelected.value = checkedCount == loadingState.value.data!.length;
// }
// }
// @override
// void handleSelect([bool checked = false, bool disableSelect = true]) {
// if (loadingState.value.isSuccess) {
// final list = loadingState.value.data;
// if (list?.isNotEmpty == true) {
// if (checked) {
// selected.addAll(list!.map(getId));
// } else {
// selected.clear();
// }
// loadingState.refresh();
// }
// }
// if (disableSelect && !checked) {
// enableMultiSelect.value = false;
// }
// }
// }

View File

@@ -97,7 +97,7 @@ class _CreateVotePageState extends State<CreateVotePage> {
..add( ..add(
_buildInput( _buildInput(
theme, theme,
key: ValueKey(e.hashCode), key: ObjectKey(e),
showDel: showDel, showDel: showDel,
onDel: () { onDel: () {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();

View File

@@ -115,11 +115,11 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
), ),
onPressed: () { onPressed: () {
if (_favNoteController.checkedCount.value != 0) { if (_favNoteController.checkedCount != 0) {
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '确定删除已选中的笔记吗?', title: '确定删除已选中的笔记吗?',
onConfirm: _favNoteController.onRemove, onConfirm: _favNoteController.onConfirm,
); );
} }
}, },

View File

@@ -34,24 +34,15 @@ class FavNoteController
: FavHttp.noteList(page: page); : FavHttp.noteList(page: page);
} }
Future<void> onRemove() async { @override
List<FavNoteItemModel> dataList = loadingState.value.data!; Future<void> onConfirm() async {
Set<FavNoteItemModel> removeList = dataList Set<FavNoteItemModel> removeList = allChecked.toSet();
.where((item) => item.checked == true)
.toSet();
final res = await FavHttp.delNote( final res = await FavHttp.delNote(
isPublish: isPublish, isPublish: isPublish,
noteIds: removeList noteIds: removeList.map((item) => isPublish ? item.cvid : item.noteId),
.map((item) => isPublish ? item.cvid : item.noteId)
.toList(),
); );
if (res['status']) { if (res['status']) {
List<FavNoteItemModel> remainList = dataList afterDelete(removeList);
.toSet()
.difference(removeList)
.toList();
loadingState.value = Success(remainList);
enableMultiSelect.value = false;
SmartDialog.showToast('删除成功'); SmartDialog.showToast('删除成功');
} else { } else {
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
@@ -59,7 +50,7 @@ class FavNoteController
} }
void onDisable() { void onDisable() {
if (checkedCount.value != 0) { if (checkedCount != 0) {
handleSelect(); handleSelect();
} }
enableMultiSelect.value = false; enableMultiSelect.value = false;

View File

@@ -128,8 +128,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
if (_favPgcController.checkedCount.value != if (_favPgcController.checkedCount != 0) {
0) {
_favPgcController.onUpdateList( _favPgcController.onUpdateList(
item.followStatus, item.followStatus,
); );

View File

@@ -48,7 +48,7 @@ class FavPgcController
); );
void onDisable() { void onDisable() {
if (checkedCount.value != 0) { if (checkedCount != 0) {
handleSelect(); handleSelect();
} }
enableMultiSelect.value = false; enableMultiSelect.value = false;
@@ -65,22 +65,19 @@ class FavPgcController
SmartDialog.showToast(result['msg']); SmartDialog.showToast(result['msg']);
} }
@override
void onConfirm() {
assert(false, 'call onUpdateList');
}
Future<void> onUpdateList(int followStatus) async { Future<void> onUpdateList(int followStatus) async {
List<FavPgcItemModel> dataList = loadingState.value.data!; final updateList = allChecked.toSet();
Set<FavPgcItemModel> updateList = dataList
.where((item) => item.checked == true)
.toSet();
final res = await VideoHttp.pgcUpdate( final res = await VideoHttp.pgcUpdate(
seasonId: updateList.map((item) => item.seasonId).toList(), seasonId: updateList.map((item) => item.seasonId).toList(),
status: followStatus, status: followStatus,
); );
if (res['status']) { if (res['status']) {
List<FavPgcItemModel> remainList = dataList afterDelete(updateList);
.toSet()
.difference(updateList)
.toList();
loadingState.value = Success(remainList);
enableMultiSelect.value = false;
try { try {
final ctr = Get.find<FavPgcController>(tag: '$type$followStatus'); final ctr = Get.find<FavPgcController>(tag: '$type$followStatus');
if (ctr.loadingState.value.isSuccess) { if (ctr.loadingState.value.isSuccess) {

View File

@@ -18,7 +18,7 @@ class FavPgcItem extends StatelessWidget {
}); });
final FavPgcItemModel item; final FavPgcItemModel item;
final MultiSelectController ctr; final MultiSelectMixin ctr;
final VoidCallback onSelect; final VoidCallback onSelect;
final VoidCallback onUpdateStatus; final VoidCallback onUpdateStatus;

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/http/fav.dart'; import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/fav_order_type.dart'; import 'package:PiliPlus/models/common/fav_order_type.dart';
@@ -11,7 +12,6 @@ import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.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 'package:get/get.dart';
@@ -90,61 +90,28 @@ class FavDetailController
order: order.value, order: order.value,
); );
void onDelChecked(BuildContext context) { @override
showDialog( void onConfirm() {
context: context, showConfirmDialog(
builder: (context) { context: Get.context!,
return AlertDialog( content: '确认删除所选收藏吗?',
title: const Text('提示'), title: '提示',
content: const Text('确认删除所选收藏吗?'), onConfirm: () async {
actions: [ final checked = allChecked.toSet();
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () async {
Get.back();
List<FavDetailItemModel> list = loadingState.value.data!
.where((e) => e.checked == true)
.toList();
var result = await FavHttp.favVideo( var result = await FavHttp.favVideo(
resources: list resources: checked.map((item) => '${item.id}:${item.type}').join(','),
.map((item) => '${item.id}:${item.type}')
.join(','),
delIds: mediaId.toString(), delIds: mediaId.toString(),
); );
if (result['status']) { if (result['status']) {
List<FavDetailItemModel> dataList = loadingState.value.data!; afterDelete(checked);
List<FavDetailItemModel> remainList = dataList
.toSet()
.difference(list.toSet())
.toList();
folderInfo folderInfo
..value.mediaCount -= list.length ..value.mediaCount -= checked.length
..refresh(); ..refresh();
if (remainList.isNotEmpty) {
loadingState.value = Success(remainList);
} else {
onReload();
}
SmartDialog.showToast('取消收藏'); SmartDialog.showToast('取消收藏');
checkedCount.value = 0;
enableMultiSelect.value = false;
} else { } else {
SmartDialog.showToast(result['msg']); SmartDialog.showToast(result['msg']);
} }
}, },
child: const Text('确认'),
),
],
);
},
); );
} }

View File

@@ -113,7 +113,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
Obx( Obx(
() { () {
return Text( return Text(
'已选: ${_favDetailController.checkedCount.value}', '已选: ${_favDetailController.checkedCount}',
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 15),
); );
}, },
@@ -327,7 +327,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
style: TextButton.styleFrom( style: TextButton.styleFrom(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
), ),
onPressed: () => _favDetailController.onDelChecked(context), onPressed: _favDetailController.onConfirm,
child: Text( child: Text(
'删除', '删除',
style: TextStyle(color: theme.colorScheme.error), style: TextStyle(color: theme.colorScheme.error),

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models_new/history/data.dart'; import 'package:PiliPlus/models_new/history/data.dart';
@@ -26,6 +27,12 @@ class HistoryController
int? max; int? max;
int? viewAt; int? viewAt;
@override
RxInt get rxCount => baseCtr.checkedCount;
@override
RxBool get enableMultiSelect => baseCtr.enableMultiSelect;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@@ -40,36 +47,6 @@ class HistoryController
return super.onRefresh(); return super.onRefresh();
} }
@override
void onSelect(HistoryItemModel item, [bool disableSelect = true]) {
List<HistoryItemModel> list = loadingState.value.data!;
item.checked = !(item.checked ?? false);
baseCtr.checkedCount.value = list
.where((item) => item.checked == true)
.length;
loadingState.refresh();
if (baseCtr.checkedCount.value == 0) {
baseCtr.enableMultiSelect.value = false;
}
}
@override
void handleSelect([bool checked = false, bool disableSelect = true]) {
if (loadingState.value.isSuccess) {
List<HistoryItemModel>? list = loadingState.value.data;
if (list?.isNotEmpty == true) {
for (HistoryItemModel item in list!) {
item.checked = checked;
}
baseCtr.checkedCount.value = checked ? list.length : 0;
loadingState.refresh();
}
}
if (!checked) {
baseCtr.enableMultiSelect.value = false;
}
}
@override @override
List<HistoryItemModel>? getDataList(HistoryData response) { List<HistoryItemModel>? getDataList(HistoryData response) {
return response.list; return response.list;
@@ -108,82 +85,43 @@ class HistoryController
// 删除某条历史记录 // 删除某条历史记录
void delHistory(HistoryItemModel item) { void delHistory(HistoryItemModel item) {
_onDelete([item]); _onDelete({item});
} }
// 删除已看历史记录 // 删除已看历史记录
void onDelHistory() { void onDelHistory() {
if (loadingState.value.isSuccess) { if (loadingState.value.isSuccess) {
List<HistoryItemModel> list = loadingState.value.data! final set = loadingState.value.data!
.where((e) => e.progress == -1) .where((e) => e.progress == -1)
.toList(); .toSet();
if (list.isNotEmpty) { if (set.isNotEmpty) {
_onDelete(list); _onDelete(set);
} else { } else {
SmartDialog.showToast('无已看记录'); SmartDialog.showToast('无已看记录');
} }
} }
} }
Future<void> _onDelete(List<HistoryItemModel> result) async { Future<void> _onDelete(Set<HistoryItemModel> result) async {
SmartDialog.showLoading(msg: '请求中'); SmartDialog.showLoading(msg: '请求中');
List<String> kidList = result.map((item) { final response = await UserHttp.delHistory(
return '${item.history.business}_${item.kid}'; result.map((item) => '${item.history.business}_${item.kid}'),
}).toList(); );
var response = await UserHttp.delHistory(kidList);
if (response['status']) { if (response['status']) {
List<HistoryItemModel> remainList = loadingState.value.data! afterDelete(result);
.toSet()
.difference(result.toSet())
.toList();
if (remainList.isNotEmpty) {
loadingState.value = Success(remainList);
} else {
onReload();
}
if (baseCtr.enableMultiSelect.value) {
baseCtr.checkedCount.value = 0;
baseCtr.enableMultiSelect.value = false;
}
} }
SmartDialog.dismiss(); SmartDialog.dismiss();
SmartDialog.showToast(response['msg']); SmartDialog.showToast(response['msg']);
} }
// 删除选中的记录 // 删除选中的记录
void onDelCheckedHistory(BuildContext context) { @override
showDialog( void onConfirm() {
context: context, showConfirmDialog(
builder: (context) { context: Get.context!,
return AlertDialog( content: '确认删除所选历史记录吗?',
title: const Text('提示'), title: '提示',
content: const Text('确认删除所选历史记录吗?'), onConfirm: () => _onDelete(allChecked.toSet()),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () {
Get.back();
if (loadingState.value.isSuccess) {
_onDelete(
loadingState.value.data!
.where((e) => e.checked == true)
.toList(),
);
}
},
child: const Text('确认'),
),
],
);
},
); );
} }

View File

@@ -1,5 +1,6 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart';
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart'; import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
@@ -65,9 +66,10 @@ class _HistoryPageState extends State<HistoryPage>
} }
}, },
child: Scaffold( child: Scaffold(
appBar: AppBarWidget( appBar: MultiSelectAppBarWidget(
visible: enableMultiSelect, visible: enableMultiSelect,
child1: AppBar( ctr: currCtr(),
child: AppBar(
title: const Text('观看记录'), title: const Text('观看记录'),
bottom: _buildPauseTip, bottom: _buildPauseTip,
actions: [ actions: [
@@ -153,36 +155,6 @@ class _HistoryPageState extends State<HistoryPage>
const SizedBox(width: 6), const SizedBox(width: 6),
], ],
), ),
child2: AppBar(
bottom: _buildPauseTip,
leading: IconButton(
tooltip: '取消',
onPressed: currCtr().handleSelect,
icon: const Icon(Icons.close_outlined),
),
title: Obx(
() => Text(
'已选: ${_historyController.baseCtr.checkedCount.value}',
),
),
actions: [
TextButton(
onPressed: () => currCtr().handleSelect(true),
child: const Text('全选'),
),
TextButton(
onPressed: () =>
currCtr().onDelCheckedHistory(context),
child: Text(
'删除',
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
),
const SizedBox(width: 6),
],
),
), ),
body: Obx( body: Obx(
() => _historyController.tabs.isNotEmpty () => _historyController.tabs.isNotEmpty
@@ -288,8 +260,7 @@ class _HistoryPageState extends State<HistoryPage>
final item = response[index]; final item = response[index];
return HistoryItem( return HistoryItem(
item: item, item: item,
ctr: _historyController.baseCtr, ctr: _historyController,
onChoose: () => _historyController.onSelect(item),
onDelete: (kid, business) => onDelete: (kid, business) =>
_historyController.delHistory(item), _historyController.delHistory(item),
); );
@@ -359,32 +330,3 @@ class _HistoryPageState extends State<HistoryPage>
@override @override
bool get wantKeepAlive => widget.type != null; bool get wantKeepAlive => widget.type != null;
} }
class AppBarWidget extends StatelessWidget implements PreferredSizeWidget {
const AppBarWidget({
required this.child1,
required this.child2,
required this.visible,
super.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

@@ -1,6 +1,5 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart'; import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart';
import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/http/search.dart';
@@ -9,7 +8,6 @@ import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models/common/history_business_type.dart'; import 'package:PiliPlus/models/common/history_business_type.dart';
import 'package:PiliPlus/models_new/history/list.dart'; import 'package:PiliPlus/models_new/history/list.dart';
import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/pages/common/multi_select_controller.dart';
import 'package:PiliPlus/pages/history/base_controller.dart';
import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/date_util.dart';
import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/duration_util.dart';
import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/feed_back.dart';
@@ -23,15 +21,13 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart
class HistoryItem extends StatelessWidget { class HistoryItem extends StatelessWidget {
final HistoryItemModel item; final HistoryItemModel item;
final dynamic ctr; final MultiSelectMixin ctr;
final Function? onChoose; final void Function(int kid, String business) onDelete;
final Function(dynamic kid, dynamic business) onDelete;
const HistoryItem({ const HistoryItem({
super.key, super.key,
required this.item, required this.item,
this.ctr, required this.ctr,
this.onChoose,
required this.onDelete, required this.onDelete,
}); });
@@ -45,12 +41,10 @@ class HistoryItem extends StatelessWidget {
type: MaterialType.transparency, type: MaterialType.transparency,
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
if (ctr is MultiSelectController || ctr is HistoryBaseController) {
if (ctr.enableMultiSelect.value) { if (ctr.enableMultiSelect.value) {
onChoose?.call(); ctr.onSelect(item);
return; return;
} }
}
if (item.history.business?.contains('article') == true) { if (item.history.business?.contains('article') == true) {
PageUtils.toDupNamed( PageUtils.toDupNamed(
'/articlePage', '/articlePage',
@@ -97,18 +91,16 @@ class HistoryItem extends StatelessWidget {
} }
}, },
onLongPress: () { onLongPress: () {
if (ctr is MultiSelectController || ctr is HistoryBaseController) {
if (!ctr.enableMultiSelect.value) { if (!ctr.enableMultiSelect.value) {
ctr.enableMultiSelect.value = true; ctr.enableMultiSelect.value = true;
onChoose?.call(); ctr.onSelect(item);
} }
return; return;
} // imageSaveDialog(
imageSaveDialog( // title: item.title,
title: item.title, // cover: item.cover,
cover: item.cover, // bvid: bvid,
bvid: bvid, // );
);
}, },
child: Stack( child: Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
@@ -205,7 +197,7 @@ class HistoryItem extends StatelessWidget {
), ),
onPressed: () { onPressed: () {
feedBack(); feedBack();
onChoose?.call(); ctr.onSelect(item);
}, },
icon: Icon( icon: Icon(
Icons.done_all_outlined, Icons.done_all_outlined,
@@ -287,7 +279,7 @@ class HistoryItem extends StatelessWidget {
), ),
PopupMenuItem<String>( PopupMenuItem<String>(
onTap: () => onTap: () =>
onDelete(item.kid, item.history.business), onDelete(item.kid!, item.history.business!),
height: 35, height: 35,
child: const Row( child: const Row(
children: [ children: [

View File

@@ -1,12 +1,19 @@
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models_new/history/data.dart'; import 'package:PiliPlus/models_new/history/data.dart';
import 'package:PiliPlus/models_new/history/list.dart'; import 'package:PiliPlus/models_new/history/list.dart';
import 'package:PiliPlus/pages/common/common_search_controller.dart'; import 'package:PiliPlus/pages/common/common_search_controller.dart';
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class HistorySearchController class HistorySearchController
extends CommonSearchController<HistoryData, HistoryItemModel> { extends CommonSearchController<HistoryData, HistoryItemModel>
with
MultiSelectMixin<HistoryItemModel>,
CommonMultiSelectMixin,
DeleteItemMixin {
@override @override
Future<LoadingState<HistoryData>> customGetData() => UserHttp.searchHistory( Future<LoadingState<HistoryData>> customGetData() => UserHttp.searchHistory(
pn: page, pn: page,
@@ -18,12 +25,14 @@ class HistorySearchController
return response.list; return response.list;
} }
Future<void> onDelHistory(int index, kid, business) async { Future<void> onDelHistory(int index, kid, String business) async {
String resKid = 'archive_$kid'; final String resKid;
if (business == 'live') { if (business == 'live') {
resKid = 'live_$kid'; resKid = 'live_$kid';
} else if (business.contains('article')) { } else if (business.contains('article')) {
resKid = 'article_$kid'; resKid = 'article_$kid';
} else {
resKid = 'archive_$kid';
} }
var res = await UserHttp.delHistory([resKid]); var res = await UserHttp.delHistory([resKid]);
@@ -34,4 +43,26 @@ class HistorySearchController
} }
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
} }
@override
void onConfirm() {
showConfirmDialog(
context: Get.context!,
content: '确认删除所选历史记录吗?',
title: '提示',
onConfirm: () async {
SmartDialog.showLoading(msg: '请求中');
final result = allChecked.toSet();
final kidList = result.map(
(item) => '${item.history.business!}_${item.kid!}',
);
var response = await UserHttp.delHistory(kidList);
if (response['status']) {
afterDelete(result);
}
SmartDialog.dismiss();
SmartDialog.showToast(response['msg']);
},
);
}
} }

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
import 'package:PiliPlus/models_new/history/data.dart'; import 'package:PiliPlus/models_new/history/data.dart';
import 'package:PiliPlus/models_new/history/list.dart'; import 'package:PiliPlus/models_new/history/list.dart';
import 'package:PiliPlus/pages/common/common_search_page.dart'; import 'package:PiliPlus/pages/common/common_search_page.dart';
@@ -28,6 +29,31 @@ class _HistorySearchPageState
tag: Utils.generateRandomString(8), tag: Utils.generateRandomString(8),
); );
@override
Widget build(BuildContext context) {
// TODO: refa
return Obx(() {
final parent = super.build(context) as Scaffold;
final enableMultiSelect = controller.enableMultiSelect.value;
return PopScope(
canPop: !enableMultiSelect,
onPopInvokedWithResult: (didPop, result) {
if (enableMultiSelect) {
controller.handleSelect();
}
},
child: Scaffold(
resizeToAvoidBottomInset: parent.resizeToAvoidBottomInset,
appBar: MultiSelectAppBarWidget(
ctr: controller,
child: parent.appBar as AppBar,
),
body: parent.body,
),
);
});
}
@override @override
Widget buildList(List<HistoryItemModel> list) { Widget buildList(List<HistoryItemModel> list) {
return SliverGrid( return SliverGrid(
@@ -42,10 +68,8 @@ class _HistorySearchPageState
return HistoryItem( return HistoryItem(
item: item, item: item,
ctr: controller, ctr: controller,
onChoose: null, onDelete: (kid, business) =>
onDelete: (kid, business) { controller.onDelHistory(index, kid, business),
controller.onDelHistory(index, kid, business);
},
); );
}, },
), ),

View File

@@ -27,6 +27,12 @@ class LaterController extends MultiSelectController<LaterData, LaterItemModel> {
final LaterBaseController baseCtr = Get.put(LaterBaseController()); final LaterBaseController baseCtr = Get.put(LaterBaseController());
@override
RxBool get enableMultiSelect => baseCtr.enableMultiSelect;
@override
RxInt get rxCount => baseCtr.checkedCount;
@override @override
Future<LoadingState<LaterData>> customGetData() => UserHttp.seeYouLater( Future<LoadingState<LaterData>> customGetData() => UserHttp.seeYouLater(
page: page, page: page,
@@ -34,36 +40,6 @@ class LaterController extends MultiSelectController<LaterData, LaterItemModel> {
asc: asc.value, asc: asc.value,
); );
@override
void onSelect(LaterItemModel item, [bool disableSelect = true]) {
List<LaterItemModel> list = loadingState.value.data!;
item.checked = !(item.checked ?? false);
baseCtr.checkedCount.value = list
.where((item) => item.checked == true)
.length;
loadingState.refresh();
if (baseCtr.checkedCount.value == 0) {
baseCtr.enableMultiSelect.value = false;
}
}
@override
void handleSelect([bool checked = false, bool disableSelect = true]) {
if (loadingState.value.isSuccess) {
List<LaterItemModel>? list = loadingState.value.data;
if (list?.isNotEmpty == true) {
for (LaterItemModel item in list!) {
item.checked = checked;
}
baseCtr.checkedCount.value = checked ? list.length : 0;
loadingState.refresh();
}
}
if (!checked) {
baseCtr.enableMultiSelect.value = false;
}
}
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@@ -102,7 +78,7 @@ class LaterController extends MultiSelectController<LaterData, LaterItemModel> {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
Get.back(); Get.back();
var res = await UserHttp.toViewDel(aids: [aid]); final res = await UserHttp.toViewDel(aids: {aid!});
if (res['status']) { if (res['status']) {
baseCtr.counts[laterViewType] = baseCtr.counts[laterViewType] =
baseCtr.counts[laterViewType]! - 1; baseCtr.counts[laterViewType]! - 1;
@@ -148,51 +124,24 @@ class LaterController extends MultiSelectController<LaterData, LaterItemModel> {
); );
} }
void onDelChecked(BuildContext context) { @override
showDialog( void onConfirm() {
context: context, showConfirmDialog(
builder: (context) { context: Get.context!,
return AlertDialog( content: '确认删除所选稍后再看吗?',
title: const Text('提示'), title: '提示',
content: const Text('确认删除所选稍后再看吗?'), onConfirm: () => _onDelete(allChecked.toSet()),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () {
Get.back();
_onDelete(
loadingState.value.data!
.where((e) => e.checked == true)
.toList(),
);
},
child: const Text('确认'),
),
],
);
},
); );
} }
Future<void> _onDelete(List<LaterItemModel> result) async { Future<void> _onDelete(Set<LaterItemModel> result) async {
SmartDialog.showLoading(msg: '请求中'); SmartDialog.showLoading(msg: '请求中');
List<int?> aids = result.map((item) => item.aid).toList(); final res = await UserHttp.toViewDel(aids: result.map((item) => item.aid!));
var res = await UserHttp.toViewDel(aids: aids);
if (res['status']) { if (res['status']) {
Set<LaterItemModel> remainList = loadingState.value.data! afterDelete(result);
.toSet()
.difference(result.toSet());
baseCtr.counts[laterViewType] = baseCtr.counts[laterViewType] =
baseCtr.counts[laterViewType]! - aids.length; baseCtr.counts[laterViewType]! - result.length;
loadingState.value = Success(remainList.toList());
if (baseCtr.enableMultiSelect.value) { if (baseCtr.enableMultiSelect.value) {
baseCtr.checkedCount.value = 0; baseCtr.checkedCount.value = 0;
baseCtr.enableMultiSelect.value = false; baseCtr.enableMultiSelect.value = false;

View File

@@ -1,8 +1,8 @@
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/models/common/later_view_type.dart'; import 'package:PiliPlus/models/common/later_view_type.dart';
import 'package:PiliPlus/models_new/later/data.dart'; import 'package:PiliPlus/models_new/later/data.dart';
import 'package:PiliPlus/models_new/later/list.dart'; import 'package:PiliPlus/models_new/later/list.dart';
import 'package:PiliPlus/pages/history/view.dart' show AppBarWidget;
import 'package:PiliPlus/pages/later/base_controller.dart'; import 'package:PiliPlus/pages/later/base_controller.dart';
import 'package:PiliPlus/pages/later/controller.dart'; import 'package:PiliPlus/pages/later/controller.dart';
import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts.dart';
@@ -128,9 +128,54 @@ class _LaterPageState extends State<LaterPage>
final theme = Theme.of(context); final theme = Theme.of(context);
Color color = theme.colorScheme.secondary; Color color = theme.colorScheme.secondary;
return AppBarWidget( return MultiSelectAppBarWidget(
visible: enableMultiSelect, visible: enableMultiSelect,
child1: AppBar( ctr: currCtr(),
children: [
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () {
final ctr = currCtr();
RequestUtils.onCopyOrMove<LaterData, LaterItemModel>(
context: context,
isCopy: true,
ctr: ctr,
mediaId: null,
mid: ctr.accountService.mid,
);
},
child: Text(
'复制',
style: TextStyle(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () {
final ctr = currCtr();
RequestUtils.onCopyOrMove<LaterData, LaterItemModel>(
context: context,
isCopy: false,
ctr: ctr,
mediaId: null,
mid: ctr.accountService.mid,
);
},
child: Text(
'移动',
style: TextStyle(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
],
child: AppBar(
title: const Text('稍后再看'), title: const Text('稍后再看'),
actions: [ actions: [
IconButton( IconButton(
@@ -251,76 +296,6 @@ class _LaterPageState extends State<LaterPage>
const SizedBox(width: 8), const SizedBox(width: 8),
], ],
), ),
child2: AppBar(
leading: IconButton(
tooltip: '取消',
onPressed: currCtr().handleSelect,
icon: const Icon(Icons.close_outlined),
),
title: Obx(() => Text('已选: ${_baseCtr.checkedCount.value}')),
actions: [
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () => currCtr().handleSelect(true),
child: const Text('全选'),
),
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () {
final ctr = currCtr();
RequestUtils.onCopyOrMove<LaterData, LaterItemModel>(
context: context,
isCopy: true,
ctr: ctr,
mediaId: null,
mid: ctr.accountService.mid,
);
},
child: Text(
'复制',
style: TextStyle(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () {
final ctr = currCtr();
RequestUtils.onCopyOrMove<LaterData, LaterItemModel>(
context: context,
isCopy: false,
ctr: ctr,
mediaId: null,
mid: ctr.accountService.mid,
);
},
child: Text(
'移动',
style: TextStyle(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
TextButton(
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
),
onPressed: () => currCtr().onDelChecked(context),
child: Text(
'移除',
style: TextStyle(color: theme.colorScheme.error),
),
),
const SizedBox(width: 6),
],
),
); );
} }
} }

View File

@@ -23,7 +23,7 @@ class LaterSearchController
return response.list; return response.list;
} }
Future<void> toViewDel(BuildContext context, int index, aid) async { Future<void> toViewDel(BuildContext context, int index, int aid) async {
var res = await UserHttp.toViewDel(aids: [aid]); var res = await UserHttp.toViewDel(aids: [aid]);
if (res['status']) { if (res['status']) {
loadingState.value.data!.removeAt(index); loadingState.value.data!.removeAt(index);
@@ -31,4 +31,25 @@ class LaterSearchController
} }
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
} }
// @override
// void onConfirm() {
// showConfirmDialog(
// context: Get.context!,
// content: '确认删除所选稍后再看吗?',
// title: '提示',
// onConfirm: () async {
// final result = allChecked.toSet();
// SmartDialog.showLoading(msg: '请求中');
// var res = await UserHttp.toViewDel(
// aids: result.map((item) => item.aid!),
// );
// if (res['status']) {
// afterDelete(result);
// }
// SmartDialog.dismiss();
// SmartDialog.showToast(res['msg']);
// },
// );
// }
} }

View File

@@ -27,6 +27,31 @@ class _LaterSearchPageState
tag: Utils.generateRandomString(8), tag: Utils.generateRandomString(8),
); );
// @override
// Widget build(BuildContext context) {
// // TODO: refa
// return Obx(() {
// final parent = super.build(context) as Scaffold;
// final enableMultiSelect = controller.enableMultiSelect.value;
// return PopScope(
// canPop: !enableMultiSelect,
// onPopInvokedWithResult: (didPop, result) {
// if (enableMultiSelect) {
// controller.handleSelect();
// }
// },
// child: Scaffold(
// resizeToAvoidBottomInset: parent.resizeToAvoidBottomInset,
// appBar: MultiSelectAppBarWidget(
// ctr: controller,
// child: parent.appBar as AppBar,
// ),
// body: parent.body,
// ),
// );
// });
// }
@override @override
Widget buildList(List<LaterItemModel> list) { Widget buildList(List<LaterItemModel> list) {
return SliverGrid( return SliverGrid(
@@ -73,7 +98,7 @@ class _LaterSearchPageState
onConfirm: () => controller.toViewDel( onConfirm: () => controller.toViewDel(
context, context,
index, index,
item.aid, item.aid!,
), ),
), ),
icon: Icons.clear, icon: Icons.clear,

View File

@@ -14,7 +14,6 @@ import 'package:PiliPlus/models/common/reply/reply_sort_type.dart';
import 'package:PiliPlus/models/common/settings_type.dart'; import 'package:PiliPlus/models/common/settings_type.dart';
import 'package:PiliPlus/models/common/super_resolution_type.dart'; import 'package:PiliPlus/models/common/super_resolution_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
import 'package:PiliPlus/pages/common/common_slide_page.dart'; import 'package:PiliPlus/pages/common/common_slide_page.dart';
import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/home/controller.dart';
import 'package:PiliPlus/pages/hot/controller.dart'; import 'package:PiliPlus/pages/hot/controller.dart';
@@ -786,9 +785,8 @@ List<SettingsModel> get extraSettings => [
onTap: () async { onTap: () async {
if (Accounts.main.isLogin) { if (Accounts.main.isLogin) {
final res = await FavHttp.allFavFolders(Accounts.main.mid); final res = await FavHttp.allFavFolders(Accounts.main.mid);
if (res['status']) { if (res.isSuccess) {
final FavFolderData data = res['data']; final list = res.data.list;
final list = data.list;
if (list.isNullOrEmpty) { if (list.isNullOrEmpty) {
return; return;
} }
@@ -821,7 +819,7 @@ List<SettingsModel> get extraSettings => [
), ),
); );
} else { } else {
SmartDialog.showToast('${res['msg']}'); res.toast();
} }
} }
}, },

View File

@@ -18,7 +18,6 @@ import 'package:PiliPlus/http/validate.dart';
import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/models/login/model.dart'; import 'package:PiliPlus/models/login/model.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/pages/common/multi_select_controller.dart';
import 'package:PiliPlus/pages/dynamics_tab/controller.dart'; import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
import 'package:PiliPlus/pages/group_panel/view.dart'; import 'package:PiliPlus/pages/group_panel/view.dart';
@@ -376,11 +375,9 @@ class RequestUtils {
required dynamic mid, required dynamic mid,
}) { }) {
FavHttp.allFavFolders(mid).then((res) { FavHttp.allFavFolders(mid).then((res) {
if (context.mounted && if (context.mounted && res.dataOrNull?.list?.isNotEmpty == true) {
res['status'] && final list = res.data.list!;
(res['data'].list as List?)?.isNotEmpty == true) { int? checkedId;
List<FavFolderInfo> list = res['data'].list;
dynamic checkedId;
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@@ -392,7 +389,7 @@ class RequestUtils {
builder: (context) => Column( builder: (context) => Column(
children: List.generate(list.length, (index) { children: List.generate(list.length, (index) {
final item = list[index]; final item = list[index];
return RadioWidget( return RadioWidget<int>(
padding: const EdgeInsets.only(left: 14), padding: const EdgeInsets.only(left: 14),
title: item.title, title: item.title,
groupValue: checkedId, groupValue: checkedId,
@@ -421,9 +418,7 @@ class RequestUtils {
TextButton( TextButton(
onPressed: () { onPressed: () {
if (checkedId != null) { if (checkedId != null) {
List resources = ctr.loadingState.value.data! Set resources = ctr.allChecked.toSet();
.where((e) => e.checked == true)
.toList();
SmartDialog.showLoading(); SmartDialog.showLoading();
FavHttp.copyOrMoveFav( FavHttp.copyOrMoveFav(
isCopy: isCopy, isCopy: isCopy,
@@ -439,22 +434,20 @@ class RequestUtils {
.toList(), .toList(),
mid: isCopy ? mid : null, mid: isCopy ? mid : null,
).then((res) { ).then((res) {
if (res['status']) { if (res.isSuccess) {
ctr.handleSelect(false); ctr.handleSelect(false);
if (!isCopy) { if (!isCopy) {
List<T> dataList = ctr.loadingState.value.data!; ctr.loadingState.value.data!.removeWhere(
List<T> remainList = dataList resources.contains,
.toSet() );
.difference(resources.toSet()) ctr.loadingState.refresh();
.toList();
ctr.loadingState.value = Success(remainList);
} }
SmartDialog.dismiss(); SmartDialog.dismiss();
SmartDialog.showToast('${isCopy ? '复制' : '移动'}成功'); SmartDialog.showToast('${isCopy ? '复制' : '移动'}成功');
Get.back(); Get.back();
} else { } else {
SmartDialog.dismiss(); SmartDialog.dismiss();
SmartDialog.showToast('${res['msg']}'); res.toast();
} }
}); });
} }
@@ -466,7 +459,7 @@ class RequestUtils {
}, },
); );
} else { } else {
SmartDialog.showToast('${res['msg']}'); res.toast();
} }
}); });
} }