Browse Source

feat : 企业信息上传页

dev
徐振升 2 weeks ago
parent
commit
31aea56732
  1. 6
      lib/app/core/pages/widgets/upload_app_bar.dart
  2. 15
      lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart
  3. 10
      lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart
  4. 4
      lib/app/features/enterprise/domain/repositories/enterprise_repository.dart
  5. 2
      lib/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart
  6. 10
      lib/app/features/enterprise/domain/usecases/get_unsynced_enterprises_usecase.dart
  7. 29
      lib/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart
  8. 51
      lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
  9. 91
      lib/app/features/enterprise/presentation/pages/enterprise_upload_page.dart
  10. 2
      lib/app/features/problem/presentation/views/problem_upload_page.dart
  11. 6
      lib/main.dart

6
lib/app/core/pages/widgets/upload_app_bar.dart

@ -6,13 +6,13 @@ import 'package:get/get.dart';
class UploadAppBar extends StatelessWidget implements PreferredSizeWidget {
const UploadAppBar({
super.key,
required this.selectedAll,
required this.allSelected,
required this.selectedCount,
required this.buttonVisible,
this.onButtonPressed,
});
final int selectedCount;
final bool selectedAll;
final bool allSelected;
final bool buttonVisible;
final VoidCallback? onButtonPressed;
@ -50,7 +50,7 @@ class UploadAppBar extends StatelessWidget implements PreferredSizeWidget {
TextButton(
onPressed: buttonVisible ? onButtonPressed : null,
child: Text(
selectedAll ? "全选" : "取消全选",
allSelected ? "取消全选" : "全选",
style: TextStyle(
color: buttonVisible ? Colors.white : Colors.grey[300],
fontSize: 15.sp,

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

@ -13,6 +13,7 @@ abstract class EnterpriseLocalDataSource {
String? type,
DateTime? startDate,
DateTime? endDate,
bool? isUploaded,
});
Future<List<EnterpriseModel>> getUnsyncedEnterprises();
@ -59,11 +60,12 @@ class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource {
String? type,
DateTime? startDate,
DateTime? endDate,
bool? isUploaded,
}) async {
final db = await _databaseService.database;
// pendingCreate 0
final int pendingCreateIndex = SyncStatus.pendingCreate.index;
final int syncedIndex = SyncStatus.synced.index;
// WHERE
final List<String> whereClauses = [];
@ -101,7 +103,12 @@ class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource {
whereClauses.add('e.creationTime <= ?');
whereArgs.add(endOfDay.millisecondsSinceEpoch);
}
// --- ---
// 5.
if (isUploaded != null) {
final operator = isUploaded ? '=' : '!=';
whereClauses.add('e.syncStatus $operator ?');
whereArgs.add(syncedIndex);
}
// WHERE
final String whereString = whereClauses.isNotEmpty
@ -114,8 +121,8 @@ class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource {
SELECT
e.*,
COUNT(p.id) AS totalProblems,
COUNT(CASE WHEN p.syncStatus != $pendingCreateIndex THEN 1 ELSE NULL END) AS uploadedProblems,
COUNT(CASE WHEN p.syncStatus == $pendingCreateIndex THEN 1 ELSE NULL END) AS pendingProblems
COUNT(CASE WHEN p.syncStatus == $syncedIndex THEN 1 ELSE NULL END) AS uploadedProblems,
COUNT(CASE WHEN p.syncStatus != $syncedIndex THEN 1 ELSE NULL END) AS pendingProblems
FROM
$_tableName e
LEFT JOIN

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

@ -1,3 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.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/model/enterprise_model.dart';
@ -53,6 +54,7 @@ class EnterpriseRepositoryImpl implements EnterpriseRepository {
String? type,
DateTime? startDate,
DateTime? endDate,
bool? isUploaded,
}) async {
// DataSource
final models = await localDataSource.getEnterpriseListItems(
@ -60,6 +62,7 @@ class EnterpriseRepositoryImpl implements EnterpriseRepository {
type: type,
startDate: startDate,
endDate: endDate,
isUploaded: isUploaded,
);
// EnterpriseListItemModel EnterpriseListItem
return models;
@ -72,11 +75,4 @@ class EnterpriseRepositoryImpl implements EnterpriseRepository {
//
await localDataSource.updateEnterprise(enterpriseModel);
}
@override
Future<List<Enterprise>> getUnsyncedEnterprises() async {
final List<EnterpriseModel> enterpriseModels = await localDataSource
.getUnsyncedEnterprises();
return enterpriseModels.map((model) => model.toEntity()).toList();
}
}

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

@ -16,8 +16,6 @@ abstract class EnterpriseRepository implements SyncableRepository<Enterprise> {
String? type,
DateTime? startDate,
DateTime? endDate,
bool? isUploaded,
});
///
Future<List<Enterprise>> getUnsyncedEnterprises();
}

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

@ -11,12 +11,14 @@ class GetEnterpriseListUsecase {
String? type,
DateTime? startDate,
DateTime? endDate,
bool? isUploaded,
}) async {
return await repository.getEnterpriseListItems(
name: name,
type: type,
startDate: startDate,
endDate: endDate,
isUploaded: isUploaded,
);
}
}

10
lib/app/features/enterprise/domain/usecases/get_unsynced_enterprises_usecase.dart

@ -1,10 +0,0 @@
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
class GetUnsyncedEnterprisesUsecase {
final EnterpriseRepository enterpriseRepository;
const GetUnsyncedEnterprisesUsecase({required this.enterpriseRepository});
Future<List<Enterprise>> call() async {
return await enterpriseRepository.getUnsyncedEnterprises();
}
}

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

@ -1,9 +1,36 @@
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/enterprise_upload_controller.dart';
class EnterpriseUploadBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<EnterpriseUploadController>(() => EnterpriseUploadController());
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.lazyPut(
() => GetEnterpriseListUsecase(
repository: Get.find<EnterpriseRepository>(),
),
);
Get.lazyPut<EnterpriseUploadController>(
() => EnterpriseUploadController(
getEnterpriseListUsecase: Get.find<GetEnterpriseListUsecase>(),
),
);
}
}

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

