Browse Source

refactor : 进行了大量重构,使代码更健壮

dev
徐振升 2 weeks ago
parent
commit
29846dca23
  1. 22
      lib/app/core/routes/app_pages.dart
  2. 44
      lib/app/core/widgets/app_bar_add.dart
  3. 66
      lib/app/core/widgets/custom_app_bar.dart
  4. 38
      lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart
  5. 7
      lib/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart
  6. 66
      lib/app/features/enterprise/presentation/controllers/base_enterprise_list_controller.dart
  7. 122
      lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart
  8. 116
      lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
  9. 6
      lib/app/features/enterprise/presentation/pages/enterprise_form_page.dart
  10. 186
      lib/app/features/enterprise/presentation/pages/enterprise_list_page.dart
  11. 10
      lib/app/features/enterprise/presentation/pages/enterprise_upload_page.dart
  12. 25
      lib/app/features/enterprise/presentation/pages/widgets/enterprise_card.dart
  13. 280
      lib/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.dart
  14. 6
      lib/app/features/home/views/home_page.dart
  15. 13
      lib/app/features/my/bindings/profile_binding.dart
  16. 48
      lib/app/features/navigation/presentation/bindings/navigation_binding.dart
  17. 48
      lib/app/features/navigation/presentation/controllers/navigation_controller.dart
  18. 80
      lib/app/features/navigation/presentation/pages/navigation_page.dart
  19. 20
      lib/app/features/problem/presentation/bindings/problem_binding.dart
  20. 30
      lib/app/features/problem/presentation/views/problem_form_page.dart
  21. 6
      lib/app/features/problem/presentation/views/problem_page.dart

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

@ -1,9 +1,13 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/bindings/enterprise_form_binding.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/bindings/enterprise_info_binding.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_form_page.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_list_page.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_upload_page.dart';
import 'package:problem_check_system/app/features/my/bindings/profile_binding.dart';
import 'package:problem_check_system/app/features/my/views/my_page.dart';
import 'package:problem_check_system/app/features/navigation/presentation/bindings/navigation_binding.dart';
import 'package:problem_check_system/app/features/navigation/presentation/pages/navigation_page.dart';
import 'package:problem_check_system/app/features/home/bindings/home_binding.dart';
@ -12,8 +16,10 @@ import 'package:problem_check_system/app/features/auth/bindings/login_binding.da
import 'package:problem_check_system/app/features/auth/views/login_page.dart';
import 'package:problem_check_system/app/features/my/bindings/change_password_binding.dart';
import 'package:problem_check_system/app/features/my/views/change_password.dart';
import 'package:problem_check_system/app/features/problem/presentation/bindings/problem_binding.dart';
import 'package:problem_check_system/app/features/problem/presentation/bindings/problem_form_binding.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_form_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_page.dart';
import 'package:problem_check_system/app/features/problem/presentation/views/problem_upload_page.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_info_page.dart';
@ -43,15 +49,31 @@ abstract class AppPages {
page: () => const ChangePasswordPage(),
binding: ChangePasswordBinding(),
),
GetPage(
name: AppRoutes.problem,
page: () => const ProblemPage(),
binding: ProblemBinding(),
),
GetPage(
name: AppRoutes.my,
page: () => const MyPage(),
binding: ProfileBinding(),
),
GetPage(
name: AppRoutes.problemUpload,
page: () => const ProblemUploadPage(),
binding: ProblemBinding(),
),
GetPage(
name: AppRoutes.problemForm,
page: () => const ProblemFormPage(),
binding: ProblemFormBinding(),
),
GetPage(
name: AppRoutes.enterpriseList,
page: () => const EnterpriseListPage(),
binding: EnterpriseListBinding(),
),
GetPage(
name: AppRoutes.enterpriseInfo,
page: () => const EnterpriseInfoPage(),

44
lib/app/core/widgets/app_bar_add.dart

@ -1,44 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class AppBarAdd extends StatelessWidget implements PreferredSizeWidget {
const AppBarAdd({super.key, required this.titleName, this.onAddPressed});
final String titleName;
final VoidCallback? onAddPressed;
@override
Widget build(BuildContext context) {
return AppBar(
title: Text(
titleName,
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'MyFont',
fontSize: 18.sp,
color: Colors.white,
),
),
backgroundColor: Colors.transparent,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF418CFC), Color(0xFF3DBFFC)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
),
elevation: 0,
centerTitle: true,
actions: [
IconButton(
icon: Icon(Icons.add, color: Colors.white), // 使 .sp
onPressed: onAddPressed,
),
],
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

66
lib/app/core/widgets/custom_app_bar.dart

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
const CustomAppBar({
super.key,
required this.titleName,
this.leadingVisible = false,
this.actionsVisible = false,
this.onAddPressed,
});
final String titleName;
final bool leadingVisible;
final bool actionsVisible;
final VoidCallback? onAddPressed;
@override
Widget build(BuildContext context) {
return AppBar(
title: Text(
titleName,
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'MyFont',
fontSize: 17.5.sp,
color: Colors.white,
),
),
automaticallyImplyLeading: leadingVisible,
leading: leadingVisible
? IconButton(
icon: Icon(
Icons.arrow_back_ios_new_rounded,
size: 17.5.sp,
color: Colors.white,
),
onPressed: () => Get.back(),
)
: null,
backgroundColor: Colors.transparent,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF418CFC), Color(0xFF3DBFFC)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
),
elevation: 0,
centerTitle: true,
actions: actionsVisible
? [
IconButton(
icon: Icon(Icons.add, color: Colors.white), // 使 .sp
onPressed: onAddPressed,
),
]
: null,
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

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

