Browse Source

fate : 上传问题页面

dev
徐振升 2 weeks ago
parent
commit
eaa836205b
  1. 55
      lib/data/providers/local_database.dart
  2. 2
      lib/modules/problem/bindings/problem_binding.dart
  3. 183
      lib/modules/problem/controllers/problem_controller.dart
  4. 66
      lib/modules/problem/controllers/problem_upload_controller.dart
  5. 34
      lib/modules/problem/views/problem_upload_page.dart
  6. 4
      lib/modules/problem/views/widgets/problem_card.dart

55
lib/data/providers/local_database.dart

@ -58,36 +58,51 @@ class LocalDatabase {
return await db.delete(_tableName, where: 'id = ?', whereArgs: [id]); return await db.delete(_tableName, where: 'id = ?', whereArgs: [id]);
} }
/// censorTaskId ///
///
///
///
///
Future<List<Problem>> getProblems({ Future<List<Problem>> getProblems({
required DateTime startDate, DateTime? startDate,
required DateTime endDate, DateTime? endDate,
required String uploadStatus, // '已上传', '未上传', '全部' String? uploadStatus, // '已上传', '未上传', '全部'
required String bindStatus, // '已绑定', '未绑定', '全部' String? bindStatus, // '已绑定', '未绑定', '全部'
}) async { }) async {
final db = await database; final db = await database;
final int startTimestamp = startDate.millisecondsSinceEpoch;
final int endTimestamp = endDate.millisecondsSinceEpoch;
final List<String> whereClauses = ['createdAt >= ?', 'createdAt <= ?']; // 使
final List<dynamic> whereArgs = [startTimestamp, endTimestamp]; final List<String> whereClauses = [];
final List<dynamic> whereArgs = [];
// // startDate
if (uploadStatus == '已上传') { if (startDate != null) {
whereClauses.add('isUploaded = ?'); whereClauses.add('createdAt >= ?');
whereArgs.add(1); whereArgs.add(startDate.millisecondsSinceEpoch);
} else if (uploadStatus == '未上传') { }
// endDate
if (endDate != null) {
whereClauses.add('createdAt <= ?');
whereArgs.add(endDate.millisecondsSinceEpoch);
}
// uploadStatus
if (uploadStatus != null && uploadStatus != '全部') {
whereClauses.add('isUploaded = ?'); whereClauses.add('isUploaded = ?');
whereArgs.add(0); whereArgs.add(uploadStatus == '已上传' ? 1 : 0);
} }
// censorTaskId // bindStatus
if (bindStatus == '已绑定') { if (bindStatus != null && bindStatus != '全部') {
whereClauses.add('censorTaskId IS NOT NULL'); if (bindStatus == '已绑定') {
} else if (bindStatus == '未绑定') { whereClauses.add('censorTaskId IS NOT NULL');
whereClauses.add('censorTaskId IS NULL'); } else {
whereClauses.add('censorTaskId IS NULL');
}
} }
// ' AND '
final String whereString = whereClauses.join(' AND '); final String whereString = whereClauses.join(' AND ');
final List<Map<String, dynamic>> maps = await db.query( final List<Map<String, dynamic>> maps = await db.query(

2
lib/modules/problem/bindings/problem_binding.dart

@ -3,7 +3,6 @@ import 'package:dio/dio.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart'; import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart'; import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'package:problem_check_system/data/providers/local_database.dart'; import 'package:problem_check_system/data/providers/local_database.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_upload_controller.dart';
class ProblemBinding implements Bindings { class ProblemBinding implements Bindings {
@override @override
@ -18,6 +17,5 @@ class ProblemBinding implements Bindings {
connectivityProvider: Get.find<ConnectivityProvider>(), connectivityProvider: Get.find<ConnectivityProvider>(),
), ),
); );
Get.lazyPut<ProblemUploadController>(() => ProblemUploadController());
} }
} }

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

