Browse Source

refactor : 页面都拥有自己的路由,通过BindingBuilder构建多页面路由

dev
徐振升 2 weeks ago
parent
commit
ba3cff08b6
  1. 1
      lib/app/core/pages/upload_page.dart
  2. 0
      lib/app/core/pages/widgets/custom_app_bar.dart
  3. 0
      lib/app/core/pages/widgets/upload_app_bar.dart
  4. 7
      lib/app/core/routes/app_pages.dart
  5. 4
      lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart
  6. 4
      lib/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart
  7. 66
      lib/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart
  8. 14
      lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart
  9. 41
      lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
  10. 2
      lib/app/features/enterprise/presentation/pages/enterprise_form_page.dart
  11. 282
      lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart
  12. 96
      lib/app/features/enterprise/presentation/pages/enterprise_upload_page.dart
  13. 53
      lib/app/features/enterprise/presentation/pages/widgets/enterprise_card.dart
  14. 280
      lib/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.dart
  15. 230
      lib/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart
  16. 2
      lib/app/features/home/views/home_page.dart
  17. 54
      lib/app/features/navigation/presentation/controllers/navigation_controller.dart
  18. 80
      lib/app/features/navigation/presentation/pages/navigation_page.dart
  19. 2
      lib/app/features/problem/presentation/views/problem_form_page.dart
  20. 2
      lib/app/features/problem/presentation/views/problem_page.dart
  21. 2
      lib/app/features/problem/presentation/views/problem_upload_page.dart

1
lib/app/core/pages/upload_page.dart

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

0
lib/app/core/widgets/custom_app_bar.dart → lib/app/core/pages/widgets/custom_app_bar.dart

0
lib/app/core/widgets/upload_app_bar.dart → lib/app/core/pages/widgets/upload_app_bar.dart

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

@ -31,7 +31,12 @@ abstract class AppPages {
GetPage(
name: AppRoutes.navigation,
page: () => const NavigationPage(),
binding: NavigationBinding(),
binding: BindingsBuilder(() {
NavigationBinding().dependencies();
EnterpriseListBinding().dependencies();
ProblemBinding().dependencies();
ProfileBinding().dependencies();
}),
),
GetPage(
name: AppRoutes.home,

4
lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart

@ -5,7 +5,6 @@ import 'package:problem_check_system/app/features/enterprise/data/datasources/en
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/get_enterprise_list_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart';
class EnterpriseListBinding extends Bindings {
@ -31,8 +30,5 @@ class EnterpriseListBinding extends Bindings {
getEnterpriseListUsecase: Get.find<GetEnterpriseListUsecase>(),
),
);
Get.lazyPut<BaseEnterpriseListController>(
() => Get.find<EnterpriseListController>(),
);
}
}

4
lib/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart

@ -1,13 +1,9 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart';
class EnterpriseUploadBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<EnterpriseUploadController>(() => EnterpriseUploadController());
Get.lazyPut<BaseEnterpriseListController>(
() => Get.find<EnterpriseUploadController>(),
);
}
}

66
lib/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart

@ -1,66 +0,0 @@
// lib/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart
import 'package:flutter/material.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
/// -----------------------------------------------------------------------------
/// []
/// -----------------------------------------------------------------------------
///
/// `EnterpriseListView`
///
/// **:**
/// - ****: `EnterpriseListView` ()
/// `EnterpriseListController` `EnterpriseUploadController` ()
/// (`BaseEnterpriseListController`)
/// - ****: `EnterpriseListView`
/// View Controller
///
abstract class BaseEnterpriseListController extends GetxController {
// --- (View ) ---
///
/// View UI
RxList<EnterpriseListItem> get enterpriseList;
///
/// View ( `CircularProgressIndicator`)
RxBool get isLoading;
/// []
TextEditingController get nameController;
/// []
Rx<CompanyType?> get selectedType;
/// []
Rx<DateTime?> get startDate;
/// []
Rx<DateTime?> get endDate;
/// []
/// 使 `Set`
RxSet<Enterprise> get selectedEnterprises;
// --- (View ) ---
/// /
/// View
void search();
///
/// View
void clearFilters();
///
/// /
void onItemTap(EnterpriseListItem item);
///
///
void onSelectionChanged(Enterprise enterprise);
}

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

