opt: GroupPanel (#940)

* opt: GroupPanel

* mod: int? operator
This commit is contained in:
My-Responsitories
2025-08-05 20:52:11 +08:00
committed by GitHub
parent e2639b6951
commit 170b2aa6d9
7 changed files with 44 additions and 63 deletions

View File

@@ -466,17 +466,16 @@ class MemberHttp {
} }
// 查询分组 // 查询分组
static Future followUpTags() async { static Future<LoadingState<List<MemberTagItemModel>>> followUpTags() async {
var res = await Request().get(Api.followUpTag); var res = await Request().get(Api.followUpTag);
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return { return Success(
'status': true, (res.data['data'] as List)
'data': res.data['data'] .map((e) => MemberTagItemModel.fromJson(e))
.map<MemberTagItemModel>((e) => MemberTagItemModel.fromJson(e))
.toList(), .toList(),
}; );
} else { } else {
return {'status': false, 'msg': res.data['message']}; return Error(res.data['message']);
} }
} }

View File

@@ -1,6 +1,4 @@
import 'package:PiliPlus/pages/common/multi_select/base.dart'; class MemberTagItemModel {
class MemberTagItemModel with MultiSelectData {
MemberTagItemModel({ MemberTagItemModel({
this.count, this.count,
this.name, this.name,

View File

@@ -13,6 +13,7 @@ import 'package:PiliPlus/models_new/article/article_info/data.dart';
import 'package:PiliPlus/models_new/article/article_view/data.dart'; import 'package:PiliPlus/models_new/article/article_view/data.dart';
import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart'; import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart';
import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/url_utils.dart'; import 'package:PiliPlus/utils/url_utils.dart';
import 'package:flutter/rendering.dart' show ScrollDirection; import 'package:flutter/rendering.dart' show ScrollDirection;
@@ -194,11 +195,10 @@ class ArticleController extends CommonDynController<MainListReply> {
: await FavHttp.communityAction(opusId: id, action: isFav ? 4 : 3); : await FavHttp.communityAction(opusId: id, action: isFav ? 4 : 3);
if (res['status']) { if (res['status']) {
favorite?.status = !isFav; favorite?.status = !isFav;
var count = favorite?.count ?? 0;
if (isFav) { if (isFav) {
favorite?.count = count - 1; favorite?.count--;
} else { } else {
favorite?.count = count + 1; favorite?.count++;
} }
stats.refresh(); stats.refresh();
SmartDialog.showToast('${isFav ? '取消' : ''}收藏成功'); SmartDialog.showToast('${isFav ? '取消' : ''}收藏成功');
@@ -216,11 +216,10 @@ class ArticleController extends CommonDynController<MainListReply> {
); );
if (res['status']) { if (res['status']) {
like?.status = !isLike; like?.status = !isLike;
int count = like?.count ?? 0;
if (isLike) { if (isLike) {
like?.count = count - 1; like?.count--;
} else { } else {
like?.count = count + 1; like?.count++;
} }
stats.refresh(); stats.refresh();
SmartDialog.showToast(!isLike ? '点赞成功' : '取消赞'); SmartDialog.showToast(!isLike ? '点赞成功' : '取消赞');

View File

@@ -32,11 +32,10 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
Future<void> queryFollowUpTags() async { Future<void> queryFollowUpTags() async {
var res = await MemberHttp.followUpTags(); var res = await MemberHttp.followUpTags();
if (res['status']) { if (res.isSuccess) {
tabs tabs
..clear() ..assign(MemberTagItemModel(name: '全部关注'))
..addAll(res['data']) ..addAll(res.data);
..insert(0, MemberTagItemModel(name: '全部关注'));
int initialIndex = 0; int initialIndex = 0;
if (tabController != null) { if (tabController != null) {
initialIndex = tabController!.index.clamp(0, tabs.length - 1); initialIndex = tabController!.index.clamp(0, tabs.length - 1);
@@ -49,7 +48,7 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
); );
followState.value = Success(tabs.hashCode); followState.value = Success(tabs.hashCode);
} else { } else {
followState.value = Error(res['msg']); followState.value = res;
} }
} }

View File

@@ -2,18 +2,19 @@ import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/member.dart'; import 'package:PiliPlus/http/member.dart';
import 'package:PiliPlus/models/member/tags.dart'; import 'package:PiliPlus/models/member/tags.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/feed_back.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
class GroupPanel extends StatefulWidget { class GroupPanel extends StatefulWidget {
final int? mid; final int mid;
final List? tags; final List? tags;
final ScrollController? scrollController; final ScrollController? scrollController;
const GroupPanel({ const GroupPanel({
super.key, super.key,
this.mid, required this.mid,
this.tags, this.tags,
this.scrollController, this.scrollController,
}); });
@@ -23,9 +24,9 @@ class GroupPanel extends StatefulWidget {
} }
class _GroupPanelState extends State<GroupPanel> { class _GroupPanelState extends State<GroupPanel> {
LoadingState<List<MemberTagItemModel>> loadingState = LoadingState<List<MemberTagItemModel>> loadingState = LoadingState.loading();
LoadingState<List<MemberTagItemModel>>.loading();
RxBool showDefaultBtn = true.obs; RxBool showDefaultBtn = true.obs;
late final Set<int> tags = widget.tags?.cast<int>().toSet() ?? {};
@override @override
void initState() { void initState() {
@@ -36,19 +37,8 @@ class _GroupPanelState extends State<GroupPanel> {
void _query() { void _query() {
MemberHttp.followUpTags().then((res) { MemberHttp.followUpTags().then((res) {
if (mounted) { if (mounted) {
if (res['status']) { loadingState = res..dataOrNull.removeFirstWhere((e) => e.tagid == 0);
List<MemberTagItemModel> tagsList = showDefaultBtn.value = tags.isEmpty;
(res['data'] as List<MemberTagItemModel>)
..removeWhere((item) => item.tagid == 0)
..map((item) {
return item.checked =
widget.tags?.contains(item.tagid) == true;
}).toList();
showDefaultBtn.value = !tagsList.any((e) => e.checked == true);
loadingState = Success(tagsList);
} else {
loadingState = Error(res['msg']);
}
setState(() {}); setState(() {});
} }
}); });
@@ -60,28 +50,14 @@ class _GroupPanelState extends State<GroupPanel> {
return; return;
} }
feedBack(); feedBack();
// 是否有选中的 有选中的带id没选使用默认0
List<MemberTagItemModel> tagsList = loadingState.data;
final bool anyHasChecked = tagsList.any(
(MemberTagItemModel e) => e.checked == true,
);
late List<int> tagidList;
if (anyHasChecked) {
final List<MemberTagItemModel> checkedList = tagsList
.where((MemberTagItemModel e) => e.checked == true)
.toList();
tagidList = checkedList.map<int>((e) => e.tagid!).toList();
} else {
tagidList = [0];
}
// 保存 // 保存
final res = await MemberHttp.addUsers( final res = await MemberHttp.addUsers(
widget.mid.toString(), widget.mid.toString(),
tagidList.join(','), tags.isEmpty ? '0' : tags.join(','),
); );
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
if (res['status']) { if (res['status']) {
Get.back(result: tagidList); Get.back(result: tags);
} }
} }
@@ -98,11 +74,16 @@ class _GroupPanelState extends State<GroupPanel> {
child: Builder( child: Builder(
builder: (context) { builder: (context) {
void onTap() { void onTap() {
item.checked = !item.checked!; final tagid = item.tagid!;
if (tags.contains(tagid)) {
tags.remove(tagid);
item.count--;
} else {
tags.add(tagid);
item.count++;
}
(context as Element).markNeedsBuild(); (context as Element).markNeedsBuild();
showDefaultBtn.value = !response.any( showDefaultBtn.value = tags.isEmpty;
(e) => e.checked == true,
);
} }
return ListTile( return ListTile(
@@ -110,15 +91,15 @@ class _GroupPanelState extends State<GroupPanel> {
dense: true, dense: true,
leading: const Icon(Icons.group_outlined), leading: const Icon(Icons.group_outlined),
minLeadingWidth: 0, minLeadingWidth: 0,
title: Text(item.name ?? ''), title: Text('${item.name} (${item.count})'),
subtitle: item.tip?.isNotEmpty == true subtitle: item.tip?.isNotEmpty == true
? Text(item.tip!) ? Text(item.tip!)
: null, : null,
trailing: Transform.scale( trailing: Transform.scale(
scale: 0.9, scale: 0.9,
child: Checkbox( child: Checkbox(
value: item.checked, value: tags.contains(item.tagid),
onChanged: (bool? checkValue) => onTap(), onChanged: (_) => onTap(),
), ),
), ),
); );

View File

@@ -19,6 +19,11 @@ extension ImageExtension on num? {
} }
} }
extension IntExt on int? {
int? operator +(int other) => this == null ? null : this! + other;
int? operator -(int other) => this == null ? null : this! - other;
}
extension ScrollControllerExt on ScrollController { extension ScrollControllerExt on ScrollController {
void animToTop() { void animToTop() {
if (!hasClients) return; if (!hasClients) return;

View File

@@ -158,7 +158,7 @@ class RequestUtils {
dense: true, dense: true,
onTap: () async { onTap: () async {
Get.back(); Get.back();
var result = await showModalBottomSheet<List?>( var result = await showModalBottomSheet<Set<int>>(
context: context, context: context,
useSafeArea: true, useSafeArea: true,
isScrollControlled: true, isScrollControlled: true,
@@ -190,7 +190,7 @@ class RequestUtils {
); );
}, },
); );
followStatus!['tag'] = result; followStatus!['tag'] = result?.toList();
if (result != null) { if (result != null) {
callback?.call(result.contains(-10) ? -10 : 2); callback?.call(result.contains(-10) ? -10 : 2);
} }