51 changed files with 2508 additions and 1996 deletions
@ -0,0 +1,10 @@
|
||||
enum FormMode { |
||||
/// 新增 |
||||
add, |
||||
|
||||
/// 编辑 |
||||
edit, |
||||
|
||||
/// 查看 |
||||
view, |
||||
} |
||||
@ -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>()), |
||||
// ); |
||||
} |
||||
} |
||||
|
||||
@ -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']], |
||||
); |
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1 @@
|
||||
class ProblemRemoteDataSource {} |
||||
@ -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, |
||||
); |
||||
} |
||||
} |
||||
@ -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, |
||||
); |
||||
} |
||||
} |
||||
@ -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 会自动解析 JSON,response.data 已经是 Map 或 List |
||||
final Map<String, dynamic> data = response.data; |
||||
final List<dynamic> items = data['items']; |
||||
// if (response.isSuccess) { |
||||
// // Dio 会自动解析 JSON,response.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; |
||||
// } |
||||
// } |
||||
|
||||
@ -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; |
||||
} |
||||
} |
||||
@ -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, |
||||
}); |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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, |
||||
); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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, |
||||
// ); |
||||
// } |
||||
// } |
||||
|
||||
@ -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), |
||||
); |
||||
} |
||||
} |
||||
|
||||
@ -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>(), |
||||
), |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@
|
||||
import 'package:get/get.dart'; |
||||
|
||||
class ProblemUploadBinding extends Bindings { |
||||
@override |
||||
void dependencies() { |
||||
// TODO: implement dependencies |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -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}, |
||||
// ); |
||||
// } |
||||
} |
||||
@ -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,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, |
||||
}); |
||||
} |
||||
@ -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 { |
||||
// // 其他视图类型(list、grid等):使用 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; |
||||
} |
||||
} |
||||
@ -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; |
||||
@ -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 } |
||||
|
||||
@ -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 对应 dx,top/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, |
||||
// ), |
||||
// ), |
||||
// ), |
||||
// ); |
||||
// }), |
||||
// ], |
||||
// ), |
||||
); |
||||
} |
||||
} |
||||
@ -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, |
||||
), |
||||
), |
||||
], |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
||||
@ -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, |
||||
), |
||||
), |
||||
], |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
||||
@ -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 { |
||||
// 其他视图类型(list、grid等):使用 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…
Reference in new issue