Преглед изворни кода

feat: Refactor main module and add new features

- Refactor the main module, including data management, material management, exception management, and statistical analysis.
- Add new i18n translations for Chinese.
- Add new SVG icons for UI elements.
- Update navigation graphs for data management, exception management, and material management.
- Add MaterialExchangeHeaderEntity for material exchange header.
- Add MaterialHomeManageFragment for material management home screen.
- Add BottomTipEvent for displaying bottom tips.
- Add MaterialExchangeFragment for material exchange functionality.
- Update RoleFunctionalPermissionsEnum with new permissions.
- Update QuickEntranceMenuItemEntity with new menu items.
- Update DataManageHomeFragment with new menu items.
- Update Context extension to use DeviceUtils for getting serial number.
- Update dimens.xml with new dimension values.
- Update MainActivity with new tab configurations and bottom navigation destinations.
- Update LoginFragment to display serial number in version text.
- Update ISCSMCApplication with delayed initialization for performance improvement.
- Update database migrations with new tables for material management.
- Add new data entities for material management, including IsMaterials, IsMaterialsCabinet, IsMaterialsChangeRecord, IsMaterialsCheckPlan, IsMaterialsCheckRecord, IsMaterialsInstructions, IsMaterialsLoan, IsMaterialsPlanCabinet, IsMaterialsProperty, IsMaterialsPropertyValue, IsMaterialsReminder, IsMaterialsRestitutionRules, and IsMaterialsType.
- Add new layouts for material exchange, exception management home, statistical analysis home, and material management home.
- Update existing layouts for main activity.
周文健 пре 2 месеци
родитељ
комит
d6bfaf8317
47 измењених фајлова са 3128 додато и 75 уклоњено
  1. 1330 0
      app/src/main/assets/i18n/zh-CN.json
  2. 2 0
      app/src/main/assets/themes/Default/icons/angle-circle-down.svg
  3. 2 0
      app/src/main/assets/themes/Default/icons/stats.svg
  4. 2 0
      app/src/main/assets/themes/Default/icons/tags.svg
  5. 39 28
      app/src/main/java/com/grkj/iscs_mc/ISCSMCApplication.kt
  6. 2 1
      app/src/main/java/com/grkj/iscs_mc/features/login/fragment/LoginFragment.kt
  7. 28 2
      app/src/main/java/com/grkj/iscs_mc/features/main/activity/MainActivity.kt
  8. 4 4
      app/src/main/java/com/grkj/iscs_mc/features/main/dialog/QuickEntranceConfigDialog.kt
  9. 23 0
      app/src/main/java/com/grkj/iscs_mc/features/main/entity/MaterialExchangeHeaderEntity.kt
  10. 51 1
      app/src/main/java/com/grkj/iscs_mc/features/main/entity/QuickEntranceMenuItemEntity.kt
  11. 35 4
      app/src/main/java/com/grkj/iscs_mc/features/main/fragment/data_manage/DataManageHomeFragment.kt
  12. 99 0
      app/src/main/java/com/grkj/iscs_mc/features/main/fragment/exception_manage/ExceptionHomeManage.kt
  13. 4 4
      app/src/main/java/com/grkj/iscs_mc/features/main/fragment/home/HomeFragment.kt
  14. 119 0
      app/src/main/java/com/grkj/iscs_mc/features/main/fragment/material_manage/MaterialExchangeFragment.kt
  15. 120 0
      app/src/main/java/com/grkj/iscs_mc/features/main/fragment/material_manage/MaterialHomeManageFragment.kt
  16. 118 0
      app/src/main/java/com/grkj/iscs_mc/features/main/fragment/statistical_analysis_manage/StatisticalAnalysisHomeManage.kt
  17. 16 0
      app/src/main/res/layout-land/activity_main.xml
  18. 30 11
      app/src/main/res/layout/activity_main.xml
  19. 15 0
      app/src/main/res/layout/fragment_exception_manage_home.xml
  20. 120 0
      app/src/main/res/layout/fragment_material_exchange.xml
  21. 15 0
      app/src/main/res/layout/fragment_material_manage_home.xml
  22. 15 0
      app/src/main/res/layout/fragment_statistical_analysis_manage_home.xml
  23. 26 0
      app/src/main/res/layout/item_material_exchange_header.xml
  24. 7 0
      app/src/main/res/navigation/nav_data_manage.xml
  25. 5 5
      app/src/main/res/navigation/nav_exception_manage.xml
  26. 19 0
      app/src/main/res/navigation/nav_material_manage.xml
  27. 11 0
      app/src/main/res/navigation/nav_statistical_analysis.xml
  28. 4 0
      data/src/main/java/com/grkj/data/common/EventConstants.kt
  29. 104 5
      data/src/main/java/com/grkj/data/enums/RoleFunctionalPermissionsEnum.kt
  30. 30 2
      data/src/main/java/com/grkj/data/local/database/ISCSMigrations.kt
  31. 102 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterials.kt
  32. 53 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsCabinet.kt
  33. 64 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsChangeRecord.kt
  34. 41 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckPlan.kt
  35. 57 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckRecord.kt
  36. 53 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsInstructions.kt
  37. 69 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsLoan.kt
  38. 43 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsPlanCabinet.kt
  39. 29 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsProperty.kt
  40. 33 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsPropertyValue.kt
  41. 33 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsReminder.kt
  42. 45 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsRestitutionRules.kt
  43. 77 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsType.kt
  44. 1 0
      shared/build.gradle.kts
  45. 28 0
      ui-base/src/main/java/com/grkj/ui_base/utils/event/BottomTipEvent.kt
  46. 4 8
      ui-base/src/main/java/com/grkj/ui_base/utils/extension/Context.kt
  47. 1 0
      ui-base/src/main/res/values/dimens.xml

+ 1330 - 0
app/src/main/assets/i18n/zh-CN.json

@@ -4,6 +4,71 @@
     "type": "text",
     "value": "中文"
   },
