Browse Source

refactor : 问题功能进行重构

dev
徐振升 6 days ago
parent
commit
cac0993b50
  1. 10
      lib/app/core/models/form_mode.dart
  2. 2
      lib/app/core/models/problem_sync_status.dart
  3. 65
      lib/app/core/models/sync_status.dart
  4. 2
      lib/app/core/repositories/auth_repository.dart
  5. 17
      lib/app/core/routes/app_pages.dart
  6. 36
      lib/app/core/services/database_service.dart
  7. 4
      lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart
  8. 12
      lib/app/features/enterprise/presentation/controllers/enterprise_form_controller.dart
  9. 2
      lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart
  10. 1
      lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart
  11. 33
      lib/app/features/home/bindings/home_binding.dart
  12. 4
      lib/app/features/home/controllers/home_controller.dart
  13. 4
      lib/app/features/navigation/presentation/controllers/navigation_controller.dart
  14. 145
      lib/app/features/problem/data/datasources/problem_local_data_source.dart
  15. 123
      lib/app/features/problem/data/datasources/problem_local_datasource.dart
  16. 1
      lib/app/features/problem/data/datasources/problem_remote_data_source.dart
  17. 103
      lib/app/features/problem/data/model/problem_local_dto.dart
  18. 144
      lib/app/features/problem/data/model/problem_remote_dto.dart
  19. 252
      lib/app/features/problem/data/repositories/problem_repository.dart
  20. 81
      lib/app/features/problem/data/repositories/problem_repository_impl.dart
  21. 67
      lib/app/features/problem/domain/entities/problem_entity.dart
  22. 19
      lib/app/features/problem/domain/repositoies/problem_repository.dart
  23. 16
      lib/app/features/problem/domain/repositories/problem_repository.dart
  24. 40
      lib/app/features/problem/domain/usecases/add_problem_usecase.dart
  25. 10
      lib/app/features/problem/domain/usecases/delete_problem.dart
  26. 22
      lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart
  27. 11
      lib/app/features/problem/domain/usecases/get_problem_by_id_usecase.dart
  28. 22
      lib/app/features/problem/domain/usecases/update_problem.dart
  29. 62
      lib/app/features/problem/presentation/bindings/problem_binding.dart
  30. 34
      lib/app/features/problem/presentation/bindings/problem_form_binding.dart
  31. 35
      lib/app/features/problem/presentation/bindings/problem_list_binding.dart
  32. 8
      lib/app/features/problem/presentation/bindings/problem_upload_binding.dart
  33. 1946
      lib/app/features/problem/presentation/controllers/problem_controller.dart
  34. 140
      lib/app/features/problem/presentation/controllers/problem_form_controller.dart
  35. 208
      lib/app/features/problem/presentation/controllers/problem_list_controller.dart
  36. 13
      lib/app/features/problem/presentation/controllers/problem_upload_controller.dart
  37. 0
      lib/app/features/problem/presentation/models/problem_card_model.dart
  38. 13
      lib/app/features/problem/presentation/models/problem_form_model.dart
  39. 10
      lib/app/features/problem/presentation/pages/problem_form_page.dart
  40. 182
      lib/app/features/problem/presentation/pages/problem_list_page.dart
  41. 25
      lib/app/features/problem/presentation/pages/problem_upload_page.dart
  42. 0
      lib/app/features/problem/presentation/pages/widgets/custom_button.dart
  43. 2
      lib/app/features/problem/presentation/pages/widgets/custom_filter_dropdown.dart
  44. 2
      lib/app/features/problem/presentation/pages/widgets/models/date_range_enum.dart
  45. 0
      lib/app/features/problem/presentation/pages/widgets/models/dropdown_option.dart
  46. 10
      lib/app/features/problem/presentation/pages/widgets/problem_card.dart
  47. 0
      lib/app/features/problem/presentation/pages/widgets/sync_progress_dialog.dart
  48. 94
      lib/app/features/problem/presentation/views/problem_page.dart
  49. 66
      lib/app/features/problem/presentation/views/widgets/current_filter_bar.dart
  50. 97
      lib/app/features/problem/presentation/views/widgets/history_filter_bar.dart
  51. 181
      lib/app/features/problem/presentation/views/widgets/problem_list_page.dart

10
lib/app/core/models/form_mode.dart

@ -0,0 +1,10 @@
enum FormMode {
///
add,
///
edit,
///
view,
}

2
lib/app/core/models/problem_sync_status.dart

@ -41,7 +41,7 @@ class ProblemStateManager extends GetxController {
location: location,
imageUrls: imageUrls,
creationTime: DateTime.now().toUtc(),
creatorId: authRepository.getUserId()!,
creatorId: authRepository.getUserId(),
lastModifiedTime: DateTime.now().toUtc(),
syncStatus: ProblemSyncStatus.pendingCreate,
);

65
lib/app/core/models/sync_status.dart