@ -8,7 +8,6 @@ import 'package:problem_check_system/app/features/enterprise/domain/entities/ent
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_form_controller.dart';
import 'base_enterprise_list_controller.dart';
/// -----------------------------------------------------------------------------
/// []
@ -22,25 +21,18 @@ import 'base_enterprise_list_controller.dart';
/// -
/// -
///
class EnterpriseListController extends BaseEnterpriseListController {
class EnterpriseListController extends GetxController {
final GetEnterpriseListUsecase getEnterpriseListUsecase;
EnterpriseListController({required this.getEnterpriseListUsecase});
// --- ---
@override
final enterpriseList = <EnterpriseListItem>[].obs;
@override
final isLoading = false.obs;
@override
final nameController = TextEditingController();
@override
final selectedType = Rx<CompanyType?>(null);
@override
final startDate = Rx<DateTime?>(null);
@override
final endDate = Rx<DateTime?>(null);
@override
final selectedEnterprises = <Enterprise>{}.obs; // Set
@override
@ -51,12 +43,10 @@ class EnterpriseListController extends BaseEnterpriseListController {
// --- ---
@override
void search() {
fetchEnterprises();
}
@override
void clearFilters() {
nameController.clear();
selectedType.value = null;
@ -65,13 +55,11 @@ class EnterpriseListController extends BaseEnterpriseListController {
fetchEnterprises();
}
@override
void onItemTap(EnterpriseListItem item) {
//
navigateToEnterpriseInfoPage(item.enterprise);
}
@override
void onSelectionChanged(Enterprise enterprise) {
//
//

41
lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart

@ -1,11 +1,8 @@
// lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
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/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'base_enterprise_list_controller.dart';
// Usecase
// import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_pending_uploads_usecase.dart';
@ -21,56 +18,26 @@ import 'base_enterprise_list_controller.dart';
/// -
/// -
///
class EnterpriseUploadController extends BaseEnterpriseListController {
class EnterpriseUploadController extends GetxController {
// final GetPendingUploadsUsecase getPendingUploadsUsecase;
// EnterpriseUploadController({required this.getPendingUploadsUsecase});
// --- ---
@override
final enterpriseList = <EnterpriseListItem>[].obs;
@override
final isLoading = false.obs;
@override
final selectedEnterprises = <Enterprise>{}.obs; // 使 Set
//
// UI中被使用
@override
final nameController = TextEditingController();
@override
final selectedType = Rx<CompanyType?>(null);
@override
final startDate = Rx<DateTime?>(null);
@override
final endDate = Rx<DateTime?>(null);
@override
void onInit() {
super.onInit();
fetchPendingUploads();
}
// --- ---
@override
void search() {
//
fetchPendingUploads();
}
@override
void clearFilters() {
//
nameController.clear();
fetchPendingUploads();
}
@override
void onItemTap(EnterpriseListItem item) {
//
onSelectionChanged(item.enterprise);
}
@override
void onSelectionChanged(Enterprise enterprise) {
//
if (selectedEnterprises.contains(enterprise)) {
@ -80,16 +47,10 @@ class EnterpriseUploadController extends BaseEnterpriseListController {
}
}
// --- EnterpriseUploadController ---
///
Future<void> fetchPendingUploads() async {
isLoading.value = true;
try {
// Usecase
// final result = await getPendingUploadsUsecase.call();
// enterpriseList.assignAll(result);
// --- 使 ---
await Future.delayed(const Duration(seconds: 1));
// final mockData = createMockEnterpriseListItems();

2
lib/app/features/enterprise/presentation/pages/enterprise_form_page.dart

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:problem_check_system/app/core/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/core/pages/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_form_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_form_view.dart';

282
lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart

@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.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';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart';
import '../controllers/enterprise_list_controller.dart';
class EnterpriseListPage extends GetView<EnterpriseListController> {
@ -15,7 +18,280 @@ class EnterpriseListPage extends GetView<EnterpriseListController> {
actionsVisible: true,
onAddPressed: () => controller.navigateToAddForm(),
),
body: EnterpriseListView(controller: controller),
body: Column(
children: [
//
_buildFilterSection(),
const Divider(height: 1, thickness: .1),
//
Expanded(child: _buildEnterpriseList()),
],
),
);
}
///
/// `BaseEnterpriseListController`
///
Widget _buildFilterSection() {
return ExpansionTile(
title: const Text('筛选查询'),
leading: const Icon(Icons.filter_alt_outlined),
initiallyExpanded: false, //
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h),
child: Column(
children: [
// 1.
TextFormField(
controller: controller.nameController,
decoration: InputDecoration(
labelText: '企业名称',
hintText: '请输入关键词',
prefixIcon: const Icon(Icons.business_center),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
isDense: true,
),
),
SizedBox(height: 12.h),
// 2.
Obx(
() => DropdownButtonFormField<CompanyType>(
initialValue: controller.selectedType.value,
decoration: InputDecoration(
labelText: '企业类型',
prefixIcon: const Icon(Icons.category),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
isDense: true,
),
hint: const Text('请选择企业类型'),
isExpanded: true,
items: CompanyType.values.map((type) {
return DropdownMenuItem(
value: type,
child: Text(type.displayText),
);
}).toList(),
onChanged: (value) {
controller.selectedType.value = value;
},
),
),
SizedBox(height: 12.h),
// 3.
Row(
children: [
Expanded(
child: _buildDatePickerField('开始日期', controller.startDate),
),
SizedBox(width: 12.w),
Expanded(
child: _buildDatePickerField('结束日期', controller.endDate),
),
],
),
SizedBox(height: 16.h),
// 4.
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: controller.clearFilters,
icon: const Icon(Icons.refresh),
label: const Text('重置'),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 12.h),
),
),
),
SizedBox(width: 16.w),
Expanded(
child: ElevatedButton.icon(
onPressed: controller.search,
icon: const Icon(Icons.search),
label: const Text('查询'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 12.h),
),
),
),
],
),
SizedBox(height: 8.h), //
],
),
),
],
);
}
///
Widget _buildDatePickerField(String label, Rx<DateTime?> date) {
return InkWell(
onTap: () async {
final pickedDate = await showDatePicker(
context: Get.context!,
initialDate: date.value ?? DateTime.now(),
firstDate: DateTime(2020), //
lastDate: DateTime(2125),
);
if (pickedDate != null) {
date.value = pickedDate;
}
},
child: InputDecorator(
decoration: InputDecoration(
labelText: label,
prefixIcon: const Icon(Icons.calendar_today),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.r)),
isDense: true,
contentPadding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 14.h,
), //
),
child: Obx(
() => Text(
date.value == null ? '请选择日期' : date.value!.toDateString(),
style: TextStyle(
fontSize: 14.sp,
color: date.value == null ? Colors.grey[700] : Colors.black87,
),
),
),
),
);
}
///
Widget _buildEnterpriseList() {
// 使 Obx controller Rx
return Obx(() {
//
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
//
if (controller.enterpriseList.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.folder_off_outlined,
size: 60.sp,
color: Colors.grey[400],
),
SizedBox(height: 16.h),
Text(
'没有找到相关企业',
style: TextStyle(fontSize: 16.sp, color: Colors.grey[600]),
),
],
),
);
}
// 使
return RefreshIndicator(
onRefresh: () async => controller.search(),
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
itemCount: controller.enterpriseList.length,
itemBuilder: (context, index) {
final item = controller.enterpriseList[index];
final enterprise = item.enterprise;
// : base controller
final isSelected = controller.selectedEnterprises.contains(
enterprise,
);
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: UnifiedEnterpriseCard(
enterpriseListItem: item,
isSelected: isSelected,
// itemMode mode
// --- [] controller ---
onTap: () => controller.onItemTap(item),
// [] actions
actions: Row(
mainAxisSize: MainAxisSize.min,
//
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//
TextButton.icon(
onPressed: () {
/* 编辑逻辑 */
},
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,
),
),
// ()
ElevatedButton(
onPressed: () {
/* 查看问题逻辑 */
},
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,
),
),
),
],
),
),
);
},
),
);
});
}
}