+  "abnormal": {
+    "key": "abnormal",
+    "type": "text",
+    "value": "异常"
+  },
+  "action_failed": {
+    "key": "action_failed",
+    "type": "text",
+    "value": "操作失败"
+  },
+  "action_hint": {
+    "key": "action_hint",
+    "type": "text",
+    "value": "操作提醒"
+  },
+  "action_succeed": {
+    "key": "action_succeed",
+    "type": "text",
+    "value": "操作成功"
+  },
+  "add_role_failed": {
+    "key": "add_role_failed",
+    "type": "text",
+    "value": "新增角色失败"
+  },
+  "add_role_succeed": {
+    "key": "add_role_succeed",
+    "type": "text",
+    "value": "新增角色成功"
+  },
+  "add_user_succeed": {
+    "key": "add_user_succeed",
+    "type": "text",
+    "value": "新增用户成功"
+  },
+  "admin_role_can_not_edit": {
+    "key": "admin_role_can_not_edit",
+    "type": "text",
+    "value": "管理员角色无法编辑"
+  },
+  "admin_username": {
+    "key": "admin_username",
+    "type": "text",
+    "value": "管理员账号:(数字、字母、6-20位)"
+  },
+  "all": {
+    "key": "all",
+    "type": "text",
+    "value": "全部"
+  },
+  "all_quick_entrance": {
+    "key": "all_quick_entrance",
+    "type": "text",
+    "value": "所有快捷入口"
+  },
+  "all_select_not_all_select": {
+    "key": "all_select_not_all_select",
+    "type": "text",
+    "value": "全选/全不选"
+  },
+  "base_info_title": {
+    "key": "base_info_title",
+    "type": "text",
+    "value": "基本信息"
+  },
   "tec_support": {
     "key": "tec_support",
     "type": "text",
@@ -203,5 +268,1270 @@
     "key": "face_login_tip",
     "type": "text",
     "value": "请将面部对准摄像头,完成人脸认证后,将自动登录。"
+  },
+  "edit": {
+    "key": "edit",
+    "type": "text",
+    "value": "编辑"
+  },
+  "user_info_title": {
+    "key": "user_info_title",
+    "type": "text",
+    "value": "个人信息"
+  },
+  "user_info": {
+    "key": "user_info",
+    "type": "text",
+    "value": "个人信息"
+  },
+  "reset_password": {
+    "key": "reset_password",
+    "type": "text",
+    "value": "重置密码"
+  },
+  "fingerprint_setting": {
+    "key": "fingerprint_setting",
+    "type": "text",
+    "value": "设置指纹"
+  },
+  "face_setting": {
+    "key": "face_setting",
+    "type": "text",
+    "value": "设置人脸"
+  },
+  "card_setting": {
+    "key": "card_setting",
+    "type": "text",
+    "value": "设置工卡"
+  },
+  "logout": {
+    "key": "logout",
+    "type": "text",
+    "value": "退出登录"
+  },
+  "data_manage": {
+    "key": "data_manage",
+    "type": "text",
+    "value": "数据管理"
+  },
+  "home": {
+    "key": "home",
+    "type": "text",
+    "value": "主页"
+  },
+  "admin": {
+    "key": "admin",
+    "type": "text",
+    "value": "超级管理员"
+  },
+  "jtdrawer": {
+    "key": "jtdrawer",
+    "type": "text",
+    "value": "作业管理员"
+  },
+  "jtlocker": {
+    "key": "jtlocker",
+    "type": "text",
+    "value": "上锁人"
+  },
+  "jtcolocker": {
+    "key": "jtcolocker",
+    "type": "text",
+    "value": "共锁人"
+  },
+  "jtguard": {
+    "key": "jtguard",
+    "type": "text",
+    "value": "作业观察员"
+  },
+  "sysconfig": {
+    "key": "sysconfig",
+    "type": "text",
+    "value": "系统配置员"
+  },
+  "cancel_countdown": {
+    "key": "cancel_countdown",
+    "type": "text",
+    "value": "取消({0}秒)"
+  },
+  "capture_tip_content": {
+    "key": "capture_tip_content",
+    "type": "text",
+    "value": "1. 系统将自动拍摄照片,在拍摄过程中请确保:\n      · 脸部正对摄像头\n      · 保持适当距离,让整个脸部出现在左侧框中\n      · 光线充足\n      · 表情自然\n2. 拍摄完成后,您可以点击确认按钮进行提交,也可以点击重拍按钮重新进行拍摄。\n3. 取消录入,请点击取消按钮"
+  },
+  "capture_tip_title": {
+    "key": "capture_tip_title",
+    "type": "text",
+    "value": "录入提示"
+  },
+  "card_already_registration": {
+    "key": "card_already_registration",
+    "type": "text",
+    "value": "卡片已录入"
+  },
+  "card_code": {
+    "key": "card_code",
+    "type": "text",
+    "value": "卡片名称"
+  },
+  "card_manage_card_detail_title": {
+    "key": "card_manage_card_detail_title",
+    "type": "text",
+    "value": "卡片详情"
+  },
+  "card_manage_delete_failed": {
+    "key": "card_manage_delete_failed",
+    "type": "text",
+    "value": "卡片删除失败"
+  },
+  "card_manage_delete_succeed": {
+    "key": "card_manage_delete_succeed",
+    "type": "text",
+    "value": "卡片删除成功"
+  },
+  "card_manage_new_card_title": {
+    "key": "card_manage_new_card_title",
+    "type": "text",
+    "value": "新增卡片"
+  },
+  "card_manage_title": {
+    "key": "card_manage_title",
+    "type": "text",
+    "value": "卡片管理"
+  },
+  "card_nfc": {
+    "key": "card_nfc",
+    "type": "text",
+    "value": "卡片 NFC"
+  },
+  "check_delete_card": {
+    "key": "check_delete_card",
+    "type": "text",
+    "value": "确定要删除选中的卡片吗?"
+  },
+  "check_delete_role": {
+    "key": "check_delete_role",
+    "type": "text",
+    "value": "您确认要删除角色吗"
+  },
+  "check_delete_user": {
+    "key": "check_delete_user",
+    "type": "text",
+    "value": "您确认要删除用户吗?"
+  },
+  "delete": {
+    "key": "delete",
+    "type": "text",
+    "value": "删除"
+  },
+  "delete_success": {
+    "key": "delete_success",
+    "type": "text",
+    "value": "删除成功"
+  },
+  "detail": {
+    "key": "detail",
+    "type": "text",
+    "value": "详情"
+  },
+  "detect_face_tip": {
+    "key": "detect_face_tip",
+    "type": "text",
+    "value": "检测到人脸,即将拍摄"
+  },
+  "doing_checking": {
+    "key": "doing_checking",
+    "type": "text",
+    "value": "正在验证......"
+  },
+  "insert": {
+    "key": "insert",
+    "type": "text",
+    "value": "新增"
+  },
+  "invalid_card": {
+    "key": "invalid_card",
+    "type": "text",
+    "value": "卡片无效"
+  },
+  "invalid_user": {
+    "key": "invalid_user",
+    "type": "text",
+    "value": "用户不存在"
+  },
+  "job_card_login_failed": {
+    "key": "job_card_login_failed",
+    "type": "text",
+    "value": "工卡无效"
+  },
+  "job_card_login_success": {
+    "key": "job_card_login_success",
+    "type": "text",
+    "value": "工卡识别成功"
+  },
+  "job_card_not_set_tip": {
+    "key": "job_card_not_set_tip",
+    "type": "text",
+    "value": "您尚未设置工卡"
+  },
+  "job_card_scan_tip": {
+    "key": "job_card_scan_tip",
+    "type": "text",
+    "value": "请在读卡器上读卡"
+  },
+  "job_card_set_tip": {
+    "key": "job_card_set_tip",
+    "type": "text",
+    "value": "您已设置了工卡数据"
+  },
+  "loading_data": {
+    "key": "loading_data",
+    "type": "text",
+    "value": "数据加载中"
+  },
+  "new_password": {
+    "key": "new_password",
+    "type": "text",
+    "value": "新密码(数字、字母、特殊符号、6-20位)"
+  },
+  "new_password_and_repeat_new_password_not_same": {
+    "key": "new_password_and_repeat_new_password_not_same",
+    "type": "text",
+    "value": "新密码与重复新密码不一致"
+  },
+  "new_password_cannot_be_the_same_as_the_old_password": {
+    "key": "new_password_cannot_be_the_same_as_the_old_password",
+    "type": "text",
+    "value": "新密码与旧密码不能相同"
+  },
+  "nickname": {
+    "key": "nickname",
+    "type": "text",
+    "value": "姓名"
+  },
+  "normal": {
+    "key": "normal",
+    "type": "text",
+    "value": "正常"
+  },
+  "not_save_tip": {
+    "key": "not_save_tip",
+    "type": "text",
+    "value": "数据还没有保存,您确定要放弃保存,离开当前页面吗?"
+  },
+  "number": {
+    "key": "number",
+    "type": "text",
+    "value": "编号:"
+  },
+  "old_password": {
+    "key": "old_password",
+    "type": "text",
+    "value": "旧密码"
+  },
+  "old_password_error": {
+    "key": "old_password_error",
+    "type": "text",
+    "value": "旧密码错误"
+  },
+  "only_one_person_allowed": {
+    "key": "only_one_person_allowed",
+    "type": "text",
+    "value": "请保持单人入镜"
+  },
+  "operation": {
+    "key": "operation",
+    "type": "text",
+    "value": "操作"
+  },
+  "password_and_repeat_password_not_same": {
+    "key": "password_and_repeat_password_not_same",
+    "type": "text",
+    "value": "密码与重复密码不一致"
+  },
+  "password_regex_tip": {
+    "key": "password_regex_tip",
+    "type": "text",
+    "value": "密码不符合要求"
+  },
+  "phone": {
+    "key": "phone",
+    "type": "text",
+    "value": "电话"
+  },
+  "please_input_card_code": {
+    "key": "please_input_card_code",
+    "type": "text",
+    "value": "请输入工卡"
+  },
+  "please_input_card_nfc": {
+    "key": "please_input_card_nfc",
+    "type": "text",
+    "value": "请输入卡片 NFC"
+  },
+  "please_input_correct_phone": {
+    "key": "please_input_correct_phone",
+    "type": "text",
+    "value": "请输入正确的手机号"
+  },
+  "please_input_key_word": {
+    "key": "please_input_key_word",
+    "type": "text",
+    "value": "请输入关键字"
+  },
+  "please_input_new_password": {
+    "key": "please_input_new_password",
+    "type": "text",
+    "value": "请输入新密码"
+  },
+  "please_input_nickname": {
+    "key": "please_input_nickname",
+    "type": "text",
+    "value": "请输入姓名"
+  },
+  "please_input_old_password": {
+    "key": "please_input_old_password",
+    "type": "text",
+    "value": "请输入旧密码"
+  },
+  "please_input_password": {
+    "key": "please_input_password",
+    "type": "text",
+    "value": "请输入密码"
+  },
+  "please_input_permission_characters": {
+    "key": "please_input_permission_characters",
+    "type": "text",
+    "value": "请输入权限字符"
+  },
+  "please_input_phone": {
+    "key": "please_input_phone",
+    "type": "text",
+    "value": "请输入电话"
+  },
+  "please_input_remark": {
+    "key": "please_input_remark",
+    "type": "text",
+    "value": "请输入备注"
+  },
+  "please_input_repeat_new_password": {
+    "key": "please_input_repeat_new_password",
+    "type": "text",
+    "value": "请重复新密码"
+  },
+  "please_input_repeat_password": {
+    "key": "please_input_repeat_password",
+    "type": "text",
+    "value": "请输入重复密码"
+  },
+  "please_input_role_name": {
+    "key": "please_input_role_name",
+    "type": "text",
+    "value": "请输入角色名称"
+  },
+  "please_input_username": {
+    "key": "please_input_username",
+    "type": "text",
+    "value": "请输入登录名"
+  },
+  "please_press_fingerprint_again": {
+    "key": "please_press_fingerprint_again",
+    "type": "text",
+    "value": "请再次按压指纹"
+  },
+  "please_re_press_fingerprint_again": {
+    "key": "please_re_press_fingerprint_again",
+    "type": "text",
+    "value": "请重新按压指纹"
+  },
+  "please_select_card_username": {
+    "key": "please_select_card_username",
+    "type": "text",
+    "value": "请选择用户名称"
+  },
+  "please_select_role": {
+    "key": "please_select_role",
+    "type": "text",
+    "value": "请选择角色"
+  },
+  "please_select_status": {
+    "key": "please_select_status",
+    "type": "text",
+    "value": "请选择状态"
+  },
+  "please_select_user": {
+    "key": "please_select_user",
+    "type": "text",
+    "value": "请选择用户"
+  },
+  "quick_entrance_most_set_tip": {
+    "key": "quick_entrance_most_set_tip",
+    "type": "text",
+    "value": "快捷入口最多设置8个"
+  },
+  "quick_entrance_title": {
+    "key": "quick_entrance_title",
+    "type": "text",
+    "value": "快捷入口配置"
+  },
+  "re_recognize": {
+    "key": "re_recognize",
+    "type": "text",
+    "value": "重新识别"
+  },
+  "real_person_verification_required": {
+    "key": "real_person_verification_required",
+    "type": "text",
+    "value": "请保持真人操作"
+  },
+  "recapture": {
+    "key": "recapture",
+    "type": "text",
+    "value": "重拍"
+  },
+  "remark": {
+    "key": "remark",
+    "type": "text",
+    "value": "备注"
+  },
+  "repeat_new_password": {
+    "key": "repeat_new_password",
+    "type": "text",
+    "value": "重复新密码(数字、字母、特殊符号、6-20位)"
+  },
+  "repeat_password": {
+    "key": "repeat_password",
+    "type": "text",
+    "value": "重复密码:(数字、字母、特殊符号、6-20位)"
+  },
+  "reset": {
+    "key": "reset",
+    "type": "text",
+    "value": "重置"
+  },
+  "reset_data_tv": {
+    "key": "reset_data_tv",
+    "type": "text",
+    "value": "点击重设"
+  },
+  "reset_password_title": {
+    "key": "reset_password_title",
+    "type": "text",
+    "value": "重置密码"
+  },
+  "reset_user_password_failed": {
+    "key": "reset_user_password_failed",
+    "type": "text",
+    "value": "用户密码重置失败"
+  },
+  "reset_user_password_succeed": {
+    "key": "reset_user_password_succeed",
+    "type": "text",
+    "value": "用户密码重置成功"
+  },
+  "restart_app_after_set": {
+    "key": "restart_app_after_set",
+    "type": "text",
+    "value": "App将在设置完成后重启"
+  },
+  "role_in_preset_tip": {
+    "key": "role_in_preset_tip",
+    "type": "text",
+    "value": "预设角色不允许删除"
+  },
+  "role_in_use": {
+    "key": "role_in_use",
+    "type": "text",
+    "value": "角色已有作业在使用"
+  },
+  "role_key_already_exists": {
+    "key": "role_key_already_exists",
+    "type": "text",
+    "value": "该角色权限字符已存在"
+  },
+  "role_manage_add_title": {
+    "key": "role_manage_add_title",
+    "type": "text",
+    "value": "添加角色"
+  },
+  "role_manage_update_title": {
+    "key": "role_manage_update_title",
+    "type": "text",
+    "value": "修改角色"
+  },
+  "role_manage_delete_failed": {
+    "key": "role_manage_delete_failed",
+    "type": "text",
+    "value": "无法删除角色"
+  },
+  "role_manage_delete_succeed": {
+    "key": "role_manage_delete_succeed",
+    "type": "text",
+    "value": "角色已删除"
+  },
+  "role_manage_permission_string": {
+    "key": "role_manage_permission_string",
+    "type": "text",
+    "value": "权限字符"
+  },
+  "role_manage_role_name": {
+    "key": "role_manage_role_name",
+    "type": "text",
+    "value": "角色名称"
+  },
+  "role_manage_role_num": {
+    "key": "role_manage_role_num",
+    "type": "text",
+    "value": "角色编号"
+  },
+  "role_manage_title": {
+    "key": "role_manage_title",
+    "type": "text",
+    "value": "角色管理"
+  },
+  "save_success": {
+    "key": "save_success",
+    "type": "text",
+    "value": "保存成功!"
+  },
+  "select": {
+    "key": "select",
+    "type": "text",
+    "value": "选择"
+  },
+  "selected_quick_entrance": {
+    "key": "selected_quick_entrance",
+    "type": "text",
+    "value": "已配置的快捷入口(最多添加8个快捷入口,可拖拽排序)"
+  },
+  "set_data_tv": {
+    "key": "set_data_tv",
+    "type": "text",
+    "value": "点击设置"
+  },
+  "set_face_title": {
+    "key": "set_face_title",
+    "type": "text",
+    "value": "设置人脸"
+  },
+  "set_fingerprint_title": {
+    "key": "set_fingerprint_title",
+    "type": "text",
+    "value": "设置指纹"
+  },
+  "set_job_card_title": {
+    "key": "set_job_card_title",
+    "type": "text",
+    "value": "设置工卡"
+  },
+  "set_password": {
+    "key": "set_password",
+    "type": "text",
+    "value": "设置密码:(数字、字母、特殊符号、6-20位)"
+  },
+  "settings": {
+    "key": "settings",
+    "type": "text",
+    "value": "设置"
+  },
+  "status": {
+    "key": "status",
+    "type": "text",
+    "value": "状态"
+  },
+  "update_role_failed": {
+    "key": "update_role_failed",
+    "type": "text",
+    "value": "角色更新失败"
+  },
+  "update_role_succeed": {
+    "key": "update_role_succeed",
+    "type": "text",
+    "value": "角色更新成功"
+  },
+  "update_user_failed": {
+    "key": "update_user_failed",
+    "type": "text",
+    "value": "用户更新失败"
+  },
+  "update_user_succeed": {
+    "key": "update_user_succeed",
+    "type": "text",
+    "value": "用户更新成功"
+  },
+  "user_manage_delete_failed": {
+    "key": "user_manage_delete_failed",
+    "type": "text",
+    "value": "无法删除用户"
+  },
+  "user_manage_delete_succeed": {
+    "key": "user_manage_delete_succeed",
+    "type": "text",
+    "value": "用户已删除"
+  },
+  "user_manage_card_code": {
+    "key": "user_manage_card_code",
+    "type": "text",
+    "value": "工卡"
+  },
+  "user_manage_filter_activate": {
+    "key": "user_manage_filter_activate",
+    "type": "text",
+    "value": "正常"
+  },
+  "user_manage_filter_deactivate": {
+    "key": "user_manage_filter_deactivate",
+    "type": "text",
+    "value": "停用"
+  },
+  "user_manage_filter_title": {
+    "key": "user_manage_filter_title",
+    "type": "text",
+    "value": "筛选条件"
+  },
+  "user_manage_new_user_title": {
+    "key": "user_manage_new_user_title",
+    "type": "text",
+    "value": "新增用户"
+  },
+  "user_manage_role": {
+    "key": "user_manage_role",
+    "type": "text",
+    "value": "角色"
+  },
+  "user_manage_title": {
+    "key": "user_manage_title",
+    "type": "text",
+    "value": "用户管理"
+  },
+  "user_manage_user_detail_title": {
+    "key": "user_manage_user_detail_title",
+    "type": "text",
+    "value": "用户详情"
+  },
+  "user_manage_view": {
+    "key": "user_manage_view",
+    "type": "text",
+    "value": "查看"
+  },
+  "user_name": {
+    "key": "user_name",
+    "type": "text",
+    "value": "登录名"
+  },
+  "username": {
+    "key": "username",
+    "type": "text",
+    "value": "用户名称"
+  },
+  "username_or_password_error": {
+    "key": "username_or_password_error",
+    "type": "text",
+    "value": "账号或密码错误"
+  },
+  "username_passowrd_login_success": {
+    "key": "username_passowrd_login_success",
+    "type": "text",
+    "value": "账号密码验证通过"
+  },
+  "username_password_not_exists": {
+    "key": "username_password_not_exists",
+    "type": "text",
+    "value": "账号密码不存在"
+  },
+  "username_regex_tip": {
+    "key": "username_regex_tip",
+    "type": "text",
+    "value": "账号不符合要求"
+  },
+  "verify_failed": {
+    "key": "verify_failed",
+    "type": "text",
+    "value": "验证失败"
+  },
+  "view": {
+    "key": "view",
+    "type": "text",
+    "value": "查看"
+  },
+  "backup_title": {
+    "key": "backup_title",
+    "type": "text",
+    "value": "备份/还原"
+  },
+  "backup": {
+    "key": "backup",
+    "type": "text",
+    "value": "备份"
+  },
+  "backup_path": {
+    "key": "backup_path",
+    "type": "text",
+    "value": "备份路径"
+  },
+  "maximum_number_of_backups": {
+    "key": "maximum_number_of_backups",
+    "type": "text",
+    "value": "备份数量上限"
+  },
+  "auto_backup": {
+    "key": "auto_backup",
+    "type": "text",
+    "value": "自动备份"
+  },
+  "common_enable": {
+    "key": "common_enable",
+    "type": "text",
+    "value": "启用"
+  },
+  "common_disable": {
+    "key": "common_disable",
+    "type": "text",
+    "value": "停用"
+  },
+  "backup_frequency": {
+    "key": "backup_frequency",
+    "type": "text",
+    "value": "备份频率"
+  },
+  "backup_time": {
+    "key": "backup_time",
+    "type": "text",
+    "value": "备份时间"
+  },
+  "backup_tip": {
+    "key": "backup_tip",
+    "type": "text",
+    "value": "注意:自动备份时必须保证应用处于启动状态。"
+  },
+  "backup_now": {
+    "key": "backup_now",
+    "type": "text",
+    "value": "立即备份"
+  },
+  "backup_range": {
+    "key": "backup_range",
+    "type": "text",
+    "value": "范围:{0}"
+  },
+  "restore": {
+    "key": "restore",
+    "type": "text",
+    "value": "还原"
+  },
+  "common_batch_export": {
+    "key": "common_batch_export",
+    "type": "text",
+    "value": "批量导出"
+  },
+  "common_batch_delete": {
+    "key": "common_batch_delete",
+    "type": "text",
+    "value": "批量删除"
+  },
+  "common_export": {
+    "key": "common_export",
+    "type": "text",
+    "value": "导出"
+  },
+  "MON": {
+    "key": "MON",
+    "type": "text",
+    "value": "星期一"
+  },
+  "TUE": {
+    "key": "TUE",
+    "type": "text",
+    "value": "星期二"
+  },
+  "WED": {
+    "key": "WED",
+    "type": "text",
+    "value": "星期三"
+  },
+  "THU": {
+    "key": "THU",
+    "type": "text",
+    "value": "星期四"
+  },
+  "FRI": {
+    "key": "FRI",
+    "type": "text",
+    "value": "星期五"
+  },
+  "SAT": {
+    "key": "SAT",
+    "type": "text",
+    "value": "星期六"
+  },
+  "SUN": {
+    "key": "SUN",
+    "type": "text",
+    "value": "星期日"
+  },
+  "backup_frequency_every_day": {
+    "key": "backup_frequency_every_day",
+    "type": "text",
+    "value": "每天"
+  },
+  "please_select_backup_frequency": {
+    "key": "please_select_backup_frequency",
+    "type": "text",
+    "value": "请选择备份频率"
+  },
+  "maximumNumberOfBackupsNotCorrect": {
+    "key": "maximumNumberOfBackupsNotCorrect",
+    "type": "text",
+    "value": "请填写正确的备份数量上限"
+  },
+  "please_select_time": {
+    "key": "please_select_time",
+    "type": "text",
+    "value": "请选择时间"
+  },
+  "backup_now_please_wait": {
+    "key": "backup_now_please_wait",
+    "type": "text",
+    "value": "正在备份中,请稍等……"
+  },
+  "backup_success": {
+    "key": "backup_success",
+    "type": "text",
+    "value": "备份成功"
+  },
+  "backup_failed": {
+    "key": "backup_failed",
+    "type": "text",
+    "value": "备份失败"
+  },
+  "delete_backup_file_confirm": {
+    "key": "delete_backup_file_confirm",
+    "type": "text",
+    "value": "是否确认删除该备份,删除后备份无法恢复。"
+  },
+  "delete_selected_backup_file_confirm": {
+    "key": "delete_selected_backup_file_confirm",
+    "type": "text",
+    "value": "是否确认删除选中备份,删除后备份无法恢复。"
+  },
+  "restore_backup_confirm": {
+    "key": "restore_backup_confirm",
+    "type": "text",
+    "value": "还原备份将清除备份日期到当前时间的所有数据,是否确认还原备份?"
+  },
+  "restore_backup_success": {
+    "key": "restore_backup_success",
+    "type": "text",
+    "value": "备份还原成功"
+  },
+  "export_success": {
+    "key": "export_success",
+    "type": "text",
+    "value": "导出成功"
+  },
+  "no_backup_data": {
+    "key": "no_backup_data",
+    "type": "text",
+    "value": "暂无备份数据"
+  },
+  "loading_backup": {
+    "key": "loading_backup",
+    "type": "text",
+    "value": "正在读取备份文件"
+  },
+  "max_backup_tip": {
+    "key": "max_backup_tip",
+    "type": "text",
+    "value": "备份数量已经达到上限,继续备份将移除最老的数据。"
+  },
+  "backup_restoring": {
+    "key": "backup_restoring",
+    "type": "text",
+    "value": "备份还原中……"
+  },
+  "user_manage": {
+    "key": "user_manage",
+    "type": "text",
+    "value": "用户管理"
+  },
+  "role_manage": {
+    "key": "role_manage",
+    "type": "text",
+    "value": "角色管理"
+  },
+  "backup_and_restore": {
+    "key": "backup_and_restore",
+    "type": "text",
+    "value": "备份/还原"
+  },
+  "card_manage": {
+    "key": "card_manage",
+    "type": "text",
+    "value": "卡片管理"
+  },
+  "user_info": {
+    "key": "user_info",
+    "type": "text",
+    "value": "个人信息"
+  },
+  "reset_password": {
+    "key": "reset_password",
+    "type": "text",
+    "value": "重置密码"
+  },
+  "fingerprint_setting": {
+    "key": "fingerprint_setting",
+    "type": "text",
+    "value": "设置指纹"
+  },
+  "face_setting": {
+    "key": "face_setting",
+    "type": "text",
+    "value": "设置人脸"
+  },
+  "card_setting": {
+    "key": "card_setting",
+    "type": "text",
+    "value": "设置工卡"
+  },
+  "logout": {
+    "key": "logout",
+    "type": "text",
+    "value": "退出登录"
+  },
+  "data_manage": {
+    "key": "data_manage",
+    "type": "text",
+    "value": "数据管理"
+  },
+  "home": {
+    "key": "home",
+    "type": "text",
+    "value": "主页"
+  },
+  "year": {
+    "key": "year",
+    "type": "text",
+    "value": "年"
+  },
+  "month": {
+    "key": "month",
+    "type": "text",
+    "value": "月"
+  },
+  "day": {
+    "key": "day",
+    "type": "text",
+    "value": "日"
+  },
+  "hour": {
+    "key": "hour",
+    "type": "text",
+    "value": "时"
+  },
+  "min": {
+    "key": "min",
+    "type": "text",
+    "value": "分"
+  },
+  "sec": {
+    "key": "sec",
+    "type": "text",
+    "value": "秒"
+  },
+  "please_select_backup_file": {
+    "key": "please_select_backup_file",
+    "type": "text",
+    "value": "请先选择备份文件"
+  },
+  "export_selected_backup_file_confirm": {
+    "key": "export_selected_backup_file_confirm",
+    "type": "text",
+    "value": "将启动路径选择器,选择之后点击右下角选择进行导出"
+  },
+  "header_pulling": {
+    "key": "header_pulling",
+    "type": "text",
+    "value": "下拉可以刷新"
+  },
+  "header_refreshing": {
+    "key": "header_refreshing",
+    "type": "text",
+    "value": "正在刷新..."
+  },
+  "header_loading": {
+    "key": "header_loading",
+    "type": "text",
+    "value": "正在加载..."
+  },
+  "header_release": {
+    "key": "header_release",
+    "type": "text",
+    "value": "释放立即刷新"
+  },
+  "header_finish": {
+    "key": "header_finish",
+    "type": "text",
+    "value": "刷新完成"
+  },
+  "header_failed": {
+    "key": "header_failed",
+    "type": "text",
+    "value": "刷新失败"
+  },
+  "header_update": {
+    "key": "header_update",
+    "type": "text",
+    "value": "上次更新 M-d HH:mm"
+  },
+  "header_secondary": {
+    "key": "header_secondary",
+    "type": "text",
+    "value": "释放进入二楼"
+  },
+  "footer_pulling": {
+    "key": "footer_pulling",
+    "type": "text",
+    "value": "上拉加载更多"
+  },
+  "footer_release": {
+    "key": "footer_release",
+    "type": "text",
+    "value": "释放立即加载"
+  },
+  "footer_loading": {
+    "key": "footer_loading",
+    "type": "text",
+    "value": "正在刷新..."
+  },
+  "footer_refreshing": {
+    "key": "footer_refreshing",
+    "type": "text",
+    "value": "正在加载..."
+  },
+  "footer_finish": {
+    "key": "footer_finish",
+    "type": "text",
+    "value": "加载完成"
+  },
+  "footer_failed": {
+    "key": "footer_failed",
+    "type": "text",
+    "value": "加载失败"
+  },
+  "footer_nothing": {
+    "key": "footer_nothing",
+    "type": "text",
+    "value": "全部加载完成"
+  },
+  "max_fingerprint_insert_tip": {
+    "key": "max_fingerprint_insert_tip",
+    "type": "text",
+    "value": "(指纹最多可录入{0}个)"
+  },
+  "fingerprint_limit_tip": {
+    "key": "fingerprint_limit_tip",
+    "type": "text",
+    "value": "指纹数量已达到上限"
+  },
+  "max_fingerprint_insert": {
+    "key": "max_fingerprint_insert",
+    "type": "text",
+    "value": "最大指纹录入数量:"
+  },
+  "auto_logout_time": {
+    "key": "auto_logout_time",
+    "type": "text",
+    "value": "自动登出时间(最低60,最高1800,单位:s):"
+  },
+  "please_input_max_fingerprint_entries_size": {
+    "key": "please_input_max_fingerprint_entries_size",
+    "type": "text",
+    "value": "请输入最大指纹录入数量"
+  },
+  "please_input_auto_logout_time": {
+    "key": "please_input_auto_logout_time",
+    "type": "text",
+    "value": "请输入自动登出时间"
+  },
+  "please_input_auto_logout_time_correct": {
+    "key": "please_input_auto_logout_time_correct",
+    "type": "text",
+    "value": "请设置正确的自动登出时间"
+  },
+  "data_export": {
+    "key": "data_export",
+    "type": "text",
+    "value": "数据导出"
+  },
+  "data_export_tip": {
+    "key": "data_export",
+    "type": "text",
+    "value": "请选择您要导出的表,然后点击导出。"
+  },
+  "data_table": {
+    "key": "data_table",
+    "type": "text",
+    "value": "数据表"
+  },
+  "last_export_datetime": {
+    "key": "last_export_datetime",
+    "type": "text",
+    "value": "上次导出时间"
+  },
+  "please_select_data_you_want_to_export": {
+    "key": "please_select_data_you_want_to_export",
+    "type": "text",
+    "value": "请选择你需要导出的数据表。"
+  },
+  "confirm_exec": {
+    "key": "confirm_exec",
+    "type": "text",
+    "value": "执行确认"
+  },
+  "data_export_success_tip": {
+    "key": "data_export_success_tip",
+    "type": "text",
+    "value": "数据导出完成,请选择文件夹并点击右下角按钮进行保存。"
+  },
+  "data_export_error": {
+    "key": "data_export_error",
+    "type": "text",
+    "value": "数据导出失败。"
+  },
+  "user": {
+    "key": "user",
+    "type": "text",
+    "value": "用户"
+  },
+  "role": {
+    "key": "role",
+    "type": "text",
+    "value": "角色"
+  },
+  "exporting": {
+    "key": "exporting",
+    "type": "text",
+    "value": "导出中……"
+  },
+  "data_in_backup": {
+    "key": "data_in_backup",
+    "type": "text",
+    "value": "数据备份中……"
+  },
+  "hardware_mode": {
+    "key": "hardware_mode",
+    "type": "text",
+    "value": "硬件模式(模式修改保存需要重启应用)"
+  },
+  "confirm": {
+    "key": "confirm",
+    "type": "text",
+    "value": "执行确认"
+  },
+  "exception_manage": {
+    "key": "exception_manage",
+    "type": "text",
+    "value": "异常管理"
+  },
+  "material_manage": {
+    "key": "material_manage",
+    "type": "text",
+    "value": "物资管理"
+  },
+  "statistical_analysis": {
+    "key": "statistical_analysis",
+    "type": "text",
+    "value": "数据统计"
+  },
+  "feature_under_development": {
+    "key": "feature_under_development",
+    "type": "text",
+    "value": "该功能正在开发中"
+  },
+  "dept_manage": {
+    "key": "dept_manage",
+    "type": "text",
+    "value": "部门管理"
+  },
+  "black_list_manage": {
+    "key": "black_list_manage",
+    "type": "text",
+    "value": "黑名单管理"
+  },
+  "login_log_manage": {
+    "key": "login_log_manage",
+    "type": "text",
+    "value": "登录日志"
+  },
+  "exchange_log_manage": {
+    "key": "exchange_log_manage",
+    "type": "text",
+    "value": "存取日志"
+  },
+  "material_exchange": {
+    "key": "material_exchange",
+    "type": "text",
+    "value": "物资存取"
+  },
+  "material_type": {
+    "key": "material_type",
+    "type": "text",
+    "value": "类型管理"
+  },
+  "material_check": {
+    "key": "material_check",
+    "type": "text",
+    "value": "物资检查"
+  },
+  "material_repair_and_replacement": {
+    "key": "material_repair_and_replacement",
+    "type": "text",
+    "value": "维修更换"
+  },
+  "material_instructions": {
+    "key": "material_instructions",
+    "type": "text",
+    "value": "物资使用说明"
+  },
+  "exception_report": {
+    "key": "exception_report",
+    "type": "text",
+    "value": "异常上报"
+  },
+  "warning_manage": {
+    "key": "warning_manage",
+    "type": "text",
+    "value": "报警管理"
+  },
+  "user_dept_analysis": {
+    "key": "user_dept_analysis",
+    "type": "text",
+    "value": "用户部门分析"
+  },
+  "use_behavior_analysis": {
+    "key": "use_behavior_analysis",
+    "type": "text",
+    "value": "使用行为分析"
+  },
+  "operation_and_maintenance_analysis": {
+    "key": "operation_and_maintenance_analysis",
+    "type": "text",
+    "value": "运行维护分析"
+  },
+  "inventory_turnover_analysis": {
+    "key": "inventory_turnover_analysis",
+    "type": "text",
+    "value": "库存周转分析"
+  },
+  "cost_benefit_analysis": {
+    "key": "cost_benefit_analysis",
+    "type": "text",
+    "value": "成本效益分析"
+  },
+  "security_compliance_analysis": {
+    "key": "security_compliance_analysis",
+    "type": "text",
+    "value": "安全合规分析"
   }
 }

