feat: space setting

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-06-07 21:21:03 +08:00
parent 6ec0d8f589
commit 19e4ae6c04
8 changed files with 390 additions and 32 deletions

View File

@@ -135,41 +135,53 @@ class _MemberPageState extends State<MemberPage> {
],
),
),
if (_userController.ownerMid != null &&
_userController.mid != _userController.ownerMid) ...[
const PopupMenuDivider(),
PopupMenuItem(
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog(
clipBehavior: Clip.hardEdge,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
content: MemberReportPanel(
name: _userController.username,
mid: _mid,
if (_userController.ownerMid != 0)
if (_userController.mid == _userController.ownerMid)
PopupMenuItem(
onTap: () => Get.toNamed('/spaceSetting'),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.settings_outlined, size: 19),
SizedBox(width: 10),
Text('空间设置'),
],
),
)
else ...[
const PopupMenuDivider(),
PopupMenuItem(
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog(
clipBehavior: Clip.hardEdge,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
content: MemberReportPanel(
name: _userController.username,
mid: _mid,
),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
size: 19,
color: theme.colorScheme.error,
),
const SizedBox(width: 10),
Text(
'举报',
style: TextStyle(color: theme.colorScheme.error),
),
],
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
size: 19,
color: theme.colorScheme.error,
),
const SizedBox(width: 10),
Text(
'举报',
style: TextStyle(color: theme.colorScheme.error),
),
],
),
),
],
],
],
),
const SizedBox(width: 4),

View File

@@ -0,0 +1,46 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models_new/space_setting/data.dart';
import 'package:PiliPlus/models_new/space_setting/privacy.dart';
import 'package:PiliPlus/pages/common/common_data_controller.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
class SpaceSettingController
extends CommonDataController<SpaceSettingData, Privacy?> {
@override
void onInit() {
super.onInit();
queryData();
}
bool? hasMod;
@override
bool customHandleResponse(
bool isRefresh, Success<SpaceSettingData> response) {
loadingState.value = Success(response.response.privacy);
return true;
}
@override
Future<LoadingState<SpaceSettingData>> customGetData() =>
UserHttp.spaceSetting();
Future<void> onMod() async {
if (hasMod == true && loadingState.value.isSuccess) {
Privacy? data = loadingState.value.data;
if (data != null) {
var res = await UserHttp.spaceSettingMod(
{
for (var e in data.list1) ...{e.key: e.value},
for (var e in data.list2) ...{e.key: e.value},
for (var e in data.list3) ...{e.key: e.value},
},
);
if (!res['status']) {
SmartDialog.showToast(res['msg']);
}
}
}
}
}

View File

@@ -0,0 +1,143 @@
import 'dart:math';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/space_setting/privacy.dart';
import 'package:PiliPlus/pages/space_setting/controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SpaceSettingPage extends StatefulWidget {
const SpaceSettingPage({super.key});
@override
State<SpaceSettingPage> createState() => _SpaceSettingPageState();
}
class _SpaceSettingPageState extends State<SpaceSettingPage> {
final _controller = Get.put(SpaceSettingController());
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('空间设置'),
),
body: Obx(() => _buildBody(theme, _controller.loadingState.value)),
);
}
@override
void dispose() {
_controller.onMod();
super.dispose();
}
Widget _buildBody(ThemeData theme, LoadingState<Privacy?> loadingState) {
return switch (loadingState) {
Loading() => const SizedBox.shrink(),
Success<Privacy?>(:var response) => response == null
? scrollErrorWidget(onReload: _controller.onReload)
: Builder(
builder: (context) {
final padding = MediaQuery.paddingOf(context);
final divider = Divider(
height: 1,
indent: max(16, padding.left),
color: theme.colorScheme.outline.withValues(alpha: 0.1),
);
final dividerL = SliverToBoxAdapter(
child: Divider(
height: 12,
thickness: 12,
color: theme.colorScheme.outline.withValues(alpha: 0.1),
),
);
return CustomScrollView(
slivers: [
dividerL,
SliverList.separated(
itemCount: response.list1.length,
itemBuilder: (context, index) {
return _item(response.list1[index]);
},
separatorBuilder: (context, index) => divider,
),
dividerL,
SliverList.separated(
itemCount: response.list2.length,
itemBuilder: (context, index) {
return _item(response.list2[index]);
},
separatorBuilder: (context, index) => divider,
),
dividerL,
SliverList.separated(
itemCount: response.list3.length,
itemBuilder: (context, index) {
return _item(response.list3[index]);
},
separatorBuilder: (context, index) => divider,
),
dividerL,
SliverToBoxAdapter(
child: SizedBox(
height: padding.bottom + 80,
),
),
],
);
},
),
Error(:var errMsg) => scrollErrorWidget(
errMsg: errMsg,
onReload: _controller.onReload,
),
};
}
Widget _item(SpaceSettingModel item) {
return Builder(
builder: (context) {
void onChanged([bool? value]) {
_controller.hasMod ??= true;
value ??= !item.boolVal;
item.value = item.isReverse
? value
? 0
: 1
: value
? 1
: 0;
(context as Element).markNeedsBuild();
}
return ListTile(
dense: true,
onTap: onChanged,
title: Text(
item.name,
style: const TextStyle(fontSize: 14),
),
trailing: Transform.scale(
alignment: Alignment.centerRight,
scale: 0.8,
child: Switch(
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
(Set<WidgetState> states) {
if (states.isNotEmpty && states.first == WidgetState.selected) {
return const Icon(Icons.done);
}
return null;
}),
value: item.boolVal,
onChanged: onChanged,
),
),
);
},
);
}
}