@ -1,60 +1,17 @@
// SyncStatus
import 'package:flutter/material.dart';
enum SyncStatus {
/// -
untracked,
untracked('未跟踪', Colors.grey),
synced('已同步', Colors.green),
pendingCreate('待新建', Colors.blue),
pendingUpdate('待更新', Colors.orange),
pendingDelete('待删除', Colors.red);
/// - git的unmodified
synced,
final String displayName;
final Color displayColor;
/// - git的untracked staged
pendingCreate,
/// - git的modified staged
pendingUpdate,
/// - git的deleted staged
pendingDelete,
}
/// SyncStatus
extension SyncStatusExtension on SyncStatus {
///
String get displayName {
switch (this) {
case SyncStatus.synced:
return '已同步';
case SyncStatus.pendingCreate:
return '待新建';
case SyncStatus.pendingUpdate:
return '待更新';
case SyncStatus.pendingDelete:
return '待删除';
case SyncStatus.untracked:
return '未跟踪';
// default
default:
return '未知状态';
}
}
/// [] UI显示
Color get displayColor {
switch (this) {
case SyncStatus.synced:
return Colors.green;
case SyncStatus.pendingCreate:
return Colors.blue;
case SyncStatus.pendingUpdate:
return Colors.orange;
case SyncStatus.pendingDelete:
return Colors.red;
case SyncStatus.untracked:
return Colors.grey;
default:
return Colors.black;
}
}
const SyncStatus(this.displayName, this.displayColor);
}
///
@ -62,6 +19,6 @@ extension SyncStatusExtension on SyncStatus {
abstract class SyncableEntity {
String get id;
SyncStatus get syncStatus;
DateTime? get lastModifiedTime;
String? get lastModifierId;
DateTime get lastModifiedTime;
String get lastModifierId;
}

2
lib/app/core/repositories/auth_repository.dart

@ -43,7 +43,7 @@ class AuthRepository extends GetxService {
storage.write(_userId, id);
}
String? getUserId() {
String getUserId() {
return storage.read(_userId);
}

17
lib/app/core/routes/app_pages.dart

@ -16,11 +16,12 @@ import 'package:problem_check_system/app/features/auth/bindings/login_binding.da
import 'package:problem_check_system/app/features/auth/views/login_page.dart';
import 'package:problem_check_system/app/features/my/bindings/change_password_binding.dart';
import 'package:problem_check_system/app/features/my/views/change_password.dart';
import 'package:problem_check_system/app/features/problem/presentation/bindings/problem_binding.dart';
import 'package:problem_check_system/app/features/problem/presentation/bindings/problem_form_binding.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_form_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_upload_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/bindings/problem_list_binding.dart';
import 'package:problem_check_system/app/features/problem/presentation/bindings/problem_upload_binding.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/problem_form_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/problem_list_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/problem_upload_page.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_info_page.dart';
import 'app_routes.dart';
@ -34,7 +35,7 @@ abstract class AppPages {
binding: BindingsBuilder(() {
NavigationBinding().dependencies();
EnterpriseListBinding().dependencies();
ProblemBinding().dependencies();
ProblemListBinding().dependencies();
ProfileBinding().dependencies();
}),
),
@ -56,8 +57,8 @@ abstract class AppPages {
),
GetPage(
name: AppRoutes.problem,
page: () => const ProblemPage(),
binding: ProblemBinding(),
page: () => const ProblemListPage(),
binding: ProblemListBinding(),
),
GetPage(
name: AppRoutes.my,
@ -67,7 +68,7 @@ abstract class AppPages {
GetPage(
name: AppRoutes.problemUpload,
page: () => const ProblemUploadPage(),
binding: ProblemBinding(),
binding: ProblemUploadBinding(),
),
GetPage(
name: AppRoutes.problemForm,

36
lib/app/core/services/database_service.dart

@ -2,21 +2,33 @@ import 'package:get/get.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
///
/// ()
const String _createProblemsTable = '''
CREATE TABLE problems(
id TEXT PRIMARY KEY,
enterpriseId TEXT NOT NULL,
description TEXT NOT NULL,
location TEXT NOT NULL,
imageUrls TEXT NOT NULL,
creationTime INTEGER NOT NULL,
-- [] imageUrls
imageUrls TEXT NOT NULL DEFAULT '[]',
creatorId TEXT NOT NULL,
lastModifiedTime INTEGER NOT NULL,
syncStatus INTEGER NOT NULL,
censorTaskId TEXT,
bindData TEXT,
isChecked INTEGER NOT NULL
creationTime TEXT NOT NULL,
lastModifierId TEXT NOT NULL,
lastModifiedTime TEXT NOT NULL,
-- [] 使 CHECK syncStatus
syncStatus TEXT NOT NULL CHECK(syncStatus IN (
'untracked',
'synced',
'pendingCreate',
'pendingUpdate',
'pendingDelete'
)),
bindData TEXT
-- SQL
)
''';
@ -81,6 +93,14 @@ class DatabaseService extends GetxService {
//
await db.execute(_createProblemsTable);
Get.log('`problems` 表创建成功');
// []
await db.execute(
'CREATE INDEX idx_problems_creationTime ON problems(creationTime)',
);
await db.execute(
'CREATE INDEX idx_problems_syncStatus ON problems(syncStatus)',
);
Get.log('`problems` 表的索引创建成功');
//
await db.execute(_createEnterprisesTable);

4
lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart

@ -34,9 +34,9 @@ class AddEnterpriseUsecase {
contactPhone: contactPhone,
majorHazardsDescription: majorHazardsDescription,
lastModifiedTime: DateTime.now(),
lastModifierId: authRepository.getUserId()!,
lastModifierId: authRepository.getUserId(),
creationTime: DateTime.now(),
creatorId: authRepository.getUserId()!,
creatorId: authRepository.getUserId(),
syncStatus: SyncStatus.pendingCreate,
);
await repository.addEnterprise(enterprise);

12
lib/app/features/enterprise/presentation/controllers/enterprise_form_controller.dart

@ -1,21 +1,11 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart';
enum FormMode {
///
add,
///
edit,
///
view,
}
class EnterpriseFormController extends GetxController {
final AddEnterpriseUsecase? addEnterpriseUsecase;
final EditEnterpriseUsecase? editEnterpriseUsecase;

2
lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_conflict.dart';
@ -11,7 +12,6 @@ import 'package:problem_check_system/app/features/enterprise/domain/entities/ent
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_form_controller.dart';
/// -----------------------------------------------------------------------------
/// []

1
lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:problem_check_system/app/core/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
// [ 1]

33
lib/app/features/home/bindings/home_binding.dart

@ -1,31 +1,26 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository.dart';
import 'package:problem_check_system/app/features/home/controllers/home_controller.dart';
import 'package:problem_check_system/app/features/my/controllers/my_controller.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
class HomeBinding implements Bindings {
@override
void dependencies() {
///
Get.lazyPut<HomeController>(() => HomeController());
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
// Get.lazyPut<EnterpriseListController>(() => EnterpriseListController());
// Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
// // Get.lazyPut<EnterpriseListController>(() => EnterpriseListController());
///
Get.lazyPut<ProblemController>(
() => ProblemController(
problemRepository: Get.find<ProblemRepository>(),
problemStateManager: Get.find<ProblemStateManager>(),
),
fenix: true,
);
// ///
// Get.lazyPut<ProblemController>(
// () => ProblemController(
// problemRepository: Get.find<ProblemRepository>(),
// problemStateManager: Get.find<ProblemStateManager>(),
// ),
// fenix: true,
// );
///
Get.lazyPut<MyController>(
() => MyController(authRepository: Get.find<AuthRepository>()),
);
// ///
// Get.lazyPut<MyController>(
// () => MyController(authRepository: Get.find<AuthRepository>()),
// );
}
}

4
lib/app/features/home/controllers/home_controller.dart

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_list_page.dart';
import 'package:problem_check_system/app/features/my/views/my_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/problem_list_page.dart';
class HomeController extends GetxController {
var selectedIndex = 0.obs;
//
final List<Widget> pages = [
const EnterpriseListPage(),
const ProblemPage(),
const ProblemListPage(),
const MyPage(),
];

4
lib/app/features/navigation/presentation/controllers/navigation_controller.dart

@ -7,7 +7,7 @@ import 'package:problem_check_system/app/features/enterprise/presentation/contro
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_list_page.dart';
import 'package:problem_check_system/app/features/home/pages/home_page.dart';
import 'package:problem_check_system/app/features/my/views/my_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/problem_list_page.dart';
class NavigationController extends GetxController {
var selectedIndex = 0.obs;
@ -30,7 +30,7 @@ class NavigationController extends GetxController {
final List<Widget> pages = const [
HomePage(),
EnterpriseListPage(),
ProblemPage(),
ProblemListPage(),
MyPage(),
];
final navigationItems = const <Widget>[

145
lib/app/features/problem/data/datasources/problem_local_data_source.dart

@ -0,0 +1,145 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart'; // SyncStatus
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:sqflite/sqflite.dart'; // DatabaseService
/// IProblemLocalDataSource
/// 'problems' CRUD (, , , )
/// (Repository) ( Sqflite)
abstract class IProblemLocalDataSource {
/// ID
///
/// [id] -
/// Map<String, dynamic> null
Future<Map<String, dynamic>?> getProblemById(String id);
///
///
/// [startDate] -
/// [endDate] -
/// [syncStatus] -
/// [bindStatus] - ()
Future<List<Map<String, dynamic>>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
});
///
///
/// [problemMap] - Map 'problems'
Future<void> addProblem(Map<String, dynamic> problemMap);
///
///
/// [problemMap] - Map 'id'
Future<void> updateProblem(Map<String, dynamic> problemMap);
/// ID
///
/// [id] -
Future<void> deleteProblem(String id);
}
// IProblemLocalDataSource
class ProblemLocalDataSource implements IProblemLocalDataSource {
final DatabaseService _databaseService;
final String _tableName = 'problems';
ProblemLocalDataSource({required DatabaseService databaseService})
: _databaseService = databaseService;
@override
Future<void> addProblem(Map<String, dynamic> problemMap) async {
final db = await _databaseService.database;
await db.insert(
_tableName,
problemMap,
// ID 使 add update
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
@override
Future<void> deleteProblem(String id) async {
final db = await _databaseService.database;
await db.delete(_tableName, where: 'id = ?', whereArgs: [id]);
}
@override
Future<List<Map<String, dynamic>>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus, // bindStatus
}) async {
final db = await _databaseService.database;
// WHERE
List<String> whereClauses = [];
List<dynamic> whereArgs = [];
if (startDate != null) {
// INTEGER ()
whereClauses.add('creationTime >= ?');
whereArgs.add(startDate.millisecondsSinceEpoch);
}
if (endDate != null) {
whereClauses.add('creationTime <= ?');
whereArgs.add(endDate.millisecondsSinceEpoch);
}
if (syncStatus != null) {
// INTEGER ( index)
// index
try {
final status = SyncStatus.values.byName(syncStatus);
whereClauses.add('syncStatus = ?');
whereArgs.add(status.index);
} catch (e) {
// status
Get.log('无效的 syncStatus 过滤器: $syncStatus', isError: true);
}
}
final String? whereString = whereClauses.isEmpty
? null
: whereClauses.join(' AND ');
final List<Map<String, dynamic>> maps = await db.query(
_tableName,
where: whereString,
whereArgs: whereArgs,
orderBy: 'creationTime DESC', //
);
return maps;
}
@override
Future<Map<String, dynamic>?> getProblemById(String id) async {
final db = await _databaseService.database;
final List<Map<String, dynamic>> maps = await db.query(
_tableName,
where: 'id = ?',
whereArgs: [id],
limit: 1, //
);
if (maps.isNotEmpty) {
return maps.first;
}
return null;
}
@override
Future<void> updateProblem(Map<String, dynamic> problemMap) async {
final db = await _databaseService.database;
await db.update(
_tableName,
problemMap,
where: 'id = ?',
whereArgs: [problemMap['id']],
);
}
}

123
lib/app/features/problem/data/datasources/problem_local_datasource.dart

@ -1,123 +0,0 @@
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:sqflite/sqflite.dart';
const String _tableName = 'problems';
///
abstract class ProblemLocalDataSource {
Future<int> insertProblem(Problem problem);
Future<int> updateProblem(Problem problem);
Future<int> deleteProblem(String problemId);
Future<Problem?> getProblemById(String id);
Future<List<Problem>> getProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
});
}
///
class ProblemLocalDataSourceImpl implements ProblemLocalDataSource {
final DatabaseService _databaseService;
ProblemLocalDataSourceImpl({required DatabaseService databaseService})
: _databaseService = databaseService;
@override
Future<int> insertProblem(Problem problem) async {
final db = await _databaseService.database;
return await db.insert(
_tableName,
problem.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
@override
Future<int> updateProblem(Problem problem) async {
final db = await _databaseService.database;
return await db.update(
_tableName,
problem.toMap(),
where: 'id = ?',
whereArgs: [problem.id],
);
}
@override
Future<int> deleteProblem(String problemId) async {
final db = await _databaseService.database;
return await db.delete(_tableName, where: 'id = ?', whereArgs: [problemId]);
}
@override
Future<Problem?> getProblemById(String id) async {
final db = await _databaseService.database;
final results = await db.query(
_tableName,
where: 'id = ?',
whereArgs: [id],
limit: 1,
);
return results.isNotEmpty ? Problem.fromMap(results.first) : null;
}
@override
Future<List<Problem>> getProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}) async {
final db = await _databaseService.database;
final whereClauses = <String>[];
final whereArgs = <dynamic>[];
//
if (startDate != null) {
whereClauses.add('creationTime >= ?');
whereArgs.add(startDate.millisecondsSinceEpoch);
}
if (endDate != null) {
whereClauses.add('creationTime <= ?');
whereArgs.add(endDate.millisecondsSinceEpoch);
}
//
if (syncStatus != null && syncStatus != '全部') {
if (syncStatus == '未上传') {
whereClauses.add('syncStatus IN (?, ?, ?)');
whereArgs.addAll([
ProblemSyncStatus.pendingCreate.index,
ProblemSyncStatus.pendingUpdate.index,
ProblemSyncStatus.pendingDelete.index,
]);
} else {
whereClauses.add('syncStatus = ?');
whereArgs.add(ProblemSyncStatus.synced.index);
}
}
//
if (bindStatus != null && bindStatus != '全部') {
if (bindStatus == '已绑定') {
whereClauses.add('bindData IS NOT NULL AND bindData != ""');
} else {
whereClauses.add('(bindData IS NULL OR bindData = "")');
}
}
final results = await db.query(
_tableName,
where: whereClauses.isNotEmpty ? whereClauses.join(' AND ') : null,
whereArgs: whereArgs.isEmpty ? null : whereArgs,
orderBy: 'creationTime DESC',
);
return results.map((json) => Problem.fromMap(json)).toList();
}
}

1
lib/app/features/problem/data/datasources/problem_remote_data_source.dart

@ -0,0 +1 @@
class ProblemRemoteDataSource {}

103
lib/app/features/problem/data/model/problem_local_dto.dart

@ -0,0 +1,103 @@
import 'dart:convert';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import '../../domain/entities/problem_entity.dart';
/// ProblemLocalDto ()
///
/// `problems`
/// (Entity) Map
/// TEXT
class ProblemLocalDto {
final String id;
final String enterpriseId;
final String description;
final String location;
final String imageUrls;
final String creatorId;
final String creationTime;
final String lastModifierId;
final String lastModifiedTime;
final SyncStatus syncStatus;
final String? bindData;
ProblemLocalDto({
required this.id,
required this.enterpriseId,
required this.description,
required this.location,
required this.imageUrls,
required this.creatorId,
required this.creationTime,
required this.lastModifierId,
required this.lastModifiedTime,
required this.syncStatus,
required this.bindData,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'enterpriseId': enterpriseId,
'description': description,
'location': location,
'imageUrls': imageUrls,
'creatorId': creatorId,
'creationTime': creationTime,
'lastModifierId': lastModifierId,
'lastModifiedTime': lastModifiedTime,
'syncStatus': syncStatus.name,
'bindData': bindData,
};
}
factory ProblemLocalDto.fromMap(Map<String, dynamic> map) {
return ProblemLocalDto(
id: map['id'] as String,
enterpriseId: map['enterpriseId'] as String,
description: map['description'] as String,
location: map['location'] as String,
imageUrls: map['imageUrls'] as String,
creatorId: map['creatorId'] as String,
creationTime: map['creationTime'] as String,
lastModifierId: map['lastModifierId'],
lastModifiedTime: map['lastModifiedTime'] as String,
syncStatus: SyncStatus.values.byName(map['syncStatus'] as String),
bindData: map['bindData'] as String?,
);
}
/// ProblemEntity DTO
factory ProblemLocalDto.fromEntity(ProblemEntity entity) {
return ProblemLocalDto(
id: entity.id,
enterpriseId: entity.enterpriseId,
description: entity.description,
location: entity.location,
imageUrls: jsonEncode(entity.imageUrls),
creatorId: entity.creatorId,
creationTime: entity.creationTime.toUtc().toIso8601String(),
lastModifierId: entity.lastModifierId,
lastModifiedTime: entity.lastModifiedTime.toUtc().toIso8601String(),
syncStatus: entity.syncStatus,
bindData: entity.bindData,
);
}
/// DTO ProblemEntity
ProblemEntity toEntity() {
return ProblemEntity(
id: id,
enterpriseId: enterpriseId,
description: description,
location: location,
imageUrls: List<String>.from(jsonDecode(imageUrls)),
creatorId: creatorId,
creationTime: DateTime.parse(creationTime),
lastModifierId: lastModifierId,
lastModifiedTime: DateTime.parse(lastModifiedTime),
syncStatus: syncStatus,
bindData: bindData,
);
}
}

144
lib/app/features/problem/data/model/problem_remote_dto.dart

@ -0,0 +1,144 @@
import 'dart:convert';
import 'package:problem_check_system/app/core/extensions/map_extensions.dart';
import 'package:problem_check_system/app/core/models/image_metadata_model.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
///
///
class ProblemRemoteDto {
///
final String id;
/// id
final String? enterpriseId;
///
final String description;
///
final String location;
///
final List<ImageMetadata> imageUrls;
///
final DateTime creationTime;
/// id
final String creatorId;
///
final ProblemSyncStatus syncStatus;
///
final DateTime lastModifiedTime;
/// ID
final String? censorTaskId;
///
final String? bindData;
/// false
final bool isChecked;
ProblemRemoteDto({
required this.id,
required this.description,
required this.location,
required this.imageUrls,
required this.creationTime,
required this.creatorId,
required this.lastModifiedTime,
this.syncStatus = ProblemSyncStatus.pendingCreate,
this.censorTaskId,
this.bindData,
this.isChecked = false,
this.enterpriseId,
});
/// copyWith
ProblemRemoteDto copyWith({
String? id,
String? enterpriseId,
String? description,
String? location,
List<ImageMetadata>? imageUrls,
DateTime? creationTime,
String? creatorId,
DateTime? lastModifiedTime,
ProblemSyncStatus? syncStatus,
bool? isDeleted,
String? censorTaskId,
String? bindData,
bool? isChecked,
}) {
return ProblemRemoteDto(
id: id ?? this.id,
enterpriseId: enterpriseId ?? this.enterpriseId,
description: description ?? this.description,
location: location ?? this.location,
imageUrls: imageUrls ?? this.imageUrls,
creationTime: creationTime ?? this.creationTime,
creatorId: creatorId ?? this.creatorId,
lastModifiedTime: lastModifiedTime ?? this.lastModifiedTime,
syncStatus: syncStatus ?? this.syncStatus,
censorTaskId: censorTaskId ?? this.censorTaskId,
bindData: bindData ?? this.bindData,
isChecked: isChecked ?? this.isChecked,
);
}
/// JSON字符串
Map<String, dynamic> toJson() {
return {
'id': id,
'enterpriseId': enterpriseId,
'description': description,
'location': location,
'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()),
'creationTime': creationTime.toIso8601String(),
'creatorId': creatorId,
'lastModifiedTime': lastModifiedTime.toIso8601String(),
'censorTaskId': censorTaskId,
'bindData': bindData,
}.withoutNullOrEmptyValues;
}
/// Map创建对象SQLite读取
factory ProblemRemoteDto.fromJson(Map<String, dynamic> map) {
// imageUrls的转换
List<ImageMetadata> imageUrlsList = [];
if (map['imageUrls'] != null) {
try {
final List<dynamic> imageList = json.decode(map['imageUrls']);
imageUrlsList = imageList.map((e) => ImageMetadata.fromMap(e)).toList();
} catch (e) {
//
imageUrlsList = [];
}
}
return ProblemRemoteDto(
id: map['id'],
enterpriseId: map['companyId'],
description: map['description'],
location: map['location'],
imageUrls: imageUrlsList,
creationTime: DateTime.fromMillisecondsSinceEpoch(
map['creationTime'],
isUtc: true,
),
creatorId: map['creatorId'],
lastModifiedTime: DateTime.fromMillisecondsSinceEpoch(
map['lastModifiedTime'],
isUtc: true,
),
syncStatus: ProblemSyncStatus.values[map['syncStatus']],
censorTaskId: map['censorTaskId'],
bindData: map['bindData'],
isChecked: map['isChecked'] == 1,
);
}
}

252
lib/app/features/problem/data/repositories/problem_repository.dart

@ -1,139 +1,139 @@
import 'package:dio/dio.dart';
import 'package:get/get.dart' hide MultipartFile, FormData, Response;
import 'package:problem_check_system/app/core/extensions/http_response_extension.dart';
import 'package:problem_check_system/app/core/utils/constants/api_endpoints.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/core/models/server_problem.dart';
import 'package:problem_check_system/app/core/services/network_status_service.dart';
import 'package:problem_check_system/app/core/services/http_provider.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_datasource.dart';
// import 'package:dio/dio.dart';
// import 'package:get/get.dart' hide MultipartFile, FormData, Response;
// import 'package:problem_check_system/app/core/extensions/http_response_extension.dart';
// import 'package:problem_check_system/app/core/utils/constants/api_endpoints.dart';
// import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
// import 'package:problem_check_system/app/core/models/server_problem.dart';
// import 'package:problem_check_system/app/core/services/network_status_service.dart';
// import 'package:problem_check_system/app/core/services/http_provider.dart';
// import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
// import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart';
///
///
class ProblemRepository extends GetxService {
final ProblemLocalDataSource problemLocalDataSource;
final HttpProvider httpProvider;
final NetworkStatusService networkStatusService;
final AuthRepository authRepository;
// ///
// ///
// class ProblemRepository extends GetxService {
// final ProblemLocalDataSource problemLocalDataSource;
// final HttpProvider httpProvider;
// final NetworkStatusService networkStatusService;
// final AuthRepository authRepository;
RxBool get isOnline => networkStatusService.isOnline;
// RxBool get isOnline => networkStatusService.isOnline;
ProblemRepository({
required this.problemLocalDataSource,
required this.httpProvider,
required this.networkStatusService,
required this.authRepository,
});
// ProblemRepository({
// required this.problemLocalDataSource,
// required this.httpProvider,
// required this.networkStatusService,
// required this.authRepository,
// });
///
Future<void> updateProblem(Problem problem) async {
await problemLocalDataSource.updateProblem(problem);
}
// ///
// Future<void> updateProblem(Problem problem) async {
// await problemLocalDataSource.updateProblem(problem);
// }
///
/// - `startDate`/`endDate`
/// - `syncStatus`'已上传', '未上传', '全部'
/// - `bindStatus`'已绑定', '未绑定', '全部'
Future getProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}) async {
return await problemLocalDataSource.getProblems(
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
);
}
// ///
// /// - `startDate`/`endDate`
// /// - `syncStatus`'已上传', '未上传', '全部'
// /// - `bindStatus`'已绑定', '未绑定', '全部'
// Future getProblems({
// DateTime? startDate,
// DateTime? endDate,
// String? syncStatus,
// String? bindStatus,
// }) async {
// return await problemLocalDataSource.getProblems(
// startDate: startDate,
// endDate: endDate,
// syncStatus: syncStatus,
// bindStatus: bindStatus,
// );
// }
Future<void> insertProblem(Problem problem) async {
await problemLocalDataSource.insertProblem(problem);
}
// Future<void> insertProblem(Problem problem) async {
// await problemLocalDataSource.insertProblem(problem);
// }
Future<void> deleteProblem(String problemId) async {
await problemLocalDataSource.deleteProblem(problemId);
}
// Future<void> deleteProblem(String problemId) async {
// await problemLocalDataSource.deleteProblem(problemId);
// }
// ProblemRepository中添加
Future<List<ServerProblem>> fetchProblemsFromServer({
DateTime? startTime,
DateTime? endTime,
int? pageNumber,
int? pageSize,
CancelToken? cancelToken,
}) async {
try {
final response = await httpProvider.get(
ApiEndpoints.getProblems,
queryParameters: {
'creatorId': authRepository.getUserId(),
if (startTime != null)
'StartTime': startTime.toUtc().toIso8601String(),
if (endTime != null) 'EndTime': endTime.toUtc().toIso8601String(),
if (pageNumber != null) 'pageNumber': pageNumber,
if (pageSize != null) 'pageSize': pageSize,
},
cancelToken: cancelToken,
);
// // ProblemRepository中添加
// Future<List<ServerProblem>> fetchProblemsFromServer({
// DateTime? startTime,
// DateTime? endTime,
// int? pageNumber,
// int? pageSize,
// CancelToken? cancelToken,
// }) async {
// try {
// final response = await httpProvider.get(
// ApiEndpoints.getProblems,
// queryParameters: {
// 'creatorId': authRepository.getUserId(),
// if (startTime != null)
// 'StartTime': startTime.toUtc().toIso8601String(),
// if (endTime != null) 'EndTime': endTime.toUtc().toIso8601String(),
// if (pageNumber != null) 'pageNumber': pageNumber,
// if (pageSize != null) 'pageSize': pageSize,
// },
// cancelToken: cancelToken,
// );
if (response.isSuccess) {
// Dio JSONresponse.data Map List
final Map<String, dynamic> data = response.data;
final List<dynamic> items = data['items'];
// if (response.isSuccess) {
// // Dio JSONresponse.data Map List
// final Map<String, dynamic> data = response.data;
// final List<dynamic> items = data['items'];
// 使 Freezed fromJson
return items.map((item) => ServerProblem.fromJson(item)).toList();
} else {
throw Exception('拉取问题失败: ${response.statusCode}');
}
} on DioException catch (e) {
Get.log("Dio 异常$e");
rethrow;
} catch (e) {
Get.log("解析失败:$e");
rethrow;
}
}
// // 使 Freezed fromJson
// return items.map((item) => ServerProblem.fromJson(item)).toList();
// } else {
// throw Exception('拉取问题失败: ${response.statusCode}');
// }
// } on DioException catch (e) {
// Get.log("Dio 异常$e");
// rethrow;
// } catch (e) {
// Get.log("解析失败:$e");
// rethrow;
// }
// }
/// post
Future<Response> post(
Map<String, Object> apiPayload,
CancelToken cancelToken,
) async {
// 3.
final response = await httpProvider.post(
ApiEndpoints.postProblem,
data: apiPayload,
cancelToken: cancelToken,
);
return response;
}
// /// post
// Future<Response> post(
// Map<String, Object> apiPayload,
// CancelToken cancelToken,
// ) async {
// // 3.
// final response = await httpProvider.post(
// ApiEndpoints.postProblem,
// data: apiPayload,
// cancelToken: cancelToken,
// );
// return response;
// }
/// put
Future<Response> put(
String id,
Map<String, Object> apiPayload,
CancelToken cancelToken,
) async {
// 3.
final response = await httpProvider.put(
ApiEndpoints.putProblemById(id),
data: apiPayload,
cancelToken: cancelToken,
);
return response;
}
// /// put
// Future<Response> put(
// String id,
// Map<String, Object> apiPayload,
// CancelToken cancelToken,
// ) async {
// // 3.
// final response = await httpProvider.put(
// ApiEndpoints.putProblemById(id),
// data: apiPayload,
// cancelToken: cancelToken,
// );
// return response;
// }
/// delete
Future<Response> delete(String id, CancelToken cancelToken) async {
// 3.
final response = await httpProvider.delete(
ApiEndpoints.deleteProblemById(id),
cancelToken: cancelToken,
);
return response;
}
}
// /// delete
// Future<Response> delete(String id, CancelToken cancelToken) async {
// // 3.
// final response = await httpProvider.delete(
// ApiEndpoints.deleteProblemById(id),
// cancelToken: cancelToken,
// );
// return response;
// }
// }

81
lib/app/features/problem/data/repositories/problem_repository_impl.dart

@ -0,0 +1,81 @@
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_local_dto.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
///
///
/// (ProblemEntity) (ProblemLocalDto)
class ProblemRepository implements IProblemRepository {
final IProblemLocalDataSource problemLocalDataSource; // 2.
ProblemRepository(this.problemLocalDataSource);
@override
Future<ProblemEntity> addProblem(ProblemEntity problem) async {
// 1. (Entity) (DTO)
final problemDto = ProblemLocalDto.fromEntity(problem);
// 2. DTO Map
await problemLocalDataSource.addProblem(problemDto.toMap());
// 3.
return problem;
}
@override
Future<void> deleteProblem(String id) async {
// ID
await problemLocalDataSource.deleteProblem(id);
}
@override
Future<List<ProblemEntity>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}) async {
// 1. (List of Maps)
final problemMaps = await problemLocalDataSource.getAllProblems(
//
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
);
// 2. Map DTO (Entity)
final problems = problemMaps
.map((map) => ProblemLocalDto.fromMap(map).toEntity())
.toList();
return problems;
}
@override
Future<ProblemEntity?> getProblemById(String id) async {
// 1. ID (Map)
final problemMap = await problemLocalDataSource.getProblemById(id);
// 2. null
if (problemMap == null) {
return null;
}
// 3. DTO
return ProblemLocalDto.fromMap(problemMap).toEntity();
}
@override
Future<ProblemEntity> updateProblem(ProblemEntity problem) async {
// 1. (Entity) (DTO)
final problemDto = ProblemLocalDto.fromEntity(problem);
// 2. DTO Map
await problemLocalDataSource.updateProblem(problemDto.toMap());
// 3.
return problem;
}
}

67
lib/app/features/problem/domain/entities/problem_entity.dart

@ -6,19 +6,74 @@ class ProblemEntity implements SyncableEntity {
final String id;
@override
final DateTime? lastModifiedTime;
final DateTime lastModifiedTime;
@override
final String? lastModifierId;
final String lastModifierId;
@override
final SyncStatus syncStatus;
// 2.
///
final String enterpriseId;
///
final String description;
///
final String location;
///
final List<String> imageUrls;
///
final DateTime creationTime;
///
final String creatorId;
///
final String? bindData;
ProblemEntity({
required this.id,
this.lastModifiedTime,
this.lastModifierId,
this.syncStatus = SyncStatus.pendingCreate,
required this.lastModifiedTime,
required this.lastModifierId,
required this.syncStatus,
required this.enterpriseId,
required this.description,
required this.location,
required this.imageUrls,
required this.creationTime,
required this.creatorId,
this.bindData,
});
ProblemEntity copyWith({
String? id,
DateTime? lastModifiedTime,
String? lastModifierId,
SyncStatus? syncStatus,
String? enterpriseId,
String? description,
String? location,
List<String>? imageUrls,
DateTime? creationTime,
String? creatorId,
String? bindData,
}) {
return ProblemEntity(
id: id ?? this.id,
syncStatus: syncStatus ?? this.syncStatus,
lastModifiedTime: lastModifiedTime ?? this.lastModifiedTime,
lastModifierId: lastModifierId ?? this.lastModifierId,
enterpriseId: enterpriseId ?? this.enterpriseId,
description: description ?? this.description,
location: location ?? this.location,
imageUrls: imageUrls ?? this.imageUrls,
creationTime: creationTime ?? this.creationTime,
creatorId: creatorId ?? this.creatorId,
bindData: bindData ?? this.bindData,
);
}
}

19
lib/app/features/problem/domain/repositoies/problem_repository.dart

@ -1,19 +0,0 @@
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
/// Problem
///
abstract class ProblemRepository {
Future<void> addProblem(Problem problem);
Future<void> updateProblem(Problem problem);
Future<void> deleteProblem(String problemId);
Future<Problem?> getProblemById(String id);
Future<void> markAsSynced(String id);
Future<List<Problem>> getProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus, // 使
String? bindStatus,
});
}

16
lib/app/features/problem/domain/repositories/problem_repository.dart

@ -0,0 +1,16 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
/// Problem
///
abstract class IProblemRepository {
Future<List<ProblemEntity>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
});
Future<ProblemEntity?> getProblemById(String id);
Future<ProblemEntity> addProblem(ProblemEntity problem);
Future<ProblemEntity> updateProblem(ProblemEntity problem);
Future<void> deleteProblem(String id);
}

