Browse Source

fate : 优化代码可读性

dev
徐振升 2 weeks ago
parent
commit
714bebc485
  1. 25
      lib/app/bindings/initial_binding.dart
  2. 2
      lib/app/routes/app_pages.dart
  3. 23
      lib/data/models/auth_model.dart
  4. 81
      lib/data/models/user/organization.dart
  5. 15
      lib/data/models/user/page.dart
  6. 15
      lib/data/models/user/role.dart
  7. 81
      lib/data/models/user/user.dart
  8. 14
      lib/data/providers/http_provider.dart
  9. 27
      lib/data/providers/local_database.dart
  10. 18
      lib/data/repositories/auth_repository.dart
  11. 62
      lib/data/repositories/problem_repository.dart
  12. 13
      lib/main.dart
  13. 11
      lib/modules/auth/bindings/auth_binding.dart
  14. 8
      lib/modules/auth/controllers/auth_controller.dart
  15. 33
      lib/modules/home/bindings/home_binding.dart
  16. 11
      lib/modules/my/bingdings/change_password_binding.dart
  17. 6
      lib/modules/my/bingdings/my_binding.dart
  18. 8
      lib/modules/my/controllers/my_controller.dart
  19. 18
      lib/modules/my/views/my_page.dart
  20. 20
      lib/modules/problem/bindings/problem_binding.dart
  21. 284
      lib/modules/problem/controllers/problem_controller.dart
  22. 1
      lib/modules/problem/views/problem_list_page.dart
  23. 13
      lib/modules/problem/views/problem_page.dart
  24. 47
      lib/modules/problem/views/problem_upload_page.dart
  25. 5
      pubspec.lock
  26. 2
      pubspec.yaml

25
lib/app/bindings/initial_binding.dart

@ -3,13 +3,32 @@ import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/http_provider.dart';
import 'package:problem_check_system/data/providers/local_database.dart';
import 'package:problem_check_system/data/repositories/auth_repository.dart';
import 'package:problem_check_system/data/repositories/problem_repository.dart';
class InitialBinding implements Bindings {
@override
void dependencies() {
///
Get.put<GetStorage>(GetStorage(), permanent: true);
Get.put<HttpProvider>(HttpProvider(), permanent: true);
Get.put<LocalDatabase>(LocalDatabase(), permanent: true);
Get.put<ConnectivityProvider>(ConnectivityProvider(), permanent: true);
Get.put<HttpProvider>(HttpProvider());
Get.put<LocalDatabase>(LocalDatabase());
Get.put<ConnectivityProvider>(ConnectivityProvider());
///
Get.lazyPut<AuthRepository>(
() => AuthRepository(
httpProvider: Get.find<HttpProvider>(),
storage: Get.find<GetStorage>(),
connectivityProvider: Get.find<ConnectivityProvider>(),
),
);
Get.lazyPut<ProblemRepository>(
() => ProblemRepository(
localDatabase: Get.find<LocalDatabase>(),
httpProvider: Get.find<HttpProvider>(),
connectivityProvider: Get.find<ConnectivityProvider>(),
),
);
}
}

2
lib/app/routes/app_pages.dart

@ -5,7 +5,6 @@ import 'package:problem_check_system/modules/auth/bindings/auth_binding.dart';
import 'package:problem_check_system/modules/auth/views/login_page.dart';
import 'package:problem_check_system/modules/my/bingdings/change_password_binding.dart';
import 'package:problem_check_system/modules/my/views/change_password.dart';
import 'package:problem_check_system/modules/problem/bindings/problem_binding.dart';
import 'package:problem_check_system/modules/problem/views/problem_upload_page.dart';
import 'app_routes.dart';
@ -32,7 +31,6 @@ abstract class AppPages {
GetPage(
name: AppRoutes.problemUpload,
page: () => const ProblemUploadPage(),
binding: ProblemBinding(),
),
];
}

23
lib/data/models/auth_model.dart

@ -51,26 +51,3 @@ class LoginResponse {
);
}
}
class Profile {
final String id;
final String name;
final String? email;
final String? signatureImage;
Profile({
required this.id,
required this.name,
this.email,
this.signatureImage,
});
factory Profile.fromJson(Map<String, dynamic> json) {
return Profile(
id: json['id'] as String,
name: json['name'] as String,
email: json['email'] as String?,
signatureImage: json['signatureImage'] as String?,
);
}
}

