|
|
|
|
@ -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/pages/widgets/custom_app_bar.dart'; |
|
|
|
|
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart'; |
|
|
|
|
@ -339,82 +340,142 @@ class ProblemListPage extends GetView<ProblemListController> {
|
|
|
|
|
itemCount: controller.problemList.length, |
|
|
|
|
itemBuilder: (context, index) { |
|
|
|
|
final item = controller.problemList[index]; |
|
|
|
|
final enterprise = item.problemEntity; |
|
|
|
|
final problemEntity = item.problemEntity; |
|
|
|
|
// 核心: 从 base controller 获取选中状态 |
|
|
|
|
final isSelected = controller.selectedProblems.contains(enterprise); |
|
|
|
|
final isSelected = controller.selectedProblems.contains( |
|
|
|
|
problemEntity, |
|
|
|
|
); |
|
|
|
|
// 检查问题是否为待删除 |
|
|
|
|
final bool isPendingDelete = |
|
|
|
|
problemEntity.syncStatus == SyncStatus.pendingDelete; |
|
|
|
|
// ✅ 1.使用 Dismissible 包裹列表项 |
|
|
|
|
return Dismissible( |
|
|
|
|
// ✅ 2.为每个 Dismissible 提供一个唯一的 Key。 |
|
|
|
|
// 这非常重要,Flutter 用它来识别和追踪列表项。 |
|
|
|
|
// 通常使用数据的唯一ID。 |
|
|
|
|
key: Key(problemEntity.id), |
|
|
|
|
|
|
|
|
|
// ✅ 3.根据状态动态设置滑动方向 |
|
|
|
|
// 如果 isPendingDelete 为 true,则设置方向为 none,禁用滑动 |
|
|
|
|
// 否则,正常设置为 endToStart |
|
|
|
|
direction: isPendingDelete |
|
|
|
|
? DismissDirection.none |
|
|
|
|
: DismissDirection.endToStart, |
|
|
|
|
|
|
|
|
|
return Padding( |
|
|
|
|
padding: EdgeInsets.only(bottom: 12.h), |
|
|
|
|
child: ProblemCard( |
|
|
|
|
problemListItem: item, |
|
|
|
|
isSelected: isSelected, |
|
|
|
|
onTap: () => |
|
|
|
|
controller.navigateToDetailsView(item.problemEntity), |
|
|
|
|
actions: Row( |
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end, |
|
|
|
|
// ✅ 4. 【背景】设置滑动时露出的背景。 |
|
|
|
|
// 这为用户提供了明确的视觉反馈。 |
|
|
|
|
background: Container( |
|
|
|
|
color: Colors.red, // 删除操作通常用红色背景 |
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 20.w), |
|
|
|
|
alignment: Alignment.centerRight, // 让图标和文字靠右显示 |
|
|
|
|
child: Row( |
|
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
|
children: [ |
|
|
|
|
// “修改信息” 按钮 |
|
|
|
|
ElevatedButton( |
|
|
|
|
onPressed: () => |
|
|
|
|
controller.navigateToEditForm(item.problemEntity), |
|
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
|
backgroundColor: const Color(0xFF42A5F5), |
|
|
|
|
foregroundColor: Colors.white, |
|
|
|
|
padding: EdgeInsets.symmetric( |
|
|
|
|
horizontal: 20.w, |
|
|
|
|
vertical: 8.h, |
|
|
|
|
), |
|
|
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
|
|
|
|
// [!!!] 移除按钮自身的阴影,因为它现在在卡片内部 |
|
|
|
|
elevation: 0, |
|
|
|
|
// [!!!] 自定义形状,只保留左上角圆角,以完美贴合卡片边缘 |
|
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
|
borderRadius: BorderRadius.only( |
|
|
|
|
topLeft: Radius.circular(12.r), |
|
|
|
|
bottomRight: Radius.circular(12.r), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
child: Text( |
|
|
|
|
"修改", |
|
|
|
|
style: TextStyle( |
|
|
|
|
fontSize: 13.sp, |
|
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
|
), |
|
|
|
|
Text( |
|
|
|
|
'删除', |
|
|
|
|
style: TextStyle( |
|
|
|
|
color: Colors.white, |
|
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
|
fontSize: 16.sp, |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
SizedBox(width: 8.w), |
|
|
|
|
// “查看问题” 按钮 (关键样式在这里) |
|
|
|
|
ElevatedButton( |
|
|
|
|
onPressed: () => |
|
|
|
|
controller.navigateToDetailsView(item.problemEntity), |
|
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
|
backgroundColor: const Color(0xFF42A5F5), |
|
|
|
|
foregroundColor: Colors.white, |
|
|
|
|
padding: EdgeInsets.symmetric( |
|
|
|
|
horizontal: 20.w, |
|
|
|
|
vertical: 8.h, |
|
|
|
|
const Icon(Icons.delete, color: Colors.white), |
|
|
|
|
], |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
|
|
|
|
|
// ✅ 5. 【确认回调】在滑动到一定程度时触发,用于弹出确认对话框。 |
|
|
|
|
// 这可以防止用户误操作。 |
|
|
|
|
confirmDismiss: (direction) async { |
|
|
|
|
// 在这里调用控制器的方法来显示确认对话框 |
|
|
|
|
return await controller.confirmDeleteProblem(problemEntity); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// ✅ 6. 【删除回调】当滑动完成或确认对话框返回 true 时触发。 |
|
|
|
|
// 这是真正执行删除逻辑的地方。 |
|
|
|
|
// 注意:confirmDismiss 返回 true 后才会执行 onDismissed |
|
|
|
|
onDismissed: (direction) { |
|
|
|
|
// 调用控制器的方法来处理删除逻辑 |
|
|
|
|
controller.deleteProblem(problemEntity); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// ✅ 7. 【子组件】将你原来的列表项作为 child 放入 Dismissible 中。 |
|
|
|
|
child: Padding( |
|
|
|
|
padding: EdgeInsets.only(bottom: 12.h), |
|
|
|
|
child: ProblemCard( |
|
|
|
|
problemListItem: item, |
|
|
|
|
isSelected: isSelected, |
|
|
|
|
onTap: () => |
|
|
|
|
controller.navigateToDetailsView(item.problemEntity), |
|
|
|
|
actions: Row( |
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end, |
|
|
|
|
children: [ |
|
|
|
|
// “修改信息” 按钮 |
|
|
|
|
if (isPendingDelete == false) |
|
|
|
|
ElevatedButton( |
|
|
|
|
onPressed: () => |
|
|
|
|
controller.navigateToEditForm(item.problemEntity), |
|
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
|
backgroundColor: const Color(0xFF42A5F5), |
|
|
|
|
foregroundColor: Colors.white, |
|
|
|
|
padding: EdgeInsets.symmetric( |
|
|
|
|
horizontal: 20.w, |
|
|
|
|
vertical: 8.h, |
|
|
|
|
), |
|
|
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
|
|
|
|
// [!!!] 移除按钮自身的阴影,因为它现在在卡片内部 |
|
|
|
|
elevation: 0, |
|
|
|
|
// [!!!] 自定义形状,只保留左上角圆角,以完美贴合卡片边缘 |
|
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
|
borderRadius: BorderRadius.only( |
|
|
|
|
topLeft: Radius.circular(12.r), |
|
|
|
|
bottomRight: Radius.circular(12.r), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
child: Text( |
|
|
|
|
"修改", |
|
|
|
|
style: TextStyle( |
|
|
|
|
fontSize: 13.sp, |
|
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
|
|
|
|
// [!!!] 移除按钮自身的阴影,因为它现在在卡片内部 |
|
|
|
|
elevation: 0, |
|
|
|
|
// [!!!] 自定义形状,只保留左上角圆角,以完美贴合卡片边缘 |
|
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
|
borderRadius: BorderRadius.only( |
|
|
|
|
topLeft: Radius.circular(12.r), |
|
|
|
|
bottomRight: Radius.circular(12.r), |
|
|
|
|
SizedBox(width: 8.w), |
|
|
|
|
// “查看问题” 按钮 (关键样式在这里) |
|
|
|
|
ElevatedButton( |
|
|
|
|
onPressed: () => controller.navigateToDetailsView( |
|
|
|
|
item.problemEntity, |
|
|
|
|
), |
|
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
|
backgroundColor: const Color(0xFF42A5F5), |
|
|
|
|
foregroundColor: Colors.white, |
|
|
|
|
padding: EdgeInsets.symmetric( |
|
|
|
|
horizontal: 20.w, |
|
|
|
|
vertical: 8.h, |
|
|
|
|
), |
|
|
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
|
|
|
|
// [!!!] 移除按钮自身的阴影,因为它现在在卡片内部 |
|
|
|
|
elevation: 0, |
|
|
|
|
// [!!!] 自定义形状,只保留左上角圆角,以完美贴合卡片边缘 |
|
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
|
borderRadius: BorderRadius.only( |
|
|
|
|
topLeft: Radius.circular(12.r), |
|
|
|
|
bottomRight: Radius.circular(12.r), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
child: Text( |
|
|
|
|
"查看", |
|
|
|
|
style: TextStyle( |
|
|
|
|
fontSize: 13.sp, |
|
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
|
child: Text( |
|
|
|
|
"查看", |
|
|
|
|
style: TextStyle( |
|
|
|
|
fontSize: 13.sp, |
|
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
], |
|
|
|
|
], |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
|