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]);
}
/// censorTaskId
///
///
///
///
///
Future<List<Problem>> getProblems({
required DateTime startDate,
required DateTime endDate,
required String uploadStatus, // '已上传', '未上传', '全部'
required String bindStatus, // '已绑定', '未绑定', '全部'
DateTime? startDate,
DateTime? endDate,
String? uploadStatus, // '已上传', '未上传', '全部'
String? bindStatus, // '已绑定', '未绑定', '全部'
}) async {
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 = [];
//
if (uploadStatus == '已上传') {
whereClauses.add('isUploaded = ?');
whereArgs.add(1);
} else if (uploadStatus == '未上传') {
// startDate
if (startDate != null) {
whereClauses.add('createdAt >= ?');
whereArgs.add(startDate.millisecondsSinceEpoch);
}
// endDate
if (endDate != null) {
whereClauses.add('createdAt <= ?');
whereArgs.add(endDate.millisecondsSinceEpoch);
}
// uploadStatus
if (uploadStatus != null && uploadStatus != '全部') {
whereClauses.add('isUploaded = ?');
whereArgs.add(0);
whereArgs.add(uploadStatus == '已上传' ? 1 : 0);
}
// censorTaskId
if (bindStatus == '已绑定') {
whereClauses.add('censorTaskId IS NOT NULL');
} else if (bindStatus == '未绑定') {
whereClauses.add('censorTaskId IS NULL');
// bindStatus
if (bindStatus != null && bindStatus != '全部') {
if (bindStatus == '已绑定') {
whereClauses.add('censorTaskId IS NOT NULL');
} else {
whereClauses.add('censorTaskId IS NULL');
}
}
// ' AND '
final String whereString = whereClauses.join(' AND ');
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/modules/problem/controllers/problem_controller.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 {
@override
@ -18,6 +17,5 @@ class ProblemBinding implements Bindings {
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
with GetSingleTickerProviderStateMixin {
///
final LocalDatabase _localDatabase;
///
final RxList<Problem> problems = <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 RxString selectedUploadStatus = '全部'.obs;
final RxString selectedBindingStatus = '全部'.obs;
///
final RxBool isLoading = false.obs;
final Dio _dio;
final ConnectivityProvider _connectivityProvider;
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({
required LocalDatabase localDatabase,
required Dio dio,
@ -34,23 +56,66 @@ class ProblemController extends GetxController
_dio = dio,
_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的尺寸和贴靠间距
final double _fabSize = 56.0; // FloatingActionButton的默认尺寸
final double _edgePaddingX = 27.0.w; //
final double _edgePaddingY = 111.0.h; //
// 使 Rx<Offset>
final fabUploadPosition = Offset(337.0, 703.7).obs;
void _updateUploadButtonState() {
isUploadEnabled.value = selectedUnUploadedProblems.isNotEmpty;
}
void selectAll() {
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) {
final screenWidth = ScreenUtil().screenWidth;
final screenHeight = ScreenUtil().screenHeight;
@ -72,6 +137,7 @@ class ProblemController extends GetxController
fabUploadPosition.value = Offset(clampedDx, clampedDy);
}
/// floatingButton
void snapToEdge() {
final screenWidth = ScreenUtil().screenWidth;
@ -91,23 +157,6 @@ class ProblemController extends GetxController
//
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() {
@ -119,30 +168,39 @@ class ProblemController extends GetxController
}
}
void loadProblems() async {
Future<void> loadProblems() async {
isLoading.value = true;
try {
if (tabController.index == 0) {
// "问题列表" Tab: 使
final startDate = selectedDateRange.value.startDate;
final endDate = DateTime.now();
final problems = await _localDatabase.getProblems(
startDate: startDate,
endDate: endDate,
uploadStatus: selectedUploadStatus.value,
bindStatus: selectedBindingStatus.value,
);
this.problems.assignAll(problems);
// Tab
final bool isProblemListTab = tabController.index == 0;
final DateTime startDate = isProblemListTab
? selectedDateRange.value.startDate
: DateTime(2000); //
final DateTime endDate = DateTime.now();
final String uploadStatus = isProblemListTab
? selectedUploadStatus.value
: '全部';
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 {
// "历史问题列表" Tab:
final allProblems = await _localDatabase.getProblems(
startDate: DateTime(2000),
endDate: DateTime.now(),
uploadStatus: '全部',
bindStatus: '全部',
);
this.historyProblems.assignAll(allProblems);
historyProblems.assignAll(loadedProblems);
}
} catch (e) {
Get.snackbar('错误', '加载问题失败: $e');
@ -151,7 +209,7 @@ class ProblemController extends GetxController
}
}
///
///
void updateFiltersAndLoadProblems({
DateRange? newDateRange,
String? newUploadStatus,
@ -167,11 +225,22 @@ class ProblemController extends GetxController
selectedBindingStatus.value = newBindingStatus;
}
// loadProblems
if (newDateRange != null ||
newUploadStatus != null ||
newBindingStatus != null) {
loadProblems();
//
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 {
final problemsToDelete = selectedProblems;
final problemsToDelete = selectedUnUploadedProblems;
if (problemsToDelete.isEmpty) {
Get.snackbar('提示', '请至少选择一个问题进行删除');
return;
@ -301,7 +370,7 @@ class ProblemController extends GetxController
return;
}
final unuploaded = unuploadedProblems;
final unuploaded = unUploadedProblems;
if (unuploaded.isEmpty) {
Get.snackbar('提示', '没有需要上传的问题');
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_screenutil/flutter_screenutil.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';
// todo 使problem_list_page,problem_controller
class ProblemUploadPage extends GetView<ProblemUploadController> {
class ProblemUploadPage extends GetView<ProblemController> {
const ProblemUploadPage({super.key});
@override
@ -21,8 +21,8 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
PreferredSizeWidget _buildAppBar() {
return AppBar(
title: Obx(() {
final selectedCount = controller.selectedProblems.length;
return Text(selectedCount > 0 ? '已选择$selectedCount项' : '问题上传');
final selectedCount = controller.selectedUnUploadedProblems.length;
return Text('已选择$selectedCount项');
}),
centerTitle: true,
leading: IconButton(icon: Icon(Icons.close), onPressed: () => Get.back()),
@ -44,9 +44,9 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
Widget _buildBody() {
return Obx(() {
return ListView.builder(
itemCount: controller.problems.length,
itemCount: controller.unUploadedProblems.length,
itemBuilder: (context, index) {
final problem = controller.problems[index];
final problem = controller.unUploadedProblems[index];
return ProblemCard(
problem,
// Checkbox
@ -65,17 +65,19 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)),
),
child: ElevatedButton(
onPressed: controller.isUploadEnabled.value
? controller.uploadProblems
: null,
child: Text('点击上传'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
child: Obx(
() => ElevatedButton(
onPressed: controller.isUploadEnabled.value
? controller.uploadProblems
: null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
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:intl/intl.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/problem_form_page.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
@ -10,7 +11,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
//
enum ProblemCardViewType { buttons, checkbox }
class ProblemCard extends StatelessWidget {
class ProblemCard extends GetView<ProblemController> {
final Problem problem;
final ProblemCardViewType viewType;
@ -126,6 +127,7 @@ class ProblemCard extends StatelessWidget {
// Checkbox controller
onChanged: (bool? value) {
problem.isChecked.value = value ?? false;
controller.onProblemCheckedChange();
},
),
),

Loading…
Cancel
Save