diff --git a/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart b/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart index 867b698..d1ccfe3 100644 --- a/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart +++ b/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart @@ -213,4 +213,10 @@ class EnterpriseRepositoryImpl implements EnterpriseRepository { // 3. 如果数据存在,则先转换为 DTO,再转换为领域实体并返回 return EnterpriseModel.fromMap(problemMap).toEntity(); } + + @override + Future deleteEnterprise(String id) async { + // 直接将 ID 传递给数据源进行删除操作 + await localDataSource.deleteEnterprise(id); + } } diff --git a/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart b/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart index e60fde9..7d022d2 100644 --- a/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart +++ b/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart @@ -36,4 +36,6 @@ abstract class EnterpriseRepository implements SyncableRepository { Future> getAllEnterprises(); // 根据企业ID获取企业信息 Future getEnterpriseById(String enterpriseId); + // 删除企业信息 + Future deleteEnterprise(String id); } diff --git a/lib/app/features/enterprise/domain/usecases/delete_enterprise_usecase.dart b/lib/app/features/enterprise/domain/usecases/delete_enterprise_usecase.dart new file mode 100644 index 0000000..ae30332 --- /dev/null +++ b/lib/app/features/enterprise/domain/usecases/delete_enterprise_usecase.dart @@ -0,0 +1,31 @@ +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'; + +class DeleteEnterpriseUsecase { + final EnterpriseRepository repository; + final AuthRepository authRepository; + DeleteEnterpriseUsecase({ + required this.repository, + required this.authRepository, + }); + + Future call(Enterprise enterprise) async { + final nowUtc = DateTime.now().toUtc(); + final userId = authRepository.getUserId(); + + // 如果是刚在本地创建还未同步的企业,直接物理删除 + if (enterprise.syncStatus == SyncStatus.pendingCreate) { + await repository.deleteEnterprise(enterprise.id); + } else { + // 否则,进行软删除(标记为待删除) + final enterpriseToDelete = enterprise.copyWith( + lastModifiedTime: nowUtc, + lastModifierId: userId, + syncStatus: SyncStatus.pendingDelete, + ); + await repository.updateEnterprise(enterpriseToDelete); + } + } +} diff --git a/lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart b/lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart index 4030a9a..cd154f5 100644 --- a/lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart +++ b/lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart @@ -5,6 +5,7 @@ import 'package:problem_check_system/app/features/enterprise/data/datasources/en import 'package:problem_check_system/app/features/enterprise/data/datasources/enterprise_remote_data_source.dart'; import 'package:problem_check_system/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; +import 'package:problem_check_system/app/features/enterprise/domain/usecases/delete_enterprise_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart'; @@ -38,12 +39,19 @@ class EnterpriseListBinding extends Bindings { Get.put( GetEnterpriseListUsecase(repository: Get.find()), ); + Get.put( + DeleteEnterpriseUsecase( + repository: Get.find(), + authRepository: Get.find(), + ), + ); Get.lazyPut( () => EnterpriseListController( getEnterpriseListUsecase: Get.find(), syncEnterprisesUsecase: Get.find(), resolveConflictUsecase: Get.find(), + deleteEnterpriseUsecase: Get.find(), ), ); } diff --git a/lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart b/lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart index 94e10eb..9a58402 100644 --- a/lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart +++ b/lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart @@ -9,6 +9,7 @@ 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/enterprise/domain/repositories/enterprise_repository.dart'; +import 'package:problem_check_system/app/features/enterprise/domain/usecases/delete_enterprise_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart'; @@ -29,11 +30,14 @@ class EnterpriseListController extends GetxController { final GetEnterpriseListUsecase getEnterpriseListUsecase; final SyncEnterprisesUsecase syncEnterprisesUsecase; // 新增 final ResolveConflictUsecase resolveConflictUsecase; // 新增 + final DeleteEnterpriseUsecase + deleteEnterpriseUsecase; // ✅ 1. 【新增】注入删除 UseCase EnterpriseListController({ required this.getEnterpriseListUsecase, required this.syncEnterprisesUsecase, required this.resolveConflictUsecase, + required this.deleteEnterpriseUsecase, // ✅ 2. 【新增】接收删除 UseCase }); // --- 实现基类中定义的属性 --- @@ -215,6 +219,52 @@ class EnterpriseListController extends GetxController { loadEnterprises(); } + // ✅ 3. 【新增】显示删除确认对话框的方法 + Future confirmDeleteEnterprise(Enterprise enterprise) async { + return await Get.dialog( + AlertDialog( + title: const Text('确认删除'), + content: Text('您确定要删除企业 "${enterprise.name}" 吗?该企业下的所有问题也将无法查看。'), + actions: [ + TextButton( + child: const Text('取消'), + onPressed: () => Get.back(result: false), + ), + TextButton( + style: TextButton.styleFrom(foregroundColor: Colors.red), + child: const Text('删除'), + onPressed: () => Get.back(result: true), + ), + ], + ), + ); + } + + // ✅ 4. 【新增】执行删除操作的方法 + void deleteEnterprise(Enterprise enterprise) async { + try { + // 调用 UseCase 执行业务逻辑 + await deleteEnterpriseUsecase(enterprise); + + // 从UI列表中移除该项 + enterpriseList.removeWhere((item) => item.enterprise.id == enterprise.id); + + Get.snackbar( + '删除成功', + '"${enterprise.name}" 已被标记为删除。', + snackPosition: SnackPosition.BOTTOM, + ); + } catch (e) { + Get.snackbar( + '删除失败', + '无法删除该企业: $e', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red, + colorText: Colors.white, + ); + } + } + Future loadEnterprises() async { expansibleController.collapse(); isLoading.value = true; diff --git a/lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart b/lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart index 296f95e..0ab3744 100644 --- a/lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart +++ b/lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart @@ -2,6 +2,7 @@ import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; import 'package:problem_check_system/app/core/extensions/datetime_extension.dart'; import 'package:problem_check_system/app/core/models/company_enum.dart'; import 'package:problem_check_system/app/core/pages/widgets/custom_app_bar.dart'; @@ -251,77 +252,121 @@ class EnterpriseListPage extends GetView { enterprise, ); - return Padding( - padding: EdgeInsets.only(bottom: 12.h), - child: UnifiedEnterpriseCard( - enterpriseListItem: item, - isSelected: isSelected, - // 根据外部传入的 itemMode 决定卡片内部的 mode - // --- [核心] 将卡片的所有交互事件转发给 controller 的抽象方法 --- - onTap: () => - controller.navigateToEnterpriseInfoPage(item.enterprise), - // [重点] 构建并传入符合贴边样式的 actions - actions: Row( + // ✅ 1. 【新增】判断企业是否处于“待删除”状态 + final bool isPendingDelete = + enterprise.syncStatus == SyncStatus.pendingDelete; + + // ✅ 2. 【新增】使用 Dismissible 包裹列表项 + return Dismissible( + key: Key(enterprise.id), // 使用企业ID作为唯一Key + // ✅ 3. 【新增】根据状态禁用滑动 + direction: isPendingDelete + ? DismissDirection.none + : DismissDirection.endToStart, + + // ✅ 4. 【新增】设置滑动背景 + background: Container( + color: Colors.red, + padding: EdgeInsets.symmetric(horizontal: 20.w), + alignment: Alignment.centerRight, + child: Row( mainAxisSize: MainAxisSize.min, - // 让按钮在垂直方向上对齐 - crossAxisAlignment: CrossAxisAlignment.center, children: [ - // “修改信息” 按钮 - TextButton.icon( - onPressed: () => - controller.navigateToEditForm(item.enterprise), - icon: Icon( - Icons.edit_outlined, - size: 16.sp, - color: Colors.grey.shade700, - ), - label: Text( - '修改信息', - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey.shade700, - ), - ), - style: TextButton.styleFrom( - padding: EdgeInsets.symmetric( - horizontal: 12.w, - vertical: 12.h, - ), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, + Text( + '删除', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16.sp, ), ), + SizedBox(width: 8.w), + const Icon(Icons.delete, color: Colors.white), + ], + ), + ), - // “查看问题” 按钮 (关键样式在这里) - ElevatedButton( - onPressed: () => controller.navigateToEnterpriseInfoPage( - item.enterprise, - ), - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF42A5F5), - foregroundColor: Colors.white, - padding: EdgeInsets.symmetric( - horizontal: 20.w, - vertical: 12.h, + // ✅ 5. 【新增】设置确认回调 + confirmDismiss: (direction) async { + return await controller.confirmDeleteEnterprise(enterprise); + }, + + // ✅ 6. 【新增】设置删除回调 + onDismissed: (direction) { + controller.deleteEnterprise(enterprise); + }, + child: Padding( + padding: EdgeInsets.only(bottom: 12.h), + child: UnifiedEnterpriseCard( + enterpriseListItem: item, + isSelected: isSelected, + // 根据外部传入的 itemMode 决定卡片内部的 mode + // --- [核心] 将卡片的所有交互事件转发给 controller 的抽象方法 --- + onTap: () => + controller.navigateToEnterpriseInfoPage(item.enterprise), + // [重点] 构建并传入符合贴边样式的 actions + actions: Row( + mainAxisSize: MainAxisSize.min, + // 让按钮在垂直方向上对齐 + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // “修改信息” 按钮 + if (!isPendingDelete) + TextButton.icon( + onPressed: () => + controller.navigateToEditForm(item.enterprise), + icon: Icon( + Icons.edit_outlined, + size: 16.sp, + color: Colors.grey.shade700, + ), + label: Text( + '修改信息', + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey.shade700, + ), + ), + style: TextButton.styleFrom( + padding: EdgeInsets.symmetric( + horizontal: 12.w, + vertical: 12.h, + ), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), ), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // [!!!] 移除按钮自身的阴影,因为它现在在卡片内部 - elevation: 0, - // [!!!] 自定义形状,只保留左上角圆角,以完美贴合卡片边缘 - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(12.r), + + // “查看问题” 按钮 (关键样式在这里) + ElevatedButton( + onPressed: () => controller + .navigateToEnterpriseInfoPage(item.enterprise), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF42A5F5), + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 20.w, + vertical: 12.h, + ), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // [!!!] 移除按钮自身的阴影,因为它现在在卡片内部 + elevation: 0, + // [!!!] 自定义形状,只保留左上角圆角,以完美贴合卡片边缘 + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(12.r), + ), ), ), - ), - child: Text( - "查看问题", - style: TextStyle( - fontSize: 13.sp, - fontWeight: FontWeight.bold, + child: Text( + "查看问题", + style: TextStyle( + fontSize: 13.sp, + fontWeight: FontWeight.bold, + ), ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart b/lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart index 6c26d59..6cef196 100644 --- a/lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart +++ b/lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; import 'package:problem_check_system/app/core/extensions/datetime_extension.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; @@ -22,6 +23,8 @@ class UnifiedEnterpriseCard extends StatelessWidget { @override Widget build(BuildContext context) { + final bool isDeleted = + enterpriseListItem.enterprise.syncStatus == SyncStatus.pendingDelete; return Card( elevation: isSelected ? 4.0 : .2, shape: RoundedRectangleBorder( @@ -33,6 +36,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { // [关键 1] 必须剪裁,才能让按钮的形状与卡片边缘完美融合 clipBehavior: Clip.antiAlias, margin: EdgeInsets.zero, + color: isDeleted ? Colors.grey[200] : Theme.of(context).cardColor, child: InkWell( onTap: onTap, // [关键 2] 使用 Column 作为主布局 @@ -48,9 +52,9 @@ class UnifiedEnterpriseCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildTopSection(), + _buildTopSection(isDeleted), SizedBox(height: 12.h), - _buildMiddleSection(), + _buildMiddleSection(isDeleted), ], ), ), @@ -60,7 +64,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { // --- 区域 2: 底部的操作栏 --- // [关键 3] 这一行是整个布局的核心 - if (actions != null) _buildBottomActionRow(), + if (actions != null) _buildBottomActionRow(isDeleted), ], ), ), @@ -68,7 +72,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { } /// 构建顶部区域:企业名称、类型和状态 - Widget _buildTopSection() { + Widget _buildTopSection(bool isDeleted) { return Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -90,7 +94,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { style: TextStyle( fontSize: 12.5.sp, fontWeight: FontWeight.w500, - color: Colors.black87, + color: isDeleted ? Colors.grey : Colors.black87, ), overflow: TextOverflow.ellipsis, ), @@ -113,7 +117,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { style: TextStyle( fontSize: 12.5.sp, fontWeight: FontWeight.w500, - color: Colors.black87, + color: isDeleted ? Colors.grey : Colors.black87, ), overflow: TextOverflow.ellipsis, ), @@ -145,7 +149,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { } /// 构建中间区域:问题总数和创建时间 - Widget _buildMiddleSection() { + Widget _buildMiddleSection(bool isDeleted) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -162,8 +166,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { enterpriseListItem.totalProblems.toString(), style: TextStyle( fontSize: 12.5.sp, - color: Colors.black87, - fontWeight: FontWeight.w500, + color: isDeleted ? Colors.grey : Colors.black87, ), ), ], @@ -173,9 +176,16 @@ class UnifiedEnterpriseCard extends StatelessWidget { children: [ Icon(Icons.access_time, color: Colors.grey, size: 16.sp), Text( - '创建时间: ${enterpriseListItem.enterprise.creationTime.toDateTimeString()}', + '创建时间:', style: TextStyle(fontSize: 12.sp, color: Colors.grey), ), + Text( + enterpriseListItem.enterprise.creationTime.toDateTimeString(), + style: TextStyle( + fontSize: 12.sp, + color: isDeleted ? Colors.grey : Colors.black87, + ), + ), ], ), ], @@ -183,7 +193,7 @@ class UnifiedEnterpriseCard extends StatelessWidget { } /// 构建底部的“标签+操作”行 - Widget _buildBottomActionRow() { + Widget _buildBottomActionRow(bool isDeleted) { return Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -194,14 +204,18 @@ class UnifiedEnterpriseCard extends StatelessWidget { children: [ TDTag( '已上传 ${enterpriseListItem.uploadedProblems}', - textColor: Colors.blue, - backgroundColor: Colors.blue.withAlpha(20), + textColor: isDeleted ? Colors.grey : Colors.blue, + backgroundColor: isDeleted + ? Colors.grey.withAlpha(20) + : Colors.blue.withAlpha(20), ), SizedBox(width: 8.w), TDTag( '未上传 ${enterpriseListItem.pendingProblems}', - textColor: Colors.red, - backgroundColor: Colors.red.withAlpha(20), + textColor: isDeleted ? Colors.grey : Colors.red, + backgroundColor: isDeleted + ? Colors.grey.withAlpha(20) + : Colors.red.withAlpha(20), ), ], ),