40
lib/app/features/problem/domain/usecases/add_problem_usecase.dart

@ -0,0 +1,40 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
import 'package:uuid/uuid.dart';
class AddProblemUsecase {
final IProblemRepository problemRepository;
final AuthRepository authRepository;
final Uuid uuid;
AddProblemUsecase({
required this.problemRepository,
required this.authRepository,
required this.uuid,
});
Future<ProblemEntity> call({
required String enterpriseId,
required String description,
required String location,
required List<String> imageUrls,
}) async {
final nowUtc = DateTime.now().toUtc();
final userId = authRepository.getUserId();
final newProblem = ProblemEntity(
id: uuid.v4(),
description: description,
location: location,
imageUrls: imageUrls,
lastModifiedTime: nowUtc,
lastModifierId: userId,
syncStatus: SyncStatus.pendingCreate,
enterpriseId: enterpriseId,
creationTime: nowUtc,
creatorId: userId,
);
return await problemRepository.addProblem(newProblem);
}
}

10
lib/app/features/problem/domain/usecases/delete_problem.dart

@ -0,0 +1,10 @@
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class DeleteProblem {
final IProblemRepository problemRepository;
DeleteProblem({required this.problemRepository});
Future<void> call(String id) async {
await problemRepository.deleteProblem(id);
}
}

