Browse Source

暂存:需要完善问题列表页面UI,卡片与列表

dev
徐振升 2 days ago
parent
commit
adea79b7e9
  1. 27
      lib/app/core/bindings/base_bindings.dart
  2. 0
      lib/app/core/domain/entities/sync_status.dart
  3. 47
      lib/app/core/models/image_metadata_model.dart
  4. 14
      lib/app/core/models/image_status.dart
  5. 104
      lib/app/core/models/problem_sync_status.dart
  6. 2
      lib/app/core/repositories/syncable_repository.dart
  7. 2
      lib/app/core/services/sync_service.dart
  8. 2
      lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart
  9. 2
      lib/app/features/enterprise/data/model/enterprise_dto.dart
  10. 2
      lib/app/features/enterprise/data/model/enterprise_model.dart
  11. 2
      lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart
  12. 2
      lib/app/features/enterprise/domain/entities/enterprise.dart
  13. 2
      lib/app/features/enterprise/domain/repositories/enterprise_repository.dart
  14. 2
      lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart
  15. 2
      lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart
  16. 2
      lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart
  17. 99
      lib/app/features/problem/data/datasources/problem_local_data_source.dart
  18. 144
      lib/app/features/problem/data/model/problem_dto.dart
  19. 103
      lib/app/features/problem/data/model/problem_local_dto.dart
  20. 187
      lib/app/features/problem/data/model/problem_model.dart
  21. 144
      lib/app/features/problem/data/model/problem_remote_dto.dart
  22. 44
      lib/app/features/problem/data/repositories/problem_repository_impl.dart
  23. 4
      lib/app/features/problem/domain/entities/problem_entity.dart
  24. 30
      lib/app/features/problem/domain/entities/problem_filter_params.dart
  25. 15
      lib/app/features/problem/domain/entities/problem_list_item_entity.dart
  26. 9
      lib/app/features/problem/domain/repositories/problem_repository.dart
  27. 7
      lib/app/features/problem/domain/usecases/add_problem_usecase.dart
  28. 17
      lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart
  29. 9
      lib/app/features/problem/domain/usecases/update_problem_usecase.dart
  30. 70
      lib/app/features/problem/presentation/bindings/problem_form_binding.dart
  31. 102
      lib/app/features/problem/presentation/controllers/problem_form_controller.dart
  32. 0
      lib/app/features/problem/presentation/models/problem_card_model.dart
  33. 97
      lib/app/features/problem/presentation/models/problem_card_state.dart
  34. 1
      lib/app/features/problem/presentation/pages/widgets/problem_card.dart

27
lib/app/core/bindings/base_bindings.dart

@ -0,0 +1,27 @@
import 'package:get/get.dart';
abstract class BaseBindings implements Bindings {
@override
void dependencies() {
register1Services();
register2DataSource();
register3Repositories();
register4Usecases();
register5Controllers();
}
///
void register1Services();
///
void register2DataSource();
///
void register3Repositories();
///
void register4Usecases();
///
void register5Controllers();
}

0
lib/app/core/models/sync_status.dart → lib/app/core/domain/entities/sync_status.dart

47
lib/app/core/models/image_metadata_model.dart

@ -1,47 +0,0 @@
// image_metadata_model.dart
import 'package:problem_check_system/app/core/models/image_status.dart';
class ImageMetadata {
final String localPath;
final String? remoteUrl;
final ImageStatus status;
ImageMetadata({
required this.localPath,
this.remoteUrl,
required this.status,
});
// For saving to SQL
Map<String, dynamic> toMap() {
return {
'localPath': localPath,
'remoteUrl': remoteUrl,
'status': status.index,
};
}
// For reading from SQL
factory ImageMetadata.fromMap(Map<String, dynamic> map) {
return ImageMetadata(
localPath: map['localPath'] as String,
remoteUrl: map['remoteUrl'] as String?,
status: ImageStatus.values[map['status'] as int],
);
}
/// Creates a new [ImageMetadata] instance with optional new values.
///
/// The original object remains unchanged.
ImageMetadata copyWith({
String? localPath,
String? remoteUrl,
ImageStatus? status,
}) {
return ImageMetadata(
localPath: localPath ?? this.localPath,
remoteUrl: remoteUrl ?? this.remoteUrl,
status: status ?? this.status,
);
}
}