96
lib/app/features/enterprise/presentation/pages/enterprise_upload_page.dart

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
import 'package:get/get_state_manager/src/simple/get_view.dart';
import 'package:problem_check_system/app/core/widgets/upload_app_bar.dart';
import 'package:problem_check_system/app/core/pages/widgets/upload_app_bar.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart';
class EnterpriseUploadPage extends GetView<EnterpriseUploadController> {
const EnterpriseUploadPage({super.key});
@ -18,11 +20,93 @@ class EnterpriseUploadPage extends GetView<EnterpriseUploadController> {
print("object");
},
),
body: EnterpriseListView(
controller: controller,
filterSectionVisible: false,
itemMode: EnterpriseListPageMode.select,
body: _buildEnterpriseList(),
bottomSheet: Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)),
),
child: ElevatedButton(
onPressed: null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
minimumSize: Size(double.infinity, 48.h),
),
child: Text('点击上传 (1)'),
),
),
);
}
Widget _buildEnterpriseList() {
// 使 Obx controller Rx
return Obx(() {
//
if (controller.isLoading.value && controller.enterpriseList.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
//
if (controller.enterpriseList.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.folder_off_outlined,
size: 60.sp,
color: Colors.grey[400],
),
SizedBox(height: 16.h),
Text(
'没有找到相关企业',
style: TextStyle(fontSize: 16.sp, color: Colors.grey[600]),
),
],
),
);
}
// 使
return RefreshIndicator(
onRefresh: () async => controller.fetchPendingUploads(),
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
itemCount: controller.enterpriseList.length,
itemBuilder: (context, index) {
final item = controller.enterpriseList[index];
final enterprise = item.enterprise;
// : base controller
final isSelected = controller.selectedEnterprises.contains(
enterprise,
);
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: UnifiedEnterpriseCard(
enterpriseListItem: item,
isSelected: isSelected,
// itemMode mode
// --- [] controller ---
onTap: () => controller.onItemTap(item),
actions: Padding(
// Checkbox
padding: EdgeInsets.only(right: 8.w),
child: Checkbox(
value: isSelected,
onChanged: (value) {},
// controller.toggleSelection(enterprise.id),
),
),
),
);
},
),
);
});
}
}