@ -0,0 +1,38 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/services/database_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';
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 {
@override
void dependencies() {
Get.put<EnterpriseLocalDataSource>(
EnterpriseLocalDataSourceImpl(
databaseService: Get.find<DatabaseService>(),
),
);
Get.put<EnterpriseRemoteDataSource>(EnterpriseRemoteDataSourceImpl());
Get.put<EnterpriseRepository>(
EnterpriseRepositoryImpl(
localDataSource: Get.find<EnterpriseLocalDataSource>(),
remoteDataSource: Get.find<EnterpriseRemoteDataSource>(),
),
);
Get.put<GetEnterpriseListUsecase>(
GetEnterpriseListUsecase(repository: Get.find<EnterpriseRepository>()),
);
Get.lazyPut<EnterpriseListController>(
() => EnterpriseListController(
getEnterpriseListUsecase: Get.find<GetEnterpriseListUsecase>(),
),
);
Get.lazyPut<BaseEnterpriseListController>(
() => Get.find<EnterpriseListController>(),
);
}
}

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

@ -1,8 +1,13 @@
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() {
// TODO: implement dependencies
Get.lazyPut<EnterpriseUploadController>(() => EnterpriseUploadController());
Get.lazyPut<BaseEnterpriseListController>(
() => Get.find<EnterpriseUploadController>(),
);
}
}

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

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

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