22
lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart

@ -0,0 +1,22 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class GetAllProblemsUsecase {
final IProblemRepository problemRepository;
GetAllProblemsUsecase({required this.problemRepository});
Future<List<ProblemEntity>> call({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}) async {
return await problemRepository.getAllProblems(
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
);
}
}

11
lib/app/features/problem/domain/usecases/get_problem_by_id_usecase.dart

@ -0,0 +1,11 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class GetProblemByIdUsecase {
final IProblemRepository problemRepository;
GetProblemByIdUsecase({required this.problemRepository});
Future<ProblemEntity?> call(String id) async {
return await problemRepository.getProblemById(id);
}
}

22
lib/app/features/problem/domain/usecases/update_problem.dart

@ -0,0 +1,22 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class UpdateProblem {
final IProblemRepository repository;
final AuthRepository authRepository;
UpdateProblem({required this.repository, required this.authRepository});
Future<ProblemEntity> call(ProblemEntity entity) async {
final nowUtc = DateTime.now().toUtc();
final userId = authRepository.getUserId();
final newProblem = entity.copyWith(
lastModifiedTime: nowUtc,
lastModifierId: userId,
syncStatus: SyncStatus.pendingUpdate,
);
return await repository.updateProblem(newProblem);
}
}

62
lib/app/features/problem/presentation/bindings/problem_binding.dart