53
lib/app/features/enterprise/presentation/pages/widgets/enterprise_card.dart

@ -3,27 +3,22 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:problem_check_system/app/core/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.dart';
//
class EnterpriseCard extends StatelessWidget {
final EnterpriseListItem enterpriseListItem;
final bool isSelected;
final VoidCallback onEdit;
final VoidCallback onViewProblems;
final VoidCallback? onTap;
final ValueChanged<bool?>? onSelectionChanged;
final EnterpriseListPageMode mode;
final Widget? trailing;
final Widget? bottomAction;
const EnterpriseCard({
super.key,
required this.enterpriseListItem,
required this.isSelected,
required this.onEdit,
required this.onViewProblems,
required this.mode,
this.isSelected = false,
this.onTap,
this.onSelectionChanged,
this.trailing,
this.bottomAction,
});
@override
@ -66,7 +61,6 @@ class EnterpriseCard extends StatelessWidget {
),
),
if (mode == EnterpriseListPageMode.normal)
Positioned(
bottom: 0, //
right: 0, //
@ -74,7 +68,7 @@ class EnterpriseCard extends StatelessWidget {
children: [
// --- ---
TextButton.icon(
onPressed: onEdit,
onPressed: () {},
icon: Icon(Icons.edit_outlined, size: 16.sp),
label: Text('修改信息', style: TextStyle(fontSize: 9.5.sp)),
style: TextButton.styleFrom(
@ -89,7 +83,7 @@ class EnterpriseCard extends StatelessWidget {
),
// --- ---
ElevatedButton(
onPressed: onViewProblems,
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF42A5F5),
foregroundColor: Colors.white,
@ -245,23 +239,22 @@ class EnterpriseCard extends StatelessWidget {
backgroundColor: Colors.red.shade50,
),
const Spacer(),
if (mode == EnterpriseListPageMode.select)
Theme(
data: Theme.of(context).copyWith(
checkboxTheme: CheckboxThemeData(
// padding
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
// ()
visualDensity: VisualDensity.compact,
),
),
child: Checkbox(
value: isSelected,
onChanged: onSelectionChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
),
// Theme(
// data: Theme.of(context).copyWith(
// checkboxTheme: CheckboxThemeData(
// // padding
// materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
// // ()
// visualDensity: VisualDensity.compact,
// ),
// ),
// child: Checkbox(
// value: isSelected,
// onChanged: onSelectionChanged,
// materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
// visualDensity: VisualDensity.compact,
// ),
// ),
],
);
}

280
lib/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.dart

@ -1,280 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart';
import 'enterprise_card.dart';
///
enum EnterpriseListPageMode {
///
normal,
///
select,
}
/// -----------------------------------------------------------------------------
/// [UI组件]
/// -----------------------------------------------------------------------------
///
/// (Dumb)UI组件
///
/// **:**
/// - ****: View UI
/// - ****: `GetView<BaseEnterpriseListController>`
/// (`BaseEnterpriseListController`)
/// 使
///
class EnterpriseListView extends StatelessWidget {
const EnterpriseListView({
super.key,
required this.controller,
this.filterSectionVisible = true,
this.itemMode = EnterpriseListPageMode.normal,
});
final BaseEnterpriseListController controller;
///
final bool filterSectionVisible;
///
final EnterpriseListPageMode itemMode;
@override
Widget build(BuildContext context) {
return Column(
children: [
//
if (filterSectionVisible) ...[
_buildFilterSection(),
const Divider(height: 1, thickness: .1),
],
//
Expanded(child: _buildEnterpriseList()),
],
);
}
///
/// `BaseEnterpriseListController`
///
Widget _buildFilterSection() {
return ExpansionTile(
title: const Text('筛选查询'),
leading: const Icon(Icons.filter_alt_outlined),
initiallyExpanded: false, //
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h),
child: Column(
children: [
// 1.
TextFormField(
controller: controller.nameController,
decoration: InputDecoration(
labelText: '企业名称',
hintText: '请输入关键词',
prefixIcon: const Icon(Icons.business_center),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
isDense: true,
),
),
SizedBox(height: 12.h),
// 2.
Obx(
() => DropdownButtonFormField<CompanyType>(
initialValue: controller.selectedType.value,
decoration: InputDecoration(
labelText: '企业类型',
prefixIcon: const Icon(Icons.category),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
isDense: true,
),
hint: const Text('请选择企业类型'),
isExpanded: true,
items: CompanyType.values.map((type) {
return DropdownMenuItem(
value: type,
child: Text(type.displayText),
);
}).toList(),
onChanged: (value) {
controller.selectedType.value = value;
},
),
),
SizedBox(height: 12.h),
// 3.
Row(
children: [
Expanded(
child: _buildDatePickerField('开始日期', controller.startDate),
),
SizedBox(width: 12.w),
Expanded(
child: _buildDatePickerField('结束日期', controller.endDate),
),
],
),
SizedBox(height: 16.h),
// 4.
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: controller.clearFilters,
icon: const Icon(Icons.refresh),
label: const Text('重置'),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 12.h),
),
),
),
SizedBox(width: 16.w),
Expanded(
child: ElevatedButton.icon(
onPressed: controller.search,
icon: const Icon(Icons.search),
label: const Text('查询'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 12.h),
),
),
),
],
),
SizedBox(height: 8.h), //
],
),
),
],
);
}
///
Widget _buildDatePickerField(String label, Rx<DateTime?> date) {
return InkWell(
onTap: () async {
final pickedDate = await showDatePicker(
context: Get.context!,
initialDate: date.value ?? DateTime.now(),
firstDate: DateTime(2020), //
lastDate: DateTime(2125),
);
if (pickedDate != null) {
date.value = pickedDate;
}
},
child: InputDecorator(
decoration: InputDecoration(
labelText: label,
prefixIcon: const Icon(Icons.calendar_today),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.r)),
isDense: true,
contentPadding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 14.h,
), //
),
child: Obx(
() => Text(
date.value == null ? '请选择日期' : date.value!.toDateString(),
style: TextStyle(
fontSize: 14.sp,
color: date.value == null ? Colors.grey[700] : Colors.black87,
),
),
),
),
);
}
///
Widget _buildEnterpriseList() {
// 使 Obx controller Rx
return Obx(() {
//
if (controller.isLoading.value && controller.enterpriseList.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
//
if (controller.enterpriseList.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.folder_off_outlined,
size: 60.sp,
color: Colors.grey[400],
),
SizedBox(height: 16.h),
Text(
'没有找到相关企业',
style: TextStyle(fontSize: 16.sp, color: Colors.grey[600]),
),
],
),
);
}
// 使
return RefreshIndicator(
onRefresh: () async => controller.search(),
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
itemCount: controller.enterpriseList.length,
itemBuilder: (context, index) {
final item = controller.enterpriseList[index];
final enterprise = item.enterprise;
// : base controller
final isSelected = controller.selectedEnterprises.contains(
enterprise,
);
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: EnterpriseCard(
enterpriseListItem: item,
isSelected: isSelected,
// itemMode mode
mode: itemMode,
// --- [] controller ---
onTap: () => controller.onItemTap(item),
onSelectionChanged: (value) =>
controller.onSelectionChanged(enterprise),
// Controller
// 使()
onEdit: () {
if (controller is EnterpriseListController) {
// EnterpriseListController
(controller as EnterpriseListController).navigateToEditForm(
enterprise,
);
}
},
onViewProblems: () {
if (controller is EnterpriseListController) {
(controller as EnterpriseListController)
.navigateToEnterpriseInfoPage(enterprise);
}
},
),
);
},
),
);
});
}
}

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