@ -1,31 +1,21 @@
// lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
import 'package:get/get.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';
// Usecase
// import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_pending_uploads_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart';
/// -----------------------------------------------------------------------------
/// []
/// -----------------------------------------------------------------------------
///
/// `BaseEnterpriseListController`
///
/// **:**
/// - ****
/// - (`selectedEnterprises`)
/// -
/// -
///
class EnterpriseUploadController extends GetxController {
// final GetPendingUploadsUsecase getPendingUploadsUsecase;
// EnterpriseUploadController({required this.getPendingUploadsUsecase});
final GetEnterpriseListUsecase getEnterpriseListUsecase;
EnterpriseUploadController({required this.getEnterpriseListUsecase});
// --- ---
final enterpriseList = <EnterpriseListItem>[].obs;
final isLoading = false.obs;
final selectedEnterprises = <Enterprise>{}.obs; // 使 Set
final enterprises = <EnterpriseListItem>[].obs;
final selectedEnterprises = <EnterpriseListItem>{}.obs;
bool get allSelected =>
enterprises.isNotEmpty &&
enterprises.length == selectedEnterprises.length;
@override
void onInit() {
@ -33,13 +23,7 @@ class EnterpriseUploadController extends GetxController {
fetchPendingUploads();
}
void onItemTap(EnterpriseListItem item) {
//
onSelectionChanged(item.enterprise);
}
void onSelectionChanged(Enterprise enterprise) {
//
void onSelectionChanged(EnterpriseListItem enterprise) {
if (selectedEnterprises.contains(enterprise)) {
selectedEnterprises.remove(enterprise);
} else {
@ -47,14 +31,21 @@ class EnterpriseUploadController extends GetxController {
}
}
void toggleSelectAll() {
if (allSelected) {
selectedEnterprises.clear();
} else {
//
selectedEnterprises.addAll(enterprises);
}
}
///
Future<void> fetchPendingUploads() async {
isLoading.value = true;
try {
// --- 使 ---
await Future.delayed(const Duration(seconds: 1));
// final mockData = createMockEnterpriseListItems();
// enterpriseList.assignAll(mockData);
final data = await getEnterpriseListUsecase();
enterprises.assignAll(data);
} catch (e) {
Get.snackbar('错误', '加载待上传列表失败: $e');
} finally {

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

@ -12,30 +12,39 @@ class EnterpriseUploadPage extends GetView<EnterpriseUploadController> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: UploadAppBar(
selectedCount: 10,
selectedAll: true,
buttonVisible: false,
onButtonPressed: () {},
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: Obx(
() => UploadAppBar(
selectedCount: controller.selectedEnterprises.length,
allSelected: controller.allSelected,
buttonVisible: controller.enterprises.isNotEmpty,
onButtonPressed: () => controller.toggleSelectAll(),
),
),
),
body: _buildEnterpriseList(),
bottomSheet: Container(
bottomNavigationBar: 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),
child: Obx(
() => ElevatedButton(
onPressed: controller.selectedEnterprises.isNotEmpty
? controller.confirmUpload
: null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
minimumSize: Size(double.infinity, 48.h),
),
minimumSize: Size(double.infinity, 48.h),
child: Text('点击上传 (${controller.selectedEnterprises.length})'),
),
child: Text('点击上传 (1)'),
),
),
);
@ -45,11 +54,11 @@ class EnterpriseUploadPage extends GetView<EnterpriseUploadController> {
// 使 Obx controller Rx
return Obx(() {
//
if (controller.isLoading.value && controller.enterpriseList.isEmpty) {
if (controller.isLoading.value && controller.enterprises.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
//
if (controller.enterpriseList.isEmpty) {
if (controller.enterprises.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -74,34 +83,36 @@ class EnterpriseUploadPage extends GetView<EnterpriseUploadController> {
onRefresh: () async => controller.fetchPendingUploads(),
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
itemCount: controller.enterpriseList.length,
itemCount: controller.enterprises.length,
itemBuilder: (context, index) {
final item = controller.enterpriseList[index];
final enterprise = item.enterprise;
final item = controller.enterprises[index];
// : 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),
return Obx(() {
final isSelected = controller.selectedEnterprises.contains(item);
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: UnifiedEnterpriseCard(
enterpriseListItem: item,
isSelected: isSelected,
// itemMode mode
// --- [] controller ---
onTap: () => controller.onSelectionChanged(item),
actions: Padding(
// Checkbox
padding: EdgeInsets.only(right: 8.w),
child: Checkbox(
value: isSelected,
activeColor: Theme.of(context).primaryColor,
onChanged: (value) {
controller.onSelectionChanged(item);
},
),
),
),
),
);
);
});
},
),
);

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

@ -14,7 +14,7 @@ class ProblemUploadPage extends GetView<ProblemController> {
return Scaffold(
appBar: UploadAppBar(
selectedCount: controller.selectedCount,
selectedAll: controller.allSelected.value,
allSelected: controller.allSelected.value,
buttonVisible: controller.unUploadedProblems.isNotEmpty,
onButtonPressed: controller.selectAll,
),

6
lib/main.dart

@ -55,10 +55,8 @@ class MainApp extends StatelessWidget {
useMaterial3: true,
// 使 colorSchemeSeed M3
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF3B82F6),
),
colorScheme: ColorScheme.fromSeed(seedColor: Colors.lightBlue),
primaryColor: Colors.lightBlue,
// () NavigationBar
navigationBarTheme: NavigationBarThemeData(
// 1.

Loading…
Cancel
Save