@ -1,4 +1,4 @@
// lib/app/modules/enterprise_list/enterprise_list_controller.dart
// lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@ -8,32 +8,81 @@ 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';
class EnterpriseListController extends GetxController {
// --- ---
var isLoading = false.obs;
var enterpriseList = <EnterpriseListItem>[].obs;
// --- ---
final nameController = TextEditingController();
final Rx<CompanyType?> selectedType = Rx<CompanyType?>(null);
final Rx<DateTime?> startDate = Rx<DateTime?>(null);
final Rx<DateTime?> endDate = Rx<DateTime?>(null);
/// -----------------------------------------------------------------------------
/// []
/// -----------------------------------------------------------------------------
///
/// `BaseEnterpriseListController`
///
/// **:**
/// -
/// -
/// -
/// -
///
class EnterpriseListController extends BaseEnterpriseListController {
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
void onInit() {
super.onInit();
fetchEnterprises(); //
}
// --- ---
@override
void search() {
fetchEnterprises();
}
@override
void clearFilters() {
nameController.clear();
selectedType.value = null;
startDate.value = null;
endDate.value = null;
fetchEnterprises();
}
@override
void onItemTap(EnterpriseListItem item) {
//
navigateToEnterpriseInfoPage(item.enterprise);
}
@override
void onSelectionChanged(Enterprise enterprise) {
//
//
}
// --- EnterpriseListController ---
///
Future<void> fetchEnterprises() async {
try {
isLoading.value = true;
try {
final result = await getEnterpriseListUsecase.call(
name: nameController.text,
type: selectedType.value?.displayText,
@ -48,38 +97,14 @@ class EnterpriseListController extends GetxController {
}
}
/// UI
void search() {
fetchEnterprises();
}
void refreshList() {
fetchEnterprises();
}
///
void clearFilters() {
nameController.clear();
selectedType.value = null;
startDate.value = null;
endDate.value = null;
fetchEnterprises();
}
@override
void onClose() {
nameController.dispose();
super.onClose();
}
///
Future<void> navigateToEditForm(Enterprise enterprise) async {
final result = await Get.toNamed(
AppRoutes.enterpriseForm,
arguments: {'data': enterprise, 'mode': FormMode.edit}, //
arguments: {'data': enterprise, 'mode': FormMode.edit},
);
if (result == true) {
refreshList(); //
search(); // true
}
}
@ -90,26 +115,21 @@ class EnterpriseListController extends GetxController {
arguments: {'mode': FormMode.add},
);
if (result == true) {
refreshList(); //
}
search();
}
///
Future<void> navigateToUploadPage() async {
// final result = await Get.toNamed(AppRoutes.test);
// if (result == true) {
// refreshList(); //
// }
}
///
Future<void> navigateToEnterpriseInfoPage(Enterprise enterprise) async {
final result = await Get.toNamed(
await Get.toNamed(
AppRoutes.enterpriseInfo,
arguments: {'data': enterprise, 'mode': FormMode.view},
);
if (result == true) {
refreshList(); //
}
@override
void onClose() {
nameController.dispose();
super.onClose();
}
}

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

@ -0,0 +1,116 @@
// 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';
/// -----------------------------------------------------------------------------
/// []
/// -----------------------------------------------------------------------------
///
/// `BaseEnterpriseListController`
///
/// **:**
/// - ****
/// - (`selectedEnterprises`)
/// -
/// -
///
class EnterpriseUploadController extends BaseEnterpriseListController {
// 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)) {
selectedEnterprises.remove(enterprise);
} else {
selectedEnterprises.add(enterprise);
}
}
// --- 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();
// enterpriseList.assignAll(mockData);
} catch (e) {
Get.snackbar('错误', '加载待上传列表失败: $e');
} finally {
isLoading.value = false;
}
}
///
void confirmUpload() {
if (selectedEnterprises.isEmpty) {
Get.snackbar('提示', '请至少选择一个企业进行上传');
return;
}
// ...
print('正在上传 ${selectedEnterprises.length} 个企业...');
// true
Get.back(result: true);
}
}

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

@ -1,6 +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/features/enterprise/presentation/controllers/enterprise_form_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_form_view.dart';
@ -13,7 +14,10 @@ class EnterpriseFormPage extends GetView<EnterpriseFormController> {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: _buildAppBar(),
appBar: CustomAppBar(
titleName: controller.pageTitle.value,
leadingVisible: true,
),
body: SafeArea(
child: Column(
children: [

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

@ -1,10 +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/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/core/widgets/app_bar_add.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_card.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 '../controllers/enterprise_list_controller.dart';
class EnterpriseListPage extends GetView<EnterpriseListController> {
@ -13,185 +10,12 @@ class EnterpriseListPage extends GetView<EnterpriseListController> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarAdd(
appBar: CustomAppBar(
titleName: '企业列表',
actionsVisible: true,
onAddPressed: () => controller.navigateToAddForm(),
),
body: Stack(
children: [
Column(
children: [
_buildFilterSection(),
const Divider(height: 1, thickness: .1),
Expanded(child: _buildEnterpriseList()),
],
),
],
),
);
}
/// []
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),
),
),
),
],
),
],
),
),
],
);
}
/// []
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(2025),
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,
),
child: Obx(
() => Text(
date.value == null ? '请选择日期' : date.value!.toDateString(),
style: TextStyle(
color: date.value == null ? Colors.grey[600] : Colors.black87,
),
),
),
),
);
}
Widget _buildEnterpriseList() {
return Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return ListView.builder(
padding: EdgeInsets.symmetric(
horizontal: 16.w, // 使 .w
vertical: 8.h, // 使 .h
),
itemCount: controller.enterpriseList.length,
itemBuilder: (context, index) {
final item = controller.enterpriseList[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h), // 使 .h
// child: _EnterpriseCard(enterprise: enterprise),
child: EnterpriseCard(
enterpriseListItem: item,
onEdit: () {
controller.navigateToEditForm(item.enterprise);
},
onViewProblems: () {
controller.navigateToEnterpriseInfoPage(item.enterprise);
},
),
);
},
body: EnterpriseListView(controller: controller),
);
});
}
}

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