@ -0,0 +1,230 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:problem_check_system/app/core/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
// [ 1]
class UnifiedEnterpriseCard extends StatelessWidget {
final EnterpriseListItem enterpriseListItem;
final bool isSelected;
final VoidCallback? onTap;
final Widget? actions;
const UnifiedEnterpriseCard({
super.key,
required this.enterpriseListItem,
this.isSelected = false, //
this.onTap,
this.actions,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: isSelected ? 4.0 : .2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
side: isSelected
? BorderSide(color: Theme.of(context).primaryColor, width: 1.5.w)
: BorderSide(color: Colors.grey.shade300, width: 1.w),
),
// [ 1]
clipBehavior: Clip.antiAlias,
margin: EdgeInsets.zero,
child: InkWell(
onTap: onTap,
// [ 2] 使 Column
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- 1: ---
//
Padding(
padding: EdgeInsets.all(16.0.w),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTopSection(),
SizedBox(height: 12.h),
_buildMiddleSection(),
],
),
),
// 线
// if (actions != null) const Divider(height: 1),
// --- 2: ---
// [ 3]
if (actions != null) _buildBottomActionRow(),
],
),
),
);
}
///
Widget _buildTopSection() {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'企业名称',
style: TextStyle(
fontSize: 9.sp,
color: Colors.grey.shade500,
), // .sp
),
SizedBox(height: 4.h), // .h
Text(
enterpriseListItem.enterprise.name,
style: TextStyle(
fontSize: 12.5.sp,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'企业类型',
style: TextStyle(fontSize: 9.sp, color: Colors.grey.shade500),
),
SizedBox(height: 4.h),
Text(
enterpriseListItem.enterprise.type,
style: TextStyle(
fontSize: 12.5.sp,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
],
),
// SizedBox(width: 8.w),
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.h),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.red.shade400, width: 1.w),
),
child: Text(
enterpriseListItem.enterprise.syncStatus == SyncStatus.synced
? '信息已上传'
: '信息未上传',
style: TextStyle(fontSize: 7.sp, color: Colors.red.shade400),
),
),
],
);
}
///
Widget _buildMiddleSection() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.description_outlined, color: Colors.grey, size: 16.sp),
SizedBox(width: 4.w),
Text(
'问题总数: ',
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
),
Text(
enterpriseListItem.totalProblems.toString(),
style: TextStyle(
fontSize: 12.5.sp,
color: Colors.black87,
fontWeight: FontWeight.w500,
),
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.access_time, color: Colors.grey, size: 16.sp),
Text(
'创建时间: ${enterpriseListItem.enterprise.creationTime.toDateTimeString()}',
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
),
],
),
],
);
}
/// +
Widget _buildBottomActionRow() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// [ 4]
Padding(
padding: EdgeInsets.only(left: 16.w),
child: Row(
children: [
_buildTag(
text: '已上传 ${enterpriseListItem.uploadedProblems}',
textColor: Colors.blue.shade700,
backgroundColor: Colors.blue.shade50,
),
SizedBox(width: 8.w),
_buildTag(
text: '未上传 ${enterpriseListItem.pendingProblems}',
textColor: Colors.red.shade600,
backgroundColor: Colors.red.shade50,
),
],
),
),
const Spacer(),
// [ 5] actions Padding
actions!,
],
);
}
///
Widget _buildTag({
required String text,
required Color textColor,
required Color backgroundColor,
}) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
decoration: BoxDecoration(
color: backgroundColor,
// borderRadius: BorderRadius.circular(4.r),
// border: Border.all(color: textColor.withAlpha(128), width: 1.w),
),
child: Text(
text,
style: TextStyle(
color: textColor,
fontSize: 10.sp,
fontWeight: FontWeight.w500,
),
),
);
}
}