81
lib/data/models/user/organization.dart

@ -0,0 +1,81 @@
class Organization {
String? id;
String? name;
dynamic organizationTypes;
dynamic newAuth;
String? otherAuth;
String? psmAuth;
String? dangerAuth;
dynamic parentId;
int? order;
bool? enabled;
String? level;
String? code;
List<dynamic>? organizationTypeIds;
String? creationTime;
dynamic creatorId;
DateTime? lastModificationTime;
String? lastModifierId;
Organization({
this.id,
this.name,
this.organizationTypes,
this.newAuth,
this.otherAuth,
this.psmAuth,
this.dangerAuth,
this.parentId,
this.order,
this.enabled,
this.level,
this.code,
this.organizationTypeIds,
this.creationTime,
this.creatorId,
this.lastModificationTime,
this.lastModifierId,
});
factory Organization.fromJson(Map<String, dynamic> json) => Organization(
id: json['id'] as String?,
name: json['name'] as String?,
organizationTypes: json['organizationTypes'] as dynamic,
newAuth: json['newAuth'] as dynamic,
otherAuth: json['otherAuth'] as String?,
psmAuth: json['psmAuth'] as String?,
dangerAuth: json['dangerAuth'] as String?,
parentId: json['parentId'] as dynamic,
order: json['order'] as int?,
enabled: json['enabled'] as bool?,
level: json['level'] as String?,
code: json['code'] as String?,
organizationTypeIds: json['organizationTypeIds'] as List<dynamic>?,
creationTime: json['creationTime'] as String?,
creatorId: json['creatorId'] as dynamic,
lastModificationTime: json['lastModificationTime'] == null
? null
: DateTime.parse(json['lastModificationTime'] as String),
lastModifierId: json['lastModifierId'] as String?,
);
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'organizationTypes': organizationTypes,
'newAuth': newAuth,
'otherAuth': otherAuth,
'psmAuth': psmAuth,
'dangerAuth': dangerAuth,
'parentId': parentId,
'order': order,
'enabled': enabled,
'level': level,
'code': code,
'organizationTypeIds': organizationTypeIds,
'creationTime': creationTime,
'creatorId': creatorId,
'lastModificationTime': lastModificationTime?.toIso8601String(),
'lastModifierId': lastModifierId,
};
}

15
lib/data/models/user/page.dart

@ -0,0 +1,15 @@
class Page {
String? id;
String? name;
dynamic url;
Page({this.id, this.name, this.url});
factory Page.fromJson(Map<String, dynamic> json) => Page(
id: json['id'] as String?,
name: json['name'] as String?,
url: json['url'] as dynamic,
);
Map<String, dynamic> toJson() => {'id': id, 'name': name, 'url': url};
}

15
lib/data/models/user/role.dart

@ -0,0 +1,15 @@
class Role {
String? id;
String? name;
dynamic desc;
Role({this.id, this.name, this.desc});
factory Role.fromJson(Map<String, dynamic> json) => Role(
id: json['id'] as String?,
name: json['name'] as String?,
desc: json['desc'] as dynamic,
);
Map<String, dynamic> toJson() => {'id': id, 'name': name, 'desc': desc};
}

81
lib/data/models/user/user.dart

@ -0,0 +1,81 @@
import 'organization.dart';
import 'page.dart';
import 'role.dart';
class User {
String? id;
String? username;
dynamic email;
String? name;
bool? enabled;
dynamic posts;
String? organizationId;
Organization? organization;
String? organizationName;
String? organizationLevel;
List<Role>? roles;
List<dynamic>? permissions;
List<Page>? pages;
dynamic company;
String? signatureImage;
User({
this.id,
this.username,
this.email,
this.name,
this.enabled,
this.posts,
this.organizationId,
this.organization,
this.organizationName,
this.organizationLevel,
this.roles,
this.permissions,
this.pages,
this.company,
this.signatureImage,
});
factory User.fromJson(Map<String, dynamic> json) => User(
id: json['id'] as String?,
username: json['username'] as String?,
email: json['email'] as dynamic,
name: json['name'] as String?,
enabled: json['enabled'] as bool?,
posts: json['posts'] as dynamic,
organizationId: json['organizationId'] as String?,
organization: json['organization'] == null
? null
: Organization.fromJson(json['organization'] as Map<String, dynamic>),
organizationName: json['organizationName'] as String?,
organizationLevel: json['organizationLevel'] as String?,
roles: (json['roles'] as List<dynamic>?)
?.map((e) => Role.fromJson(e as Map<String, dynamic>))
.toList(),
permissions: json['permissions'] as List<dynamic>?,
pages: (json['pages'] as List<dynamic>?)
?.map((e) => Page.fromJson(e as Map<String, dynamic>))
.toList(),
company: json['company'] as dynamic,
signatureImage: json['signatureImage'] as String?,
);
Map<String, dynamic> toJson() => {
'id': id,
'username': username,
'email': email,
'name': name,
'enabled': enabled,
'posts': posts,
'organizationId': organizationId,
'organization': organization?.toJson(),
'organizationName': organizationName,
'organizationLevel': organizationLevel,
'roles': roles?.map((e) => e.toJson()).toList(),
'permissions': permissions,
'pages': pages?.map((e) => e.toJson()).toList(),
'company': company,
'signatureImage': signatureImage,
};
}