+ 2 - 0
app/src/main/assets/themes/Default/icons/angle-circle-down.svg

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" id="arrow-circle-down" viewBox="0 0 24 24" width="512" height="512"><path d="M12,0A12,12,0,1,0,24,12,12.013,12.013,0,0,0,12,0Zm1.414,15.414a2,2,0,0,1-2.828,0L5.913,10.741,7.327,9.327,12,14l4.712-4.711L18.126,10.7Z"/></svg>

+ 2 - 0
app/src/main/assets/themes/Default/icons/stats.svg

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" id="Bold" viewBox="0 0 24 24" width="512" height="512"><path d="M5.5,21A2.5,2.5,0,0,1,3,18.5V1.5A1.5,1.5,0,0,0,1.5,0h0A1.5,1.5,0,0,0,0,1.5v17A5.5,5.5,0,0,0,5.5,24h17A1.5,1.5,0,0,0,24,22.5h0A1.5,1.5,0,0,0,22.5,21Z"/><path d="M19.5,18A1.5,1.5,0,0,0,21,16.5v-6a1.5,1.5,0,0,0-3,0v6A1.5,1.5,0,0,0,19.5,18Z"/><path d="M7.5,18A1.5,1.5,0,0,0,9,16.5v-6a1.5,1.5,0,0,0-3,0v6A1.5,1.5,0,0,0,7.5,18Z"/><path d="M13.5,18A1.5,1.5,0,0,0,15,16.5V5.5a1.5,1.5,0,0,0-3,0v11A1.5,1.5,0,0,0,13.5,18Z"/></svg>

+ 2 - 0
app/src/main/assets/themes/Default/icons/tags.svg

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="M21.526,8.284L13.937,.879C13.278,.219,12.33-.104,11.409,.028L4.521,.97c-.547,.075-.93,.579-.855,1.126,.075,.547,.578,.929,1.127,.855l6.889-.942c.306-.042,.622,.063,.851,.292l7.59,7.405c1.045,1.045,1.147,2.68,.323,3.847-.234-.467-.523-.912-.911-1.3l-7.475-7.412c-.658-.658-1.597-.975-2.528-.851l-6.889,.942c-.454,.062-.808,.425-.858,.881l-.765,6.916c-.1,.911,.214,1.804,.864,2.453l7.416,7.353c.944,.945,2.199,1.464,3.534,1.464h.017c1.342-.004,2.6-.532,3.543-1.487l3.167-3.208c.927-.939,1.393-2.159,1.423-3.388l.577-.576c1.925-1.95,1.914-5.112-.032-7.057Zm-15.526,1.716c-.552,0-1-.448-1-1,.006-1.308,1.994-1.307,2,0,0,.552-.448,1-1,1Z"/></svg>

+ 39 - 28
app/src/main/java/com/grkj/iscs_mc/ISCSMCApplication.kt