@ -12,20 +12,42 @@ import 'package:problem_check_system/data/providers/connectivity_provider.dart';
class ProblemController extends GetxController class ProblemController extends GetxController
with GetSingleTickerProviderStateMixin { with GetSingleTickerProviderStateMixin {
///
final LocalDatabase _localDatabase; final LocalDatabase _localDatabase;
///
final RxList<Problem> problems = <Problem>[].obs; final RxList<Problem> problems = <Problem>[].obs;
///
final RxList<Problem> historyProblems = <Problem>[].obs; final RxList<Problem> historyProblems = <Problem>[].obs;
///
final RxList<Problem> unUploadedProblems = <Problem>[].obs;
final RxList<Problem> selectedUnUploadedProblems = <Problem>[].obs;
final Rx<bool> allSelected = false.obs;
final RxBool isUploadEnabled = false.obs;
///
final Rx<DateRange> selectedDateRange = DateRange.oneWeek.obs; final Rx<DateRange> selectedDateRange = DateRange.oneWeek.obs;
final RxString selectedUploadStatus = '全部'.obs; final RxString selectedUploadStatus = '全部'.obs;
final RxString selectedBindingStatus = '全部'.obs; final RxString selectedBindingStatus = '全部'.obs;
///
final RxBool isLoading = false.obs; final RxBool isLoading = false.obs;
final Dio _dio; final Dio _dio;
final ConnectivityProvider _connectivityProvider; final ConnectivityProvider _connectivityProvider;
late TabController tabController; late TabController tabController;
/// floatingButton
final double _fabSize = 56.0;
final double _edgePaddingX = 27.0.w;
final double _edgePaddingY = 111.0.h;
final fabUploadPosition = Offset(337.0, 703.7).obs;
/// get
RxBool get isOnline => _connectivityProvider.isOnline;
ProblemController({ ProblemController({
required LocalDatabase localDatabase, required LocalDatabase localDatabase,
required Dio dio, required Dio dio,
@ -34,23 +56,66 @@ class ProblemController extends GetxController
_dio = dio, _dio = dio,
_connectivityProvider = connectivityProvider; _connectivityProvider = connectivityProvider;
RxBool get isOnline => _connectivityProvider.isOnline; @override
void onInit() {
super.onInit();
tabController = TabController(length: 2, vsync: this);
tabController.addListener(_onTabChanged);
// unUploadedProblems selectedProblems
ever(unUploadedProblems, (_) => _updateSelectedList());
// selectedProblems
ever(selectedUnUploadedProblems, (_) => _updateUploadButtonState());
loadProblems();
//
loadUnUploadedProblems();
}
@override
void onClose() {
tabController.dispose();
super.onClose();
}
List<Problem> get selectedProblems { ///
return historyProblems.where((p) => p.isChecked.value).toList(); void onProblemCheckedChange() {
// selectedUnUploadedProblems
_updateSelectedList();
// _updateSelectedList selectedUnUploadedProblems
// _updateUploadButtonState() ever
} }
List<Problem> get unuploadedProblems { ///
return problems.where((p) => !p.isUploaded).toList(); void _updateSelectedList() {
selectedUnUploadedProblems.clear();
for (var problem in unUploadedProblems) {
if (problem.isChecked.value) {
selectedUnUploadedProblems.add(problem);
}
}
} }
// FAB的尺寸和贴靠间距 void _updateUploadButtonState() {
final double _fabSize = 56.0; // FloatingActionButton的默认尺寸 isUploadEnabled.value = selectedUnUploadedProblems.isNotEmpty;
final double _edgePaddingX = 27.0.w; // }
final double _edgePaddingY = 111.0.h; //
// 使 Rx<Offset> void selectAll() {
final fabUploadPosition = Offset(337.0, 703.7).obs; final bool newState = !allSelected.value;
for (var problem in unUploadedProblems) {
problem.isChecked.value = newState;
}
allSelected.value = newState;
_updateSelectedList();
}
void uploadProblems() {
if (selectedUnUploadedProblems.isEmpty) return;
// API
print('开始上传 ${selectedUnUploadedProblems.length} 个问题...');
//
selectedUnUploadedProblems.clear();
}
/// floatingButton更新位置
void updateFabUploadPosition(Offset delta) { void updateFabUploadPosition(Offset delta) {
final screenWidth = ScreenUtil().screenWidth; final screenWidth = ScreenUtil().screenWidth;
final screenHeight = ScreenUtil().screenHeight; final screenHeight = ScreenUtil().screenHeight;
@ -72,6 +137,7 @@ class ProblemController extends GetxController
fabUploadPosition.value = Offset(clampedDx, clampedDy); fabUploadPosition.value = Offset(clampedDx, clampedDy);
} }
/// floatingButton
void snapToEdge() { void snapToEdge() {
final screenWidth = ScreenUtil().screenWidth; final screenWidth = ScreenUtil().screenWidth;
@ -91,23 +157,6 @@ class ProblemController extends GetxController
// //
fabUploadPosition.value = Offset(newDx, fabUploadPosition.value.dy); fabUploadPosition.value = Offset(newDx, fabUploadPosition.value.dy);
print(fabUploadPosition.value);
}
@override
void onInit() {
super.onInit();
tabController = TabController(length: 2, vsync: this);
tabController.addListener(_onTabChanged);
loadProblems();
}
@override
void onClose() {
tabController.dispose();
super.onClose();
} }
void _onTabChanged() { void _onTabChanged() {
@ -119,30 +168,39 @@ class ProblemController extends GetxController
} }
} }
void loadProblems() async { Future<void> loadProblems() async {
isLoading.value = true; isLoading.value = true;
try { try {
if (tabController.index == 0) { // Tab
// "问题列表" Tab: 使 final bool isProblemListTab = tabController.index == 0;
final startDate = selectedDateRange.value.startDate;
final endDate = DateTime.now(); final DateTime startDate = isProblemListTab
? selectedDateRange.value.startDate
final problems = await _localDatabase.getProblems( : DateTime(2000); //
startDate: startDate,
endDate: endDate, final DateTime endDate = DateTime.now();
uploadStatus: selectedUploadStatus.value,
bindStatus: selectedBindingStatus.value, final String uploadStatus = isProblemListTab
); ? selectedUploadStatus.value
this.problems.assignAll(problems); : '全部';
final String bindStatus = isProblemListTab
? selectedBindingStatus.value
: '全部';
//
final loadedProblems = await _localDatabase.getProblems(
startDate: startDate,
endDate: endDate,
uploadStatus: uploadStatus,
bindStatus: bindStatus,
);
// Tab
if (isProblemListTab) {
problems.assignAll(loadedProblems);
} else { } else {
// "历史问题列表" Tab: historyProblems.assignAll(loadedProblems);
final allProblems = await _localDatabase.getProblems(
startDate: DateTime(2000),
endDate: DateTime.now(),
uploadStatus: '全部',
bindStatus: '全部',
);
this.historyProblems.assignAll(allProblems);
} }
} catch (e) { } catch (e) {
Get.snackbar('错误', '加载问题失败: $e'); Get.snackbar('错误', '加载问题失败: $e');
@ -151,7 +209,7 @@ class ProblemController extends GetxController
} }
} }
/// ///
void updateFiltersAndLoadProblems({ void updateFiltersAndLoadProblems({
DateRange? newDateRange, DateRange? newDateRange,
String? newUploadStatus, String? newUploadStatus,
@ -167,11 +225,22 @@ class ProblemController extends GetxController
selectedBindingStatus.value = newBindingStatus; selectedBindingStatus.value = newBindingStatus;
} }
// loadProblems //
if (newDateRange != null || loadProblems();
newUploadStatus != null || }
newBindingStatus != null) {
loadProblems(); //
Future<void> loadUnUploadedProblems() async {
isLoading.value = true;
try {
// _localDatabase.getProblems '未上传'
unUploadedProblems.value = await _localDatabase.getProblems(
uploadStatus: '未上传',
);
} catch (e) {
Get.snackbar('错误', '加载未上传问题失败: $e');
} finally {
isLoading.value = false;
} }
} }
@ -209,7 +278,7 @@ class ProblemController extends GetxController
} }
Future<void> deleteSelectedProblems() async { Future<void> deleteSelectedProblems() async {
final problemsToDelete = selectedProblems; final problemsToDelete = selectedUnUploadedProblems;
if (problemsToDelete.isEmpty) { if (problemsToDelete.isEmpty) {
Get.snackbar('提示', '请至少选择一个问题进行删除'); Get.snackbar('提示', '请至少选择一个问题进行删除');
return; return;
@ -301,7 +370,7 @@ class ProblemController extends GetxController
return; return;
} }
final unuploaded = unuploadedProblems; final unuploaded = unUploadedProblems;
if (unuploaded.isEmpty) { if (unuploaded.isEmpty) {
Get.snackbar('提示', '没有需要上传的问题'); Get.snackbar('提示', '没有需要上传的问题');
return; return;

66
lib/modules/problem/controllers/problem_upload_controller.dart

@ -1,66 +0,0 @@
// problem_upload_controller.dart
import 'package:get/get.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
// import 'package:problem_check_system/services/problem_service.dart';
class ProblemUploadController extends GetxController {
ProblemUploadController();
//
final RxList<Problem> problems = <Problem>[].obs;
//
final RxList<Problem> selectedProblems = <Problem>[].obs;
@override
void onInit() {
super.onInit();
// problems selectedProblems
ever(problems, (_) => _updateSelectedList());
// selectedProblems
ever(selectedProblems, (_) => _updateUploadButtonState());
fetchProblems(); //
}
void fetchProblems() async {
//
// final fetched = await ProblemService.getProblemsForUpload();
// problems.assignAll(fetched);
}
// isChecked selectedProblems
void _updateSelectedList() {
selectedProblems.clear();
for (var problem in problems) {
if (problem.isChecked.value) {
selectedProblems.add(problem);
}
}
}
//
RxBool isUploadEnabled = false.obs;
void _updateUploadButtonState() {
isUploadEnabled.value = selectedProblems.isNotEmpty;
}
// /
RxBool allSelected = false.obs;
void selectAll() {
final bool newState = !allSelected.value;
for (var problem in problems) {
problem.isChecked.value = newState;
}
allSelected.value = newState;
}
void uploadProblems() {
if (selectedProblems.isEmpty) return;
// API
print('开始上传 ${selectedProblems.length} 个问题...');
//
selectedProblems.clear();
}
}

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

@ -1,11 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_upload_controller.dart'; import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'package:problem_check_system/modules/problem/views/widgets/problem_card.dart'; import 'package:problem_check_system/modules/problem/views/widgets/problem_card.dart';
// todo 使problem_list_page,problem_controller // todo 使problem_list_page,problem_controller
class ProblemUploadPage extends GetView<ProblemUploadController> { class ProblemUploadPage extends GetView<ProblemController> {
const ProblemUploadPage({super.key}); const ProblemUploadPage({super.key});
@override @override
@ -21,8 +21,8 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
PreferredSizeWidget _buildAppBar() { PreferredSizeWidget _buildAppBar() {
return AppBar( return AppBar(
title: Obx(() { title: Obx(() {
final selectedCount = controller.selectedProblems.length; final selectedCount = controller.selectedUnUploadedProblems.length;
return Text(selectedCount > 0 ? '已选择$selectedCount项' : '问题上传'); return Text('已选择$selectedCount项');
}), }),
centerTitle: true, centerTitle: true,
leading: IconButton(icon: Icon(Icons.close), onPressed: () => Get.back()), leading: IconButton(icon: Icon(Icons.close), onPressed: () => Get.back()),
@ -44,9 +44,9 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
Widget _buildBody() { Widget _buildBody() {
return Obx(() { return Obx(() {
return ListView.builder( return ListView.builder(
itemCount: controller.problems.length, itemCount: controller.unUploadedProblems.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final problem = controller.problems[index]; final problem = controller.unUploadedProblems[index];
return ProblemCard( return ProblemCard(
problem, problem,
// Checkbox // Checkbox
@ -65,17 +65,19 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
color: Colors.white, color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)), border: Border(top: BorderSide(color: Colors.grey.shade300)),
), ),
child: ElevatedButton( child: Obx(
onPressed: controller.isUploadEnabled.value () => ElevatedButton(
? controller.uploadProblems onPressed: controller.isUploadEnabled.value
: null, ? controller.uploadProblems
child: Text('点击上传'), : null,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
foregroundColor: Colors.white, foregroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
),
), ),
child: Text('点击上传'),
), ),
), ),
); );

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

@ -3,6 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:problem_check_system/data/models/problem_model.dart'; import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'package:problem_check_system/modules/problem/views/widgets/custom_button.dart'; import 'package:problem_check_system/modules/problem/views/widgets/custom_button.dart';
import 'package:problem_check_system/modules/problem/views/problem_form_page.dart'; import 'package:problem_check_system/modules/problem/views/problem_form_page.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart';
@ -10,7 +11,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
// //
enum ProblemCardViewType { buttons, checkbox } enum ProblemCardViewType { buttons, checkbox }
class ProblemCard extends StatelessWidget { class ProblemCard extends GetView<ProblemController> {
final Problem problem; final Problem problem;
final ProblemCardViewType viewType; final ProblemCardViewType viewType;
@ -126,6 +127,7 @@ class ProblemCard extends StatelessWidget {
// Checkbox controller // Checkbox controller
onChanged: (bool? value) { onChanged: (bool? value) {
problem.isChecked.value = value ?? false; problem.isChecked.value = value ?? false;
controller.onProblemCheckedChange();
}, },
), ),
), ),

Loading…
Cancel
Save