@ -1,7 +1,10 @@
import 'package:flutter/material.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/features/enterprise/presentation/controllers/enterprise_upload_controller.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/enterprise_list_view.dart';
class EnterpriseUploadPage extends StatelessWidget {
class EnterpriseUploadPage extends GetView<EnterpriseUploadController> {
const EnterpriseUploadPage({super.key});
@override
@ -15,6 +18,11 @@ class EnterpriseUploadPage extends StatelessWidget {
print("object");
},
),
body: EnterpriseListView(
controller: controller,
filterSectionVisible: false,
itemMode: EnterpriseListPageMode.select,
),
);
}
}

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

@ -3,24 +3,33 @@ 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;
const EnterpriseCard({
super.key,
required this.enterpriseListItem,
required this.isSelected,
required this.onEdit,
required this.onViewProblems,
required this.mode,
this.onTap,
this.onSelectionChanged,
});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
return InkWell(
onTap: onTap,
child: Container(
// 1 Container padding
// padding: EdgeInsets.only(...), // <---
@ -57,8 +66,7 @@ class EnterpriseCard extends StatelessWidget {
),
),
// --- ---
// 4 Positioned
if (mode == EnterpriseListPageMode.normal)
Positioned(
bottom: 0, //
right: 0, //
@ -237,7 +245,7 @@ class EnterpriseCard extends StatelessWidget {
backgroundColor: Colors.red.shade50,
),
const Spacer(),
if (mode == EnterpriseListPageMode.select)
Theme(
data: Theme.of(context).copyWith(
checkboxTheme: CheckboxThemeData(
@ -248,9 +256,10 @@ class EnterpriseCard extends StatelessWidget {
),
),
child: Checkbox(
value: true,
onChanged: (bool? value) {},
// ...
value: isSelected,
onChanged: onSelectionChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
),
],

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

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

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

@ -2,6 +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/features/home/controllers/home_controller.dart';
class HomePage extends GetView<HomeController> {
@ -9,6 +10,9 @@ class HomePage extends GetView<HomeController> {
@override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text("首页")));
return Scaffold(
appBar: CustomAppBar(titleName: "首页"),
body: Center(child: Text("首页")),
);
}
}

13
lib/app/features/my/bindings/profile_binding.dart

@ -0,0 +1,13 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/my/controllers/my_controller.dart';
class ProfileBinding implements Bindings {
@override
void dependencies() {
///
Get.lazyPut<MyController>(
() => MyController(authRepository: Get.find<AuthRepository>()),
);
}
}

48
lib/app/features/navigation/presentation/bindings/navigation_binding.dart

@ -1,18 +1,6 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/core/services/database_service.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';
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/problem/data/repositories/problem_repository.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart';
import 'package:problem_check_system/app/features/navigation/presentation/controllers/navigation_controller.dart';
import 'package:problem_check_system/app/features/my/controllers/my_controller.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
class NavigationBinding extends Bindings {
@override
@ -23,41 +11,5 @@ class NavigationBinding extends Bindings {
networkStatusService: Get.find<NetworkStatusService>(),
),
);
Get.put<EnterpriseLocalDataSource>(
EnterpriseLocalDataSourceImpl(
databaseService: Get.find<DatabaseService>(),
),
);
Get.put<EnterpriseRemoteDataSource>(EnterpriseRemoteDataSourceImpl());
Get.put<EnterpriseRepository>(
EnterpriseRepositoryImpl(
localDataSource: Get.find<EnterpriseLocalDataSource>(),
remoteDataSource: Get.find<EnterpriseRemoteDataSource>(),
),
);
Get.put<GetEnterpriseListUsecase>(
GetEnterpriseListUsecase(repository: Get.find<EnterpriseRepository>()),
);
Get.lazyPut<EnterpriseListController>(
() => EnterpriseListController(
getEnterpriseListUsecase: Get.find<GetEnterpriseListUsecase>(),
),
);
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
///
Get.lazyPut<ProblemController>(
() => ProblemController(
problemRepository: Get.find<ProblemRepository>(),
problemStateManager: Get.find<ProblemStateManager>(),
),
fenix: true,
);
///
Get.lazyPut<MyController>(
() => MyController(authRepository: Get.find<AuthRepository>()),
);
}
}

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