@@ -56,47 +56,58 @@ class ISCSMCApplication : Application() {
      */
     override fun onCreate() {
         super.onCreate()
+        // —— 只留轻量、必要初始化(主线程) ——
         DialogX.init(this)
         SIKCore.init(this)
-        //todo 模拟器不支持
-        ArcSoftUtil.checkActiveStatus(this)
-        ArcSoftUtil.initEngine(this)
         if (!EventBus.getDefault().isRegistered(this)) {
             EventBus.getDefault().register(this)
         }
         GlobalCrashCatch.instance.init(this).setGlobalCrashHandlerListener {
-            logger.error("异常发生", it)
-            true
+            logger.error("异常发生", it); true
         }
-        if (ISCSConfig.DEBUG) {
-            LogUtils.setGlobalLogLevel(Level.DEBUG)
-        } else {
-            LogUtils.setGlobalLogLevel(Level.INFO)
-        }
-        System.loadLibrary("sqlcipher")   // 新库必须手动加载
-        MMKV.initialize(this)
-        I18nManager.init(
-            defaultLocale = LanguageStore.resolveEffectiveLocale(this),
-            initialSources = arrayOf(
-                AssetsI18nSource(this, "i18n"),
-                FileI18nSource(this, "i18n")
-            ),
-            eagerLoad = true
-        )
-
-        // 建议:可提前刷新一下目录(不强制)
-        LanguageCatalog.refresh(this)
+        LogUtils.setGlobalLogLevel(if (ISCSConfig.DEBUG) Level.DEBUG else Level.INFO)
 
-        // 若“跟随系统”,监听系统语言变化
-        LanguageStore.registerSystemLocaleObserver(this)
-        AutoSizeConfig.getInstance().isCustomFragment = true
+        // 这些通常很轻,可以保留在主线程(如需极致,可推迟到首 Activity onCreate)
         StateConfig.emptyLayout = com.grkj.ui_base.R.layout.layout_empty
+        AutoSizeConfig.getInstance().isCustomFragment = true
+
+        // —— 把可疑重活统统后台化 ——
         ThreadUtils.runOnIO {
+            // 1) so 延后加载(在首次真的要用到加密 DB 前加载)
+            kotlin.runCatching { System.loadLibrary("sqlcipher") }
+                .onFailure { logger.error("loadLibrary(sqlcipher) 失败", it) }
+
+            // 2) MMKV 也放 IO(通常很快,但避免冷启动抖动)
+            kotlin.runCatching { MMKV.initialize(this@ISCSMCApplication) }
+                .onFailure { logger.error("MMKV 初始化失败", it) }
+
+            // 3) I18n 延后且取消 eagerLoad;先快速起一个最小集,完整加载后台做
+            kotlin.runCatching {
+                I18nManager.init(
+                    defaultLocale = LanguageStore.resolveEffectiveLocale(this@ISCSMCApplication),
+                    initialSources = arrayOf(
+                        AssetsI18nSource(this@ISCSMCApplication, "i18n"),
+                        FileI18nSource(this@ISCSMCApplication, "i18n")
+                    ),
+                    eagerLoad = false      // 👈 关键:不要在主线程做全量加载
+                )
+                LanguageCatalog.refresh(this@ISCSMCApplication) // 目录扫描放后台
+                // 跟随系统语言监听无需重活,可在主线程也行
+                LanguageStore.registerSystemLocaleObserver(this@ISCSMCApplication)
+            }.onFailure { logger.error("I18n 初始化失败", it) }
+
+            // 4) 人脸引擎:license/引擎初始化通常很贵 → 后台预热
+            kotlin.runCatching {
+                ArcSoftUtil.checkActiveStatus(this@ISCSMCApplication)
+                ArcSoftUtil.initEngine(this@ISCSMCApplication)
+            }.onFailure { logger.error("ArcSoft 引擎初始化失败", it) }
+
+            // 5) 数据库/业务/硬件连接(你原来就放 IO,很好)
             DbReadyGate.await()
             LogicManager.init(this@ISCSMCApplication)
             CanHelper.connect()
-            CanHelper.addDeviceChangeListener(this) {
-                logger.info("有设备更新:${it}")
+            CanHelper.addDeviceChangeListener(this@ISCSMCApplication) {
+                logger.info("有设备更新: $it")
             }
         }
     }

+ 2 - 1
app/src/main/java/com/grkj/iscs_mc/features/login/fragment/LoginFragment.kt

@@ -43,6 +43,7 @@ import com.grkj.shared.utils.ArcSoftUtil
 import com.grkj.shared.utils.ArcSoftUtil.inDetecting
 import com.grkj.ui_base.utils.event.RFIDReadEvent
 import com.grkj.ui_base.utils.extension.getAppVersionName
+import com.grkj.ui_base.utils.extension.serialNo
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikandroid.activity.ActivityTracker
 import com.sik.sikcore.extension.setDebouncedClickListener
@@ -334,7 +335,7 @@ class LoginFragment : BaseFragment<FragmentLoginBinding>() {
 
     override fun initData() {
         super.initData()
-        binding.version.text = requireContext().getAppVersionName()
+        binding.version.text = "${requireContext().getAppVersionName()}-${requireContext().serialNo()}"
         loginMenuList.apply {
             add(
                 LoginMenuEntity(

+ 28 - 2
app/src/main/java/com/grkj/iscs_mc/features/main/activity/MainActivity.kt

@@ -28,6 +28,7 @@ import com.grkj.shared.utils.extension.toByteArrays
 import com.grkj.shared.utils.extension.toHexStrings
 import com.grkj.shared.utils.i18n.I18nManager
 import com.grkj.ui_base.base.BaseNavActivity
+import com.grkj.ui_base.utils.event.BottomTipEvent
 import com.grkj.ui_base.utils.event.RFIDReadEvent
 import com.grkj.ui_base.utils.extension.removeTint
 import com.sik.sikcore.extension.file
@@ -35,6 +36,7 @@ import com.sik.sikcore.extension.getMMKVData
 import com.sik.sikcore.extension.setDebouncedClickListener
 import com.sik.sikimage.ImageConvertUtils
 import dagger.hilt.android.AndroidEntryPoint
+import org.checkerframework.common.subtyping.qual.Bottom
 
 /**
  * 首页
@@ -59,19 +61,36 @@ class MainActivity() : BaseNavActivity<ActivityMainBinding>() {
             "icon_bottom_menu_data_manage.svg",
             RoleFunctionalPermissionsEnum.DATA_HOME_MANAGE.functionalPermission
         ),
+        TabConfig(
+            View.generateViewId(),
+            R.navigation.nav_material_manage,
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_HOME_MANAGE.description),
+            "shelves.svg",
+            RoleFunctionalPermissionsEnum.MATERIAL_HOME_MANAGE.functionalPermission
+        ),
         TabConfig(
             View.generateViewId(),
             R.navigation.nav_exception_manage,
             I18nManager.t(RoleFunctionalPermissionsEnum.EXCEPTION_HOME_MANAGE.description),
-            "icon_bottom_menu_exception_manage.svg",
+            "hexagon-exclamation.svg",
             RoleFunctionalPermissionsEnum.EXCEPTION_HOME_MANAGE.functionalPermission
         ),
+        TabConfig(
+            View.generateViewId(),
+            R.navigation.nav_statistical_analysis,
+            I18nManager.t(RoleFunctionalPermissionsEnum.STATISTICAL_ANALYSIS_MANAGE.description),
+            "stats.svg",
+            RoleFunctionalPermissionsEnum.STATISTICAL_ANALYSIS_MANAGE.functionalPermission
+        )
     )
 
     private val bottomNavDestinations = setOf(
         R.id.homeFragment,
         R.id.dataManageHomeFragment,
-        R.id.exceptionHomeFragment
+        R.id.materialHomeManageFragment,
+        R.id.exceptionHomeManage,
+        R.id.statisticalAnalysisHomeManage,
+        R.id.userInfoHomeFragment
     )
 
     override fun navHostFragmentId() = R.id.nav_host_fragment
@@ -143,6 +162,13 @@ class MainActivity() : BaseNavActivity<ActivityMainBinding>() {
             EventConstants.EVENT_LOGOUT -> {
                 logout()
             }
+
+            EventConstants.EVENT_BOTTOM_TIP -> {
+                (event.data as BottomTipEvent).let {
+                    binding.bottomTip.isVisible = !it.msg.isNullOrEmpty()
+                    binding.bottomTip.text = it.msg.toString()
+                }
+            }
         }
     }
 

+ 4 - 4
app/src/main/java/com/grkj/iscs_mc/features/main/dialog/QuickEntranceConfigDialog.kt

@@ -58,10 +58,10 @@ class QuickEntranceConfigDialog(private val save: (String) -> Unit) :
                     QuickEntranceMenuItemEntity(
                         0,
                         "document.svg",
-                        I18nManager.t(RoleFunctionalPermissionsEnum.EXCHANGE_RECORD.description),
-                        RoleFunctionalPermissionsEnum.EXCHANGE_RECORD,
-                        QuickEntranceMenuItemEntity.getNavGraphId(RoleFunctionalPermissionsEnum.EXCHANGE_RECORD),
-                        QuickEntranceMenuItemEntity.getDestId(RoleFunctionalPermissionsEnum.EXCHANGE_RECORD)
+                        I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE.description),
+                        RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE,
+                        QuickEntranceMenuItemEntity.getNavGraphId(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE),
+                        QuickEntranceMenuItemEntity.getDestId(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE)
                     )
                 )
             } else {

+ 23 - 0
app/src/main/java/com/grkj/iscs_mc/features/main/entity/MaterialExchangeHeaderEntity.kt

@@ -0,0 +1,23 @@
+package com.grkj.iscs_mc.features.main.entity
+
+/**
+ * 物资取环表头实体
+ */
+data class MaterialExchangeHeaderEntity(
+    /**
+     * 头部图标
+     */
+    val headerIcon: String,
+    /**
+     * 头部标题 key
+     */
+    val headerTitle: String,
+    /**
+     * 头部数量
+     */
+    val headerNumber: Int,
+    /**
+     * 类型
+     */
+    val materialsTypeId: Long
+)

+ 51 - 1
app/src/main/java/com/grkj/iscs_mc/features/main/entity/QuickEntranceMenuItemEntity.kt

@@ -29,6 +29,31 @@ data class QuickEntranceMenuItemEntity(
             return when (permission) {
                 RoleFunctionalPermissionsEnum.USER_MANAGE -> "user.svg"
                 RoleFunctionalPermissionsEnum.ROLE_MANAGE -> "users-alt.svg"
+                RoleFunctionalPermissionsEnum.DEPT_MANAGE -> "boss.svg"
+                RoleFunctionalPermissionsEnum.BLACK_LIST_MANAGE -> "member-list.svg"
+                RoleFunctionalPermissionsEnum.LOGIN_LOG_MANAGE -> "log-file.svg"
+                RoleFunctionalPermissionsEnum.EXCHANGE_LOG_MANAGE -> "table-list.svg"
+                RoleFunctionalPermissionsEnum.DATA_EXPORT -> "icon_data_export.png"
+                RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE -> "back-up.svg"
+
+                RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE -> "box-loading.svg"
+                RoleFunctionalPermissionsEnum.MATERIAL_TYPE -> "tags.svg"
+                RoleFunctionalPermissionsEnum.MATERIAL_MANAGE -> "boxes.svg"
+                RoleFunctionalPermissionsEnum.MATERIAL_CHECK -> "inventory-alt.svg"
+                RoleFunctionalPermissionsEnum.MATERIAL_REPAIR_AND_REPLACEMENT -> "dialogue-exchange.svg"
+                RoleFunctionalPermissionsEnum.MATERIAL_INSTRUCTIONS -> "play-alt.svg"
+
+                RoleFunctionalPermissionsEnum.EXCEPTION_REPORT -> "limit-hand.svg"
+                RoleFunctionalPermissionsEnum.EXCEPTION_MANAGE -> "sensor-alert.svg"
+                RoleFunctionalPermissionsEnum.WARNING_MANAGE -> "light-emergency-on.svg"
+
+                RoleFunctionalPermissionsEnum.USER_DEPT_ANALYSIS -> "chart-user.svg"
+                RoleFunctionalPermissionsEnum.USE_BEHAVIOR_ANALYSIS -> "supplier-alt.svg"
+                RoleFunctionalPermissionsEnum.OPERATION_AND_MAINTENANCE_ANALYSIS -> "data-transfer.svg"
+                RoleFunctionalPermissionsEnum.INVENTORY_TURNOVER_ANALYSIS -> "store-buyer.svg"
+                RoleFunctionalPermissionsEnum.COST_BENEFIT_ANALYSIS -> "money-income.svg"
+                RoleFunctionalPermissionsEnum.SECURITY_COMPLIANCE_ANALYSIS -> "shield-check.svg"
+
                 RoleFunctionalPermissionsEnum.USER_INFO -> "chalkboard-user.svg"
                 RoleFunctionalPermissionsEnum.RESET_PASSWORD -> "password-lock.svg"
                 RoleFunctionalPermissionsEnum.FINGERPRINT_SETTING -> "fingerprint.svg"
@@ -44,7 +69,32 @@ data class QuickEntranceMenuItemEntity(
         @JvmStatic
         fun getNavGraphId(permission: RoleFunctionalPermissionsEnum): Int {
             return when (permission) {
-                RoleFunctionalPermissionsEnum.USER_MANAGE, RoleFunctionalPermissionsEnum.ROLE_MANAGE,
+                RoleFunctionalPermissionsEnum.USER_MANAGE,
+                RoleFunctionalPermissionsEnum.ROLE_MANAGE,
+                RoleFunctionalPermissionsEnum.DEPT_MANAGE,
+                RoleFunctionalPermissionsEnum.BLACK_LIST_MANAGE,
+                RoleFunctionalPermissionsEnum.LOGIN_LOG_MANAGE,
+                RoleFunctionalPermissionsEnum.EXCHANGE_LOG_MANAGE,
+                RoleFunctionalPermissionsEnum.DATA_EXPORT,
+                RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE -> R.navigation.nav_data_manage
+
+                RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE,
+                RoleFunctionalPermissionsEnum.MATERIAL_TYPE,
+                RoleFunctionalPermissionsEnum.MATERIAL_MANAGE,
+                RoleFunctionalPermissionsEnum.MATERIAL_CHECK,
+                RoleFunctionalPermissionsEnum.MATERIAL_REPAIR_AND_REPLACEMENT,
+                RoleFunctionalPermissionsEnum.MATERIAL_INSTRUCTIONS -> R.navigation.nav_material_manage
+
+                RoleFunctionalPermissionsEnum.EXCEPTION_REPORT,
+                RoleFunctionalPermissionsEnum.EXCEPTION_MANAGE,
+                RoleFunctionalPermissionsEnum.WARNING_MANAGE -> R.navigation.nav_exception_manage
+
+                RoleFunctionalPermissionsEnum.USER_DEPT_ANALYSIS,
+                RoleFunctionalPermissionsEnum.USE_BEHAVIOR_ANALYSIS,
+                RoleFunctionalPermissionsEnum.OPERATION_AND_MAINTENANCE_ANALYSIS,
+                RoleFunctionalPermissionsEnum.INVENTORY_TURNOVER_ANALYSIS,
+                RoleFunctionalPermissionsEnum.COST_BENEFIT_ANALYSIS,
+                RoleFunctionalPermissionsEnum.SECURITY_COMPLIANCE_ANALYSIS -> R.navigation.nav_statistical_analysis
 
                 RoleFunctionalPermissionsEnum.USER_INFO,
                 RoleFunctionalPermissionsEnum.RESET_PASSWORD,

+ 35 - 4
app/src/main/java/com/grkj/iscs_mc/features/main/fragment/data_manage/DataManageHomeFragment.kt

@@ -20,6 +20,7 @@ import com.grkj.iscs_mc.features.main.entity.MenuItemEntity
 import com.grkj.shared.utils.i18n.I18nManager
 import com.grkj.ui_base.base.BaseFragment
 import com.grkj.ui_base.skin.loadSkinIcon
+import com.grkj.ui_base.utils.CommonUtils
 import com.grkj.ui_base.utils.changeBgTint
 import com.sik.sikcore.extension.setDebouncedClickListener
 import dagger.hilt.android.AndroidEntryPoint
@@ -42,18 +43,42 @@ class DataManageHomeFragment : BaseFragment<FragmentDataManageHomeBinding>() {
             I18nManager.t(RoleFunctionalPermissionsEnum.ROLE_MANAGE.description),
             RoleFunctionalPermissionsEnum.ROLE_MANAGE.functionalPermission
         ),
+        MenuItemEntity(
+            2,
+            "boss.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.DEPT_MANAGE.description),
+            RoleFunctionalPermissionsEnum.DEPT_MANAGE.functionalPermission
+        ),
+        MenuItemEntity(
+            3,
+            "member-list.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.BLACK_LIST_MANAGE.description),
+            RoleFunctionalPermissionsEnum.BLACK_LIST_MANAGE.functionalPermission
+        ),
         MenuItemEntity(
             4,
-            "back-up.svg",
-            I18nManager.t(RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE.description),
-            RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE.functionalPermission
+            "log-file.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.LOGIN_LOG_MANAGE.description),
+            RoleFunctionalPermissionsEnum.LOGIN_LOG_MANAGE.functionalPermission
         ),
         MenuItemEntity(
             5,
+            "table-list.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.EXCHANGE_LOG_MANAGE.description),
+            RoleFunctionalPermissionsEnum.EXCHANGE_LOG_MANAGE.functionalPermission
+        ),
+        MenuItemEntity(
+            6,
             "icon_data_export.png",
             I18nManager.t(RoleFunctionalPermissionsEnum.DATA_EXPORT.description),
             RoleFunctionalPermissionsEnum.DATA_EXPORT.functionalPermission
         ),
+        MenuItemEntity(
+            7,
+            "back-up.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE.description),
+            RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE.functionalPermission
+        ),
     )
 
     override fun getLayoutId(): Int {
@@ -97,7 +122,13 @@ class DataManageHomeFragment : BaseFragment<FragmentDataManageHomeBinding>() {
 
     private fun onMenuClick(menuType: Int) {
         when (menuType) {
-
+            0->navController.navigate(R.id.action_dataManageHomeFragment_to_userManageFragment)
+            1->navController.navigate(R.id.action_dataManageHomeFragment_to_roleManageFragment)
+            6->navController.navigate(R.id.action_dataManageHomeFragment_to_dataExportFragment)
+            7->navController.navigate(R.id.action_dataManageHomeFragment_to_backupAndRestoreFragment)
+            else -> {
+                showToast(CommonUtils.getStr("feature_under_development"))
+            }
         }
     }
 }

+ 99 - 0
app/src/main/java/com/grkj/iscs_mc/features/main/fragment/exception_manage/ExceptionHomeManage.kt

@@ -0,0 +1,99 @@
+package com.grkj.iscs_mc.features.main.fragment.exception_manage
+
+import androidx.annotation.OptIn
+import com.drake.brv.BindingAdapter
+import com.drake.brv.annotaion.DividerOrientation
+import com.drake.brv.utils.dividerSpace
+import com.drake.brv.utils.models
+import com.drake.brv.utils.setup
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexWrap
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.material.badge.ExperimentalBadgeUtils
+import com.grkj.data.common.MainDomainData
+import com.grkj.data.enums.RoleFunctionalPermissionsEnum
+import com.grkj.iscs_mc.R
+import com.grkj.iscs_mc.databinding.FragmentExceptionManageHomeBinding
+import com.grkj.iscs_mc.databinding.ItemHomeMenuBinding
+import com.grkj.iscs_mc.features.main.entity.MenuItemEntity
+import com.grkj.shared.utils.i18n.I18nManager
+import com.grkj.ui_base.base.BaseFragment
+import com.grkj.ui_base.skin.loadSkinIcon
+import com.grkj.ui_base.utils.CommonUtils
+import com.grkj.ui_base.utils.changeBgTint
+import com.sik.sikcore.extension.setDebouncedClickListener
+import dagger.hilt.android.AndroidEntryPoint
+
+/**
+ * 异常首页
+ */
+@AndroidEntryPoint
+class ExceptionHomeManage: BaseFragment<FragmentExceptionManageHomeBinding>() {
+    private var menuData: MutableList<MenuItemEntity> = mutableListOf(
+        MenuItemEntity(
+            0,
+            "limit-hand.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.EXCEPTION_REPORT.description),
+            RoleFunctionalPermissionsEnum.EXCEPTION_REPORT.functionalPermission
+        ),
+        MenuItemEntity(
+            1,
+            "sensor-alert.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.EXCEPTION_MANAGE.description),
+            RoleFunctionalPermissionsEnum.EXCEPTION_MANAGE.functionalPermission
+        ),
+        MenuItemEntity(
+            2,
+            "light-emergency-on.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.WARNING_MANAGE.description),
+            RoleFunctionalPermissionsEnum.WARNING_MANAGE.functionalPermission
+        ),
+    )
+    override fun getLayoutId(): Int {
+        return R.layout.fragment_exception_manage_home
+    }
+
+    override fun initView() {
+        binding.homeMenuRv.apply {
+            layoutManager = FlexboxLayoutManager(requireContext()).apply {
+                flexDirection = FlexDirection.ROW        // 横向布局
+                flexWrap = FlexWrap.WRAP
+                alignItems = AlignItems.STRETCH   // ✅ 不会被压扁
+            }
+            dividerSpace(40, DividerOrientation.GRID)
+        }.setup {
+            addType<MenuItemEntity>(R.layout.item_home_menu)
+            onBind {
+                onHomeMenuBinding(this)
+            }
+        }
+    }
+
+    override fun initData() {
+        super.initData()
+        menuData =
+            menuData.filter { MainDomainData.permissions.contains(it.permission) }.toMutableList()
+        binding.homeMenuRv.models = menuData
+    }
+
+    @OptIn(ExperimentalBadgeUtils::class)
+    private fun BindingAdapter.BindingViewHolder.onHomeMenuBinding(holder: BindingAdapter.BindingViewHolder) {
+        val itemBinding = holder.getBinding<ItemHomeMenuBinding>()
+        val item = holder.getModel<MenuItemEntity>()
+        itemBinding.homeMenuIv.loadSkinIcon(item.menuIcon)
+        itemBinding.homeMenuTv.text = item.menuText
+        itemBinding.homeMenuLayout.changeBgTint(item.menuBgTint)
+        itemBinding.root.setDebouncedClickListener {
+            onMenuClick(item.type)
+        }
+    }
+
+    private fun onMenuClick(menuType: Int) {
+        when (menuType) {
+            else -> {
+                showToast(CommonUtils.getStr("feature_under_development"))
+            }
+        }
+    }
+}

+ 4 - 4
app/src/main/java/com/grkj/iscs_mc/features/main/fragment/home/HomeFragment.kt

@@ -37,10 +37,10 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
                     QuickEntranceMenuItemEntity(
                         0,
                         "document.svg",
-                        I18nManager.t(RoleFunctionalPermissionsEnum.EXCHANGE_RECORD.description),
-                        RoleFunctionalPermissionsEnum.EXCHANGE_RECORD,
-                        QuickEntranceMenuItemEntity.getNavGraphId(RoleFunctionalPermissionsEnum.EXCHANGE_RECORD),
-                        QuickEntranceMenuItemEntity.getDestId(RoleFunctionalPermissionsEnum.EXCHANGE_RECORD)
+                        I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE.description),
+                        RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE,
+                        QuickEntranceMenuItemEntity.getNavGraphId(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE),
+                        QuickEntranceMenuItemEntity.getDestId(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE)
                     )
                 )
             } else {

+ 119 - 0
app/src/main/java/com/grkj/iscs_mc/features/main/fragment/material_manage/MaterialExchangeFragment.kt

@@ -0,0 +1,119 @@
+package com.grkj.iscs_mc.features.main.fragment.material_manage
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.drake.brv.BindingAdapter
+import com.drake.brv.utils.linear
+import com.drake.brv.utils.setup
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexWrap
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.flexbox.JustifyContent
+import com.grkj.iscs_mc.R
+import com.grkj.iscs_mc.databinding.FragmentMaterialExchangeBinding
+import com.grkj.iscs_mc.databinding.ItemMaterialExchangeHeaderBinding
+import com.grkj.iscs_mc.features.main.entity.MaterialExchangeHeaderEntity
+import com.grkj.ui_base.base.BaseFragment
+import com.grkj.ui_base.skin.loadSkinIcon
+import com.grkj.ui_base.utils.CommonUtils
+import com.sik.sikcore.extension.setDebouncedClickListener
+import dagger.hilt.android.AndroidEntryPoint
+
+/**
+ * 物资取还
+ */
+@AndroidEntryPoint
+class MaterialExchangeFragment : BaseFragment<FragmentMaterialExchangeBinding>() {
+    override fun getLayoutId(): Int {
+        return R.layout.fragment_material_exchange
+    }
+
+    override fun initView() {
+        binding.headerRvList.linear(LinearLayoutManager.HORIZONTAL).setup {
+            addType<MaterialExchangeHeaderEntity>(R.layout.item_material_exchange_header)
+            onBind {
+                onHeaderRVListBinding()
+            }
+        }
+    }
+
+    private fun BindingAdapter.BindingViewHolder.onHeaderRVListBinding() {
+        val item = getModel<MaterialExchangeHeaderEntity>()
+        val itemBinding = getBinding<ItemMaterialExchangeHeaderBinding>()
+        itemBinding.headerIcons.loadSkinIcon(item.headerIcon)
+        itemBinding.headerTitle.text = CommonUtils.getStr(item.headerTitle, item.headerNumber)
+        itemBinding.root.setDebouncedClickListener {
+            changeMaterialData(item.materialsTypeId)
+        }
+    }
+
+    /**
+     * 变更物资数据
+     */
+    private fun changeMaterialData(materialsTypeId: Long) {
+
+    }
+
+    fun toggleHeader(expanded: Boolean) {
+        val rv = binding.headerRvList
+
+        // 切换前压一下布局 & 动画,避免闪烁
+        rv.itemAnimator = null
+        rv.suppressLayout(true)
+
+        if (expanded) {
+            // 展开:Flexbox 多行换行
+            val flex = FlexboxLayoutManager(rv.context).apply {
+                flexDirection = FlexDirection.ROW
+                flexWrap = FlexWrap.WRAP
+                justifyContent = JustifyContent.FLEX_START
+                alignItems = AlignItems.FLEX_START
+            }
+            // 如果之前加过 DividerItemDecoration(Linear 用),先移除
+            removeLinearDividerIfAny(rv)
+
+            rv.layoutManager = flex
+            // 展开时通常需要自适应高度
+            rv.layoutParams = rv.layoutParams.apply {
+                height = ViewGroup.LayoutParams.WRAP_CONTENT
+            }
+            // Flex 场景一般不需要过度回弹
+            rv.overScrollMode = View.OVER_SCROLL_NEVER
+        } else {
+            // 收起:单行横向
+            val linear = LinearLayoutManager(rv.context, RecyclerView.HORIZONTAL, false)
+            rv.layoutManager = linear
+            // 收起时限制高度为一行(按你项目的 chip 高度 + padding 来)
+            rv.layoutParams = rv.layoutParams.apply {
+                height =
+                    rv.resources.getDimensionPixelSize(com.grkj.ui_base.R.dimen.header_chip_row_height)
+            }
+            // 如需单行分隔/间距可恢复 DividerItemDecoration(HORIZONTAL)
+            addLinearDividerIfNeeded(rv)
+        }
+
+        // 触发行高/换行的重新测量
+        rv.requestLayout()
+        rv.suppressLayout(false)
+        rv.itemAnimator = DefaultItemAnimator() // 需要再开动画就恢复
+    }
+
+    private fun removeLinearDividerIfAny(rv: RecyclerView) {
+        val count = rv.itemDecorationCount
+        for (i in count - 1 downTo 0) {
+            val d = rv.getItemDecorationAt(i)
+            if (d is DividerItemDecoration) rv.removeItemDecoration(d)
+        }
+    }
+
+    private fun addLinearDividerIfNeeded(rv: RecyclerView) {
+        // 视需求添加;如果你用的是 item 自带 margin,那就不用 Divider
+        // rv.addItemDecoration(DividerItemDecoration(rv.context, RecyclerView.HORIZONTAL))
+    }
+
+}

+ 120 - 0
app/src/main/java/com/grkj/iscs_mc/features/main/fragment/material_manage/MaterialHomeManageFragment.kt

@@ -0,0 +1,120 @@
+package com.grkj.iscs_mc.features.main.fragment.material_manage
+
+import androidx.annotation.OptIn
+import com.drake.brv.BindingAdapter
+import com.drake.brv.annotaion.DividerOrientation
+import com.drake.brv.utils.dividerSpace
+import com.drake.brv.utils.models
+import com.drake.brv.utils.setup
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexWrap
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.material.badge.ExperimentalBadgeUtils
+import com.grkj.data.common.MainDomainData
+import com.grkj.data.enums.RoleFunctionalPermissionsEnum
+import com.grkj.iscs_mc.R
+import com.grkj.iscs_mc.databinding.FragmentMaterialManageHomeBinding
+import com.grkj.iscs_mc.databinding.ItemHomeMenuBinding
+import com.grkj.iscs_mc.features.main.entity.MenuItemEntity
+import com.grkj.shared.utils.i18n.I18nManager
+import com.grkj.ui_base.base.BaseFragment
+import com.grkj.ui_base.skin.loadSkinIcon
+import com.grkj.ui_base.utils.CommonUtils
+import com.grkj.ui_base.utils.changeBgTint
+import com.sik.sikcore.extension.setDebouncedClickListener
+import dagger.hilt.android.AndroidEntryPoint
+
+/**
+ * 物资管理
+ */
+@AndroidEntryPoint
+class MaterialHomeManageFragment : BaseFragment<FragmentMaterialManageHomeBinding>() {
+
+    private var menuData: MutableList<MenuItemEntity> = mutableListOf(
+        MenuItemEntity(
+            0,
+            "box-loading.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE.description),
+            RoleFunctionalPermissionsEnum.MATERIAL_EXCHANGE.functionalPermission
+        ),
+        MenuItemEntity(
+            1,
+            "tags.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_TYPE.description),
+            RoleFunctionalPermissionsEnum.MATERIAL_TYPE.functionalPermission
+        ),
+        MenuItemEntity(
+            2,
+            "boxes.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_MANAGE.description),
+            RoleFunctionalPermissionsEnum.MATERIAL_MANAGE.functionalPermission
+        ),
+        MenuItemEntity(
+            3,
+            "inventory-alt.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_CHECK.description),
+            RoleFunctionalPermissionsEnum.MATERIAL_CHECK.functionalPermission
+        ),
+        MenuItemEntity(
+            4,
+            "dialogue-exchange.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_REPAIR_AND_REPLACEMENT.description),
+            RoleFunctionalPermissionsEnum.MATERIAL_REPAIR_AND_REPLACEMENT.functionalPermission
+        ),
+        MenuItemEntity(
+            5,
+            "play-alt.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.MATERIAL_INSTRUCTIONS.description),
+            RoleFunctionalPermissionsEnum.MATERIAL_INSTRUCTIONS.functionalPermission
+        ),
+    )
+
+    override fun getLayoutId(): Int {
+        return R.layout.fragment_material_manage_home
+    }
+
+    override fun initView() {
+        binding.homeMenuRv.apply {
+            layoutManager = FlexboxLayoutManager(requireContext()).apply {
+                flexDirection = FlexDirection.ROW        // 横向布局
+                flexWrap = FlexWrap.WRAP
+                alignItems = AlignItems.STRETCH   // ✅ 不会被压扁
+            }
+            dividerSpace(40, DividerOrientation.GRID)
+        }.setup {
+            addType<MenuItemEntity>(R.layout.item_home_menu)
+            onBind {
+                onHomeMenuBinding(this)
+            }
+        }
+    }
+
+    override fun initData() {
+        super.initData()
+        menuData =
+            menuData.filter { MainDomainData.permissions.contains(it.permission) }.toMutableList()
+        binding.homeMenuRv.models = menuData
+    }
+
+    @OptIn(ExperimentalBadgeUtils::class)
+    private fun BindingAdapter.BindingViewHolder.onHomeMenuBinding(holder: BindingAdapter.BindingViewHolder) {
+        val itemBinding = holder.getBinding<ItemHomeMenuBinding>()
+        val item = holder.getModel<MenuItemEntity>()
+        itemBinding.homeMenuIv.loadSkinIcon(item.menuIcon)
+        itemBinding.homeMenuTv.text = item.menuText
+        itemBinding.homeMenuLayout.changeBgTint(item.menuBgTint)
+        itemBinding.root.setDebouncedClickListener {
+            onMenuClick(item.type)
+        }
+    }
+
+    private fun onMenuClick(menuType: Int) {
+        when (menuType) {
+            0 -> navController.navigate(R.id.action_materialHomeManageFragment_to_materialExchangeFragment)
+            else -> {
+                showToast(CommonUtils.getStr("feature_under_development"))
+            }
+        }
+    }
+}

+ 118 - 0
app/src/main/java/com/grkj/iscs_mc/features/main/fragment/statistical_analysis_manage/StatisticalAnalysisHomeManage.kt

@@ -0,0 +1,118 @@
+package com.grkj.iscs_mc.features.main.fragment.statistical_analysis_manage
+
+import androidx.annotation.OptIn
+import com.drake.brv.BindingAdapter
+import com.drake.brv.annotaion.DividerOrientation
+import com.drake.brv.utils.dividerSpace
+import com.drake.brv.utils.models
+import com.drake.brv.utils.setup
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexWrap
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.material.badge.ExperimentalBadgeUtils
+import com.grkj.data.common.MainDomainData
+import com.grkj.data.enums.RoleFunctionalPermissionsEnum
+import com.grkj.iscs_mc.R
+import com.grkj.iscs_mc.databinding.FragmentStatisticalAnalysisManageHomeBinding
+import com.grkj.iscs_mc.databinding.ItemHomeMenuBinding
+import com.grkj.iscs_mc.features.main.entity.MenuItemEntity
+import com.grkj.shared.utils.i18n.I18nManager
+import com.grkj.ui_base.base.BaseFragment
+import com.grkj.ui_base.skin.loadSkinIcon
+import com.grkj.ui_base.utils.CommonUtils
+import com.grkj.ui_base.utils.changeBgTint
+import com.sik.sikcore.extension.setDebouncedClickListener
+import dagger.hilt.android.AndroidEntryPoint
+
+/**
+ * 数据统计首页
+ */
+@AndroidEntryPoint
+class StatisticalAnalysisHomeManage : BaseFragment<FragmentStatisticalAnalysisManageHomeBinding>() {
+
+    private var menuData: MutableList<MenuItemEntity> = mutableListOf(
+        MenuItemEntity(
+            0,
+            "chart-user.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.USER_DEPT_ANALYSIS.description),
+            RoleFunctionalPermissionsEnum.USER_DEPT_ANALYSIS.functionalPermission
+        ),
+        MenuItemEntity(
+            1,
+            "supplier-alt.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.USE_BEHAVIOR_ANALYSIS.description),
+            RoleFunctionalPermissionsEnum.USE_BEHAVIOR_ANALYSIS.functionalPermission
+        ),
+        MenuItemEntity(
+            2,
+            "data-transfer.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.OPERATION_AND_MAINTENANCE_ANALYSIS.description),
+            RoleFunctionalPermissionsEnum.OPERATION_AND_MAINTENANCE_ANALYSIS.functionalPermission
+        ),
+        MenuItemEntity(
+            3,
+            "store-buyer.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.INVENTORY_TURNOVER_ANALYSIS.description),
+            RoleFunctionalPermissionsEnum.INVENTORY_TURNOVER_ANALYSIS.functionalPermission
+        ),
+        MenuItemEntity(
+            4,
+            "money-income.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.COST_BENEFIT_ANALYSIS.description),
+            RoleFunctionalPermissionsEnum.COST_BENEFIT_ANALYSIS.functionalPermission
+        ),
+        MenuItemEntity(
+            5,
+            "shield-check.svg",
+            I18nManager.t(RoleFunctionalPermissionsEnum.SECURITY_COMPLIANCE_ANALYSIS.description),
+            RoleFunctionalPermissionsEnum.SECURITY_COMPLIANCE_ANALYSIS.functionalPermission
+        ),
+    )
+    override fun getLayoutId(): Int {
+        return R.layout.fragment_statistical_analysis_manage_home
+    }
+
+    override fun initView() {
+        binding.homeMenuRv.apply {
+            layoutManager = FlexboxLayoutManager(requireContext()).apply {
+                flexDirection = FlexDirection.ROW        // 横向布局
+                flexWrap = FlexWrap.WRAP
+                alignItems = AlignItems.STRETCH   // ✅ 不会被压扁
+            }
+            dividerSpace(40, DividerOrientation.GRID)
+        }.setup {
+            addType<MenuItemEntity>(R.layout.item_home_menu)
+            onBind {
+                onHomeMenuBinding(this)
+            }
+        }
+    }
+
+    override fun initData() {
+        super.initData()
+        menuData =
+            menuData.filter { MainDomainData.permissions.contains(it.permission) }.toMutableList()
+        binding.homeMenuRv.models = menuData
+    }
+
+    @OptIn(ExperimentalBadgeUtils::class)
+    private fun BindingAdapter.BindingViewHolder.onHomeMenuBinding(holder: BindingAdapter.BindingViewHolder) {
+        val itemBinding = holder.getBinding<ItemHomeMenuBinding>()
+        val item = holder.getModel<MenuItemEntity>()
+        itemBinding.homeMenuIv.loadSkinIcon(item.menuIcon)
+        itemBinding.homeMenuTv.text = item.menuText
+        itemBinding.homeMenuLayout.changeBgTint(item.menuBgTint)
+        itemBinding.root.setDebouncedClickListener {
+            onMenuClick(item.type)
+        }
+    }
+
+    private fun onMenuClick(menuType: Int) {
+        when (menuType) {
+            else -> {
+                showToast(CommonUtils.getStr("feature_under_development"))
+            }
+        }
+    }
+}

+ 16 - 0
app/src/main/res/layout-land/activity_main.xml

@@ -87,11 +87,27 @@
             android:name="androidx.navigation.fragment.NavHostFragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:layout_above="@+id/login_tip"
             android:layout_below="@+id/header_layout"
             android:layout_toRightOf="@+id/nav_bar"
             app:defaultNavHost="true"
             app:navGraph="@navigation/nav_home" />
 
+        <TextView
+            android:id="@+id/bottom_tip"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="@dimen/iscs_space_4"
+            android:layout_marginVertical="@dimen/iscs_space_2"
+            android:layout_toRightOf="@+id/nav_bar"
+            android:drawableLeft="@mipmap/tip"
+            android:drawablePadding="@dimen/iscs_space_2"
+            android:textColor="?attr/colorTextPrimary"
+            android:textSize="@dimen/iscs_text_md"
+            android:visibility="gone" />
+
         <com.grkj.ui_base.widget.CustomNavBar
             android:id="@+id/nav_bar"
             android:layout_width="@dimen/home_bottom_nav_size"

+ 30 - 11
app/src/main/res/layout/activity_main.xml

@@ -87,21 +87,40 @@
             android:name="androidx.navigation.fragment.NavHostFragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_above="@+id/nav_bar"
+            android:layout_above="@+id/bottom_layout"
             android:layout_below="@+id/header_layout"
             app:defaultNavHost="true"
             app:navGraph="@navigation/nav_home" />
 
-        <com.grkj.ui_base.widget.CustomNavBar
-            android:id="@+id/nav_bar"
+        <FrameLayout
+            android:id="@+id/bottom_layout"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/home_bottom_nav_size"
-            android:layout_alignParentBottom="true"
-            app:navBarBackground="?attr/colorSecBg"
-            app:navIconSelectedSize="@dimen/home_bottom_nav_icon_size"
-            app:navIconSize="@dimen/home_bottom_nav_icon_size"
-            app:navOrientation="horizontal"
-            app:navTextSelectedSize="@dimen/iscs_text_sm"
-            app:navTextSize="@dimen/iscs_text_sm" />
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true">
+
+            <TextView
+                android:id="@+id/bottom_tip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_marginLeft="@dimen/iscs_space_4"
+                android:drawableLeft="@mipmap/tip"
+                android:drawablePadding="@dimen/iscs_space_2"
+                android:textColor="?attr/colorTextPrimary"
+                android:visibility="gone"
+                android:textSize="@dimen/iscs_text_md" />
+
+            <com.grkj.ui_base.widget.CustomNavBar
+                android:id="@+id/nav_bar"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/home_bottom_nav_size"
+                app:navBarBackground="?attr/colorSecBg"
+                app:navIconSelectedSize="@dimen/home_bottom_nav_icon_size"
+                app:navIconSize="@dimen/home_bottom_nav_icon_size"
+                app:navOrientation="horizontal"
+                app:navTextSelectedSize="@dimen/iscs_text_sm"
+                app:navTextSize="@dimen/iscs_text_sm" />
+        </FrameLayout>
+
     </RelativeLayout>
 </layout>

+ 15 - 0
app/src/main/res/layout/fragment_exception_manage_home.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/home_menu_rv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/iscs_space_5"
+            android:layout_marginTop="@dimen/iscs_space_5" />
+    </RelativeLayout>
+</layout>

+ 120 - 0
app/src/main/res/layout/fragment_material_exchange.xml

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="@dimen/iscs_space_2"
+        android:background="@drawable/home_card_bg"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:id="@+id/title_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:paddingHorizontal="@dimen/iscs_space_2">
+
+            <ImageView
+                android:layout_width="@dimen/title_icon_size"
+                android:layout_height="@dimen/title_icon_size"
+                android:tint="?attr/colorPrimary"
+                app:skinSrc='@{"box-loading.svg"}' />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/iscs_space_2"
+                android:layout_weight="1"
+                android:textColor="?attr/colorTextPrimary"
+                android:textSize="@dimen/iscs_text_md"
+                android:textStyle="bold"
+                app:i18nKey='@{"material_exchange"}' />
+
+            <TextView
+                android:id="@+id/back"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginVertical="5dp"
+                android:layout_marginLeft="@dimen/iscs_space_2"
+                android:background="@drawable/common_btn_secondary"
+                android:drawableLeft="@mipmap/icon_back"
+                android:drawablePadding="@dimen/iscs_space_2"
+                android:drawableTint="?attr/colorPrimary"
+                android:gravity="center"
+                android:minHeight="@dimen/common_btn_height"
+                android:paddingHorizontal="@dimen/iscs_space_4"
+                android:textColor="?attr/colorTextPrimary"
+                android:textSize="@dimen/iscs_text_md"
+                app:i18nKey='@{"back"}' />
+        </LinearLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/divider_line_space"
+            android:background="?attr/colorBlack" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingHorizontal="@dimen/iscs_space_2"
+            android:paddingVertical="@dimen/iscs_space_2">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/iscs_space_2"
+                android:layout_weight="1"
+                android:textColor="?attr/colorTextPrimary"
+                android:textSize="@dimen/iscs_text_md"
+                app:i18nKey='@{"data_export_tip"}' />
+
+            <TextView
+                android:id="@+id/export"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/iscs_space_2"
+                android:layout_marginRight="@dimen/iscs_space_2"
+                android:background="@drawable/common_btn_secondary"
+                android:paddingHorizontal="@dimen/iscs_space_4"
+                android:textColor="?attr/colorTextPrimary"
+                android:textSize="@dimen/iscs_text_md"
+                app:i18nKey='@{"common_export"}' />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/iscs_space_4"
+            android:layout_marginTop="@dimen/iscs_space_2"
+            android:background="@drawable/common_card_header_bg"
+            android:orientation="horizontal"
+            android:showDividers="middle">
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/header_rv_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
+
+            <ImageView
+                android:id="@+id/header_expand_iv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="@dimen/iscs_space_1"
+                android:layout_marginTop="@dimen/iscs_space_1"
+                app:skinSrc='@{"angle-circle-down.svg"}' />
+        </LinearLayout>
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/list_rv"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginHorizontal="@dimen/iscs_space_4"
+            android:layout_marginBottom="@dimen/iscs_space_2"
+            android:background="@drawable/common_card_bg" />
+    </LinearLayout>
+</layout>

+ 15 - 0
app/src/main/res/layout/fragment_material_manage_home.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/home_menu_rv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/iscs_space_5"
+            android:layout_marginTop="@dimen/iscs_space_5" />
+    </RelativeLayout>
+</layout>

+ 15 - 0
app/src/main/res/layout/fragment_statistical_analysis_manage_home.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/home_menu_rv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/iscs_space_5"
+            android:layout_marginTop="@dimen/iscs_space_5" />
+    </RelativeLayout>
+</layout>

+ 26 - 0
app/src/main/res/layout/item_material_exchange_header.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/header_icons"
+            android:layout_width="@dimen/title_icon_size"
+            android:layout_height="@dimen/title_icon_size"
+            android:adjustViewBounds="true"
+            android:scaleType="fitCenter"
+            android:tint="?attr/colorPrimary" />
+
+        <TextView
+            android:id="@+id/header_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/iscs_space_1"
+            android:textColor="?attr/colorTextPrimary"
+            android:textSize="@dimen/iscs_text_sm" />
+    </LinearLayout>
+</layout>

+ 7 - 0
app/src/main/res/navigation/nav_data_manage.xml

@@ -17,6 +17,9 @@
         <action
             android:id="@+id/action_dataManageHomeFragment_to_dataExportFragment"
             app:destination="@id/dataExportFragment" />
+        <action
+            android:id="@+id/action_dataManageHomeFragment_to_backupAndRestoreFragment"
+            app:destination="@id/backupAndRestoreFragment" />
     </fragment>
     <fragment
         android:id="@+id/userManageFragment"
@@ -30,4 +33,8 @@
         android:id="@+id/dataExportFragment"
         android:name="com.grkj.iscs_mc.features.main.fragment.data_manage.DataExportFragment"
         android:label="DataExportFragment" />
+    <fragment
+        android:id="@+id/backupAndRestoreFragment"
+        android:name="com.grkj.iscs_mc.features.main.fragment.data_manage.BackupAndRestoreFragment"
+        android:label="BackupAndRestoreFragment" />
 </navigation>

+ 5 - 5
app/src/main/res/navigation/nav_exception_manage.xml

@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <navigation xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/nav_exception_manage"
-    app:startDestination="@id/exceptionHomeFragment">
+    android:id="@+id/nav_material_manage"
+    app:startDestination="@id/exceptionHomeManage">
 
     <fragment
-        android:id="@+id/exceptionHomeFragment"
-        android:name="com.grkj.iscs_mc.features.main.fragment.ExceptionHomeFragment"
-        android:label="ExceptionHomeFragment" />
+        android:id="@+id/exceptionHomeManage"
+        android:name="com.grkj.iscs_mc.features.main.fragment.exception_manage.ExceptionHomeManage"
+        android:label="ExceptionHomeManage" />
 </navigation>

+ 19 - 0
app/src/main/res/navigation/nav_material_manage.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/nav_material_manage"
+    app:startDestination="@id/materialHomeManageFragment">
+
+    <fragment
+        android:id="@+id/materialHomeManageFragment"
+        android:name="com.grkj.iscs_mc.features.main.fragment.material_manage.MaterialHomeManageFragment"
+        android:label="MaterialHomeManageFragment" >
+        <action
+            android:id="@+id/action_materialHomeManageFragment_to_materialExchangeFragment"
+            app:destination="@id/materialExchangeFragment" />
+    </fragment>
+    <fragment
+        android:id="@+id/materialExchangeFragment"
+        android:name="com.grkj.iscs_mc.features.main.fragment.material_manage.MaterialExchangeFragment"
+        android:label="MaterialExchangeFragment" />
+</navigation>

+ 11 - 0
app/src/main/res/navigation/nav_statistical_analysis.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/nav_material_manage"
+    app:startDestination="@id/statisticalAnalysisHomeManage">
+
+    <fragment
+        android:id="@+id/statisticalAnalysisHomeManage"
+        android:name="com.grkj.iscs_mc.features.main.fragment.statistical_analysis_manage.StatisticalAnalysisHomeManage"
+        android:label="StatisticalAnalysisHomeManage" />
+</navigation>

+ 4 - 0
data/src/main/java/com/grkj/data/common/EventConstants.kt

@@ -28,6 +28,10 @@ object EventConstants {
      * 跳转事件,设计导航切换
      */
     const val EVENT_JUMP_TO: Int = 100_000_005
+    /**
+     * 底部提示事件
+     */
+    const val EVENT_BOTTOM_TIP: Int = 100_000_006
     //---------------------------硬件通知------------------------
     /**
      * RFID读卡事件

+ 104 - 5
data/src/main/java/com/grkj/data/enums/RoleFunctionalPermissionsEnum.kt

@@ -11,6 +11,10 @@ enum class RoleFunctionalPermissionsEnum(
 ) {
     USER_MANAGE("data_manage:user_manage", "user_manage", 1, listOf()),
     ROLE_MANAGE("data_manage:role_manage", "role_manage", 1, listOf()),
+    DEPT_MANAGE("data_manage:dept_manage", "dept_manage", 1, listOf()),
+    BLACK_LIST_MANAGE("data_manage:black_list_manage", "black_list_manage", 1, listOf()),
+    LOGIN_LOG_MANAGE("data_manage:login_log_manage", "login_log_manage", 1, listOf()),
+    EXCHANGE_LOG_MANAGE("data_manage:exchange_log_manage", "exchange_log_manage", 1, listOf()),
     BACKUP_AND_RESTORE(
         "data_manage:backup_and_restore",
         "backup_and_restore",
@@ -18,12 +22,82 @@ enum class RoleFunctionalPermissionsEnum(
         listOf()
     ),
     DATA_EXPORT("data_manage:data_export", "data_export", 1, listOf()),
-    EXCHANGE_RECORD(
-        "data_manage:exchange_record",
-        "exchange_record",
+    MATERIAL_EXCHANGE(
+        "material_manage:material_exchange",
+        "material_exchange",
         1,
         listOf()
     ),
+    MATERIAL_TYPE(
+        "material_manage:material_type",
+        "material_type",
+        1,
+        listOf()
+    ),
+    MATERIAL_MANAGE(
+        "material_manage:material_manage",
+        "material_manage",
+        1,
+        listOf()
+    ),
+    MATERIAL_CHECK(
+        "material_manage:material_check",
+        "material_check",
+        1,
+        listOf()
+    ),
+    MATERIAL_REPAIR_AND_REPLACEMENT(
+        "material_manage:material_repair_and_replacement",
+        "material_repair_and_replacement",
+        1,
+        listOf()
+    ),
+    MATERIAL_INSTRUCTIONS(
+        "material_manage:material_instructions",
+        "material_instructions",
+        1,
+        listOf()
+    ),
+    EXCEPTION_REPORT("exception_manage:exception_report", "exception_report", 1, listOf()),
+    EXCEPTION_MANAGE("exception_manage:exception_manage", "exception_manage", 1, listOf()),
+    WARNING_MANAGE("exception_manage:warning_manage", "warning_manage", 1, listOf()),
+    USER_DEPT_ANALYSIS(
+        "statistical_analysis:user_dept_analysis",
+        "user_dept_analysis",
+        1,
+        listOf()
+    ),
+    USE_BEHAVIOR_ANALYSIS(
+        "statistical_analysis:use_behavior_analysis",
+        "use_behavior_analysis",
+        1,
+        listOf()
+    ),
+    OPERATION_AND_MAINTENANCE_ANALYSIS(
+        "statistical_analysis:operation_and_maintenance_analysis",
+        "operation_and_maintenance_analysis",
+        1,
+        listOf()
+    ),
+    INVENTORY_TURNOVER_ANALYSIS(
+        "statistical_analysis:inventory_turnover_analysis",
+        "inventory_turnover_analysis",
+        1,
+        listOf()
+    ),
+    COST_BENEFIT_ANALYSIS(
+        "statistical_analysis:cost_benefit_analysis",
+        "cost_benefit_analysis",
+        1,
+        listOf()
+    ),
+    SECURITY_COMPLIANCE_ANALYSIS(
+        "statistical_analysis:security_compliance_analysis",
+        "security_compliance_analysis",
+        1,
+        listOf()
+    ),
+
     USER_INFO("user_info:user_info", "user_info", 1, listOf()),
     RESET_PASSWORD("user_info:reset_password", "reset_password", 1, listOf()),
     FINGERPRINT_SETTING(
@@ -51,16 +125,37 @@ enum class RoleFunctionalPermissionsEnum(
             LOGOUT
         )
     ),
+    STATISTICAL_ANALYSIS_MANAGE(
+        "statistical_analysis",
+        "statistical_analysis", 0,
+        listOf(
+            USER_DEPT_ANALYSIS,
+            USE_BEHAVIOR_ANALYSIS,
+            OPERATION_AND_MAINTENANCE_ANALYSIS,
+            INVENTORY_TURNOVER_ANALYSIS,
+            COST_BENEFIT_ANALYSIS,
+            SECURITY_COMPLIANCE_ANALYSIS
+        )
+    ),
     EXCEPTION_HOME_MANAGE(
         "exception_manage",
         "exception_manage", 0,
-        listOf()
+        listOf(
+            EXCEPTION_REPORT,
+            EXCEPTION_MANAGE,
+            WARNING_MANAGE
+        )
     ),
     MATERIAL_HOME_MANAGE(
         "material_manage",
         "material_manage", 0,
         listOf(
-            EXCHANGE_RECORD
+            MATERIAL_EXCHANGE,
+            MATERIAL_TYPE,
+            MATERIAL_MANAGE,
+            MATERIAL_CHECK,
+            MATERIAL_REPAIR_AND_REPLACEMENT,
+            MATERIAL_INSTRUCTIONS
         )
     ),
     DATA_HOME_MANAGE(
@@ -69,6 +164,10 @@ enum class RoleFunctionalPermissionsEnum(
         listOf(
             USER_MANAGE,
             ROLE_MANAGE,
+            DEPT_MANAGE,
+            BLACK_LIST_MANAGE,
+            LOGIN_LOG_MANAGE,
+            EXCHANGE_LOG_MANAGE,
             BACKUP_AND_RESTORE,
             DATA_EXPORT
         )

+ 30 - 2
data/src/main/java/com/grkj/data/local/database/ISCSMigrations.kt

@@ -12,16 +12,24 @@ object ISCSMigrations {
     /**
      * 版本号
      */
-    const val VERSION = 3
+    const val VERSION = 4
 
     /**
      * 升级数据
      */
     val migrationData: Array<Migration> by lazy {
         arrayOf(MIGRATION_1_2,
-            MIGRATION_2_3,)
+            MIGRATION_2_3,
+            MIGRATION_3_4,
+            )
     }
 
+    /**
+     * 新增区域表
+     * 新增菜单表
+     * 新增角色菜单表
+     * 新增角色表
+     */
     private val MIGRATION_1_2 = object : Migration(1, 2) {
         override fun migrate(database: SupportSQLiteDatabase) {
             database.execSQL("CREATE TABLE IF NOT EXISTS `is_workstation` (`workstation_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `workstation_code` TEXT, `workstation_name` TEXT NOT NULL, `workstation_type` TEXT, `parent_id` INTEGER, `ancestors` TEXT, `order_num` INTEGER, `status` TEXT, `del_flag` TEXT, `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
@@ -31,6 +39,10 @@ object ISCSMigrations {
         }
     }
 
+    /**
+     * 删除区域表
+     * 创建用户角色关联表
+     */
     private val MIGRATION_2_3 = object : Migration(2, 3) {
         override fun migrate(database: SupportSQLiteDatabase) {
             database.execSQL("DROP TABLE IF EXISTS `is_workstation`")
@@ -43,4 +55,20 @@ object ISCSMigrations {
             """.trimIndent())
         }
     }
+
+    /**
+     * 新增物资相关的表
+     */
+    private val MIGRATION_3_4 = object : Migration(3, 4) {
+        override fun migrate(database: SupportSQLiteDatabase) {
+            database.execSQL("DROP TABLE IF EXISTS `is_workstation`")
+            database.execSQL("""
+                CREATE TABLE IF NOT EXISTS sys_user_role (
+                    user_id INTEGER NOT NULL,
+                    role_id INTEGER NOT NULL,
+                    PRIMARY KEY (user_id, role_id)
+                );
+            """.trimIndent())
+        }
+    }
 }

+ 102 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterials.kt

@@ -0,0 +1,102 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.grkj.data.utils.ExcelColumn
+import com.grkj.data.utils.ExcelIgnore
+import com.grkj.data.utils.ExcelSheet
+
+/**
+ * 物资表
+ */
+@ExcelSheet(name = "materials")
+@Entity(tableName = "is_materials")
+open class IsMaterials : BaseBean() {
+
+    @ExcelIgnore
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "materials_id")
+    var materialsId: Long = 0L
+
+    @ExcelIgnore
+    @ColumnInfo(name = "materials_code")
+    var materialsCode: String? = null
+
+    /** 物资名称 */
+    @ExcelColumn(header = "materials_name", order = 0)
+    @ColumnInfo(name = "materials_name")
+    var materialsName: String? = null
+
+    /** 物资类型id */
+    @ExcelColumn(header = "materials_type_id", order = 1)
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long? = null
+
+    @ExcelIgnore
+    @ColumnInfo(name = "workarea_id")
+    var workAreaId: Long? = null
+
+    @ExcelIgnore
+    @ColumnInfo(name = "materials_cabinet_id")
+    var materialsCabinetId: Long? = null
+
+    /** 可用寿命 */
+    @ExcelColumn(header = "service_life", order = 2)
+    @ColumnInfo(name = "service_life")
+    var serviceLife: String? = null
+
+    /** 剩余寿命 */
+    @ExcelColumn(header = "available_life", order = 3)
+    @ColumnInfo(name = "available_life")
+    var availableLife: String? = null
+
+    /** 可用次数 */
+    @ExcelColumn(header = "service_times", order = 4)
+    @ColumnInfo(name = "service_times")
+    var serviceTimes: Int? = null
+
+    /** 剩余次数 */
+    @ExcelColumn(header = "available_times", order = 5)
+    @ColumnInfo(name = "available_times")
+    var availableTimes: Int? = null
+
+    /** 有效期 */
+    @ExcelColumn(header = "expiration_date", order = 6)
+    @ColumnInfo(name = "expiration_date")
+    var expirationDate: String? = null
+
+    /** 物资RFID */
+    @ExcelColumn(header = "materials_rfid", order = 7)
+    @ColumnInfo(name = "materials_rfid")
+    var materialsRfid: String? = null
+
+    /** 供应商 */
+    @ExcelColumn(header = "supplier", order = 8)
+    @ColumnInfo(name = "supplier")
+    var supplier: String? = null
+
+    /** 启用时间 */
+    @ExcelColumn(header = "start_time", order = 9)
+    @ColumnInfo(name = "start_time")
+    var startTime: String? = null
+
+    @ExcelIgnore
+    @ColumnInfo(name = "properties")
+    var properties: String? = null
+
+    /** 借还状态(0-借出 1-柜中),默认 1 */
+    @ExcelIgnore
+    @ColumnInfo(name = "loan_state", defaultValue = "1")
+    var loanState: String = "1"
+
+    /** 物资状态(0正常 1损坏 2过期 3放错柜子),默认 0 */
+    @ExcelIgnore
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ExcelIgnore
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 53 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsCabinet.kt

@@ -0,0 +1,53 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资柜表
+ */
+@Entity(tableName = "is_materials_cabinet")
+open class IsMaterialsCabinet : BaseBean() {
+
+    /** 物资柜ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "cabinet_id")
+    var cabinetId: Long = 0L
+
+    /** 物资柜编号(NOT NULL) */
+    @ColumnInfo(name = "cabinet_code")
+    var cabinetCode: String = ""
+
+    /** 物资柜名称(NOT NULL) */
+    @ColumnInfo(name = "cabinet_name")
+    var cabinetName: String = ""
+
+    /** 硬件ID */
+    @ColumnInfo(name = "hardware_id")
+    var hardwareId: Long? = null
+
+    /** 区域ID */
+    @ColumnInfo(name = "workarea_id")
+    var workareaId: Long? = null
+
+    /** 所属岗位ID */
+    @ColumnInfo(name = "workstation_id")
+    var workstationId: Long? = null
+
+    /** 物资柜图标 */
+    @ColumnInfo(name = "cabinet_icon")
+    var cabinetIcon: String? = null
+
+    /** 物资柜略图 */
+    @ColumnInfo(name = "cabinet_picture")
+    var cabinetPicture: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 状态(0-正常 1-使用中 2-异常),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 64 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsChangeRecord.kt

@@ -0,0 +1,64 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资更换记录
+ */
+@Entity(
+    tableName = "is_materials_change_record",
+    ignoredColumns = ["remark"] // 该表无 remark,BaseBean 有,需忽略
+)
+open class IsMaterialsChangeRecord : BaseBean() {
+
+    /** 物资更换id (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "change_id")
+    var changeId: Long = 0L
+
+    /** 物资检查记录id */
+    @ColumnInfo(name = "check_record_id")
+    var checkRecordId: Long? = null
+
+    /** 原物资id */
+    @ColumnInfo(name = "old_materials_id")
+    var oldMaterialsId: Long? = null
+
+    /** 原物资rfid */
+    @ColumnInfo(name = "old_materials_rfid")
+    var oldMaterialsRfid: String? = null
+
+    /** 新物资id */
+    @ColumnInfo(name = "new_materials_id")
+    var newMaterialsId: Long? = null
+
+    /** 新物资rfid */
+    @ColumnInfo(name = "new_materials_rfid")
+    var newMaterialsRfid: String? = null
+
+    /** 更换人id */
+    @ColumnInfo(name = "change_user_id")
+    var changeUserId: Long? = null
+
+    /** 更换时间(yyyy-MM-dd HH:mm:ss) */
+    @ColumnInfo(name = "change_date")
+    var changeDate: String? = null
+
+    /** 更换类型(0-手动更换 1-自动更换) */
+    @ColumnInfo(name = "change_type")
+    var changeType: String? = null
+
+    /** 操作类型(0-取出 1-放入 2-替换 3-维修) */
+    @ColumnInfo(name = "operate_type")
+    var operateType: String? = null
+
+    /** 状态 */
+    @ColumnInfo(name = "status")
+    var status: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 41 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckPlan.kt

@@ -0,0 +1,41 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资检查计划表
+ */
+@Entity(tableName = "is_materials_check_plan")
+open class IsMaterialsCheckPlan : BaseBean() {
+
+    /** 物资计划id (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "plan_id")
+    var planId: Long = 0L
+
+    /** 计划名称 */
+    @ColumnInfo(name = "plan_name")
+    var planName: String? = null
+
+    /** 所属岗位ID */
+    @ColumnInfo(name = "workstation_id")
+    var workstationId: Long? = null
+
+    /** 计划日期(建议 yyyy-MM-dd) */
+    @ColumnInfo(name = "plan_date")
+    var planDate: String = ""
+
+    /** 检查员ID */
+    @ColumnInfo(name = "check_user_id")
+    var checkUserId: Long? = null
+
+    /** 检查状态(字典checking_status) - 默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+
+    /** 删除标志(0存在 2删除)- 默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 57 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckRecord.kt

@@ -0,0 +1,57 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资检查记录表
+ */
+@Entity(tableName = "is_materials_check_record")
+open class IsMaterialsCheckRecord : BaseBean() {
+
+    /** 检查记录id (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "record_id")
+    var recordId: Long = 0L
+
+    /** 计划id */
+    @ColumnInfo(name = "plan_id")
+    var planId: Long = 0L
+
+    /** 所属岗位ID */
+    @ColumnInfo(name = "workstation_id")
+    var workstationId: Long? = null
+
+    /** 物资柜id */
+    @ColumnInfo(name = "cabinet_id")
+    var cabinetId: Long? = null
+
+    /** 物资id */
+    @ColumnInfo(name = "materials_id")
+    var materialsId: Long = 0L
+
+    /** 检查员 */
+    @ColumnInfo(name = "check_user_id")
+    var checkUserId: Long? = null
+
+    /** 检查时间(yyyy-MM-dd HH:mm:ss) */
+    @ColumnInfo(name = "check_date")
+    var checkDate: String? = null
+
+    /** 检查记录状态(字典checks_status) 0正常 1异常 */
+    @ColumnInfo(name = "status")
+    var status: String? = null
+
+    /** 异常原因(字典exceptions_status) 1损坏 2过期 */
+    @ColumnInfo(name = "reason")
+    var reason: String? = null
+
+    /** 措施 */
+    @ColumnInfo(name = "measure")
+    var measure: String? = null
+
+    /** 删除标志(0存在 2删除)- 默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 53 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsInstructions.kt

@@ -0,0 +1,53 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资使用说明表
+ */
+@Entity(tableName = "is_materials_instructions")
+open class IsMaterialsInstructions : BaseBean() {
+
+    /** 说明ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "instructions_id")
+    var instructionsId: Long = 0L
+
+    /** 标题 */
+    @ColumnInfo(name = "instructions_title")
+    var instructionsTitle: String? = null
+
+    /** 物资类型ID */
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long? = null
+
+    /** 文件地址 */
+    @ColumnInfo(name = "file_url")
+    var fileUrl: String? = null
+
+    /** PDF转图片后的文件地址 */
+    @ColumnInfo(name = "file_url_img")
+    var fileUrlImg: String? = null
+
+    /** PDF转图片后的文件路径 */
+    @ColumnInfo(name = "file_path_img")
+    var filePathImg: String? = null
+
+    /** 文件类型 */
+    @ColumnInfo(name = "file_type")
+    var fileType: String? = null
+
+    /** 排序,默认 1 */
+    @ColumnInfo(name = "order_num", defaultValue = "1")
+    var orderNum: Int = 1
+
+    /** 物资状态(0-正常 1-异常 2-被更换),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 69 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsLoan.kt

@@ -0,0 +1,69 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资借出表
+ */
+@Entity(tableName = "is_materials_loan")
+open class IsMaterialsLoan : BaseBean() {
+
+    /** 出借记录ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "materials_loan_id")
+    var materialsLoanId: Long = 0L
+
+    /** 物资ID */
+    @ColumnInfo(name = "materials_id")
+    var materialsId: Long = 0L
+
+    /** 领取人ID */
+    @ColumnInfo(name = "loan_user_id")
+    var loanUserId: Long = 0L
+
+    /** 领取柜ID */
+    @ColumnInfo(name = "loan_from_id")
+    var loanFromId: Long? = null
+
+    /** 领取时间 */
+    @ColumnInfo(name = "loan_time")
+    var loanTime: String? = null
+
+    /** 提醒时间 */
+    @ColumnInfo(name = "reminder_time")
+    var reminderTime: String? = null
+
+    /** 归还人ID */
+    @ColumnInfo(name = "restitution_user_id")
+    var restitutionUserId: Long? = null
+
+    /** 归还柜ID */
+    @ColumnInfo(name = "restitution_to_id")
+    var restitutionToId: Long? = null
+
+    /** 理应归还时间 */
+    @ColumnInfo(name = "restitution_time")
+    var restitutionTime: String? = null
+
+    /** 实际归还时间 */
+    @ColumnInfo(name = "actual_restitution_time")
+    var actualRestitutionTime: String? = null
+
+    /** 超时报警时间 */
+    @ColumnInfo(name = "timeout_alarm")
+    var timeoutAlarm: String? = null
+
+    /** 是否需要归还 (1需要 0不需要),默认 1 */
+    @ColumnInfo(name = "restitution_required", defaultValue = "1")
+    var restitutionRequired: Int = 1
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 状态(0-待归还 1-已归还 2-超时未归还),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 43 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsPlanCabinet.kt

@@ -0,0 +1,43 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+
+/**
+ * 物资检查计划关联物资柜表
+ */
+@Entity(
+    tableName = "is_materials_plan_cabinet",
+    primaryKeys = ["plan_id", "cabinet_id"],
+    ignoredColumns = ["create_by", "create_time", "update_by", "update_time", "remark"]
+)
+open class IsMaterialsPlanCabinet : BaseBean() {
+
+    /** 物资计划ID */
+    @ColumnInfo(name = "plan_id")
+    var planId: Long = 0L
+
+    /** 物资柜ID */
+    @ColumnInfo(name = "cabinet_id")
+    var cabinetId: Long = 0L
+
+    /** 检查员 */
+    @ColumnInfo(name = "check_user_id")
+    var checkUserId: Long? = null
+
+    /** 签名图片(长文本) */
+    @ColumnInfo(name = "signature_img")
+    var signatureImg: String? = null
+
+    /** 签名时间(yyyy-MM-dd HH:mm:ss) */
+    @ColumnInfo(name = "signature_time")
+    var signatureTime: String? = null
+
+    /** 是否提交(0-否 1-是),默认 0 */
+    @ColumnInfo(name = "submit", defaultValue = "0")
+    var submit: String = "0"
+
+    /** 柜子检查状态(字典checking_status),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 29 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsProperty.kt

@@ -0,0 +1,29 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资属性项
+ */
+@Entity(tableName = "is_materials_property")
+open class IsMaterialsProperty : BaseBean() {
+
+    /** 编号 (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "property_id")
+    var propertyId: Long = 0L
+
+    /** 属性项名称 */
+    @ColumnInfo(name = "property_name")
+    var propertyName: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 物资状态(0-正常 1-异常 2-被更换),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 33 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsPropertyValue.kt

@@ -0,0 +1,33 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资属性值
+ */
+@Entity(tableName = "is_materials_property_value")
+open class IsMaterialsPropertyValue : BaseBean() {
+
+    /** 编号 (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "record_id")
+    var recordId: Long = 0L
+
+    /** 属性项的编号 */
+    @ColumnInfo(name = "property_id")
+    var propertyId: Long = 0L
+
+    /** 属性值名称 */
+    @ColumnInfo(name = "value_name")
+    var valueName: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 物资状态(0-正常 1-异常 2-被更换),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 33 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsReminder.kt

@@ -0,0 +1,33 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资借出提醒表
+ */
+@Entity(tableName = "is_materials_reminder")
+open class IsMaterialsReminder : BaseBean() {
+
+    /** 记录ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "record_id")
+    var recordId: Long = 0L
+
+    /** 出借记录ID */
+    @ColumnInfo(name = "materials_loan_id")
+    var materialsLoanId: Long = 0L
+
+    /** 提醒类型(0-提醒 1-警告) */
+    @ColumnInfo(name = "reminder_type")
+    var reminderType: Int? = null
+
+    /** 是否已读(0-未读 1-已读),默认 0 */
+    @ColumnInfo(name = "read_flag", defaultValue = "0")
+    var readFlag: Int = 0
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 45 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsRestitutionRules.kt

@@ -0,0 +1,45 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资借出规则
+ */
+@Entity(tableName = "is_materials_restitution_rules")
+open class IsMaterialsRestitutionRules : BaseBean() {
+
+    /** 物资规则ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "rule_id")
+    var ruleId: Long = 0L
+
+    /** 物资规则编号/物资类型ID */
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long = 0L
+
+    /** 是否需要归还(0-不需要 1-需要),默认 1 */
+    @ColumnInfo(name = "restitution_required", defaultValue = "1")
+    var restitutionRequired: Int = 1
+
+    /** 是否归还原位 */
+    @ColumnInfo(name = "restoration_required")
+    var restorationRequired: Int? = null
+
+    /** 出借时长 */
+    @ColumnInfo(name = "loan_duration")
+    var loanDuration: Int? = null
+
+    /** 提醒时间 */
+    @ColumnInfo(name = "reminder_time")
+    var reminderTime: Int? = null
+
+    /** 超时报警 */
+    @ColumnInfo(name = "timeout_alarm")
+    var timeoutAlarm: Int? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 77 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsType.kt

@@ -0,0 +1,77 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资类型表
+ */
+@Entity(tableName = "is_materials_type")
+open class IsMaterialsType : BaseBean() {
+
+    /** 物资类型ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long = 0L
+
+    /** 物资类型编号 */
+    @ColumnInfo(name = "materials_type_code")
+    var materialsTypeCode: String? = null
+
+    /** 物资类型名称(NOT NULL) */
+    @ColumnInfo(name = "materials_type_name")
+    var materialsTypeName: String = ""
+
+    /** 父类型ID(NOT NULL,默认 0) */
+    @ColumnInfo(name = "parent_id", defaultValue = "0")
+    var parentId: Long = 0L
+
+    /** 所有父节点ID(NOT NULL) */
+    @ColumnInfo(name = "ancestors")
+    var ancestors: String = ""
+
+    /** 是否启用(NOT NULL,默认 Y) */
+    @ColumnInfo(name = "enable_flag", defaultValue = "Y")
+    var enableFlag: String = "Y"
+
+    /** 可用寿命 */
+    @ColumnInfo(name = "service_life")
+    var serviceLife: String? = null
+
+    /** 剩余寿命 */
+    @ColumnInfo(name = "available_life")
+    var availableLife: String? = null
+
+    /** 可用次数 */
+    @ColumnInfo(name = "service_times")
+    var serviceTimes: Int? = null
+
+    /** 剩余次数 */
+    @ColumnInfo(name = "available_times")
+    var availableTimes: Int? = null
+
+    /** 物资类型图标 */
+    @ColumnInfo(name = "materials_type_icon")
+    var materialsTypeIcon: String? = null
+
+    /** 物资类型缩略图 */
+    @ColumnInfo(name = "materials_type_picture")
+    var materialsTypePicture: String? = null
+
+    /** 物资检查标准 */
+    @ColumnInfo(name = "check_standard")
+    var checkStandard: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 状态 */
+    @ColumnInfo(name = "status")
+    var status: String? = null
+
+    /** 规格属性项 */
+    @ColumnInfo(name = "property_ids")
+    var propertyIds: String? = null
+}

+ 1 - 0
shared/build.gradle.kts

@@ -58,6 +58,7 @@ dependencies {
     api(libs.sik.extension.encrypt)
     api(libs.sik.extension.image)
     api("org.mindrot:jbcrypt:0.4")
+//    api("com.github.SilverIceKey:AndroidPdfViewer:3.2.1")
     implementation("com.machinezoo.sourceafis:sourceafis:3.15.0")
     implementation("com.google.dagger:hilt-android:2.56.2")
     ksp("com.google.dagger:hilt-android-compiler:2.56.2")

+ 28 - 0
ui-base/src/main/java/com/grkj/ui_base/utils/event/BottomTipEvent.kt

@@ -0,0 +1,28 @@
+package com.grkj.ui_base.utils.event
+
+import com.grkj.data.common.EventConstants
+import com.grkj.shared.model.EventBean
+import com.grkj.shared.utils.event.EventHelper
+
+/**
+ * 底部提示事件
+ */
+class BottomTipEvent(
+    val msg: String? = null
+) {
+    companion object {
+        /**
+         * 发送底部提示通知
+         */
+        @JvmStatic
+        fun sendBottomTipEvent(
+            msg: String? = null
+        ) {
+            val bottomTipEventBean = EventBean<BottomTipEvent>(
+                EventConstants.EVENT_BOTTOM_TIP,
+                BottomTipEvent(msg)
+            )
+            EventHelper.sendEvent(bottomTipEventBean)
+        }
+    }
+}

+ 4 - 8
ui-base/src/main/java/com/grkj/ui_base/utils/extension/Context.kt

@@ -7,17 +7,13 @@ import android.content.pm.PackageManager
 import android.os.Build
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
+import com.sik.sikcore.SIKCore
+import com.sik.sikcore.device.DeviceUtils
 import java.util.Locale
 
 @SuppressLint("MissingPermission")
 fun Context.serialNo(): String {
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-        && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
-        == PackageManager.PERMISSION_GRANTED
-    ) {
-        return Build.getSerial().uppercase(Locale.ROOT)
-    }
-    return Build.SERIAL.uppercase(Locale.ROOT)
+    return DeviceUtils.getDeviceId(this, length = 8)
 }
 
 /**
@@ -28,7 +24,7 @@ fun Context.checkPermissions(permissions: Array<String>): List<Boolean> {
         ContextCompat.checkSelfPermission(
             this,
             it
-        ) != PackageManager.PERMISSION_GRANTED
+        ) == PackageManager.PERMISSION_GRANTED
     }
 }
 

+ 1 - 0
ui-base/src/main/res/values/dimens.xml

@@ -41,6 +41,7 @@
     <dimen name="home_menu_item_iv_size">35dp</dimen>
     <dimen name="avatar_user_info_size">120dp</dimen>
     <dimen name="dialog_common_root_width">400dp</dimen>
+    <dimen name="header_chip_row_height">30dp</dimen>
 
 
     <dimen name="avatar_size">30dp</dimen>