@ -1,37 +1,29 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/core/services/http_provider.dart';
import 'package:problem_check_system/app/core/services/network_status_service.dart';
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_datasource.dart';
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
// import 'package:get/get.dart';
// import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
// import 'package:problem_check_system/app/core/services/database_service.dart';
// import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart';
// import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository_impl.dart';
// import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class ProblemBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<ProblemLocalDataSource>(
() => ProblemLocalDataSourceImpl(
databaseService: Get.find<DatabaseService>(),
),
);
Get.lazyPut<ProblemRepository>(
() => ProblemRepository(
problemLocalDataSource: Get.find<ProblemLocalDataSource>(),
httpProvider: Get.find<HttpProvider>(),
networkStatusService: Get.find<NetworkStatusService>(),
authRepository: Get.find(),
),
);
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
// class ProblemBinding extends Bindings {
// @override
// void dependencies() {
// Get.lazyPut<IProblemLocalDataSource>(
// () =>
// ProblemLocalDataSource(databaseService: Get.find<DatabaseService>()),
// );
// Get.lazyPut<IProblemRepository>(
// () => ProblemRepository(Get.find<ProblemLocalDataSource>()),
// );
// Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
///
Get.lazyPut<ProblemController>(
() => ProblemController(
problemRepository: Get.find<ProblemRepository>(),
problemStateManager: Get.find<ProblemStateManager>(),
),
fenix: true,
);
}
}
// ///
// Get.lazyPut<ProblemController>(
// () => ProblemController(
// problemRepository: Get.find<ProblemRepository>(),
// problemStateManager: Get.find<ProblemStateManager>(),
// ),
// fenix: true,
// );
// }
// }

34
lib/app/features/problem/presentation/bindings/problem_form_binding.dart