@ -1,16 +1,23 @@
import 'package:curved_navigation_bar/curved_navigation_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/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;
@ -21,15 +28,15 @@ class NavigationController extends GetxController {
/// get
RxBool get isOnline => networkStatusService.isOnline;
//
final List<Widget> pages = [
const HomePage(),
const EnterpriseListPage(),
const ProblemPage(),
const MyPage(),
];
// //
// final List<Widget> pages = [
// const HomePage(),
// const EnterpriseListPage(),
// const ProblemPage(),
// const MyPage(),
// ];
get currentPage => pages[selectedIndex.value];
// get currentPage => pages[selectedIndex.value];
@override
void onInit() {
@ -38,7 +45,24 @@ 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

@ -1,22 +1,39 @@
// lib/app/features/navigation/presentation/pages/navigation_page.dart
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});
@override
Widget build(BuildContext context) {
// 1. Stack
return Stack(
children: [
// 2. Scaffold Stack
// 1. Scaffold Stack
Scaffold(
// 3. Scaffold floatingActionButton
// 使 controller GlobalKey CurvedNavigationBar
//
bottomNavigationBar: CurvedNavigationBar(
key: controller.bottomNavigationKey,
backgroundColor: Colors.transparent,
color: Colors.blueAccent,
items: const <Widget>[
@ -29,10 +46,23 @@ class NavigationPage extends GetView<NavigationController> {
controller.changePageIndex(index);
},
),
body: Obx(() => controller.currentPage),
// [] 使 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),
],
),
),
),
// 4. Stack 使 Positioned
// 2. Stack 使 Positioned
Obx(() {
final isOnline = controller.isOnline.value;
return Positioned(
@ -65,4 +95,40 @@ 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,
),
);
}
}

20
lib/app/features/problem/presentation/bindings/problem_binding.dart

@ -0,0 +1,20 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_controller.dart';
class ProblemBinding extends Bindings {
@override
void dependencies() {
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
///
Get.lazyPut<ProblemController>(
() => ProblemController(
problemRepository: Get.find<ProblemRepository>(),
problemStateManager: Get.find<ProblemStateManager>(),
),
fenix: true,
);
}
}

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

@ -3,6 +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/features/problem/presentation/controllers/problem_form_controller.dart';
class ProblemFormPage extends GetView<ProblemFormController> {
@ -12,34 +13,11 @@ class ProblemFormPage extends GetView<ProblemFormController> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF418CFC), Color(0xFF3DBFFC)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
),
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios_new_rounded,
color: Colors.white,
),
onPressed: () {
Navigator.pop(context);
},
),
//
title: Text(
controller.isReadOnly
appBar: CustomAppBar(
titleName: controller.isReadOnly
? '问题详情'
: (controller.problem == null ? '新增问题' : '编辑问题'),
style: const TextStyle(color: Colors.white),
),
centerTitle: true,
backgroundColor: Colors.transparent,
leadingVisible: true,
),
body: Column(
children: [

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

@ -1,7 +1,6 @@
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/app_bar_add.dart';
import 'package:problem_check_system/app/core/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';
@ -13,8 +12,9 @@ class ProblemPage extends GetView<ProblemController> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarAdd(
appBar: CustomAppBar(
titleName: '问题列表',
actionsVisible: true,
onAddPressed: () => controller.toProblemFormPageAndRefresh(),
),
body: Column(

Loading…
Cancel
Save