2
lib/app/features/home/views/home_page.dart

@ -2,7 +2,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/core/pages/widgets/custom_app_bar.dart';
import 'package:problem_check_system/app/features/home/controllers/home_controller.dart';
class HomePage extends GetView<HomeController> {

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

@ -4,39 +4,39 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/core/services/network_status_service.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_list_page.dart';
import 'package:problem_check_system/app/features/home/views/home_page.dart';
import 'package:problem_check_system/app/features/my/views/my_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_page.dart';
class NavigationController extends GetxController {
static const int navigationKeyId = 1;
var selectedIndex = 0.obs;
final GlobalKey<CurvedNavigationBarState> bottomNavigationKey = GlobalKey();
final List<String> pageRoutes = [
AppRoutes.home,
AppRoutes.enterpriseList,
AppRoutes.problem,
AppRoutes.my,
];
/// floatingButton
final double _fabSize = 56.0;
final double _edgePaddingX = 27.0.w;
final double _edgePaddingY = 111.0.h;
final fabUploadPosition = Offset(337.0, 703.7).obs;
final fabUploadPosition = Offset(0, 0).obs;
final NetworkStatusService networkStatusService;
NavigationController({required this.networkStatusService});
/// get
RxBool get isOnline => networkStatusService.isOnline;
// //
// final List<Widget> pages = [
// const HomePage(),
// const EnterpriseListPage(),
// const ProblemPage(),
// const MyPage(),
// ];
//
final List<Widget> pages = const [
HomePage(),
EnterpriseListPage(),
ProblemPage(),
MyPage(),
];
final navigationItems = const <Widget>[
Icon(Icons.home, color: Colors.white, size: 30),
Icon(Icons.business, color: Colors.white, size: 30),
Icon(Icons.list, color: Colors.white, size: 30),
Icon(Icons.person, color: Colors.white, size: 30),
];
// get currentPage => pages[selectedIndex.value];
get currentPage => pages[selectedIndex.value];
@override
void onInit() {
@ -45,22 +45,6 @@ class NavigationController extends GetxController {
snapToEdge();
}
// void changePageIndex(int index) {
// selectedIndex.value = index;
// }
// void changePageIndex(int index) {
// if (selectedIndex.value == index) return; //
// selectedIndex.value = index;
// // offAllNamed nested Navigator
// //
// Get.offAllNamed(
// pageRoutes[index],
// id: navigationKeyId, // Navigator
// );
// }
void changePageIndex(int index) {
if (selectedIndex.value == index) return;
selectedIndex.value = index;

80
lib/app/features/navigation/presentation/pages/navigation_page.dart

@ -2,25 +2,8 @@ import 'package:curved_navigation_bar/curved_navigation_bar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/routes/app_pages.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/features/navigation/presentation/controllers/navigation_controller.dart';
/// -----------------------------------------------------------------------------
/// [] NavigationPage
/// -----------------------------------------------------------------------------
///
///
///
/// **:**
/// - **Stack布局**: 使 `Stack` `Scaffold` () `FloatingActionButton`
/// () 使
/// - **IndexedStack**:
/// Tab时的页面重建
///
/// - **GetX驱动**: `NavigationController` `Obx`
/// `selectedIndex` `IndexedStack`
///
class NavigationPage extends GetView<NavigationController> {
const NavigationPage({super.key});
@ -28,41 +11,20 @@ class NavigationPage extends GetView<NavigationController> {
Widget build(BuildContext context) {
return Stack(
children: [
// 1. Scaffold Stack
Scaffold(
// 使 controller GlobalKey CurvedNavigationBar
//
bottomNavigationBar: CurvedNavigationBar(
key: controller.bottomNavigationKey,
backgroundColor: Colors.transparent,
color: Colors.blueAccent,
items: const <Widget>[
Icon(Icons.home, color: Colors.white, size: 30),
Icon(Icons.business, color: Colors.white, size: 30),
Icon(Icons.list, color: Colors.white, size: 30),
Icon(Icons.person, color: Colors.white, size: 30),
],
onTap: (index) {
controller.changePageIndex(index);
},
items: controller.navigationItems,
onTap: controller.changePageIndex,
),
// [] 使 Obx IndexedStack
body: Obx(
() => IndexedStack(
// IndexedStack controller selectedIndex
index: controller.selectedIndex.value,
// []
children: [
_getPageForRoute(AppRoutes.home),
_getPageForRoute(AppRoutes.enterpriseList),
_getPageForRoute(AppRoutes.problem),
_getPageForRoute(AppRoutes.my),
],
children: controller.pages,
),
),
),
// 2. Stack 使 Positioned
Obx(() {
final isOnline = controller.isOnline.value;
return Positioned(
@ -95,40 +57,4 @@ class NavigationPage extends GetView<NavigationController> {
],
);
}
/// ---------------------------------------------------------------------------
/// [] _getPageForRoute
/// ---------------------------------------------------------------------------
///
/// `AppPages.routes` `GetPage`
/// `binding` `Widget`
///
/// GetX `IndexedStack`
///
/// @param route (e.g., AppRoutes.home)
/// @return Widget Widget
///
Widget _getPageForRoute(String route) {
// 使 .firstWhereOrNull
final GetPage? page = AppPages.routes.firstWhereOrNull(
(p) => p.name == route,
);
//
if (page != null) {
// []
// GetX ( fenix: true) Binding
page.binding?.dependencies();
// page() Widget
return page.page();
}
// AppPages
return Center(
child: Text(
'错误: 路由 "$route" 未在 AppPages 中定义',
textAlign: TextAlign.center,
),
);
}
}

2
lib/app/features/problem/presentation/views/problem_form_page.dart

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:problem_check_system/app/core/widgets/custom_app_bar.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_form_controller.dart';
class ProblemFormPage extends GetView<ProblemFormController> {

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

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/widgets/custom_app_bar.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';

2
lib/app/features/problem/presentation/views/problem_upload_page.dart

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/widgets/upload_app_bar.dart';
import 'package:problem_check_system/app/core/pages/widgets/upload_app_bar.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_list_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/widgets/problem_card.dart';

Loading…
Cancel
Save