@ -1,27 +1,33 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_form_controller.dart';
class ProblemFormBinding extends Bindings {
@override
void dependencies() {
final dynamic arguments = Get.arguments;
final bool readOnly = Get.parameters['isReadOnly'] == 'true';
// final dynamic arguments = Get.arguments;
// final bool readOnly = Get.parameters['isReadOnly'] == 'true';
Problem? problem;
if (arguments != null && arguments is Problem) {
problem = arguments;
ProblemEntity? problem;
FormMode formMode = FormMode.view;
if (Get.arguments is Map) {
final arguments = Get.arguments as Map;
//
if (arguments.containsKey('mode')) {
formMode = arguments['mode'] as FormMode;
}
//
if (arguments.containsKey('data')) {
problem = arguments['data'] as ProblemEntity?;
}
}
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
Get.lazyPut<ProblemFormController>(
() => ProblemFormController(
problemRepository: Get.find(),
problemStateManager: Get.find(),
problem: problem,
isReadOnly: readOnly,
),
() => ProblemFormController(problem: problem, formMode: formMode),
);
}
}

35
lib/app/features/problem/presentation/bindings/problem_list_binding.dart

@ -0,0 +1,35 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart';
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository_impl.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/get_all_problems_usecase.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_list_controller.dart';
class ProblemListBinding extends Bindings {
@override
void dependencies() {
//
Get.lazyPut<IProblemLocalDataSource>(
() =>
ProblemLocalDataSource(databaseService: Get.find<DatabaseService>()),
);
//
Get.lazyPut<IProblemRepository>(
() => ProblemRepository(Get.find<IProblemLocalDataSource>()),
);
//
Get.lazyPut<GetAllProblemsUsecase>(
() => GetAllProblemsUsecase(
problemRepository: Get.find<IProblemRepository>(),
),
);
///
Get.lazyPut<ProblemListController>(
() => ProblemListController(
getAllProblemsUsecase: Get.find<GetAllProblemsUsecase>(),
),
);
}
}

8
lib/app/features/problem/presentation/bindings/problem_upload_binding.dart

@ -0,0 +1,8 @@
import 'package:get/get.dart';
class ProblemUploadBinding extends Bindings {
@override
void dependencies() {
// TODO: implement dependencies
}
}

1946
lib/app/features/problem/presentation/controllers/problem_controller.dart

File diff suppressed because it is too large Load Diff

140
lib/app/features/problem/presentation/controllers/problem_form_controller.dart

@ -6,42 +6,39 @@ import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as path;
import 'package:permission_handler/permission_handler.dart';
import 'package:path_provider/path_provider.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/models/image_status.dart';
import 'package:problem_check_system/app/core/models/image_metadata_model.dart';
import 'dart:io';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:uuid/uuid.dart';
class ProblemFormController extends GetxController {
final Problem? problem;
final RxBool isLoading = false.obs;
final ProblemEntity? problem;
late Map<String, dynamic> bindInfo;
final bool isReadOnly;
final ProblemRepository problemRepository;
final ProblemStateManager problemStateManager;
final FormMode formMode;
bool get isReadOnly {
return formMode == FormMode.view;
}
final titleName = "问题表单页".obs;
final TextEditingController descriptionController = TextEditingController();
final TextEditingController locationController = TextEditingController();
final RxList<XFile> selectedImages = <XFile>[].obs;
final RxBool isLoading = false.obs;
// 使便
ProblemFormController({
required this.problemRepository,
required this.problemStateManager,
this.problem,
this.isReadOnly = false,
}) {
ProblemFormController({this.problem, this.formMode = FormMode.view}) {
if (problem != null) {
if (problem!.bindData != null) {
bindInfo = jsonDecode(problem!.bindData!);
}
descriptionController.text = problem!.description;
locationController.text = problem!.location;
final imagePaths = problem!.imageUrls
.map((meta) => XFile(meta.localPath))
.toList();
final imagePaths = problem!.imageUrls.map((e) => XFile(e)).toList();
selectedImages.assignAll(imagePaths);
} else {
descriptionController.clear();
@ -50,11 +47,31 @@ class ProblemFormController extends GetxController {
}
}
// @override
// void onInit() {
// super.onInit();
@override
void onInit() {
super.onInit();
}
// }
@override
void onClose() {
descriptionController.dispose();
locationController.dispose();
super.onClose();
}
void updatePageTitle() {
switch (formMode) {
case FormMode.add:
titleName.value = '新增问题';
break;
case FormMode.edit:
titleName.value = '修改问题';
break;
case FormMode.view:
titleName.value = '查看问题';
break;
}
}
// pickImage
Future<void> pickImage(ImageSource source) async {
@ -146,44 +163,44 @@ class ProblemFormController extends GetxController {
///
Future<void> saveProblem() async {
if (!_validateForm()) {
return;
}
isLoading.value = true;
try {
//
final List<ImageMetadata> imagePaths = await _saveImagesToLocal();
if (problem != null) {
//
final updatedProblem = problem!.copyWith(
description: descriptionController.text,
location: locationController.text,
imageUrls: imagePaths,
);
//
final modifyProblem = problemStateManager.modifyProblem(updatedProblem);
await problemRepository.updateProblem(modifyProblem);
} else {
//
final newProblem = problemStateManager.createNewProblem(
description: descriptionController.text,
location: locationController.text,
imageUrls: imagePaths,
);
// if (!_validateForm()) {
// return;
// }
await problemRepository.insertProblem(newProblem);
}
Get.back(result: true); //
Get.snackbar('成功', '问题已更新');
} catch (e) {
Get.snackbar('错误', '保存问题失败: $e');
} finally {
isLoading.value = false;
}
// isLoading.value = true;
// try {
// //
// final List<ImageMetadata> imagePaths = await _saveImagesToLocal();
// if (problem != null) {
// //
// final updatedProblem = problem!.copyWith(
// description: descriptionController.text,
// location: locationController.text,
// imageUrls: imagePaths,
// );
// //
// final modifyProblem = problemStateManager.modifyProblem(updatedProblem);
// await problemRepository.updateProblem(modifyProblem);
// } else {
// //
// final newProblem = problemStateManager.createNewProblem(
// description: descriptionController.text,
// location: locationController.text,
// imageUrls: imagePaths,
// );
// await problemRepository.insertProblem(newProblem);
// }
// Get.back(result: true); //
// Get.snackbar('成功', '问题已更新');
// } catch (e) {
// Get.snackbar('错误', '保存问题失败: $e');
// } finally {
// isLoading.value = false;
// }
}
//
@ -261,11 +278,4 @@ class ProblemFormController extends GetxController {
locationController.text != problem!.location ||
selectedImages.length != problem!.imageUrls.length;
}
@override
void onClose() {
descriptionController.dispose();
locationController.dispose();
super.onClose();
}
}

208
lib/app/features/problem/presentation/controllers/problem_list_controller.dart

@ -0,0 +1,208 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/get_all_problems_usecase.dart';
class ProblemListController extends GetxController {
final GetAllProblemsUsecase getAllProblemsUsecase;
// final SyncEnterprisesUsecase syncEnterprisesUsecase; //
// final ResolveConflictUsecase resolveConflictUsecase; //
ProblemListController({
required this.getAllProblemsUsecase,
// required this.syncEnterprisesUsecase,
// required this.resolveConflictUsecase,
});
// --- ---
final enterpriseList = <EnterpriseListItem>[].obs;
final isLoading = false.obs;
final isSyncing = false.obs;
final nameController = TextEditingController();
final selectedType = Rx<CompanyType?>(null);
final startDate = Rx<DateTime?>(null);
final endDate = Rx<DateTime?>(null);
final selectedEnterprises = <Enterprise>{}.obs;
final ExpansibleController expansibleController = ExpansibleController();
@override
void onInit() {
// -
// loadAndSyncEnterprises();
search();
super.onInit();
}
@override
void onClose() {
nameController.dispose();
expansibleController.dispose();
super.onClose();
}
void search() {}
// --- ---
//
// Future<void> loadAndSyncEnterprises() async {
// try {
// isLoading(true);
// isSyncing(true);
// // 1:
// final syncResult = await syncEnterprisesUsecase();
// // 2:
// if (syncResult.hasConflicts) {
// //
// for (final conflict in syncResult.conflicts) {
// final chosenVersion = await _showConflictDialog(conflict);
// if (chosenVersion != null) {
// //
// await resolveConflictUsecase(chosenVersion);
// }
// }
// }
// isSyncing(false);
// // 3:
// await loadEnterprises();
// } catch (e) {
// Get.snackbar('错误', '操作失败: $e');
// } finally {
// isLoading(false);
// isSyncing(false);
// }
// }
// // []
// Future<Enterprise?> _showConflictDialog(EnterpriseConflict conflict) {
// return Get.dialog<Enterprise>(
// AlertDialog(
// title: Text('数据冲突'),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.stretch,
// children: [
// Text('${conflict.localVersion.name} 服务器上的数据与本地数据不一致,请选择要保留的版本。'),
// const SizedBox(height: 24),
// // --- ---
// Row(
// children: [
// // 使 Expanded
// Expanded(
// child: ElevatedButton(
// child: Text(
// '使用客户端版本\n(修改于: ${conflict.localVersion.lastModifiedTime.toLocal().toDateTimeString2()})',
// textAlign: TextAlign.center,
// ),
// onPressed: () => Get.back(result: conflict.localVersion),
// ),
// ),
// const SizedBox(width: 8), //
// //
// IconButton(
// icon: const Icon(Icons.info_outline),
// tooltip: '查看客户端版本详情',
// onPressed: () => navigateToDetailsView(conflict.localVersion),
// ),
// ],
// ),
// const SizedBox(height: 8),
// // --- ---
// Row(
// children: [
// Expanded(
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Get.theme.colorScheme.primary,
// foregroundColor: Get.theme.colorScheme.onPrimary,
// ),
// child: Text(
// '使用服务器版本\n(修改于: ${conflict.serverVersion.lastModifiedTime.toLocal().toDateTimeString2()})',
// textAlign: TextAlign.center,
// ),
// onPressed: () => Get.back(result: conflict.serverVersion),
// ),
// ),
// const SizedBox(width: 8),
// IconButton(
// icon: const Icon(Icons.info_outline),
// tooltip: '查看服务器版本详情',
// onPressed: () =>
// navigateToDetailsView(conflict.serverVersion),
// ),
// ],
// ),
// ],
// ),
// ),
// // false
// barrierDismissible: false,
// );
// }
// Future<void> loadEnterprises() async {
// expansibleController.collapse();
// isLoading.value = true;
// try {
// final result = await getEnterpriseListUsecase.call(
// name: nameController.text,
// type: selectedType.value?.displayText,
// startDate: startDate.value,
// endDate: endDate.value,
// );
// enterpriseList.assignAll(result);
// } catch (e) {
// Get.snackbar('错误', '加载企业列表失败: $e');
// } finally {
// isLoading.value = false;
// }
// }
// void clearFilters() {
// nameController.clear();
// selectedType.value = null;
// startDate.value = null;
// endDate.value = null;
// loadEnterprises();
// }
///
Future<void> navigateToProblemForm({
Enterprise? enterprise,
FormMode? fromMode,
}) async {
final result = await Get.toNamed(
AppRoutes.problemForm,
arguments: {'data': enterprise, 'mode': fromMode},
);
if (result == true) {
search();
Get.snackbar(
'操作成功',
'问题信息已更新',
backgroundColor: Colors.green[600],
colorText: Colors.white,
icon: const Icon(Icons.check_circle, color: Colors.white),
duration: const Duration(seconds: 3),
);
}
}
///
// Future<void> navigateToEnterpriseInfoPage(Enterprise enterprise) async {
// await Get.toNamed(
// AppRoutes.enterpriseInfo,
// arguments: {'data': enterprise, 'mode': FormMode.view},
// );
// }
}

13
lib/app/features/problem/presentation/controllers/problem_upload_controller.dart

@ -0,0 +1,13 @@
import 'dart:ui';
class ProblemUploadController {
int get selectedCount => 10;
get allSelected => null;
get unUploadedProblems => null;
VoidCallback? get selectAll => null;
get handleUpload => null;
}

0
lib/app/features/problem/presentation/models/problem_card_model.dart

13
lib/app/features/problem/presentation/models/problem_form_model.dart

@ -0,0 +1,13 @@
class ProblemFormModel {
final String enterpriseName;
final String description;
final String location;
final List<String> imageUrls;
ProblemFormModel({
required this.enterpriseName,
required this.description,
required this.location,
required this.imageUrls,
});
}

10
lib/app/features/problem/presentation/views/problem_form_page.dart → lib/app/features/problem/presentation/pages/problem_form_page.dart

@ -14,12 +14,11 @@ class ProblemFormPage extends GetView<ProblemFormController> {
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
titleName: controller.isReadOnly
? '问题详情'
: (controller.problem == null ? '新增问题' : '编辑问题'),
titleName: controller.titleName.value,
leadingVisible: true,
),
body: Column(
body: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
@ -85,6 +84,7 @@ class ProblemFormPage extends GetView<ProblemFormController> {
if (!controller.isReadOnly) _bottomButton(),
],
),
),
);
}
@ -349,7 +349,7 @@ class ProblemFormPage extends GetView<ProblemFormController> {
Widget _bottomButton() {
return Container(
width: 375.w,
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.only(

182
lib/app/features/problem/presentation/pages/problem_list_page.dart

@ -0,0 +1,182 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/pages/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_list_controller.dart';
class ProblemListPage extends GetView<ProblemListController> {
const ProblemListPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
titleName: '企业列表',
actionsVisible: true,
onAddPressed: () {
controller.navigateToProblemForm(fromMode: FormMode.add);
},
),
body: Text("问题列表"),
);
// return Obx(() {
// if (true) {
// return const Center(child: CircularProgressIndicator());
// }
// return EasyRefresh(
// header: ClassicHeader(
// dragText: '下拉刷新'.tr,
// armedText: '释放开始'.tr,
// readyText: '刷新中...'.tr,
// processingText: '刷新中...'.tr,
// processedText: '成功了'.tr,
// noMoreText: 'No more'.tr,
// failedText: '失败'.tr,
// messageText: '最后更新于 %T'.tr,
// ),
// onRefresh: () async {
// //
// await controller.pullDataFromServer();
// },
// child: ListView.builder(
// padding: EdgeInsets.symmetric(horizontal: 17.w),
// itemCount: problemsToShow.length,
// itemBuilder: (context, index) {
// // if (index == problemsToShow.length) {
// // return SizedBox(height: 79.5.h);
// // }
// final problem = problemsToShow[index];
// return _buildSwipeableProblemCard(problem);
// },
// ),
// );
// });
}
// Widget _buildSwipeableProblemCard(Problem problem) {
// //
// final bool isPendingDelete =
// problem.syncStatus == ProblemSyncStatus.pendingDelete;
// if (viewType == ProblemCardViewType.buttons) {
// // buttons
// if (!isPendingDelete) {
// //
// return Dismissible(
// key: ValueKey('${problem.id}-${problem.syncStatus}'),
// direction: DismissDirection.endToStart,
// background: Container(
// color: Colors.red,
// alignment: Alignment.centerRight,
// padding: EdgeInsets.only(right: 20.w),
// child: Icon(Icons.delete, color: Colors.white, size: 30.sp),
// ),
// confirmDismiss: (direction) async {
// return await _showDeleteConfirmationDialog(problem);
// },
// onDismissed: (direction) {
// // controller.deleteProblem(problem);
// Get.snackbar('成功', '问题已删除');
// },
// child: ProblemCard(
// key: ValueKey(problem.id),
// problem: problem,
// viewType: viewType,
// isSelected: false,
// ),
// );
// } else {
// //
// return ProblemCard(
// key: ValueKey(problem.id),
// problem: problem,
// viewType: viewType,
// isSelected: false,
// );
// }
// } else {
// // listgrid等使 Obx
// return Obx(() {
// final isSelected = controller.selectedProblems.contains(problem);
// return ProblemCard(
// key: ValueKey(problem.id),
// problem: problem,
// viewType: viewType,
// isSelected: isSelected,
// onChanged: (problem, isChecked) {
// controller.updateProblemSelection(problem, isChecked);
// },
// );
// });
// }
// }
Future<bool> _showDeleteConfirmationDialog(Problem problem) async {
// snackbar
if (Get.isSnackbarOpen) {
Get.closeCurrentSnackbar();
}
return await Get.bottomSheet<bool>(
Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
const Text(
'确认删除',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'确定要删除这个问题吗?此操作不可撤销。',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => Get.back(result: true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
'删除',
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
const SizedBox(height: 8),
TextButton(
onPressed: () => Get.back(result: false),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: Text(
'取消',
style: TextStyle(color: Colors.grey[700], fontSize: 16),
),
),
const SizedBox(height: 16),
],
),
),
),
) ??
false;
}
}

25
lib/app/features/problem/presentation/views/problem_upload_page.dart → lib/app/features/problem/presentation/pages/problem_upload_page.dart

@ -2,11 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/pages/widgets/upload_app_bar.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_list_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_card.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_upload_controller.dart';
class ProblemUploadPage extends GetView<ProblemController> {
class ProblemUploadPage extends GetView<ProblemUploadController> {
const ProblemUploadPage({super.key});
@override
@ -24,17 +22,20 @@ class ProblemUploadPage extends GetView<ProblemController> {
}
Widget _buildBody() {
return Obx(() {
if (controller.unUploadedProblems.isEmpty) {
return Center(
child: Text('暂无未上传的问题', style: TextStyle(fontSize: 16.sp)),
);
}
return ProblemListPage(
problemsToShow: controller.unUploadedProblems,
viewType: ProblemCardViewType.checkbox,
);
});
// return Obx(() {
// if (controller.unUploadedProblems.isEmpty) {
// return Center(
// child: Text('暂无未上传的问题', style: TextStyle(fontSize: 16.sp)),
// );
// }
// return ProblemListPage(
// problemsToShow: controller.unUploadedProblems,
// viewType: ProblemCardViewType.checkbox,
// );
// });
}
Widget _buildBottomBar() {

0
lib/app/features/problem/presentation/views/widgets/custom_button.dart → lib/app/features/problem/presentation/pages/widgets/custom_button.dart

2
lib/app/features/problem/presentation/views/widgets/custom_filter_dropdown.dart → lib/app/features/problem/presentation/pages/widgets/custom_filter_dropdown.dart

@ -1,7 +1,7 @@
// widgets/custom_filter_dropdown.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/models/dropdown_option.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/widgets/models/dropdown_option.dart';
class CustomFilterDropdown extends StatelessWidget {
final String title;

2
lib/app/features/problem/presentation/views/widgets/models/date_range_enum.dart → lib/app/features/problem/presentation/pages/widgets/models/date_range_enum.dart

@ -1,6 +1,6 @@
// models/date_range_enum.dart
import 'package:flutter/material.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/models/dropdown_option.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/widgets/models/dropdown_option.dart';
enum DateRange { threeDays, oneWeek, oneMonth }

0
lib/app/features/problem/presentation/views/widgets/models/dropdown_option.dart → lib/app/features/problem/presentation/pages/widgets/models/dropdown_option.dart

10
lib/app/features/problem/presentation/views/widgets/problem_card.dart → lib/app/features/problem/presentation/pages/widgets/problem_card.dart

@ -5,18 +5,18 @@ import 'package:intl/intl.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/custom_button.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/widgets/custom_button.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'dart:io'; //
//
enum ProblemCardViewType { buttons, checkbox }
class ProblemCard extends GetView<ProblemController> {
class ProblemCard extends StatelessWidget {
final Problem problem;
final ProblemCardViewType viewType;
final Function(Problem, bool)? onChanged;
final Function()? onTap;
final bool isSelected; //
const ProblemCard({
@ -24,6 +24,7 @@ class ProblemCard extends GetView<ProblemController> {
required this.problem,
this.viewType = ProblemCardViewType.buttons,
this.onChanged,
this.onTap,
required this.isSelected, //
});
@ -213,7 +214,8 @@ class ProblemCard extends GetView<ProblemController> {
CustomButton(
text: '修改',
onTap: () {
controller.toProblemFormPageAndRefresh(problem: problem);
onTap;
// controller.toProblemFormPageAndRefresh(problem: problem);
},
),
if (!isDeleted) SizedBox(width: 8.w),

0
lib/app/features/problem/presentation/views/widgets/sync_progress_dialog.dart → lib/app/features/problem/presentation/pages/widgets/sync_progress_dialog.dart

94
lib/app/features/problem/presentation/views/problem_page.dart

@ -1,94 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/pages/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_list_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/current_filter_bar.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_card.dart';
class ProblemPage extends GetView<ProblemController> {
const ProblemPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
titleName: '问题列表',
actionsVisible: true,
onAddPressed: () => controller.toProblemFormPageAndRefresh(),
),
body: Column(
children: [
CurrentFilterBar(),
Expanded(
child: // 使
ProblemListPage(
problemsToShow: controller.problems,
viewType: ProblemCardViewType.buttons,
),
),
],
),
// floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
// 使 Stack
// floatingActionButton: Stack(
// children: [
// // "添加"
// // 使 Align Positioned
// Align(
// alignment: Alignment.bottomCenter,
// child: Padding(
// padding: EdgeInsets.only(bottom: 24.h), //
// child: FloatingActionButton(
// heroTag: "btn_add",
// onPressed: () {
// controller.toProblemFormPageAndRefresh();
// },
// shape: const CircleBorder(),
// backgroundColor: Colors.blue[300],
// foregroundColor: Colors.white,
// child: const Icon(Icons.add),
// ),
// ),
// ),
// // "上传"
// Obx(() {
// final isOnline = controller.isOnline.value;
// return Positioned(
// // 使left/right dxtop/bottom dy
// left: controller.fabUploadPosition.value.dx,
// top: controller.fabUploadPosition.value.dy,
// child: GestureDetector(
// onPanUpdate: (details) {
// //
// controller.updateFabUploadPosition(details.delta);
// },
// onPanEnd: (details) {
// //
// controller.snapToEdge();
// },
// child: FloatingActionButton(
// heroTag: "btn_upload",
// onPressed: isOnline
// ? () => controller.showUploadPage()
// : null,
// foregroundColor: Colors.white,
// backgroundColor: isOnline
// ? Colors.red[300]
// : Colors.grey[400],
// child: Icon(
// isOnline
// ? Icons.file_upload_outlined
// : Icons.cloud_off_outlined,
// ),
// ),
// ),
// );
// }),
// ],
// ),
);
}
}

66
lib/app/features/problem/presentation/views/widgets/current_filter_bar.dart

@ -1,66 +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/app/features/problem/presentation/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,
),
),
],
],
),
);
}
}