14
lib/app/core/models/image_status.dart

@ -1,14 +0,0 @@
///
enum ImageStatus {
///
synced,
//
pendingUpload,
///
pendingDeleted,
///
pendingDownload,
}

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

@ -1,104 +0,0 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/image_metadata_model.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:uuid/uuid.dart';
enum ProblemSyncStatus {
/// -
untracked,
/// - git的unmodified
synced,
/// - git的untracked staged
pendingCreate,
/// - git的modified staged
pendingUpdate,
/// - git的deleted staged
pendingDelete,
}
/// - git add/git commit
class ProblemStateManager extends GetxController {
/// uuid
final Uuid uuid;
final AuthRepository authRepository;
ProblemStateManager({required this.uuid, required this.authRepository});
///
Problem createNewProblem({
required String description,
required String location,
required List<ImageMetadata> imageUrls,
}) {
return Problem(
id: uuid.v4(),
description: description,
location: location,
imageUrls: imageUrls,
creationTime: DateTime.now().toUtc(),
creatorId: authRepository.getUserId(),
lastModifiedTime: DateTime.now().toUtc(),
syncStatus: ProblemSyncStatus.pendingCreate,
);
}
///
Problem modifyProblem(Problem problem) {
final newStatus = problem.syncStatus == ProblemSyncStatus.synced
? ProblemSyncStatus
.pendingUpdate //
: problem.syncStatus; //
return problem.copyWith(
syncStatus: newStatus,
lastModifiedTime: DateTime.now().toUtc(),
);
}
///
Problem markForDeletion(Problem problem) {
switch (problem.syncStatus) {
case ProblemSyncStatus.pendingCreate:
//
return problem.copyWith(
syncStatus: ProblemSyncStatus.untracked,
lastModifiedTime: DateTime.now().toUtc(),
);
case ProblemSyncStatus.synced:
case ProblemSyncStatus.pendingUpdate:
//
return problem.copyWith(
syncStatus: ProblemSyncStatus.pendingDelete,
lastModifiedTime: DateTime.now().toUtc(),
);
case ProblemSyncStatus.untracked:
case ProblemSyncStatus.pendingDelete:
//
return problem;
}
}
/// git reset
Problem undoDeletion(Problem problem) {
if (problem.syncStatus == ProblemSyncStatus.pendingDelete) {
return problem.copyWith(
syncStatus: ProblemSyncStatus.pendingUpdate,
lastModifiedTime: DateTime.now().toUtc(),
);
}
return problem;
}
/// git commit
Problem markAsSynced(Problem problem) {
return problem.copyWith(
syncStatus: ProblemSyncStatus.synced,
lastModifiedTime: DateTime.now().toUtc(),
);
}
}

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
abstract class SyncableRepository<T extends SyncableEntity> {
// --- () ---

2
lib/app/core/services/sync_service.dart

@ -1,6 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/syncable_repository.dart';
import 'package:problem_check_system/app/core/services/sync_metadata_service.dart';

2
lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_list_item_model.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';

2
lib/app/features/enterprise/data/model/enterprise_dto.dart

@ -1,5 +1,5 @@
import 'package:problem_check_system/app/core/extensions/map_extensions.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';
/// EnterpriseDto (Data Transfer Object)

2
lib/app/features/enterprise/data/model/enterprise_model.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
/// `EnterpriseModel` `Enterprise`

2
lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/services/network_status_service.dart';
import 'package:problem_check_system/app/features/enterprise/data/datasources/enterprise_local_data_source.dart';
import 'package:problem_check_system/app/features/enterprise/data/datasources/enterprise_remote_data_source.dart';

2
lib/app/features/enterprise/domain/entities/enterprise.dart

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
/// `Enterprise`
///

2
lib/app/features/enterprise/domain/repositories/enterprise_repository.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/syncable_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/sync_result.dart';

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';

2
lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';

2
lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:get/get.dart'; // GetX
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
///
class UploadResult {

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

@ -1,6 +1,7 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart'; // SyncStatus
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; // SyncStatus
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:sqflite/sqflite.dart'; // DatabaseService
/// IProblemLocalDataSource
@ -19,12 +20,7 @@ abstract class IProblemLocalDataSource {
/// [endDate] -
/// [syncStatus] -
/// [bindStatus] - ()
Future<List<Map<String, dynamic>>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
});
Future<List<Map<String, dynamic>>> getAllProblems(ProblemFilterParams filter);
///
///
@ -69,49 +65,70 @@ class ProblemLocalDataSource implements IProblemLocalDataSource {
}
@override
Future<List<Map<String, dynamic>>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus, // bindStatus
}) async {
Future<List<Map<String, dynamic>>> getAllProblems(
ProblemFilterParams filter,
) async {
final db = await _databaseService.database;
// WHERE
// 使 JOIN
final baseQuery = '''
SELECT
p.id as problem_id,
p.description as problem_description,
p.location as problem_location,
p.creationTime as problem_creationTime,
c.name as enterprise_name
FROM problems p
LEFT JOIN enterprises c ON p.enterpriseId = c.id
''';
// WHERE
List<String> whereClauses = [];
List<dynamic> whereArgs = [];
List<dynamic> arguments = [];
// 1. ()
if (filter.enterpriseName != null && filter.enterpriseName!.isNotEmpty) {
whereClauses.add('c.name LIKE ?');
arguments.add('%${filter.enterpriseName}%');
}
// 2.
if (filter.startTime != null) {
whereClauses.add('p.creationTime >= ?');
//
arguments.add(filter.startTime!.toIso8601String());
}
if (startDate != null) {
// INTEGER ()
whereClauses.add('creationTime >= ?');
whereArgs.add(startDate.millisecondsSinceEpoch);
// 3.
if (filter.endTime != null) {
whereClauses.add('p.creationTime <= ?');
arguments.add(filter.endTime!.toIso8601String());
}
if (endDate != null) {
whereClauses.add('creationTime <= ?');
whereArgs.add(endDate.millisecondsSinceEpoch);
// 4.
if (filter.syncStatus != null) {
whereClauses.add('p.syncStatus = ?');
// SyncStatus
arguments.add(filter.syncStatus!.name);
}
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);
// 5.
if (filter.isBound != null) {
if (filter.isBound!) {
whereClauses.add('p.bindData IS NOT NULL AND p.bindData != ""');
} else {
whereClauses.add('p.bindData IS NULL OR p.bindData = ""');
}
}
final String? whereString = whereClauses.isEmpty
? null
: whereClauses.join(' AND ');
//
String finalQuery = baseQuery;
if (whereClauses.isNotEmpty) {
finalQuery += ' WHERE ' + whereClauses.join(' AND ');
}
final List<Map<String, dynamic>> maps = await db.query(
_tableName,
where: whereString,
whereArgs: whereArgs,
orderBy: 'creationTime DESC', //
// 使 `?` SQL
final List<Map<String, dynamic>> maps = await db.rawQuery(
finalQuery,
arguments,
);
return maps;
}

144
lib/app/features/problem/data/model/problem_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<String> 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,
// );
// }
// }

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

@ -1,103 +0,0 @@
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,
);
}
}

