Browse Source

feat : 查询功能

dev
徐振升 3 days ago
parent
commit
4d50b91a16
  1. 6
      lib/data/repositories/problem_repository.dart
  2. 14
      lib/modules/problem/bindings/problem_form_binding.dart
  3. 69
      lib/modules/problem/controllers/problem_controller.dart
  4. 38
      lib/modules/problem/controllers/problem_form_controller.dart
  5. 27
      lib/modules/problem/views/problem_form_page.dart
  6. 34
      lib/modules/problem/views/problem_page.dart
  7. 122
      lib/modules/problem/views/widgets/compact_filter_bar.dart
  8. 66
      lib/modules/problem/views/widgets/current_filter_bar.dart
  9. 53
      lib/modules/problem/views/widgets/custom_object_dropdown.dart
  10. 97
      lib/modules/problem/views/widgets/history_filter_bar.dart
  11. 9
      lib/modules/problem/views/widgets/problem_card.dart

6
lib/data/repositories/problem_repository.dart

@ -44,17 +44,19 @@ class ProblemRepository extends GetxService {
///
/// - `startDate`/`endDate`
/// - `uploadStatus`'已上传', '未上传', '全部'
/// - `syncStatus`'已上传', '未上传', '全部'
/// - `bindStatus`'已绑定', '未绑定', '全部'
Future getProblems({
DateTime? startDate,
DateTime? endDate,
String? uploadStatus,
String? syncStatus,
String? bindStatus,
}) async {
return await sqliteProvider.getProblems(
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
);
}

14
lib/modules/problem/bindings/problem_form_binding.dart

@ -1,11 +1,23 @@
import 'package:get/get.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_form_controller.dart';
class ProblemFormBinding extends Bindings {
@override
void dependencies() {
final dynamic arguments = Get.arguments;
final bool readOnly = Get.parameters['isReadOnly'] == 'true';
Problem? problem;
if (arguments != null && arguments is Problem) {
problem = arguments;
}
Get.lazyPut<ProblemFormController>(
() => ProblemFormController(problemRepository: Get.find()),
() => ProblemFormController(
problemRepository: Get.find(),
problem: problem,
isReadOnly: readOnly,
),
);
}
}

69
lib/modules/problem/controllers/problem_controller.dart

@ -59,7 +59,9 @@ class ProblemController extends GetxController
final RxString currentBindFilter = '全部'.obs;
//
final Rx<DateTime> historyStartTime = DateTime.now().obs;
final Rx<DateTime> historyStartTime = DateTime.now()
.subtract(const Duration(days: 7))
.obs;
final Rx<DateTime> historyEndTime = DateTime.now().obs;
final RxString historyUploadFilter = '全部'.obs;
final RxString historyBindFilter = '全部'.obs;
@ -285,8 +287,9 @@ class ProblemController extends GetxController
}
// #endregion
//
//
void updateDateRange(String rangeValue) {
void updateCurrentDateRange(String rangeValue) {
final newRange = rangeValue.toDateRange();
if (newRange != null) {
currentDateRange.value = newRange;
@ -294,17 +297,50 @@ class ProblemController extends GetxController
}
}
//
void updateUploadFilter(String value) {
void updateCurrentUpload(String value) {
currentUploadFilter.value = value;
loadProblems(); //
}
void updateBindFilter(String value) {
void updateCurrentBind(String value) {
currentBindFilter.value = value;
loadProblems(); //
}
//
///
Future<void> selectDateRange(BuildContext context) async {
final initialDateRange = DateTimeRange(
start: historyStartTime.value,
end: historyEndTime.value,
);
final DateTimeRange? picked = await showDateRangePicker(
context: context,
firstDate: DateTime(2025, 8, 1), //
lastDate: DateTime(2101), //
initialDateRange: initialDateRange,
);
if (picked != null) {
//
historyStartTime.value = picked.start;
historyEndTime.value = picked.end;
loadProblems();
log('选择的日期范围是: ${picked.start}${picked.end}');
}
}
void updateHistoryUpload(String value) {
historyUploadFilter.value = value;
loadProblems(); //
}
void updateHistoryBind(String value) {
historyBindFilter.value = value;
loadProblems(); //
}
///
Future<void> loadProblems() async {
isLoading.value = true;
@ -332,7 +368,7 @@ class ProblemController extends GetxController
final loadedProblems = await problemRepository.getProblems(
startDate: startDate,
endDate: endDate,
uploadStatus: uploadStatus,
syncStatus: uploadStatus,
bindStatus: bindStatus,
);
@ -409,27 +445,6 @@ class ProblemController extends GetxController
}
}
///
///
Future<void> selectDateRange(BuildContext context) async {
final initialDateRange = DateTimeRange(
start: DateTime.now().subtract(const Duration(days: 7)),
end: DateTime.now(),
);
final DateTimeRange? picked = await showDateRangePicker(
context: context,
firstDate: DateTime(2025, 8, 1), //
lastDate: DateTime(2101), //
initialDateRange: initialDateRange,
);
if (picked != null) {
//
log('选择的日期范围是: ${picked.start}${picked.end}');
}
}
Future<void> toProblemFormPageAndRefresh() async {
await Get.toNamed(AppRoutes.problemForm);
loadProblems();

38
lib/modules/problem/controllers/problem_form_controller.dart

@ -11,31 +11,27 @@ import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/data/repositories/problem_repository.dart';
class ProblemFormController extends GetxController {
final Problem? problem;
final bool isReadOnly;
final ProblemRepository problemRepository;
final TextEditingController descriptionController = TextEditingController();
final TextEditingController locationController = TextEditingController();
final RxList<XFile> selectedImages = <XFile>[].obs;
final RxBool isLoading = false.obs;
//
Problem? _currentProblem;
// 使便
ProblemFormController({required this.problemRepository});
///
void init(Problem? problem) {
_currentProblem = problem;
ProblemFormController({
required this.problemRepository,
this.problem,
this.isReadOnly = false,
}) {
if (problem != null) {
descriptionController.text = problem.description;
locationController.text = problem.location;
// ImageMetadata
// List<String>
final imagePaths = problem.imageUrls
descriptionController.text = problem!.description;
locationController.text = problem!.location;
final imagePaths = problem!.imageUrls
.map((meta) => XFile(meta.localPath))
.toList();
// selectedImages是一个RxList<String>,UI重建
selectedImages.assignAll(imagePaths);
} else {
descriptionController.clear();
@ -144,9 +140,9 @@ class ProblemFormController extends GetxController {
//
final List<ImageMetadata> imagePaths = await _saveImagesToLocal();
if (_currentProblem != null) {
if (problem != null) {
//
final updatedProblem = _currentProblem!.copyWith(
final updatedProblem = problem!.copyWith(
description: descriptionController.text,
location: locationController.text,
imageUrls: imagePaths,
@ -238,7 +234,7 @@ class ProblemFormController extends GetxController {
//
bool _hasFormChanges() {
if (_currentProblem == null) {
if (problem == null) {
//
return descriptionController.text.isNotEmpty ||
locationController.text.isNotEmpty ||
@ -246,9 +242,9 @@ class ProblemFormController extends GetxController {
}
//
return descriptionController.text != _currentProblem!.description ||
locationController.text != _currentProblem!.location ||
selectedImages.length != _currentProblem!.imageUrls.length;
return descriptionController.text != problem!.description ||
locationController.text != problem!.location ||
selectedImages.length != problem!.imageUrls.length;
}
@override

27
lib/modules/problem/views/problem_form_page.dart

@ -4,18 +4,13 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_form_controller.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
class ProblemFormPage extends GetView<ProblemFormController> {
final Problem? problem;
final bool isReadOnly;
//
const ProblemFormPage({super.key, this.problem, this.isReadOnly = false});
const ProblemFormPage({super.key});
@override
Widget build(BuildContext context) {
controller.init(problem);
return Scaffold(
appBar: AppBar(
flexibleSpace: Container(
@ -38,7 +33,9 @@ class ProblemFormPage extends GetView<ProblemFormController> {
),
//
title: Text(
isReadOnly ? '问题详情' : (problem == null ? '新增问题' : '编辑问题'),
controller.isReadOnly
? '问题详情'
: (controller.problem == null ? '新增问题' : '编辑问题'),
style: const TextStyle(color: Colors.white),
),
centerTitle: true,
@ -52,12 +49,12 @@ class ProblemFormPage extends GetView<ProblemFormController> {
children: [
_buildInputCard(
title: '问题描述',
controller: controller.descriptionController,
textController: controller.descriptionController,
hintText: '请输入问题描述',
),
_buildInputCard(
title: '所在位置',
controller: controller.locationController,
textController: controller.locationController,
hintText: '请输入问题所在位置',
),
_buildImageCard(context),
@ -66,7 +63,7 @@ class ProblemFormPage extends GetView<ProblemFormController> {
),
),
//
if (!isReadOnly) _bottomButton(),
if (!controller.isReadOnly) _bottomButton(),
],
),
);
@ -75,7 +72,7 @@ class ProblemFormPage extends GetView<ProblemFormController> {
///
Widget _buildInputCard({
required String title,
required TextEditingController controller,
required TextEditingController textController,
required String hintText,
}) {
return Card(
@ -92,8 +89,8 @@ class ProblemFormPage extends GetView<ProblemFormController> {
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
child: TextField(
maxLines: null,
controller: controller,
readOnly: isReadOnly, //
controller: textController,
readOnly: controller.isReadOnly, //
decoration: InputDecoration(
hintText: hintText,
border: InputBorder.none,
@ -130,7 +127,7 @@ class ProblemFormPage extends GetView<ProblemFormController> {
Widget _buildImageGrid(BuildContext context) {
return Obx(() {
//
final bool showAddButton = !isReadOnly;
final bool showAddButton = !controller.isReadOnly;
final int itemCount =
controller.selectedImages.length + (showAddButton ? 1 : 0);
@ -202,7 +199,7 @@ class ProblemFormPage extends GetView<ProblemFormController> {
),
),
//
if (!isReadOnly)
if (!controller.isReadOnly)
Positioned(
top: 0,
right: 0,

34
lib/modules/problem/views/problem_page.dart

@ -4,8 +4,8 @@ import 'package:get/get.dart';
import 'package:problem_check_system/app/routes/app_routes.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'package:problem_check_system/modules/problem/views/problem_list_page.dart'; // ProblemListPage
import 'package:problem_check_system/modules/problem/views/widgets/compact_filter_bar.dart';
import 'package:problem_check_system/modules/problem/views/widgets/custom_object_dropdown.dart';
import 'package:problem_check_system/modules/problem/views/widgets/current_filter_bar.dart';
import 'package:problem_check_system/modules/problem/views/widgets/history_filter_bar.dart';
import 'package:problem_check_system/modules/problem/views/widgets/problem_card.dart'; //
class ProblemPage extends GetView<ProblemController> {
@ -68,16 +68,7 @@ class ProblemPage extends GetView<ProblemController> {
decoration: BoxDecoration(color: Color(0xfff7f7f7)),
child: Column(
children: [
CompactFilterBar(
showDateRangeFilter: true,
showUploadFilter: true,
showBindFilter: true,
padding: EdgeInsets.symmetric(
horizontal: 17.w,
vertical: 0.h,
),
),
CurrentFilterBar(),
Expanded(
child: // 使
ProblemListPage(
@ -93,26 +84,11 @@ class ProblemPage extends GetView<ProblemController> {
decoration: BoxDecoration(color: Color(0xfff7f7f7)),
child: Column(
children: [
CompactFilterBar(
showDateRangeFilter: false,
showUploadFilter: true,
showBindFilter: true,
showCustomButton: true,
customButtonIcon: Icons.date_range,
customButtonText: "选择日期",
onCustomButtonPressed: () {
//
},
// padding: EdgeInsets.symmetric(
// horizontal: 0.w,
// vertical: 0.h,
// ),
),
HistoryFilterBar(),
Expanded(
child: // 使
ProblemListPage(
problemsToShow: controller.problems,
problemsToShow: controller.historyProblems,
viewType: ProblemCardViewType.buttons,
),
),

122
lib/modules/problem/views/widgets/compact_filter_bar.dart

@ -1,122 +0,0 @@
// widgets/compact_filter_bar.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'custom_filter_dropdown.dart';
class CompactFilterBar extends GetView<ProblemController> {
final bool showDateRangeFilter;
final bool showUploadFilter;
final bool showBindFilter;
final bool showCustomButton; //
final String? customButtonText; //
final IconData? customButtonIcon; //
final VoidCallback? onCustomButtonPressed; //
final EdgeInsetsGeometry? padding;
const CompactFilterBar({
super.key,
this.showDateRangeFilter = true,
this.showUploadFilter = true,
this.showBindFilter = true,
this.showCustomButton = false, //
this.customButtonText,
this.customButtonIcon,
this.onCustomButtonPressed,
this.padding,
});
@override
Widget build(BuildContext context) {
return Container(
padding: padding ?? EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
color: Colors.grey[50],
child: Row(
children: [
//
if (showCustomButton) ...[_buildCustomButton()],
//
if (showDateRangeFilter) ...[
Obx(
() => CustomFilterDropdown(
title: '时间范围',
options: controller.dateRangeOptions,
selectedValue: controller.currentDateRange.value.name,
onChanged: controller.updateDateRange,
width: 100.w,
showBorder: false,
),
),
],
//
if (showUploadFilter) ...[
Obx(
() => CustomFilterDropdown(
title: '上传状态',
options: controller.uploadOptions,
selectedValue: controller.currentUploadFilter.value,
onChanged: controller.updateUploadFilter,
width: 100.w,
showBorder: false,
),
),
],
//
if (showBindFilter) ...[
Obx(
() => CustomFilterDropdown(
title: '绑定状态',
options: controller.bindOptions,
selectedValue: controller.currentBindFilter.value,
onChanged: controller.updateBindFilter,
width: 100.w,
showBorder: false,
),
),
],
],
),
);
}
//
Widget _buildCustomButton() {
return SizedBox(
width: 110.w,
// decoration: BoxDecoration(
// border: Border.all(color: Colors.grey.shade300),
// borderRadius: BorderRadius.circular(8.r),
// ),
child: TextButton(
onPressed: onCustomButtonPressed,
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (customButtonIcon != null) ...[
Icon(customButtonIcon, size: 16.sp, color: Colors.grey[700]),
SizedBox(width: 4.w),
],
Text(
customButtonText ?? '自定义',
style: TextStyle(
fontSize: 14.sp,
color: Colors.black87,
fontWeight: FontWeight.normal,
),
),
],
),
),
);
}
}

66
lib/modules/problem/views/widgets/current_filter_bar.dart

@ -0,0 +1,66 @@
// widgets/compact_filter_bar.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'custom_filter_dropdown.dart';
class CurrentFilterBar extends GetView<ProblemController> {
final EdgeInsetsGeometry? padding;
const CurrentFilterBar({super.key, this.padding});
@override
Widget build(BuildContext context) {
return Container(
padding: padding ?? EdgeInsets.symmetric(horizontal: 16.w, vertical: 5.h),
color: Colors.grey[50],
child: Row(
children: [
//
...[
Obx(
() => CustomFilterDropdown(
title: '时间范围',
options: controller.dateRangeOptions,
selectedValue: controller.currentDateRange.value.name,
onChanged: controller.updateCurrentDateRange,
width: 100.w,
showBorder: false,
),
),
],
//
...[
Obx(
() => CustomFilterDropdown(
title: '上传状态',
options: controller.uploadOptions,
selectedValue: controller.currentUploadFilter.value,
onChanged: controller.updateCurrentUpload,
width: 100.w,
showBorder: false,
),
),
],
//
...[
Obx(
() => CustomFilterDropdown(
title: '绑定状态',
options: controller.bindOptions,
selectedValue: controller.currentBindFilter.value,
onChanged: controller.updateCurrentBind,
width: 100.w,
showBorder: false,
),
),
],
],
),
);
}
}

53
lib/modules/problem/views/widgets/custom_object_dropdown.dart

@ -1,53 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/modules/problem/views/widgets/models/dropdown_option.dart';
class CustomObjectDropdown extends StatelessWidget {
final RxString selectedValue;
final List<DropdownOption> items;
final Function(String) onChanged;
const CustomObjectDropdown({
super.key,
required this.selectedValue,
required this.items,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return Obx(
() => Container(
height: 30.h,
margin: EdgeInsets.only(right: 10.w, top: 10.w, bottom: 10.w),
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 12.5.w, vertical: 7.5.h),
decoration: BoxDecoration(
color: const Color.fromARGB(90, 158, 158, 158),
borderRadius: BorderRadius.circular(7.5.w),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: selectedValue.value,
onChanged: (String? newValue) {
if (newValue != null) {
onChanged(newValue);
}
},
items: items.map((DropdownOption option) {
return DropdownMenuItem<String>(
value: option.value,
child: Text(option.label),
);
}).toList(),
icon: const Icon(Icons.arrow_drop_down, size: 15),
style: TextStyle(fontSize: 10.sp, color: Colors.black),
dropdownColor: Colors.white,
underline: const SizedBox.shrink(),
),
),
),
);
}
}

97
lib/modules/problem/views/widgets/history_filter_bar.dart

@ -0,0 +1,97 @@
// widgets/compact_filter_bar.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'custom_filter_dropdown.dart';
class HistoryFilterBar extends GetView<ProblemController> {
final EdgeInsetsGeometry? padding;
const HistoryFilterBar({super.key, this.padding});
@override
Widget build(BuildContext context) {
return Container(
padding: padding ?? EdgeInsets.symmetric(horizontal: 16.w, vertical: 5.h),
color: Colors.grey[50],
child: Row(
children: [
//
...[
SizedBox(
width: 110.w,
// decoration: BoxDecoration(
// border: Border.all(color: Colors.grey.shade300),
// borderRadius: BorderRadius.circular(8.r),
// ),
child: TextButton(
onPressed: () {
controller.selectDateRange(context);
},
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 4.h,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.date_range,
size: 16.sp,
color: Colors.grey[700],
),
SizedBox(width: 4.w),
Text(
'选择日期',
style: TextStyle(
fontSize: 14.sp,
color: Colors.black87,
fontWeight: FontWeight.normal,
),
),
],
),
),
),
],
//
...[
Obx(
() => CustomFilterDropdown(
title: '上传状态',
options: controller.uploadOptions,
selectedValue: controller.historyUploadFilter.value,
onChanged: controller.updateHistoryUpload,
width: 100.w,
showBorder: false,
),
),
],
//
...[
Obx(
() => CustomFilterDropdown(
title: '绑定状态',
options: controller.bindOptions,
selectedValue: controller.historyBindFilter.value,
onChanged: controller.updateHistoryBind,
width: 100.w,
showBorder: false,
),
),
],
],
),
);
}
}

9
lib/modules/problem/views/widgets/problem_card.dart

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:problem_check_system/app/routes/app_routes.dart';
import 'package:problem_check_system/data/models/sync_status.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/modules/problem/views/widgets/custom_button.dart';
@ -122,14 +123,18 @@ class ProblemCard extends StatelessWidget {
CustomButton(
text: '修改',
onTap: () {
Get.to(ProblemFormPage(problem: problem));
Get.toNamed(AppRoutes.problemForm, arguments: problem);
},
),
SizedBox(width: 8.w),
CustomButton(
text: '查看',
onTap: () {
Get.to(ProblemFormPage(problem: problem, isReadOnly: true));
Get.toNamed(
AppRoutes.problemForm,
arguments: problem,
parameters: {'isReadOnly': 'true'},
);
},
),
SizedBox(width: 16.w),

Loading…
Cancel
Save