diff --git a/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt b/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt index 7e05329..3555471 100644 --- a/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt @@ -1,5 +1,17 @@ package com.example.pj_supermarket_cloud import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugins.facediscern.flutter.FlutterFaceDiscernPlugin -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + // 人脸识别插件 + val flutterFaceDiscernPlugin = FlutterFaceDiscernPlugin() + flutterEngine.plugins.add(flutterFaceDiscernPlugin) + // tts插件 + + // 箭头全局键盘插件 + } +} diff --git a/lib/app.dart b/lib/app.dart index 30db278..b70a1bd 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -21,7 +21,7 @@ class MainApp extends StatelessWidget { final fTheme = appTheme.theme.value; final sizeScalar = appTheme.sizeScalar.value; final themeData = FThemeData.inherit( - colorScheme: fTheme.colorScheme.copyWith(), + colorScheme: fTheme.colorScheme, typography: fTheme.typography.scale(sizeScalar: sizeScalar), style: fTheme.style.copyWith( borderRadius: BorderRadius.all( diff --git a/lib/common/utils/app/app_file_tool.dart b/lib/common/utils/app/app_file_tool.dart index 8579232..bbd37ea 100644 --- a/lib/common/utils/app/app_file_tool.dart +++ b/lib/common/utils/app/app_file_tool.dart @@ -164,5 +164,16 @@ abstract class AppFileTool { } } - // 备份日志 + // 获取文件后缀 + static String getFileExtension(String url) { + int lastDotIndex = url.lastIndexOf('.'); + if (lastDotIndex == -1) { + return ''; + } + int lastSlashIndex = url.lastIndexOf('/'); + if (lastDotIndex > lastSlashIndex) { + return url.substring(lastDotIndex); + } + return ''; + } } diff --git a/lib/common/utils/plugs/face_discern_plugs.dart b/lib/common/utils/plugs/face_discern_plugs.dart index 352cd9e..9b51ce2 100644 --- a/lib/common/utils/plugs/face_discern_plugs.dart +++ b/lib/common/utils/plugs/face_discern_plugs.dart @@ -8,10 +8,11 @@ abstract class FaceDiscernPlugs { static const String _tag = "人脸识别plug"; static const MethodChannel _channel = MethodChannel( - "local/face_discern", + Constants.facePlugsMethodChannel, ); + static const EventChannel _eventChannel = EventChannel( - "local/face_discern_event", + Constants.facePlugsEventChannel, ); static MethodChannel get getChannel => _channel; diff --git a/lib/common/utils/sync/sync_face_tool.dart b/lib/common/utils/sync/sync_face_tool.dart index 049cc24..b213f3e 100644 --- a/lib/common/utils/sync/sync_face_tool.dart +++ b/lib/common/utils/sync/sync_face_tool.dart @@ -1,12 +1,43 @@ import 'dart:io'; import 'package:common_utils/common_utils.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; + import 'package:pj_supermarket_cloud/common/index.dart'; class SyncFaceTool { static const _tag = "sync人脸工具"; + // 同步人脸 + static Future syncFace({ + bool isAll = false, + VoidCallback? onSyncGroup, + }) async { + final appSetting = await AppSettingTool.getSysSetting(); + final merchantDeptId = appSetting.merchantDeptId; + // 人脸存储路径 + final storagePath = await AppFileTool.getFacePath(); + if (ObjectUtil.isEmpty(storagePath)) { + logger.e("|$_tag|同步人脸错误,创建人脸存储路径错误"); + return; + } + // 同步没有的人脸 + int pageNo = 1; + bool isNext = true; + while (isNext) { + final bool = await syncGroupFace( + pageNo: pageNo, + merchantDeptId: isAll ? "" : merchantDeptId!, + storagePath: storagePath!, + ); + if (isNext != bool) isNext = bool; + if (!bool) break; + pageNo += 1; + onSyncGroup?.call(); + } + } + static Future syncGroupFace({ required int pageNo, required String merchantDeptId, @@ -16,7 +47,7 @@ class SyncFaceTool { }) async { final app = Get.find(); final faceDao = app.dataBase.getFaceDao; - const size = 20; + const size = 50; var pa = { "current": pageNo, "size": size, @@ -166,7 +197,7 @@ class SyncFaceTool { required String facePath, }) { if (ObjectUtil.isEmpty(facePath)) return null; - return "${userId}_${sampleId}_$username$facePath"; + return "${userId}_${sampleId}_$username${AppFileTool.getFileExtension(facePath)}"; } /// 写入人脸头像到本地磁盘 diff --git a/lib/common/values/constants.dart b/lib/common/values/constants.dart index 1308635..398dddd 100644 --- a/lib/common/values/constants.dart +++ b/lib/common/values/constants.dart @@ -20,6 +20,12 @@ class Constants { /// ttl android 插件方法管道 static const localTTSPlugsMethodChannel = 'local/tts'; + /// 人脸识别 插件方法管道 + static const facePlugsMethodChannel = 'local/face_discern'; + + /// 人脸识别 插件回调管道 + static const facePlugsEventChannel = 'local/face_discern_event'; + /// 数据库名字 static const databaseName = 'cash_register.db'; } diff --git a/lib/pages/panel/controller.dart b/lib/pages/panel/controller.dart index 2b66338..70227aa 100644 --- a/lib/pages/panel/controller.dart +++ b/lib/pages/panel/controller.dart @@ -56,8 +56,10 @@ class PanelController extends GetxController { Get.toNamed(RouteNames.orderHistory); break; case PanelType.setting: - final flag = await settingPasswordDialog(Get.context!); - if (!flag) return; + if (AppSysTool.isProd()) { + final flag = await settingPasswordDialog(Get.context!); + if (!flag) return; + } Get.toNamed(RouteNames.setting); break; case PanelType.cardActive: diff --git a/lib/pages/setting/widgets/setting_baseInfo.dart b/lib/pages/setting/widgets/setting_baseInfo.dart index 546588a..7a83eac 100644 --- a/lib/pages/setting/widgets/setting_baseInfo.dart +++ b/lib/pages/setting/widgets/setting_baseInfo.dart @@ -15,7 +15,7 @@ Future _bindServiceDialog(BuildContext context, SysSetting sys) async { if (ObjectUtil.isNotEmpty(sys.serviceId)) { uniqueCodeC.text = sys.serviceId ?? ""; } else if (AppSysTool.isDev()) { - uniqueCodeC.text = "1896157295523049473"; + uniqueCodeC.text = "1894303392833716226"; } await showAdaptiveDialog( context: context, diff --git a/lib/pages/setting/widgets/setting_face.dart b/lib/pages/setting/widgets/setting_face.dart index ba118e4..48df20d 100644 --- a/lib/pages/setting/widgets/setting_face.dart +++ b/lib/pages/setting/widgets/setting_face.dart @@ -12,6 +12,8 @@ import 'package:pj_supermarket_cloud/common/index.dart'; class FaceManageController extends GetxController { static const String _tag = "人脸管理 Controller"; + final appC = Get.find(); + RxList faceList = RxList(); RxInt currentPage = RxInt(1); @@ -45,19 +47,16 @@ class FaceManageController extends GetxController { ); } try { - final app = Get.find(); - final faceDao = app.dataBase.getFaceDao; + final faceDao = appC.dataBase.getFaceDao; String userName = usernameC.text; if (ObjectUtil.isEmpty(userName)) userName = "-1"; final findCount = await faceDao.findCount(userName); + totalPage.value = findCount ?? 0; final list = await faceDao.findAll( userName, pageSize.value, (currentPage.value - 1) * pageSize.value, ); - if (findCount != null) { - totalPage.value = (findCount / pageSize.value).ceil(); - } faceList.value = list; } finally { if (isLoading) { @@ -78,14 +77,14 @@ class FaceManageController extends GetxController { Toast.error(msg: "请先绑定设备"); return; } - final isActive = await AppFaceTool.isActiveSdk(); - if (!isActive) { - Toast.error(msg: "人脸sdk激活失败"); - return; - } + // final isActive = await AppFaceTool.isActiveSdk(); + // if (!isActive) { + // Toast.error(msg: "人脸sdk激活失败"); + // return; + // } final isSure = await appMessageDialog( Get.context!, - "是否同步所有人脸,这个过程肯能比较长", + "是否同步所有人脸,这个过程可能比较长", ); if (!isSure) return; EasyLoading.show( @@ -93,10 +92,18 @@ class FaceManageController extends GetxController { maskType: EasyLoadingMaskType.black, ); try { - // await SyncFaceTool.syncFace(isAll: isAll); + await SyncFaceTool.syncFace( + isAll: isAll, + onSyncGroup: () { + currentPage.value = 1; + getFaceList(); + }, + ); } finally { currentPage.value = 1; getFaceList(); + await AppFaceTool.faceRegister(forceRegister: true); + Toast.success(msg: "同步成功"); EasyLoading.dismiss(); } } @@ -112,23 +119,22 @@ class FaceManageController extends GetxController { maskType: EasyLoadingMaskType.black, ); try { - final app = Get.find(); - final faceDao = app.dataBase.getFaceDao; + final faceDao = appC.dataBase.getFaceDao; await faceDao.delAll(); final bool = await AppFaceTool.removeFace(sampleId: "-1"); logger.i(bool); } finally { Future.delayed(const Duration(seconds: 1), () { getFaceList(); + Toast.success(msg: "同步成功"); EasyLoading.dismiss(); }); } } _listenMqCallback() { - final app = Get.find(); mqUpdateListen = - app.mqUpdateEventBus.on().listen((event) async { + appC.mqUpdateEventBus.on().listen((event) async { switch (event) { case MqtActionEnum.face: getFaceList(isLoading: true); @@ -142,6 +148,30 @@ class FaceManageController extends GetxController { if (page != null) currentPage.value = page; await getFaceList(isLoading: true); } + + // 删除人脸 + Future deleteFace(FaceEntity v) async { + final isSure = await appMessageDialog(Get.context!, "是否删除人脸"); + if (!isSure) return; + EasyLoading.show( + status: '删除中...', + maskType: EasyLoadingMaskType.black, + ); + try { + final faceDao = appC.dataBase.getFaceDao; + if (ObjectUtil.isNotEmpty(v.path)) { + await SyncFaceTool.delFaceByStorage(filePath: v.path!); + } + await faceDao.delById(v.id!); + await AppFaceTool.removeFace(sampleId: v.sampleId); + } finally { + Future.delayed(const Duration(seconds: 1), () { + getFaceList(); + Toast.success(msg: "删除成功"); + EasyLoading.dismiss(); + }); + } + } } /// 搜索 @@ -202,28 +232,70 @@ Widget buildFaceSearch(BuildContext context) { /// 列表 Widget buildFaceList(context) { - return [].toRow(); -} - -/// 分页 -Widget buildFacePage(context) { - final faceC = Get.find(); - return appPage( - context, - pageNo: faceC.currentPage.value, - pageSize: faceC.pageSize.value, - total: faceC.totalPage.value, - onPageChanged: (pageNo) => faceC.doPage(page: pageNo), + final theme = FTheme.of(context); + final productC = Get.find(); + return Obx( + () => GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 5, + mainAxisSpacing: 10.w, + crossAxisSpacing: 10.w, + childAspectRatio: 1.2, + ), + itemCount: productC.faceList.length, + itemBuilder: (BuildContext context, int index) { + final v = productC.faceList[index]; + return FCard( + style: theme.cardStyle.copyWith( + contentStyle: theme.cardStyle.contentStyle.copyWith( + padding: EdgeInsets.all(0), + ), + ), + image: appImg(v.path, width: 120.w, height: 120.w) + .center() + .marginOnly(top: 5.h), + title: Text( + v.userName, + maxLines: 1, + style: theme.typography.sm, + ).center(), + subtitle: [ + Text( + "用户编号:${v.userId}", + style: theme.typography.xs.copyWith(height: 1), + ), + Text( + "样本编号:${v.sampleId}", + style: theme.typography.xs.copyWith(height: 1), + ), + ] + .toColumnSpace( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + ) + .width(double.infinity) + .marginOnly(top: 5.h), + ).onTap(() => productC.deleteFace(v)); + }, + ), ); } Widget settingFace(BuildContext context) { - Get.put( + final faceC = Get.put( FaceManageController(), ); return [ buildFaceSearch(context), buildFaceList(context).expanded(), - buildFacePage(context), + Obx( + () => appPage( + context, + pageNo: faceC.currentPage.value, + pageSize: faceC.pageSize.value, + total: faceC.totalPage.value, + onPageChanged: (pageNo) => faceC.doPage(page: pageNo), + ), + ), ].toColumn(); }