187
lib/app/features/problem/data/model/problem_model.dart

@ -1,152 +1,97 @@
import 'dart:convert';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.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 Problem {
///
class ProblemModel {
final String id;
/// id
final String? companyId;
///
final String enterpriseId;
final String description;
///
final String location;
///
final List<ImageMetadata> imageUrls;
///
final DateTime creationTime;
/// id
final String imageUrls;
final String creatorId;
///
final ProblemSyncStatus syncStatus;
///
final DateTime lastModifiedTime;
/// ID
final String? censorTaskId;
///
final String creationTime;
final String lastModifierId;
final String lastModifiedTime;
final SyncStatus syncStatus;
final String? bindData;
/// false
final bool isChecked;
Problem({
ProblemModel({
required this.id,
required this.enterpriseId,
required this.description,
required this.location,
required this.imageUrls,
required this.creationTime,
required this.creatorId,
required this.creationTime,
required this.lastModifierId,
required this.lastModifiedTime,
this.syncStatus = ProblemSyncStatus.pendingCreate,
this.censorTaskId,
this.bindData,
this.isChecked = false,
this.companyId,
required this.syncStatus,
required this.bindData,
});
/// copyWith
Problem copyWith({
String? id,
String? companyId,
String? description,
String? location,
List<ImageMetadata>? imageUrls,
DateTime? creationTime,
String? creatorId,
DateTime? lastModifiedTime,
ProblemSyncStatus? syncStatus,
bool? isDeleted,
String? censorTaskId,
String? bindData,
bool? isChecked,
}) {
return Problem(
id: id ?? this.id,
companyId: companyId ?? this.companyId,
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,
);
}
/// MapSQLite存储
Map<String, dynamic> toMap() {
return {
'id': id,
'companyId': companyId,
'enterpriseId': enterpriseId,
'description': description,
'location': location,
'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()),
'creationTime': creationTime.millisecondsSinceEpoch,
'imageUrls': imageUrls,
'creatorId': creatorId,
'lastModifiedTime': lastModifiedTime.millisecondsSinceEpoch,
'syncStatus': syncStatus.index,
'censorTaskId': censorTaskId,
'creationTime': creationTime,
'lastModifierId': lastModifierId,
'lastModifiedTime': lastModifiedTime,
'syncStatus': syncStatus.name,
'bindData': bindData,
'isChecked': isChecked ? 1 : 0,
};
}
/// Map创建对象SQLite读取
factory Problem.fromMap(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 Problem(
id: map['id'],
companyId: 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,
factory ProblemModel.fromMap(Map<String, dynamic> map) {
return ProblemModel(
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?,
);
}
/// JSON字符串
String toJson() => json.encode(toMap());
/// ProblemEntity DTO
factory ProblemModel.fromEntity(ProblemEntity entity) {
return ProblemModel(
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,
);
}
/// JSON字符串创建对象
factory Problem.fromJson(String source) =>
Problem.fromMap(json.decode(source));
/// 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

@ -1,144 +0,0 @@
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,
);
}
}

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

@ -1,11 +1,13 @@
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/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
///
///
/// (ProblemEntity) (ProblemLocalDto)
/// (ProblemEntity) (ProblemModel)
class ProblemRepository implements IProblemRepository {
final IProblemLocalDataSource problemLocalDataSource; // 2.
@ -14,7 +16,7 @@ class ProblemRepository implements IProblemRepository {
@override
Future<ProblemEntity> addProblem(ProblemEntity problem) async {
// 1. (Entity) (DTO)
final problemDto = ProblemLocalDto.fromEntity(problem);
final problemDto = ProblemModel.fromEntity(problem);
// 2. DTO Map
await problemLocalDataSource.addProblem(problemDto.toMap());
@ -30,27 +32,23 @@ class ProblemRepository implements IProblemRepository {
}
@override
Future<List<ProblemEntity>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
Future<List<ProblemListItemEntity>> getAllProblemListItem({
required ProblemFilterParams filter,
}) async {
// 1. (List of Maps)
final problemMaps = await problemLocalDataSource.getAllProblems(
//
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
);
final problemDataMaps = await problemLocalDataSource.getAllProblems(filter);
// 2. Map DTO (Entity)
final problems = problemMaps
.map((map) => ProblemLocalDto.fromMap(map).toEntity())
.toList();
return problems;
// 2. Map ProblemListItemEntity
//
return problemDataMaps.map((map) {
return ProblemListItemEntity(
id: map['problem_id'],
description: map['problem_description'],
location: map['problem_location'],
creationTime: DateTime.parse(map['problem_creationTime']),
enterpriseName: map['enterprise_name'] ?? '未知企业',
);
}).toList();
}
@override
@ -64,13 +62,13 @@ class ProblemRepository implements IProblemRepository {
}
// 3. DTO
return ProblemLocalDto.fromMap(problemMap).toEntity();
return ProblemModel.fromMap(problemMap).toEntity();
}
@override
Future<ProblemEntity> updateProblem(ProblemEntity problem) async {
// 1. (Entity) (DTO)
final problemDto = ProblemLocalDto.fromEntity(problem);
final problemDto = ProblemModel.fromEntity(problem);
// 2. DTO Map
await problemLocalDataSource.updateProblem(problemDto.toMap());

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
class ProblemEntity implements SyncableEntity {
// 1. getter final
@ -14,7 +14,7 @@ class ProblemEntity implements SyncableEntity {
@override
final SyncStatus syncStatus;
///
/// Id
final String enterpriseId;
///

30
lib/app/features/problem/domain/entities/problem_filter_params.dart

@ -0,0 +1,30 @@
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
///
class ProblemFilterParams {
/// ()
final String? enterpriseName;
/// -
final DateTime? startTime;
/// -
final DateTime? endTime;
/// ()
final SyncStatus? syncStatus;
/// (true: , false: , null: )
final bool? isBound;
ProblemFilterParams({
this.enterpriseName,
this.startTime,
this.endTime,
this.syncStatus,
this.isBound,
});
/// '无筛选'
factory ProblemFilterParams.initial() => ProblemFilterParams();
}

15
lib/app/features/problem/domain/entities/problem_list_item_entity.dart

@ -0,0 +1,15 @@
class ProblemListItemEntity {
final String id;
final String description;
final String location;
final DateTime creationTime;
final String enterpriseName;
ProblemListItemEntity({
required this.id,
required this.description,
required this.location,
required this.creationTime,
required this.enterpriseName,
});
}

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

@ -1,13 +1,12 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
/// Problem
///
abstract class IProblemRepository {
Future<List<ProblemEntity>> getAllProblems({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
Future<List<ProblemListItemEntity>> getAllProblemListItem({
required ProblemFilterParams filter,
});
Future<ProblemEntity?> getProblemById(String id);
Future<ProblemEntity> addProblem(ProblemEntity problem);

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

@ -1,4 +1,5 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/core/domain/entities/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';
@ -16,7 +17,7 @@ class AddProblemUsecase {
});
Future<ProblemEntity> call({
required String enterpriseId,
required Enterprise enterprise,
required String description,
required String location,
required List<String> imageUrls,
@ -31,7 +32,7 @@ class AddProblemUsecase {
lastModifiedTime: nowUtc,
lastModifierId: userId,
syncStatus: SyncStatus.pendingCreate,
enterpriseId: enterpriseId,
enterpriseId: enterprise.id,
creationTime: nowUtc,
creatorId: userId,
);

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

@ -1,4 +1,5 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class GetAllProblemsUsecase {
@ -6,17 +7,11 @@ class GetAllProblemsUsecase {
GetAllProblemsUsecase({required this.problemRepository});
Future<List<ProblemEntity>> call({
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
Future<List<ProblemListItemEntity>> call({
ProblemFilterParams? filter,
}) async {
return await problemRepository.getAllProblems(
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
return await problemRepository.getAllProblemListItem(
filter: filter ?? ProblemFilterParams.initial(),
);
}
}

9
lib/app/features/problem/domain/usecases/update_problem.dart → lib/app/features/problem/domain/usecases/update_problem_usecase.dart

@ -1,13 +1,16 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/domain/entities/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 {
class UpdateProblemUsecase {
final IProblemRepository repository;
final AuthRepository authRepository;
UpdateProblem({required this.repository, required this.authRepository});
UpdateProblemUsecase({
required this.repository,
required this.authRepository,
});
Future<ProblemEntity> call(ProblemEntity entity) async {
final nowUtc = DateTime.now().toUtc();

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

@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/bindings/base_bindings.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/core/services/http_provider.dart';
@ -8,31 +9,18 @@ import 'package:problem_check_system/app/features/enterprise/data/repositories_i
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprises_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/add_problem_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/update_problem_usecase.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_form_controller.dart';
class ProblemFormBinding extends Bindings {
class ProblemFormBinding extends BaseBindings {
@override
void dependencies() {
// final dynamic arguments = Get.arguments;
// final bool readOnly = Get.parameters['isReadOnly'] == 'true';
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?;
}
}
void register1Services() {
// TODO: implement register1Services
}
@override
void register2DataSource() {
Get.put<EnterpriseLocalDataSource>(
EnterpriseLocalDataSourceImpl(
databaseService: Get.find<DatabaseService>(),
@ -41,7 +29,10 @@ class ProblemFormBinding extends Bindings {
Get.put<EnterpriseRemoteDataSource>(
EnterpriseRemoteDataSourceImpl(http: Get.find<HttpProvider>()),
);
}
@override
void register3Repositories() {
Get.put<EnterpriseRepository>(
EnterpriseRepositoryImpl(
localDataSource: Get.find<EnterpriseLocalDataSource>(),
@ -50,16 +41,53 @@ class ProblemFormBinding extends Bindings {
uuid: Get.find(),
),
);
}
@override
void register4Usecases() {
Get.put<GetEnterprisesUsecase>(
GetEnterprisesUsecase(repository: Get.find<EnterpriseRepository>()),
);
Get.lazyPut(
() => AddProblemUsecase(
problemRepository: Get.find(),
authRepository: Get.find(),
uuid: Get.find(),
),
);
Get.lazyPut(
() => UpdateProblemUsecase(
repository: Get.find(),
authRepository: Get.find(),
),
);
}
@override
void register5Controllers() {
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.lazyPut<ProblemFormController>(
() => ProblemFormController(
problem: problem,
formMode: formMode,
getEnterprisesUsecase: Get.find<GetEnterprisesUsecase>(),
addProblemUsecase: Get.find(),
updateProblemUsecase: Get.find(),
),
);
}

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

@ -7,12 +7,12 @@ 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 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprises_usecase.dart';
import 'dart:io';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/add_problem_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/update_problem_usecase.dart';
import 'package:uuid/uuid.dart';
class ProblemFormController extends GetxController {
@ -40,13 +40,18 @@ class ProblemFormController extends GetxController {
final TextEditingController descriptionController = TextEditingController();
final TextEditingController locationController = TextEditingController();
final RxList<XFile> selectedImages = <XFile>[].obs;
//
final GetEnterprisesUsecase getEnterprisesUsecase;
final AddProblemUsecase addProblemUsecase;
final UpdateProblemUsecase updateProblemUsecase;
// 使便
ProblemFormController({
this.problem,
this.formMode = FormMode.view,
required this.getEnterprisesUsecase,
required this.addProblemUsecase,
required this.updateProblemUsecase,
}) {
if (problem != null) {
if (problem!.bindData != null) {
@ -105,12 +110,12 @@ class ProblemFormController extends GetxController {
}
}
// 5. : UI调用以更新状态
// UI调用以更新状态
void selectEnterprise(Enterprise? enterprise) {
selectedEnterprise.value = enterprise;
//
if (enterprise != null) {
print('Selected: ${enterprise.name}');
Get.log('选择: ${enterprise.name}');
}
}
@ -184,6 +189,11 @@ class ProblemFormController extends GetxController {
//
bool _validateForm() {
if (selectedEnterprise.value == null) {
Get.snackbar('提示', '请选择一个企业', snackPosition: SnackPosition.TOP);
return false;
}
if (descriptionController.text.isEmpty) {
Get.snackbar('提示', '请填写问题描述', snackPosition: SnackPosition.TOP);
return false;
@ -204,49 +214,45 @@ 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,
// );
// await problemRepository.insertProblem(newProblem);
// }
// Get.back(result: true); //
// Get.snackbar('成功', '问题已更新');
// } catch (e) {
// Get.snackbar('错误', '保存问题失败: $e');
// } finally {
// isLoading.value = false;
// }
if (!_validateForm()) {
return;
}
isLoading.value = true;
try {
//
final List<String> imagePaths = await _saveImagesToLocal();
if (problem != null) {
//
final updatedProblem = problem!.copyWith(
description: descriptionController.text,
location: locationController.text,
imageUrls: imagePaths,
);
await updateProblemUsecase(updatedProblem);
} else {
//
await addProblemUsecase(
enterprise: selectedEnterprise.value!,
description: descriptionController.text,
location: locationController.text,
imageUrls: imagePaths,
);
}
Get.back(result: true); //
Get.snackbar('成功', '问题已更新');
} catch (e) {
Get.snackbar('错误', '保存问题失败: $e');
} finally {
isLoading.value = false;
}
}
//
Future<List<ImageMetadata>> _saveImagesToLocal() async {
final List<ImageMetadata> imagePaths = [];
Future<List<String>> _saveImagesToLocal() async {
final List<String> imagePaths = [];
final directory = await getApplicationDocumentsDirectory();
final imagesDir = Directory('${directory.path}/problem_images');
@ -254,22 +260,18 @@ class ProblemFormController extends GetxController {
if (!await imagesDir.exists()) {
await imagesDir.create(recursive: true);
}
//
for (var image in selectedImages) {
try {
final String fileName = '${Uuid().v4()}_${path.basename(image.name)}';
final String imagePath = '${imagesDir.path}/$fileName';
final ImageMetadata imageData = ImageMetadata(
localPath: imagePath,
status: ImageStatus.pendingUpload,
);
final File imageFile = File(imagePath);
//
final imageBytes = await image.readAsBytes();
await imageFile.writeAsBytes(imageBytes);
imagePaths.add(imageData);
imagePaths.add(imagePath);
} catch (e) {
throw Exception(e);
}

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

97
lib/app/features/problem/presentation/models/problem_card_state.dart

@ -0,0 +1,97 @@
// presentation/states/problem_card_state.dart
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
class ProblemCardState extends Equatable {
final String problemId; //
final String? previewImageUrl; // []
final String description; // []
final String enterpriseName; // []
final String location; // []
final String creationTime; // []
final StatusTagState uploadStatus; // []
final StatusTagState bindingStatus; // []
const ProblemCardState({
required this.problemId,
this.previewImageUrl,
required this.description,
required this.enterpriseName,
required this.location,
required this.creationTime,
required this.uploadStatus,
required this.bindingStatus,
});
// []
factory ProblemCardState.fromEntities({
required ProblemEntity problem,
required Enterprise enterprise,
}) {
// 1. null
final String? previewImageUrl = problem.imageUrls.isNotEmpty
? problem.imageUrls.first
: null;
// 2. DateTime
final String formattedTime =
'${problem.creationTime.year}-${problem.creationTime.month.toString().padLeft(2, '0')}-${problem.creationTime.day.toString().padLeft(2, '0')}';
// 3. SyncStatus UI的 StatusTagState
StatusTagState uploadStatusState;
switch (problem.syncStatus) {
case SyncStatus.synced:
uploadStatusState = StatusTagState(text: '已上传', color: Colors.green);
break;
case SyncStatus.pendingCreate:
case SyncStatus.pendingUpdate:
case SyncStatus.pendingDelete:
uploadStatusState = StatusTagState(text: '待上传', color: Colors.orange);
break;
case SyncStatus.untracked:
default:
uploadStatusState = StatusTagState(text: '未上传', color: Colors.grey);
break;
}
// 4. bindData
final StatusTagState bindingStatusState =
(problem.bindData != null && problem.bindData!.isNotEmpty)
? StatusTagState(text: '已绑定', color: Colors.blue)
: StatusTagState(text: '未绑定', color: Colors.red);
return ProblemCardState(
problemId: problem.id,
previewImageUrl: previewImageUrl,
description: problem.description,
enterpriseName: enterprise.name,
location: problem.location,
creationTime: formattedTime,
uploadStatus: uploadStatusState,
bindingStatus: bindingStatusState,
);
}
@override
List<Object?> get props => [
problemId,
previewImageUrl,
description,
enterpriseName,
location,
creationTime,
uploadStatus,
bindingStatus,
];
}
//
class StatusTagState {
final String text;
final Color color;
StatusTagState({required this.text, required this.color});
}

1
lib/app/features/problem/presentation/pages/widgets/problem_card.dart

@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
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/pages/widgets/custom_button.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';

Loading…
Cancel
Save