mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
split report dialog (#460)
This commit is contained in:
committed by
GitHub
parent
0b8e95477c
commit
7854c5e6b9
@@ -1,15 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
Widget radioWidget<T>({
|
class RadioWidget<T> extends StatelessWidget {
|
||||||
required T value,
|
final T value;
|
||||||
T? groupValue,
|
final T? groupValue;
|
||||||
required ValueChanged onChanged,
|
final ValueChanged<T?> onChanged;
|
||||||
required String title,
|
final String title;
|
||||||
EdgeInsetsGeometry? padding,
|
final EdgeInsetsGeometry? padding;
|
||||||
}) {
|
|
||||||
Widget child() => Row(
|
const RadioWidget({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
this.groupValue,
|
||||||
|
required this.onChanged,
|
||||||
|
required this.title,
|
||||||
|
this.padding,
|
||||||
|
});
|
||||||
|
|
||||||
|
Widget _child() => Row(
|
||||||
children: [
|
children: [
|
||||||
Radio(
|
Radio<T>(
|
||||||
value: value,
|
value: value,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
@@ -18,13 +27,67 @@ Widget radioWidget<T>({
|
|||||||
Text(title),
|
Text(title),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => onChanged(value),
|
onTap: () => onChanged(value),
|
||||||
child: padding != null
|
child: padding != null
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: padding,
|
padding: padding!,
|
||||||
child: child(),
|
child: _child(),
|
||||||
)
|
)
|
||||||
: child(),
|
: _child(),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WrapRadioOptionsGroup<T> extends StatelessWidget {
|
||||||
|
final String groupTitle;
|
||||||
|
final Map<T, String> options;
|
||||||
|
final T? selectedValue;
|
||||||
|
final ValueChanged<T?> onChanged;
|
||||||
|
final EdgeInsetsGeometry? itemPadding;
|
||||||
|
|
||||||
|
const WrapRadioOptionsGroup({
|
||||||
|
super.key,
|
||||||
|
required this.groupTitle,
|
||||||
|
required this.options,
|
||||||
|
required this.selectedValue,
|
||||||
|
required this.onChanged,
|
||||||
|
this.itemPadding,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (groupTitle.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 22),
|
||||||
|
child: Text(
|
||||||
|
groupTitle,
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Wrap(
|
||||||
|
children: options.entries.map((entry) {
|
||||||
|
return IntrinsicWidth(
|
||||||
|
child: RadioWidget<T>(
|
||||||
|
value: entry.key,
|
||||||
|
groupValue: selectedValue,
|
||||||
|
onChanged: onChanged,
|
||||||
|
title: entry.value,
|
||||||
|
padding: itemPadding ?? const EdgeInsets.only(right: 10),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.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';
|
||||||
|
|
||||||
@@ -16,149 +15,56 @@ void autoWrapReportDialog(
|
|||||||
late final key = GlobalKey<FormState>();
|
late final key = GlobalKey<FormState>();
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) => StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('举报'),
|
title: const Text('举报'),
|
||||||
titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22),
|
titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22),
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 5),
|
contentPadding: const EdgeInsets.symmetric(vertical: 5),
|
||||||
actionsPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 10),
|
actionsPadding:
|
||||||
content: Builder(
|
const EdgeInsets.only(left: 16, right: 16, bottom: 10),
|
||||||
builder: (context) {
|
content: Form(
|
||||||
return Column(
|
key: key,
|
||||||
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Material(
|
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: AnimatedSize(
|
child: AnimatedSize(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
const Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 22,
|
left: 22,
|
||||||
right: 22,
|
right: 22,
|
||||||
bottom: 5,
|
bottom: 5,
|
||||||
),
|
),
|
||||||
child: const Text('请选择举报的理由:'),
|
child: Text('请选择举报的理由:'),
|
||||||
),
|
),
|
||||||
...options.entries.map<Widget>((title) {
|
...options.entries.map(
|
||||||
return Column(
|
(entry) => WrapRadioOptionsGroup<int>(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
groupTitle: entry.key,
|
||||||
children: [
|
options: entry.value,
|
||||||
if (title.key.isNotEmpty)
|
selectedValue: reasonType,
|
||||||
Padding(
|
onChanged: (value) =>
|
||||||
padding: const EdgeInsets.only(left: 22),
|
setState(() => reasonType = value),
|
||||||
child: Text(
|
|
||||||
title.key,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12),
|
|
||||||
child: Wrap(
|
|
||||||
children: title.value.entries.map((opt) {
|
|
||||||
return IntrinsicWidth(
|
|
||||||
child: radioWidget<int>(
|
|
||||||
value: opt.key,
|
|
||||||
groupValue: reasonType,
|
|
||||||
title: opt.value,
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
right: 10),
|
|
||||||
onChanged: (value) {
|
|
||||||
reasonType = value;
|
|
||||||
if (context.mounted) {
|
|
||||||
(context as Element?)
|
|
||||||
?.markNeedsBuild();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
if (reasonType == 0)
|
if (reasonType == 0)
|
||||||
Padding(
|
ReasonField(
|
||||||
padding: const EdgeInsets.only(
|
onChanged: (value) => reasonDesc = value),
|
||||||
left: 22,
|
|
||||||
top: 5,
|
|
||||||
right: 22,
|
|
||||||
),
|
|
||||||
child: Form(
|
|
||||||
key: key,
|
|
||||||
child: TextFormField(
|
|
||||||
autofocus: true,
|
|
||||||
minLines: 4,
|
|
||||||
maxLines: 4,
|
|
||||||
initialValue: reasonDesc,
|
|
||||||
inputFormatters: [
|
|
||||||
LengthLimitingTextInputFormatter(60),
|
|
||||||
],
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText:
|
|
||||||
'为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息',
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
contentPadding: EdgeInsets.all(10),
|
|
||||||
),
|
|
||||||
onChanged: (value) => reasonDesc = value,
|
|
||||||
validator: (value) {
|
|
||||||
if (value.isNullOrEmpty) {
|
|
||||||
return '理由不能为空';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
BanUserCheckbox(onChanged: (value) => banUid = value),
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
banUid = !banUid;
|
|
||||||
(context as Element?)?.markNeedsBuild();
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 18, top: 10),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
size: 22,
|
|
||||||
banUid
|
|
||||||
? Icons.check_box_outlined
|
|
||||||
: Icons.check_box_outline_blank,
|
|
||||||
color: banUid
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
' 拉黑该用户',
|
|
||||||
style: TextStyle(
|
|
||||||
color: banUid
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: Get.back,
|
onPressed: Get.back,
|
||||||
@@ -193,9 +99,97 @@ void autoWrapReportDialog(
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReasonField extends StatefulWidget {
|
||||||
|
final ValueChanged<String> onChanged;
|
||||||
|
String? _validator(String? value) => value.isNullOrEmpty ? '理由不能为空' : null;
|
||||||
|
|
||||||
|
const ReasonField({super.key, required this.onChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReasonField> createState() => _ReasonFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReasonFieldState extends State<ReasonField> {
|
||||||
|
final _controller = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 22, top: 5, right: 22),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _controller,
|
||||||
|
autofocus: true,
|
||||||
|
minLines: 4,
|
||||||
|
maxLines: 4,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding: EdgeInsets.all(10),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
widget.onChanged(value);
|
||||||
|
},
|
||||||
|
validator: widget._validator,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BanUserCheckbox extends StatefulWidget {
|
||||||
|
final ValueChanged<bool> onChanged;
|
||||||
|
|
||||||
|
const BanUserCheckbox({super.key, required this.onChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BanUserCheckbox> createState() => _BanUserCheckboxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BanUserCheckboxState extends State<BanUserCheckbox> {
|
||||||
|
bool _banUid = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() => _banUid = !_banUid);
|
||||||
|
widget.onChanged(_banUid);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 18, top: 10),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
size: 22,
|
||||||
|
_banUid
|
||||||
|
? Icons.check_box_outlined
|
||||||
|
: Icons.check_box_outline_blank,
|
||||||
|
color: _banUid
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
' 拉黑该用户',
|
||||||
|
style: TextStyle(
|
||||||
|
color: _banUid ? Theme.of(context).colorScheme.primary : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ReportOptions {
|
class ReportOptions {
|
||||||
// from https://s1.hdslb.com/bfs/seed/jinkela/comment-h5/static/js/605.chunks.js
|
// from https://s1.hdslb.com/bfs/seed/jinkela/comment-h5/static/js/605.chunks.js
|
||||||
static Map<String, Map<int, String>> get commentReport => {
|
static Map<String, Map<int, String>> get commentReport => {
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ class _ReportPanelState extends State<ReportPanel> {
|
|||||||
const Text('举报理由(单选,非必选)'),
|
const Text('举报理由(单选,非必选)'),
|
||||||
...List.generate(
|
...List.generate(
|
||||||
5,
|
5,
|
||||||
(index) => radioWidget<int>(
|
(index) => RadioWidget<int>(
|
||||||
value: index,
|
value: index,
|
||||||
groupValue: _reasonV2,
|
groupValue: _reasonV2,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ class Utils {
|
|||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => Column(
|
builder: (context) => Column(
|
||||||
children: List.generate(list.length, (index) {
|
children: List.generate(list.length, (index) {
|
||||||
return radioWidget(
|
return RadioWidget(
|
||||||
padding: const EdgeInsets.only(left: 14),
|
padding: const EdgeInsets.only(left: 14),
|
||||||
title: list[index].title ?? '',
|
title: list[index].title ?? '',
|
||||||
groupValue: checkedId,
|
groupValue: checkedId,
|
||||||
|
|||||||
Reference in New Issue
Block a user