97
lib/app/features/problem/presentation/views/widgets/history_filter_bar.dart

@ -1,97 +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/app/features/problem/presentation/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,
),
),
],
],
),
);
}
}

181
lib/app/features/problem/presentation/views/widgets/problem_list_page.dart

@ -1,181 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_card.dart';
class ProblemListPage extends GetView<ProblemController> {
final RxList<Problem> problemsToShow;
final ProblemCardViewType viewType;
const ProblemListPage({
super.key,
required this.problemsToShow,
this.viewType = ProblemCardViewType.buttons,
});
@override
Widget build(BuildContext context) {
return Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return EasyRefresh(
header: ClassicHeader(
dragText: '下拉刷新'.tr,
armedText: '释放开始'.tr,
readyText: '刷新中...'.tr,
processingText: '刷新中...'.tr,
processedText: '成功了'.tr,
noMoreText: 'No more'.tr,
failedText: '失败'.tr,
messageText: '最后更新于 %T'.tr,
),
onRefresh: () async {
//
await controller.pullDataFromServer();
},
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 17.w),
itemCount: problemsToShow.length,
itemBuilder: (context, index) {
// if (index == problemsToShow.length) {
// return SizedBox(height: 79.5.h);
// }
final problem = problemsToShow[index];
return _buildSwipeableProblemCard(problem);
},
),
);
});
}
Widget _buildSwipeableProblemCard(Problem problem) {
//
final bool isPendingDelete =
problem.syncStatus == ProblemSyncStatus.pendingDelete;
if (viewType == ProblemCardViewType.buttons) {
// buttons
if (!isPendingDelete) {
//
return Dismissible(
key: ValueKey('${problem.id}-${problem.syncStatus}'),
direction: DismissDirection.endToStart,
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 20.w),
child: Icon(Icons.delete, color: Colors.white, size: 30.sp),
),
confirmDismiss: (direction) async {
return await _showDeleteConfirmationDialog(problem);
},
onDismissed: (direction) {
controller.deleteProblem(problem);
Get.snackbar('成功', '问题已删除');
},
child: ProblemCard(
key: ValueKey(problem.id),
problem: problem,
viewType: viewType,
isSelected: false,
),
);
} else {
//
return ProblemCard(
key: ValueKey(problem.id),
problem: problem,
viewType: viewType,
isSelected: false,
);
}
} else {
// listgrid等使 Obx
return Obx(() {
final isSelected = controller.selectedProblems.contains(problem);
return ProblemCard(
key: ValueKey(problem.id),
problem: problem,
viewType: viewType,
isSelected: isSelected,
onChanged: (problem, isChecked) {
controller.updateProblemSelection(problem, isChecked);
},
);
});
}
}
Future<bool> _showDeleteConfirmationDialog(Problem problem) async {
// snackbar
if (Get.isSnackbarOpen) {
Get.closeCurrentSnackbar();
}
return await Get.bottomSheet<bool>(
Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
const Text(
'确认删除',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'确定要删除这个问题吗?此操作不可撤销。',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => Get.back(result: true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
'删除',
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
const SizedBox(height: 8),
TextButton(
onPressed: () => Get.back(result: false),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: Text(
'取消',
style: TextStyle(color: Colors.grey[700], fontSize: 16),
),
),
const SizedBox(height: 16),
],
),
),
),
) ??
false;
}
}
Loading…
Cancel
Save