14
lib/data/providers/http_provider.dart

@ -94,16 +94,10 @@ class HttpProvider extends GetxService {
} on Exception catch (e) {
debugPrint('刷新 token 失败: $e');
// token
try {
final authController = Get.find<AuthController>();
await authController.logout();
} catch (e) {
// AuthController
final authRepository = Get.find<AuthRepository>();
authRepository.clearAuthData();
if (Get.currentRoute != '/login') {
Get.offAllNamed('/login');
}
final authRepository = Get.find<AuthRepository>();
authRepository.clearAuthData();
if (Get.currentRoute != '/login') {
Get.offAllNamed('/login');
}
//
return handler.next(error);

27
lib/data/providers/local_database.dart

@ -52,6 +52,12 @@ class LocalDatabase extends GetxService {
return await _database.insert(_tableName, problem.toMap());
}
/// ID
///
Future<int> deleteProblem(String id) async {
return await _database.delete(_tableName, where: 'id = ?', whereArgs: [id]);
}
///
///
Future<int> updateProblem(Problem problem) async {
@ -63,10 +69,23 @@ class LocalDatabase extends GetxService {
);
}
/// ID
///
Future<int> deleteProblem(String id) async {
return await _database.delete(_tableName, where: 'id = ?', whereArgs: [id]);
/// ID
/// Problem null
Future<Problem?> getProblemById(String id) async {
final List<Map<String, dynamic>> maps = await _database.query(
_tableName,
where: 'id = ?',
whereArgs: [id],
limit: 1, // 1ID是唯一的
);
if (maps.isNotEmpty) {
// Problem
return Problem.fromMap(maps.first);
} else {
// null
return null;
}
}
///

18
lib/data/repositories/auth_repository.dart

@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/data/models/auth_model.dart';
import 'package:problem_check_system/data/models/user/user.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/http_provider.dart';
@ -101,12 +102,12 @@ class AuthRepository extends GetxService {
}
/// API
Future<Profile> getUserProfile() async {
Future<User> getUserProfile() async {
try {
final response = await httpProvider.get('/api/Accounts/Profile');
// JSON Profile
return Profile.fromJson(response.data);
return User.fromJson(response.data);
} on DioException catch (e) {
//
// DioException
@ -139,17 +140,4 @@ class AuthRepository extends GetxService {
throw Exception(e);
}
}
/// Logs the user out by calling the API and then clearing local data.
Future<void> logout() async {
try {
// Attempt to call the API, but even if it fails, continue to clear local data.
await httpProvider.post('/auth/logout');
} on DioException catch (e) {
log('退出登录API调用失败: ${e.message}');
} finally {
// Regardless of API success or failure, always clear local storage.
clearAuthData();
}
}
}

62
lib/data/repositories/problem_repository.dart

@ -0,0 +1,62 @@
import 'package:get/get.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/http_provider.dart';
import 'package:problem_check_system/data/providers/local_database.dart';
///
///
class ProblemRepository {
final LocalDatabase localDatabase;
final HttpProvider httpProvider;
final ConnectivityProvider connectivityProvider;
RxBool get isOnline => connectivityProvider.isOnline;
ProblemRepository({
required this.localDatabase,
required this.httpProvider,
required this.connectivityProvider,
});
///
///
Future<void> updateProblem(Problem problem) async {
// ID判断
final existingProblem = await localDatabase.getProblemById(problem.id!);
if (existingProblem != null) {
//
await localDatabase.updateProblem(problem);
} else {
//
await localDatabase.insertProblem(problem);
}
}
///
/// - `startDate`/`endDate`
/// - `uploadStatus`'已上传', '未上传', '全部'
/// - `bindStatus`'已绑定', '未绑定', '全部'
Future getProblems({
DateTime? startDate,
DateTime? endDate,
String uploadStatus = '全部',
String bindStatus = '全部',
}) async {
return await localDatabase.getProblems(
startDate: startDate,
endDate: endDate,
uploadStatus: uploadStatus,
bindStatus: bindStatus,
);
}
Future<void> insertProblem(Problem problem) async {
await localDatabase.insertProblem(problem);
}
Future<void> deleteProblem(String id) async {
await localDatabase.deleteProblem(id);
}
}

13
lib/main.dart

@ -6,6 +6,7 @@ import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/app/routes/app_pages.dart';
import 'package:problem_check_system/app/routes/app_routes.dart'; //
import 'package:problem_check_system/app/bindings/initial_binding.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() async {
// Flutter Binding
@ -37,6 +38,18 @@ class MainApp extends StatelessWidget {
// 使 _ child
return GetMaterialApp(
debugShowCheckedModeBanner: false,
// --- ---
localizationsDelegates: const [
// Material
GlobalMaterialLocalizations.delegate,
// Widgets
GlobalWidgetsLocalizations.delegate,
// iOS
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('zh', 'CN'), //
],
title: '问题检查系统', // 使
theme: ThemeData(useMaterial3: true, primarySwatch: Colors.blue),
// 使 GetX

11
lib/modules/auth/bindings/auth_binding.dart

@ -1,21 +1,10 @@
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/http_provider.dart';
import 'package:problem_check_system/data/repositories/auth_repository.dart';
import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart';
class AuthBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<AuthRepository>(
() => AuthRepository(
httpProvider: Get.find<HttpProvider>(),
storage: Get.find<GetStorage>(),
connectivityProvider: Get.find<ConnectivityProvider>(),
),
);
// 2. (AuthController) AuthProvider
// Get.find()
Get.lazyPut<AuthController>(

8
lib/modules/auth/controllers/auth_controller.dart

@ -88,12 +88,4 @@ class AuthController extends GetxController {
Get.snackbar('登录失败', '无网络连接,且无法验证本地凭证');
}
}
/// Handles the logout process by delegating to the repository.
Future<void> logout() async {
isLoading.value = true;
await _authRepository.logout();
isLoading.value = false;
Get.offAllNamed(AppRoutes.login);
}
}

33
lib/modules/home/bindings/home_binding.dart

@ -1,7 +1,10 @@
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/http_provider.dart';
import 'package:problem_check_system/data/providers/local_database.dart';
import 'package:problem_check_system/data/repositories/auth_repository.dart';
import 'package:problem_check_system/data/repositories/problem_repository.dart';
import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart';
import 'package:problem_check_system/modules/home/controllers/home_controller.dart';
import 'package:problem_check_system/modules/my/controllers/my_controller.dart';
@ -10,31 +13,23 @@ import 'package:problem_check_system/modules/problem/controllers/problem_control
class HomeBinding implements Bindings {
@override
void dependencies() {
final LocalDatabase database = Get.find<LocalDatabase>();
final ConnectivityProvider connectivityProvider =
Get.find<ConnectivityProvider>();
// HomeController
///
Get.lazyPut<HomeController>(() => HomeController());
Get.lazyPut<ProblemController>(
() => ProblemController(
localDatabase: database,
connectivityProvider: connectivityProvider,
httpProvider: Get.find(),
),
///
Get.lazyPut<ProblemController>(
() => ProblemController(problemRepository: Get.find<ProblemRepository>()),
fenix: true,
);
Get.lazyPut<AuthRepository>(
() => AuthRepository(
httpProvider: Get.find(),
storage: Get.find(),
connectivityProvider: connectivityProvider,
),
);
///
Get.lazyPut<AuthController>(
() => AuthController(authRepository: Get.find()),
() => AuthController(authRepository: Get.find<AuthRepository>()),
);
///
Get.lazyPut<MyController>(
() => MyController(authRepository: Get.find<AuthRepository>()),
);
Get.lazyPut<MyController>(() => MyController(authRepository: Get.find()));
}
}

11
lib/modules/my/bingdings/change_password_binding.dart

@ -5,16 +5,9 @@ import 'package:problem_check_system/modules/my/controllers/change_password_cont
class ChangePasswordBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<AuthRepository>(
() => AuthRepository(
httpProvider: Get.find(),
storage: Get.find(),
connectivityProvider: Get.find(),
),
);
Get.lazyPut<ChangePasswordController>(
//
() => ChangePasswordController(authRepository: Get.find()),
() =>
ChangePasswordController(authRepository: Get.find<AuthRepository>()),
);
}
}

6
lib/modules/my/bingdings/my_binding.dart

@ -1,6 +0,0 @@
import 'package:get/get.dart';
class MyBinding implements Bindings {
@override
void dependencies() {}
}

8
lib/modules/my/controllers/my_controller.dart

@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/routes/app_routes.dart';
import 'package:problem_check_system/data/repositories/auth_repository.dart';
class MyController extends GetxController {
@ -20,10 +21,15 @@ class MyController extends GetxController {
// API加载用户信息
Future<void> _loadUserInfo() async {
var userProfile = await authRepository.getUserProfile();
userName.value = userProfile.name;
userName.value = userProfile.name ?? "";
userPhone.value = userProfile.email ?? '138****8547';
userImage.value = userProfile.signatureImage.toString();
}
void logout() {
authRepository.clearAuthData();
Get.offAllNamed(AppRoutes.login);
}
//
}

18
lib/modules/my/views/my_page.dart

@ -6,15 +6,13 @@ import 'package:problem_check_system/modules/auth/controllers/auth_controller.da
import '../../../app/routes/app_routes.dart';
class MyPage extends StatelessWidget {
class MyPage extends GetView<MyController> {
const MyPage({super.key});
@override
Widget build(BuildContext context) {
// MyController
final MyController controller = Get.find<MyController>();
// AuthController 退
final AuthController authController = Get.find<AuthController>();
return Scaffold(
body: Stack(
@ -22,7 +20,7 @@ class MyPage extends StatelessWidget {
//
_buildBackground(),
//
_buildContent(controller, authController),
_buildContent(controller),
],
),
);
@ -48,7 +46,7 @@ class MyPage extends StatelessWidget {
}
///
Widget _buildContent(MyController controller, AuthController authController) {
Widget _buildContent(MyController controller) {
return Positioned(
top: 100.h,
left: 20.w,
@ -56,16 +54,16 @@ class MyPage extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildUserInfoCard(controller),
_buildUserInfoCard(),
SizedBox(height: 20.h),
_buildActionButtons(authController),
_buildActionButtons(),
],
),
);
}
///
Widget _buildUserInfoCard(MyController controller) {
Widget _buildUserInfoCard() {
return Obx(
() => Container(
width: 335.w,
@ -164,7 +162,7 @@ class MyPage extends StatelessWidget {
}
///
Widget _buildActionButtons(AuthController authController) {
Widget _buildActionButtons() {
return Column(
children: [
_buildActionButton(
@ -179,7 +177,7 @@ class MyPage extends StatelessWidget {
isLogout: true,
onTap: () {
// AuthController 退
authController.logout();
controller.logout();
},
),
],

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

@ -1,20 +0,0 @@
import 'package:get/get.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';
class ProblemBinding implements Bindings {
@override
void dependencies() {
// 2. ProblemController
//
// Get.find()
Get.lazyPut<ProblemController>(
() => ProblemController(
localDatabase: Get.find<LocalDatabase>(),
httpProvider: Get.find(),
connectivityProvider: Get.find<ConnectivityProvider>(),
),
);
}
}

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

@ -1,22 +1,20 @@
// modules/problem/controllers/problem_controller.dart
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart' hide MultipartFile, FormData;
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:problem_check_system/data/providers/http_provider.dart';
import 'package:problem_check_system/data/repositories/problem_repository.dart';
import 'package:problem_check_system/modules/problem/views/widgets/custom_data_range_dropdown.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/data/providers/local_database.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
class ProblemController extends GetxController
with GetSingleTickerProviderStateMixin {
///
final LocalDatabase localDatabase;
final HttpProvider httpProvider;
final ConnectivityProvider connectivityProvider;
///
final ProblemRepository problemRepository;
///
final RxList<Problem> problems = <Problem>[].obs;
@ -26,9 +24,19 @@ class ProblemController extends GetxController
///
final RxList<Problem> unUploadedProblems = <Problem>[].obs;
final RxList<Problem> selectedUnUploadedProblems = <Problem>[].obs;
final Rx<bool> allSelected = false.obs;
final RxBool isUploadEnabled = false.obs;
final RxDouble uploadProgress = 0.0.obs;
int get selectedCount {
return unUploadedProblems
.where((problem) => problem.isChecked.value)
.length;
}
List<Problem> get selectedUnUploadedProblems {
return unUploadedProblems
.where((problem) => problem.isChecked.value)
.toList();
}
///
final Rx<DateRange> selectedDateRange = DateRange.oneWeek.obs;
@ -47,23 +55,15 @@ class ProblemController extends GetxController
final fabUploadPosition = Offset(337.0, 703.7).obs;
/// get
RxBool get isOnline => connectivityProvider.isOnline;
RxBool get isOnline => problemRepository.isOnline;
ProblemController({
required this.localDatabase,
required this.httpProvider,
required this.connectivityProvider,
});
ProblemController({required this.problemRepository});
@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();
@ -75,45 +75,87 @@ class ProblemController extends GetxController
super.onClose();
}
// #region
///
void onProblemCheckedChange() {
// selectedUnUploadedProblems
_updateSelectedList();
// _updateSelectedList selectedUnUploadedProblems
// _updateUploadButtonState() ever
}
///
void _updateSelectedList() {
selectedUnUploadedProblems.clear();
for (var problem in unUploadedProblems) {
if (problem.isChecked.value) {
selectedUnUploadedProblems.add(problem);
}
}
}
void _updateUploadButtonState() {
isUploadEnabled.value = selectedUnUploadedProblems.isNotEmpty;
}
void onProblemCheckedChange() {}
///
void selectAll() {
final bool newState = !allSelected.value;
for (var problem in unUploadedProblems) {
problem.isChecked.value = newState;
}
allSelected.value = newState;
_updateSelectedList();
// _updateSelectedList();
}
void uploadProblems() {
if (selectedUnUploadedProblems.isEmpty) return;
// API
void uploadProblems() async {
// if (selectedUnUploadedProblems.isEmpty) return;
// // API
print('开始上传 ${selectedUnUploadedProblems.length} 个问题...');
//
selectedUnUploadedProblems.clear();
// //
// selectedUnUploadedProblems.clear();
for (var problem in selectedUnUploadedProblems) {
await uploadProblem(problem);
}
}
Future<bool> uploadProblem(Problem problem) async {
try {
final formData = FormData.fromMap({
'description': problem.description,
'location': problem.location,
'createdAt': problem.createdAt.toIso8601String(),
'boundInfo': problem.bindData ?? '',
});
for (var imageUrl in problem.imageUrls) {
final file = File(imageUrl);
if (await file.exists()) {
formData.files.add(
MapEntry(
'images',
await MultipartFile.fromFile(
imageUrl,
filename: path.basename(imageUrl),
),
),
);
}
}
// final response = await httpProvider.post(
// 'https://your-server.com/api/problems',
// data: formData,
// options: Options(
// sendTimeout: const Duration(seconds: 30),
// receiveTimeout: const Duration(seconds: 30),
// ),
// );
// if (response.statusCode == 200) {
// final updatedProblem = problem.copyWith(isUploaded: true);
// await updateProblem(updatedProblem);
return true;
// } else {
// throw Exception('服务器返回错误状态码: ${response.statusCode}');
// }
} on DioException catch (e) {
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.receiveTimeout) {
Get.snackbar('网络超时', '请检查网络连接后重试');
} else {
Get.snackbar('网络错误', '上传问题失败: ${e.message}');
}
return false;
} catch (e) {
Get.snackbar('错误', '上传问题失败: $e');
return false;
}
}
// #endregion
// #region
/// floatingButton更新位置
void updateFabUploadPosition(Offset delta) {
final screenWidth = ScreenUtil().screenWidth;
@ -158,6 +200,9 @@ class ProblemController extends GetxController
fabUploadPosition.value = Offset(newDx, fabUploadPosition.value.dy);
}
// #endregion
// #region ta按钮
void _onTabChanged() {
if (!tabController.indexIsChanging) {
selectedDateRange.value = DateRange.oneWeek;
@ -166,7 +211,9 @@ class ProblemController extends GetxController
loadProblems();
}
}
// #endregion
///
Future<void> loadProblems() async {
isLoading.value = true;
try {
@ -188,7 +235,7 @@ class ProblemController extends GetxController
: '全部';
//
final loadedProblems = await localDatabase.getProblems(
final loadedProblems = await problemRepository.getProblems(
startDate: startDate,
endDate: endDate,
uploadStatus: uploadStatus,
@ -233,7 +280,7 @@ class ProblemController extends GetxController
isLoading.value = true;
try {
// _localDatabase.getProblems '未上传'
unUploadedProblems.value = await localDatabase.getProblems(
unUploadedProblems.value = await problemRepository.getProblems(
uploadStatus: '未上传',
);
} catch (e) {
@ -245,7 +292,7 @@ class ProblemController extends GetxController
Future<void> addProblem(Problem problem) async {
try {
await localDatabase.insertProblem(problem);
await problemRepository.insertProblem(problem);
loadProblems();
} catch (e) {
Get.snackbar('错误', '保存问题失败: $e');
@ -255,7 +302,7 @@ class ProblemController extends GetxController
Future<void> updateProblem(Problem problem) async {
try {
await localDatabase.updateProblem(problem);
await problemRepository.updateProblem(problem);
loadProblems();
} catch (e) {
Get.snackbar('错误', '更新问题失败: $e');
@ -266,7 +313,7 @@ class ProblemController extends GetxController
Future<void> deleteProblem(Problem problem) async {
try {
if (problem.id != null) {
await localDatabase.deleteProblem(problem.id!);
await problemRepository.deleteProblem(problem.id!);
await _deleteProblemImages(problem);
loadProblems();
}
@ -276,26 +323,6 @@ class ProblemController extends GetxController
}
}
Future<void> deleteSelectedProblems() async {
final problemsToDelete = selectedUnUploadedProblems;
if (problemsToDelete.isEmpty) {
Get.snackbar('提示', '请至少选择一个问题进行删除');
return;
}
try {
for (var problem in problemsToDelete) {
if (problem.id != null) {
await localDatabase.deleteProblem(problem.id!);
await _deleteProblemImages(problem);
}
}
Get.snackbar('成功', '已删除${problemsToDelete.length}个问题');
loadProblems();
} catch (e) {
Get.snackbar('错误', '删除问题失败: $e');
}
}
Future<void> _deleteProblemImages(Problem problem) async {
for (var imagePath in problem.imageUrls) {
try {
@ -309,109 +336,24 @@ class ProblemController extends GetxController
}
}
Future<bool> uploadProblem(Problem problem) async {
try {
final formData = FormData.fromMap({
'description': problem.description,
'location': problem.location,
'createdAt': problem.createdAt.toIso8601String(),
'boundInfo': problem.bindData ?? '',
});
for (var imageUrl in problem.imageUrls) {
final file = File(imageUrl);
if (await file.exists()) {
formData.files.add(
MapEntry(
'images',
await MultipartFile.fromFile(
imageUrl,
filename: path.basename(imageUrl),
),
),
);
}
}
final response = await httpProvider.post(
'https://your-server.com/api/problems',
data: formData,
options: Options(
sendTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
),
);
if (response.statusCode == 200) {
final updatedProblem = problem.copyWith(isUploaded: true);
await updateProblem(updatedProblem);
return true;
} else {
throw Exception('服务器返回错误状态码: ${response.statusCode}');
}
} on DioException catch (e) {
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.receiveTimeout) {
Get.snackbar('网络超时', '请检查网络连接后重试');
} else {
Get.snackbar('网络错误', '上传问题失败: ${e.message}');
}
return false;
} catch (e) {
Get.snackbar('错误', '上传问题失败: $e');
return false;
}
}
Future<void> uploadAllUnuploaded() async {
if (!isOnline.value) {
Get.snackbar('提示', '当前无网络,无法上传');
return;
}
final unuploaded = unUploadedProblems;
if (unuploaded.isEmpty) {
Get.snackbar('提示', '没有需要上传的问题');
return;
}
isLoading.value = true;
int successCount = 0;
for (var problem in unuploaded) {
final success = await uploadProblem(problem);
if (success) {
successCount++;
}
await Future.delayed(const Duration(milliseconds: 500));
}
isLoading.value = false;
if (successCount > 0) {
Get.snackbar('成功', '已成功上传$successCount个问题');
}
if (successCount < unuploaded.length) {
Get.snackbar('部分成功', '${unuploaded.length - successCount}个问题上传失败');
}
loadProblems();
}
///
///
Future<void> selectDateRange(BuildContext context) async {
final initialDateRange = DateTimeRange(
start: DateTime.now().subtract(const Duration(days: 7)),
end: DateTime.now(),
);
Future<void> bindInfoToProblem(String id, String info) async {
try {
final problem = historyProblems.firstWhere((p) => p.id == id);
final updatedProblem = problem.copyWith(bindData: info);
await updateProblem(updatedProblem);
Get.snackbar('成功', '信息已绑定');
} catch (e) {
Get.snackbar('错误', '未找到问题或绑定失败: $e');
}
}
final DateTimeRange? picked = await showDateRangePicker(
context: context,
firstDate: DateTime(2025, 8, 1), //
lastDate: DateTime(2101), //
initialDateRange: initialDateRange,
);
void clearSelections() {
for (var problem in historyProblems) {
problem.isChecked.value = false;
if (picked != null) {
//
log('选择的日期范围是: ${picked.start}${picked.end}');
}
}
}

1
lib/modules/problem/views/problem_list_page.dart

@ -59,7 +59,6 @@ class ProblemListPage extends GetView<ProblemController> {
child: ProblemCard(problem, viewType: viewType),
);
} else {
//
return ProblemCard(problem, viewType: viewType);
}
}

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

@ -124,15 +124,12 @@ class ProblemPage extends GetView<ProblemController> {
padding: EdgeInsets.symmetric(horizontal: 17.w),
child: Row(
children: [
CustomDateRangeDropdown(
selectedRange: controller.selectedDateRange,
onChanged: (rangeValue) {
controller.updateFiltersAndLoadProblems(
newDateRange: rangeValue,
);
},
//
ElevatedButton(
onPressed: () =>
controller.selectDateRange(context),
child: const Text('选择日期范围'),
),
CustomStringDropdown(
selectedValue: controller.selectedUploadStatus,
items: const ['全部', '未上传', '已上传'],

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

@ -2,9 +2,9 @@ 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 'package:problem_check_system/modules/problem/views/problem_list_page.dart';
import 'package:problem_check_system/modules/problem/views/widgets/problem_card.dart';
// todo 使problem_list_page,problem_controller
class ProblemUploadPage extends GetView<ProblemController> {
const ProblemUploadPage({super.key});
@ -21,7 +21,7 @@ class ProblemUploadPage extends GetView<ProblemController> {
PreferredSizeWidget _buildAppBar() {
return AppBar(
title: Obx(() {
final selectedCount = controller.selectedUnUploadedProblems.length;
final selectedCount = controller.selectedCount;
return Text('已选择$selectedCount项');
}),
centerTitle: true,
@ -42,19 +42,10 @@ class ProblemUploadPage extends GetView<ProblemController> {
//
Widget _buildBody() {
return Obx(() {
return ListView.builder(
itemCount: controller.unUploadedProblems.length,
itemBuilder: (context, index) {
final problem = controller.unUploadedProblems[index];
return ProblemCard(
problem,
// Checkbox
viewType: ProblemCardViewType.checkbox,
);
},
);
});
return ProblemListPage(
problemsToShow: controller.unUploadedProblems,
viewType: ProblemCardViewType.checkbox,
);
}
//
@ -67,7 +58,7 @@ class ProblemUploadPage extends GetView<ProblemController> {
),
child: Obx(
() => ElevatedButton(
onPressed: controller.isUploadEnabled.value
onPressed: controller.selectedCount > 0
? controller.uploadProblems
: null,
style: ElevatedButton.styleFrom(
@ -82,4 +73,28 @@ class ProblemUploadPage extends GetView<ProblemController> {
),
);
}
Widget uploadProgressWidget() {
return AlertDialog(
title: Text('上传中...'),
content: Obx(() {
final progress = (controller.uploadProgress.value * 100).toInt();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
LinearProgressIndicator(
value: controller.uploadProgress.value,
backgroundColor: Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
),
SizedBox(height: 16),
Text('已完成: $progress%'),
Text(
'已上传: ${controller.unUploadedProblems.length} / ${controller.selectedCount}',
),
],
);
}),
);
}
}

5
pubspec.lock

@ -182,6 +182,11 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:

2
pubspec.yaml

@ -12,6 +12,8 @@ dependencies:
dio: ^5.9.0
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
flutter_screenutil: ^5.9.3
get: ^4.7.2
get_storage: ^2.1.1

Loading…
Cancel
Save