270 次代码提交 ade8d708ed ... 1359dd5d04

作者 SHA1 备注 提交日期
  liukai 1359dd5d04 Merge remote-tracking branch 'origin/dev' into dev 3 周之前
  liukai d099655467 图片的修改2 3 周之前
  glglove 1ca374b007 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 3 周之前
  glglove c55aff5d45 修改sevices 分类图 3 周之前
  liukai f02b56a4f5 图片的修改 3 周之前
  liukai 2c89468729 小问题修复 3 周之前
  liukai e5ed38704e 权限框架回退到旧版本,会导致无法编译的问题需要升级 Gradle 3 周之前
  liukai faa17e4565 rewards的相关UI改动 4 周之前
  liukai b8ebf956a7 Me页面,设置页面,设施页面,表单模块相关调整 4 周之前
  liukai 7a2fb58e19 登录注册与首页的相关修改 4 周之前
  liukai 96a1f70ceb 首页的修改与相关调试 1 月之前
  liukai ad88eec974 Merge remote-tracking branch 'origin/dev' into dev 1 月之前
  liukai 4ad22271e9 主题色值的修改 1 月之前
  glglove 08259eec69 update ui 样式 1 月之前
  liukai b86dee65e8 1、国家区号的选择与封装。 1 月之前
  liukai 78de3eac47 权限库与Album库的升级与修改 1 月之前
  liukai 9a67184cad 验证码的Code为空的校验 1 月之前
  liukai aa6c6ef3db 验证码的图片PNG与JPG的适配 1 月之前
  liukai 23761d8e0b 部分模块的暗色模式适配 2 月之前
  liukai e7c502108e 现有的模块基本抽取国际化文本的key出来。大致已完成 2 月之前
  liukai 313fc32d36 Merge remote-tracking branch 'origin/dev_services' into dev_services 3 月之前
  liukai bed73b9e86 指定的错误,页面显示修复 3 月之前
  “shanwenxin” b10f3a5d83 处理bug 3 月之前
  glglove b125014f5a image_nine_grid 增加didUpdateWidget 钩子 3 月之前
  liukai 15346c48e2 Merge remote-tracking branch 'origin/dev_services' into dev_services 3 月之前
  liukai cd85308bb1 Splash的图片修改 3 月之前
  glglove 62658eab94 Merge branch 'dev' into dev_services 3 月之前
  glglove 6f59cbea26 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 3 月之前
  glglove ee27bea798 mypost 的rent 和sale 可以删除 和编辑 3 月之前
  jiangwei 34a63a2d0c 修改启动图 3 月之前
  jiangwei e6baedcfc1 iOS这边更改了桌面图标 3 月之前
  liukai 4264196940 YYHome改名为24iFM ,相关应用的名称与logo修改 3 月之前
  liukai 2da3c92f52 修改布局适配 3 月之前
  glglove 199c7facdd Merge branch 'dev_services' of http://git.wmzhubo.com/guadoutech/YYHome into dev_services 3 月之前
  glglove d8ec6900c3 修改一些ui 3 月之前
  “shanwenxin” ed718b0abe 处理rewards问题 3 月之前
  glglove 1539246c54 fix services bug 4 月之前
  glglove 3428e2133c Merge branch 'dev' into dev_services 4 月之前
  glglove 99c7850c96 fix service bug 4 月之前
  liukai f2397f0c83 首页的Grid选项重新适配 4 月之前
  liukai 973fce29ff 对用户的默认小区名称的抽取 4 月之前
  liukai 488db0042d intl的生成报错问题排查与解决,能正显示国际化文案 4 月之前
  liukai 7832ace53d 添加国际化标识 4 月之前
  “shanwenxin” 00399be50c Merge branch 'dev_services' of http://git.wmzhubo.com/guadoutech/YYHome into dev_services 4 月之前
  “shanwenxin” 55aef2383b update 4 月之前
  glglove 89b5a37140 fix bug 4 月之前
  glglove cd83b20f29 update 4 月之前
  glglove c3fa282dfa update 4 月之前
  glglove e32651aa81 add repair get a quote dialog 4 月之前
  glglove c32dee82b1 update 4 月之前
  glglove e0fb53711f update 4 月之前
  “shanwenxin” 9ef796d00f update service 4 月之前
  liukai f676595c50 Merge remote-tracking branch 'origin/dev_services' into dev_services 4 月之前
  liukai fe36b275fa 合并代码 json生成-2 4 月之前
  glglove ba2d99a680 Merge branch 'dev_services' of http://git.wmzhubo.com/guadoutech/YYHome into dev_services 4 月之前
  glglove 04b439392e update 4 月之前
  liukai f5ef3381b5 Merge remote-tracking branch 'origin/dev_services' into dev_services 4 月之前
  “shanwenxin” 045247c40c Merge branch 'dev_services' of http://git.wmzhubo.com/guadoutech/YYHome into dev_services 4 月之前
  “shanwenxin” 5591935d6b update 4 月之前
  liukai 6eea39072b Merge remote-tracking branch 'origin/dev_services' into dev_services 4 月之前
  glglove b1a96249d4 update service 4 月之前
  liukai da5a00fb0e Merge remote-tracking branch 'origin/dev_services' into dev_services 4 月之前
  liukai b2e404e2bc 拨打电话完善,取消订单之后的通知完善 4 月之前
  glglove 12be05b8aa 修改了int serviceTypeCode 变为 String cleanServiceType 4 月之前
  glglove 4ef1af0a3e update service 4 月之前
  “shanwenxin” f2f9249a8f Merge branch 'dev_services' of http://git.wmzhubo.com/guadoutech/YYHome into dev_services 4 月之前
  “shanwenxin” e978f8e383 评价列表提交,资讯服务详情提交 4 月之前
  liukai 7b3e5726dd inporgress列表 4 月之前
  glglove d5f9cdd859 update 4 月之前
  glglove 43fbfcc1e2 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 4 月之前
  glglove 792a16c7e2 update 4 月之前
  liukai e736ce3467 首页欢迎文本 4 月之前
  glglove 4476e15cd2 update services 4 月之前
  glglove 687de18dd3 update service 4 月之前
  glglove f16e701292 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 4 月之前
  glglove 4619409279 update 4 月之前
  liukai 6d877fa99a 解决冲突,运行成功 4 月之前
  glglove 86a816ae95 update 4 月之前
  glglove eed3026bfe Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 4 月之前
  glglove 3fb0d90909 update services 4 月之前
  “shanwenxin” c823a96f32 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 4 月之前
  “shanwenxin” f09a7f296a update rewards 4 月之前
  liukai 094c38593d dialog_engine的一些属性抽取配置 4 月之前
  liukai 30d318ba05 dialog_engine的一些属性抽取配置 4 月之前
  liukai e75e50b538 首页的管理员指引的列表与详情 5 月之前
  liukai 7aa450955a 首页的其他新闻的展示与跳转· 5 月之前
  liukai 7bd9fd5345 首页的接口调试与信息展示 5 月之前
  liukai a7541f80a7 首页的排版修改 5 月之前
  glglove d26c60e77a fix bug 5 月之前
  glglove 05d541148e fix bug 5 月之前
  glglove 20cc5789a0 fix bug 5 月之前
  glglove 4cbca569c3 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 5 月之前
  glglove 980ebeb289 fix bug 5 月之前
  liukai ff6645fc9c 首页的资源修改 5 月之前
  liukai 7a3f902c05 splash的相关配置 5 月之前
  liukai c32d72d237 Merge remote-tracking branch 'origin/dev' into dev 5 月之前
  liukai ca25672669 添加eventbus的刷新用户信息 5 月之前
  glglove 4c20397075 fix bug 5 月之前
  glglove 264c256c13 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 5 月之前
  glglove 4786380141 fix bug 5 月之前
  jiangwei d189387e93 修改iOS配置 5 月之前
  glglove 0ec930a41d update 5 月之前
  liukai 99248bf7d9 合并与更新 5 月之前
  “shanwenxin” 4a99b84abc Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 5 月之前
  “shanwenxin” 752cd7c389 update rewards 5 月之前
  glglove 1f619ed43e Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 5 月之前
  glglove 738e94efa8 update 5 月之前
  liukai 00e0e35879 Merge remote-tracking branch 'origin/dev' into dev 5 月之前
  glglove 9fc710ed28 update 5 月之前
  glglove 2853acaed0 update community 5 月之前
  glglove beb8266a99 update community 5 月之前
  glglove 26d2c3366b update community 5 月之前
  liukai 31c26d7c14 payment模块的修改 5 月之前
  liukai 93cff3863f Merge remote-tracking branch 'origin/dev' into dev 5 月之前
  liukai c2344a65d8 访客的模块 5 月之前
  glglove 79223bd04f update community 5 月之前
  liukai aa491fc8bc Facility的图标宽高适配与本地资源删除 5 月之前
  liukai 625fe300d1 合并-2 5 月之前
  liukai d81ef257a6 Merge remote-tracking branch 'origin/dev' into dev 5 月之前
  liukai 5b0fec3bcf 合并 5 月之前
  glglove 5d8e81dc08 update community 5 月之前
  glglove fbbca6886c update community 5 月之前
  liukai aaafd46381 Merge remote-tracking branch 'origin/dev' into dev 5 月之前
  liukai 5569eb7cae 设施的申请详情与申请成功的页面 5 月之前
  “shanwenxin” 797981e4b7 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 5 月之前
  “shanwenxin” 7cd88c5ec0 update notice board 5 月之前
  glglove 898b658dde Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 5 月之前
  glglove 22baab12fa update community 5 月之前
  liukai e36631a8e5 Merge remote-tracking branch 'origin/dev' into dev 5 月之前
  liukai 64f08cc424 设施的申请与已申请列表 5 月之前
  glglove 3bb9d271b5 update community 5 月之前
  glglove 3502160bff update community 5 月之前
  glglove 6e90fd0c60 update community 5 月之前
  liukai 04420e7b5b 设施的图片资源与数据仓库创建 5 月之前
  liukai 5a8826c34d 在线表单的详情与复显 5 月之前
  liukai e285811fb4 在线表单的几种状态的列表 6 月之前
  liukai 7b00d04f3a Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 619a504588 反馈的分类,创建,状态列表,详情接口调试 6 月之前
  glglove 14c1191108 update community 6 月之前
  glglove 1beee9afe1 update community 6 月之前
  liukai a0e258fa16 合并代码-解决数据实体模块的冲突 6 月之前
  liukai 4ac3dbaeef Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 8c94a8ef06 添加搜索房产 6 月之前
  “shanwenxin” 3d7251ca88 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  “shanwenxin” b538ee8ebc reward部分 6 月之前
  glglove 0a8d90919c update property 6 月之前
  liukai a67407fefe 忘记密码。修改密码,修改手机号码 6 月之前
  liukai 8bc0ed140f 更改用户信息 6 月之前
  glglove 82f9408b28 update 6 月之前
  liukai 7fb318f34b Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 4427c70e28 登录注册与用户信息的接口 6 月之前
  glglove 08de8d33c3 update 6 月之前
  glglove 8a3629c8c5 update 6 月之前
  glglove 079763de25 update 6 月之前
  glglove a4a01d6cef update commutnity 6 月之前
  glglove 5a3cde8ab3 update community 6 月之前
  glglove 763ce1226c update community 6 月之前
  liukai fa602e83bb 页面的修改调试 6 月之前
  liukai 0b2800157f 修改白色背景的whiteBg 为backgraoundWhite 的背景颜色。在黑暗模式下的适配, 6 月之前
  liukai 375dd11ed5 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 7c234f76d9 修改白色背景的whiteBg 为backgraoundWhite 的背景颜色。在黑暗模式下的适配, 6 月之前
  glglove 65a8087ca7 update community 6 月之前
  glglove 644b704684 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove 7acea35ed0 update community 6 月之前
  liukai 5f4ce6bbae 设施的地址Banner 6 月之前
  liukai 46e30646e4 form 表单的详情 6 月之前
  liukai 9aca4fcd48 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai f61ea22a01 form的申请12中步骤详情页面 6 月之前
  glglove a6af3bc30a update 6 月之前
  glglove c232a7cca3 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove b6019f33a9 update 6 月之前
  liukai 3de0bfd232 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 4a36d606da form的详情配置 6 月之前
  glglove a9005ee62d Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove 0f8faef95a update 6 月之前
  glglove 1275823c9f update 6 月之前
  liukai 85e15a0ec8 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 0a2627971b form的资源与首页 6 月之前
  glglove b14cd246ea Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove 1fce7d5ac7 update 6 月之前
  liukai 2ad8360ac7 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 192bc9adc1 路由跳转 6 月之前
  liukai 3db1dee2f2 payment 切换支付方式设置主卡,添加银行卡 6 月之前
  glglove 74f02a9f2e update 6 月之前
  liukai 866e7f60e3 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  glglove 9e109ba294 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove 885c8f6894 update 6 月之前
  liukai 9730f0f154 payment 支付信息,支付确认,支付成功 6 月之前
  liukai 962347694f Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 690ac0edb5 payment首页完成 6 月之前
  liukai c6de70fae3 payment首页 6 月之前
  glglove 1aa85865e9 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove ec6a3acd8c update 6 月之前
  liukai e2b1624b32 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 8653907739 设施的确认页面与详情页面 6 月之前
  liukai 9814a01fd4 完善两个日期选择的联动 6 月之前
  “shanwenxin” 7b6936683b Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  “shanwenxin” 99669ad5ce services部分 6 月之前
  liukai 279da333f7 星期选择与日历选择的初步完成 6 月之前
  glglove 9511318070 update community module 6 月之前
  glglove 4a45355f3b update community_module 6 月之前
  liukai 06b97233e1 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai f1042e8669 同步代码 6 月之前
  gaol 7b1c10282f update community 6 月之前
  liukai 68a02cd481 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 76e06b2c27 facility 的详情与booking 6 月之前
  glglove 77c62285f2 update 6 月之前
  liukai 0c0ce0ba33 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 7c4a5de683 facility 的列表详情 6 月之前
  gaol bc18818557 update 6 月之前
  gaol 17dd74df95 update 6 月之前
  glglove 9fd93598bc update 6 月之前
  glglove 57ca2daa6c Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove 7240b5a580 update commmunity module 6 月之前
  liukai f1bd8c0ffa Facility的首页一级页面 6 月之前
  liukai a7eb00e316 我的房产的删除弹窗与页面完善 6 月之前
  liukai 032e4e99e5 我的房产 - 分页列表,刷新与加载列表,悬停头实现。 6 月之前
  “shanwenxin” ec3ec51fb9 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  “shanwenxin” b0409b832e notice_board页面 6 月之前
  gaol 4d4dd50b48 update community module 6 月之前
  glglove 8fc3062e37 Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  glglove 65171848af update community module 6 月之前
  liukai c26ff93d01 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 49fc1baad1 编辑用户信息与头像编辑弹窗 6 月之前
  liukai 575cf404d0 反馈的创建与详情 6 月之前
  “shanwenxin” f1c319555f Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  “shanwenxin” 0ab6370a5d notice_board页面 6 月之前
  liukai 2db68129f4 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  gaol 846c9195a8 update community module 6 月之前
  liukai 0c7985c936 Merge remote-tracking branch 'origin/dev' into dev 6 月之前
  liukai 2dd91b38e3 访客的创建表单 6 月之前
  glglove ea69975fbe update community module 6 月之前
  liukai cd3cd0b5a9 Home页面的二级页面 6 月之前
  liukai 6dc88fe653 通知列表完成,LoadingView + Refresh 刷新,加载更多的模板。分组悬停模板 6 月之前
  liukai 2414b87cb6 首页me页面完善。 6 月之前
  glglove 3825756631 update community module 6 月之前
  liukai fdbd505bd9 main首页中visitor与feedbak的一级页面。 6 月之前
  liukai 5411ae637e home首页修改,适配小屏,亮色暗色模式 6 月之前
  “shanwenxin” b342a2e65d Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 6 月之前
  “shanwenxin” 7cb5630b0c notice页面部分 6 月之前
  liukai 923c8f5842 合并代码上传 7 月之前
  liukai 11822521d2 Merge remote-tracking branch 'origin/dev' into dev 7 月之前
  liukai d8e902f9a0 main页面与home页面的部分 7 月之前
  glglove eab81cee25 update property module 7 月之前
  glglove 83bdd93f8f update property module 7 月之前
  glglove d018151158 update property module 7 月之前
  liukai 53810f0670 重新定义全局默认弹窗的样式AddDefaultDialog。 7 月之前
  glglove 35e0de80bd Merge branch 'dev' of http://git.wmzhubo.com/guadoutech/YYHome into dev 7 月之前
  glglove 8e666a2312 update property module 7 月之前
  liukai befdbb26c5 重置密码 7 月之前
  liukai 8d2799d9bb 修改电话号码 7 月之前
  liukai 0535edee31 设置页面 7 月之前
  glglove f749fcabf1 update 7 月之前
  glglove 6043edcd1b update 7 月之前
  glglove 012adef1ef update 7 月之前
  liukai db916c0ee8 修改九宫格图片的样式 7 月之前
  liukai 64a934a7b4 选择社区选择楼栋单元房号,选择角色,上传文档 7 月之前
  glglove 5049afa98b update 7 月之前
  liukai 9e11134c90 选择业主还是租户 7 月之前
  liukai 733bda7175 选择房子与单元 7 月之前
  liukai 75b26443f0 Merge branch 'dev-lk' into dev 7 月之前
  glglove 27a9046829 resource 冲突 7 月之前
  liukai 36715636ae 注册与注册成功页面 7 月之前
  glglove 7a2b537297 update property 7 月之前
  glglove 713e8ef1e5 update property 7 月之前
  liukai 5aa4bec8cb 注册页面 7 月之前
  liukai 16dfd1372c 忘记密码与忘记密码的验证页面 7 月之前
  liukai fb036f55e2 调整部分底层控件样式 7 月之前
  liukai e029544124 Appbar 的调整 7 月之前
  liukai 160dcd75b5 开发分支 7 月之前
共有 100 个文件被更改,包括 5954 次插入197 次删除
  1. 2 2
      app/android/app/build.gradle
  2. 6 0
      app/android/app/proguard-rules.pro
  3. 1 1
      app/android/app/src/main/AndroidManifest.xml
  4. 二进制
      app/android/app/src/main/ic_launcher-playstore.png
  5. 4 25
      app/android/app/src/main/kotlin/com/hongyegroup/property_management/MainActivity.java
  6. 1 1
      app/android/app/src/main/res/drawable-v21/launch_background.xml
  7. 二进制
      app/android/app/src/main/res/drawable-xhdpi/yy_business_top_logo.webp
  8. 二进制
      app/android/app/src/main/res/drawable-xhdpi/yyhome_splash.png
  9. 二进制
      app/android/app/src/main/res/drawable-xxhdpi/yy_business_top_logo.webp
  10. 二进制
      app/android/app/src/main/res/drawable-xxhdpi/yyhome_splash.png
  11. 1 1
      app/android/app/src/main/res/drawable/launch_background.xml
  12. 1 6
      app/android/app/src/main/res/drawable/shape_page_bg.xml
  13. 二进制
      app/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
  14. 二进制
      app/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
  15. 二进制
      app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  16. 二进制
      app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  17. 二进制
      app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
  18. 二进制
      app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  19. 二进制
      app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  20. 二进制
      app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
  21. 二进制
      app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  22. 1 0
      app/android/gradle.properties
  23. 4 0
      app/devtools_options.yaml
  24. 12 12
      app/ios/Runner.xcodeproj/project.pbxproj
  25. 二进制
      app/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png
  26. 二进制
      app/ios/Runner/Assets.xcassets/AppIcon.appiconset/32C047592C8D514741501471B52D6D07.png
  27. 1 1
      app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  28. 二进制
      app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
  29. 二进制
      app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
  30. 5 20
      app/ios/Runner/Base.lproj/LaunchScreen.storyboard
  31. 6 6
      app/ios/Runner/Info.plist
  32. 31 16
      app/lib/main.dart
  33. 9 25
      app/lib/modules/splash/page/splash_page.dart
  34. 3 6
      app/lib/modules/splash/vm/splash_view_model.dart
  35. 6 0
      app/lib/router/component/app_component_service_impl.dart
  36. 1 4
      app/lib/router/component/app_service_provider.dart
  37. 23 11
      melos.yaml
  38. 4 0
      packages/cpt_auth/devtools_options.yaml
  39. 264 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_page.dart
  40. 73 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_state.dart
  41. 150 0
      packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.dart
  42. 3 3
      packages/cpt_auth/lib/modules/auth_login/vm/auth_login_view_model.g.dart
  43. 0 34
      packages/cpt_auth/lib/modules/auth_login/page/Profile_edit_page.dart
  44. 0 14
      packages/cpt_auth/lib/modules/auth_login/vm/auth_login_view_model.dart
  45. 117 0
      packages/cpt_auth/lib/modules/estate_upload_success/estate_upload_success_page.dart
  46. 22 0
      packages/cpt_auth/lib/modules/estate_upload_success/estate_upload_success_view_model.dart
  47. 27 0
      packages/cpt_auth/lib/modules/estate_upload_success/estate_upload_success_view_model.g.dart
  48. 184 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_page.dart
  49. 54 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_state.dart
  50. 84 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.dart
  51. 27 0
      packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.g.dart
  52. 237 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_page.dart
  53. 78 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_state.dart
  54. 216 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.dart
  55. 27 0
      packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.g.dart
  56. 61 0
      packages/cpt_auth/lib/modules/select_estate/attach_input_widget.dart
  57. 74 0
      packages/cpt_auth/lib/modules/select_estate/dialog/attach_search_dialog.dart
  58. 172 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_page.dart
  59. 59 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_state.dart
  60. 91 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.dart
  61. 27 0
      packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.g.dart
  62. 64 0
      packages/cpt_auth/lib/modules/select_role/screen_owner.dart
  63. 57 0
      packages/cpt_auth/lib/modules/select_role/screen_tenant.dart
  64. 115 0
      packages/cpt_auth/lib/modules/select_role/select_role_page.dart
  65. 15 0
      packages/cpt_auth/lib/modules/select_role/select_role_state.dart
  66. 30 0
      packages/cpt_auth/lib/modules/select_role/select_role_view_model.dart
  67. 27 0
      packages/cpt_auth/lib/modules/select_role/select_role_view_model.g.dart
  68. 250 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_page.dart
  69. 35 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_state.dart
  70. 75 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.dart
  71. 27 0
      packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.g.dart
  72. 302 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_page.dart
  73. 111 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_state.dart
  74. 219 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.dart
  75. 26 0
      packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.g.dart
  76. 107 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_page.dart
  77. 16 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.dart
  78. 27 0
      packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.g.dart
  79. 236 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_page.dart
  80. 30 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_state.dart
  81. 128 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.dart
  82. 27 0
      packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.g.dart
  83. 99 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_page.dart
  84. 20 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_state.dart
  85. 69 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.dart
  86. 27 0
      packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.g.dart
  87. 8 6
      packages/cpt_auth/lib/router/component/auth_component_service.dart
  88. 23 2
      packages/cpt_auth/lib/router/page/auth_page_router.dart
  89. 333 1
      packages/cpt_auth/lib/router/page/auth_page_router.gr.dart
  90. 3 0
      packages/cpt_auth/pubspec.yaml
  91. 4 0
      packages/cpt_community/devtools_options.yaml
  92. 139 0
      packages/cpt_community/lib/components/comments_dialog.dart
  93. 254 0
      packages/cpt_community/lib/components/garage_card.dart
  94. 85 0
      packages/cpt_community/lib/components/newfeed_card_header.dart
  95. 161 0
      packages/cpt_community/lib/components/newsfeed_card_content.dart
  96. 147 0
      packages/cpt_community/lib/components/newsfeed_card_footer.dart
  97. 392 0
      packages/cpt_community/lib/modules/community/community_page.dart
  98. 17 0
      packages/cpt_community/lib/modules/community/community_pageview_idx_data.dart
  99. 80 0
      packages/cpt_community/lib/modules/community/community_state.dart
  100. 0 0
      packages/cpt_community/lib/modules/community/community_vm.dart

+ 2 - 2
app/android/app/build.gradle

@@ -49,7 +49,7 @@ android {
     }
 
     defaultConfig {
-        applicationId "com.hongyegroup.property_management"
+        applicationId "com.hongyegroup.proanperty_management"
         minSdkVersion 21
         targetSdkVersion 34
         versionCode 100          //Android打包上线记得要加固并重新签名再传
@@ -59,7 +59,7 @@ android {
 
         ndk {
             //选择要添加的对应 cpu 类型的 .so 库。
-//            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
+            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
             abiFilters 'armeabi-v7a', 'arm64-v8a'
 //            abiFilters 'armeabi-v7a'
 //            abiFilters 'arm64-v8a'

+ 6 - 0
app/android/app/proguard-rules.pro

@@ -155,5 +155,11 @@ public void *(android.webkit.WebView, jav.lang.String);
 -dontwarn cn.jpush.**
 -keep class cn.jpush.** { *; }
 
+# Stripe支付
+-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivity$g
+-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args
+-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error
+-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter
+-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider
 
 

+ 1 - 1
app/android/app/src/main/AndroidManifest.xml

@@ -42,7 +42,7 @@
         android:name="android.app.src.main.kotlin.com.hongyegroup.property_management.MyApplication"
         android:allowBackup="false"
         android:icon="@mipmap/ic_launcher"
-        android:label="物业管理"
+        android:label="24IFM"
         android:networkSecurityConfig="@xml/network_security_config"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:usesCleartextTraffic="true">

二进制
app/android/app/src/main/ic_launcher-playstore.png


+ 4 - 25
app/android/app/src/main/kotlin/com/hongyegroup/property_management/MainActivity.java

@@ -11,6 +11,7 @@ import androidx.annotation.Nullable;
 import io.flutter.embedding.android.FlutterActivity;
 import io.flutter.embedding.engine.FlutterEngine;
 import io.flutter.plugin.common.MethodChannel;
+import android.os.Build;
 
 public class MainActivity extends FlutterActivity {
 
@@ -30,31 +31,9 @@ public class MainActivity extends FlutterActivity {
         MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor(), "com.room/opapp");
 
         methodChannel.setMethodCallHandler((call, result) -> {
-            //检查人脸数量
-           if (call.method.equals("bringAppToForeground")) {
-                //启动MainActivity自己
-                Log.d("MainActivity", "执行 methodChannel -> bringAppToForeground");
-
-//                Intent intent = new Intent(this, MainActivity.class);
-//                intent.setAction(Intent.ACTION_MAIN);
-//                intent.addCategory(Intent.CATEGORY_LAUNCHER);
-//                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-//                intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-//                startActivity(intent);
-
-//                ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-//                List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
-//                if (!appTasks.isEmpty()) {
-//                    appTasks.get(0).moveToFront();
-//                }
-
-//                PackageManager packageManager = getPackageManager();
-//                Intent intent = packageManager.getLaunchIntentForPackage("com.hongyegroup.zhurijob");
-//                if (intent != null) {
-//                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-//                    startActivity(intent);
-//                }
-
+            //返回安卓版本
+           if (call.method.equals("getAndroidSdkInt")) {
+               result.success(Build.VERSION.SDK_INT);
             } else if (call.method.equals("getNativeRouterName")) {
                 // 获取当前需要跳转的子路由
                 Log.d("MainActivity", "执行 methodChannel -> getNativeRouterName");

+ 1 - 1
app/android/app/src/main/res/drawable-v21/launch_background.xml

@@ -5,7 +5,7 @@
     <item >
         <bitmap
             android:gravity="center"
-            android:src="@drawable/yy_business_top_logo"/>
+            android:src="@drawable/yyhome_splash"/>
     </item>
 
 </layer-list>

二进制
app/android/app/src/main/res/drawable-xhdpi/yy_business_top_logo.webp


二进制
app/android/app/src/main/res/drawable-xhdpi/yyhome_splash.png


二进制
app/android/app/src/main/res/drawable-xxhdpi/yy_business_top_logo.webp


二进制
app/android/app/src/main/res/drawable-xxhdpi/yyhome_splash.png


+ 1 - 1
app/android/app/src/main/res/drawable/launch_background.xml

@@ -5,7 +5,7 @@
     <item >
         <bitmap
             android:gravity="center"
-            android:src="@drawable/yy_business_top_logo"/>
+            android:src="@drawable/yyhome_splash"/>
     </item>
 
 </layer-list>

+ 1 - 6
app/android/app/src/main/res/drawable/shape_page_bg.xml

@@ -2,11 +2,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
 
-    <gradient
-        android:startColor="#091D44"
-        android:centerColor="#245A8A"
-        android:endColor="#7F7CEC"
-        android:type="linear"
-        android:angle="270" />
+    <solid android:color="#FFFFFF" /> <!-- 设置为纯白色 -->
 
 </shape>

二进制
app/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp


二进制
app/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp


二进制
app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp


二进制
app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp


二进制
app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp


二进制
app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


二进制
app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


二进制
app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp


二进制
app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


+ 1 - 0
app/android/gradle.properties

@@ -1,4 +1,5 @@
 org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
 android.useAndroidX=true
 android.enableJetifier=true
+android.enableR8.fullMode=false
 maven={ url 'https://maven.aliyun.com/repository/public' }

+ 4 - 0
app/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 12 - 12
app/ios/Runner.xcodeproj/project.pbxproj

@@ -396,19 +396,19 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
-				CURRENT_PROJECT_VERSION = 1.0.5;
+				CURRENT_PROJECT_VERSION = 1.0;
 				DEVELOPMENT_TEAM = CW4J9W8LB6;
 				ENABLE_BITCODE = NO;
 				FLUTTER_BUILD_MODE = profile;
 				INFOPLIST_FILE = Runner/Info.plist;
-				INFOPLIST_KEY_CFBundleDisplayName = "YY Employers";
+				INFOPLIST_KEY_CFBundleDisplayName = "YY Home";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0;
-				PRODUCT_BUNDLE_IDENTIFIER = com.guadou.YYEmployer;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.hongyegroup.YYHome;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
 				SUPPORTS_MACCATALYST = NO;
@@ -537,19 +537,19 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
-				CURRENT_PROJECT_VERSION = 1.0.5;
+				CURRENT_PROJECT_VERSION = 1.0;
 				DEVELOPMENT_TEAM = CW4J9W8LB6;
 				ENABLE_BITCODE = NO;
 				FLUTTER_BUILD_MODE = debug;
 				INFOPLIST_FILE = Runner/Info.plist;
-				INFOPLIST_KEY_CFBundleDisplayName = "YY Employers";
+				INFOPLIST_KEY_CFBundleDisplayName = "YY Home";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0;
-				PRODUCT_BUNDLE_IDENTIFIER = com.guadou.YYEmployer;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.hongyegroup.YYHome;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
 				SUPPORTS_MACCATALYST = NO;
@@ -570,19 +570,19 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
-				CURRENT_PROJECT_VERSION = 1.0.5;
+				CURRENT_PROJECT_VERSION = 1.0;
 				DEVELOPMENT_TEAM = CW4J9W8LB6;
 				ENABLE_BITCODE = NO;
 				FLUTTER_BUILD_MODE = release;
 				INFOPLIST_FILE = Runner/Info.plist;
-				INFOPLIST_KEY_CFBundleDisplayName = "YY Employers";
+				INFOPLIST_KEY_CFBundleDisplayName = "YY Home";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0;
-				PRODUCT_BUNDLE_IDENTIFIER = com.guadou.YYEmployer;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.hongyegroup.YYHome;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
 				SUPPORTS_MACCATALYST = NO;

二进制
app/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png


二进制
app/ios/Runner/Assets.xcassets/AppIcon.appiconset/32C047592C8D514741501471B52D6D07.png


+ 1 - 1
app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -1,7 +1,7 @@
 {
   "images" : [
     {
-      "filename" : "1024.png",
+      "filename" : "32C047592C8D514741501471B52D6D07.png",
       "idiom" : "universal",
       "platform" : "ios",
       "size" : "1024x1024"

二进制
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png


二进制
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png


+ 5 - 20
app/ios/Runner/Base.lproj/LaunchScreen.storyboard

@@ -5,7 +5,6 @@
         <deployment identifier="iOS"/>
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
-        <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
@@ -17,23 +16,13 @@
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
-                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LaunchBG.png" translatesAutoresizingMaskIntoConstraints="NO" id="tNR-J0-C2x">
-                                <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
-                            </imageView>
-                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="Pu5-Up-dfw">
-                                <rect key="frame" x="104.5" y="299.5" width="166" height="68"/>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="Pu5-Up-dfw">
+                                <rect key="frame" x="123" y="202.5" width="129" height="56.5"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                             </imageView>
                         </subviews>
                         <viewLayoutGuide key="safeArea" id="UaE-PJ-QZk"/>
-                        <color key="backgroundColor" systemColor="tertiaryLabelColor"/>
-                        <constraints>
-                            <constraint firstAttribute="bottom" secondItem="tNR-J0-C2x" secondAttribute="bottom" id="1Lq-rq-g1S"/>
-                            <constraint firstItem="Pu5-Up-dfw" firstAttribute="centerX" secondItem="tNR-J0-C2x" secondAttribute="centerX" id="5UC-LA-jKf"/>
-                            <constraint firstItem="tNR-J0-C2x" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="DWN-Tu-Vde"/>
-                            <constraint firstItem="Pu5-Up-dfw" firstAttribute="centerY" secondItem="tNR-J0-C2x" secondAttribute="centerY" id="ewy-U3-5wj"/>
-                            <constraint firstAttribute="trailing" secondItem="tNR-J0-C2x" secondAttribute="trailing" id="fWW-7q-1ZL"/>
-                            <constraint firstItem="tNR-J0-C2x" firstAttribute="leading" secondItem="UaE-PJ-QZk" secondAttribute="leading" id="mD2-Fb-bwB"/>
-                        </constraints>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                     </view>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -42,10 +31,6 @@
         </scene>
     </scenes>
     <resources>
-        <image name="LaunchBG.png" width="375" height="667"/>
-        <image name="LaunchImage" width="166" height="67.5"/>
-        <systemColor name="tertiaryLabelColor">
-            <color red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.29803921570000003" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
+        <image name="LaunchImage" width="129" height="56.5"/>
     </resources>
 </document>

+ 6 - 6
app/ios/Runner/Info.plist

@@ -7,7 +7,7 @@
 	<key>CFBundleDevelopmentRegion</key>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
 	<key>CFBundleDisplayName</key>
-	<string>YY Employers</string>
+	<string>YY Home</string>
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
 	<key>CFBundleIdentifier</key>
@@ -68,15 +68,15 @@
 		<true/>
 	</dict>
 	<key>NSCameraUsageDescription</key>
-	<string>“YY Employers”apply for your camera permission to use functions such as taking avatars, uploading videos, uploading and saving pictures, and real-name authentication.</string>
+	<string>“YY Home”apply for your camera permission to use functions such as taking avatars, uploading videos, uploading and saving pictures, and real-name authentication.</string>
 	<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
-	<string>YY Employers needs your location to recommend nearby jobs</string>
+	<string>YY Home needs your location to recommend nearby jobs</string>
 	<key>NSLocationWhenInUseUsageDescription</key>
-	<string>YY Employers needs your location to recommend nearby jobs.</string>
+	<string>YY Home needs your location to recommend nearby jobs.</string>
 	<key>NSPhotoLibraryAddUsageDescription</key>
-	<string>"YY Employers" wants to access your photos for image upload, image saving and other functions.</string>
+	<string>"YY Home" wants to access your photos for image upload, image saving and other functions.</string>
 	<key>NSPhotoLibraryUsageDescription</key>
-	<string>"YY Employers" wants to access your photos for image upload, image saving and other functions.</string>
+	<string>"YY Home" wants to access your photos for image upload, image saving and other functions.</string>
 	<key>NSUserTrackingUsageDescription</key>
 	<string>This identifier will be used to deliver personalized job to you.</string>
 	<key>UIApplicationSupportsIndirectInputEvents</key>

+ 31 - 16
app/lib/main.dart

@@ -9,11 +9,14 @@ import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:initializer/app_initializer.dart';
 import 'package:plugin_basic/basic_export.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
 import 'package:plugin_basic/provider/app_config/app_config_service.dart';
 import 'package:plugin_basic/provider/global_provider_container.dart';
+import 'package:plugin_platform/engine/sp/sp_util.dart';
 import 'package:router/componentRouter/component_service_manager.dart';
 import 'package:cpt_profile/router/component/profile_service_provider.dart';
 import 'package:plugin_basic/obs/my_navigator_observer.dart';
+import 'package:shared/utils/device_utils.dart';
 import 'package:shared/utils/log_utils.dart';
 import 'package:widgets/dialog/custom_toast_widget.dart';
 import 'package:widgets/dialog/custom_error_widget.dart';
@@ -39,7 +42,7 @@ void main() async {
   await AppInitializer.initializeRunalone();
 
   //组件路由的注入
-  final serviceManager = ComponentServiceManager()
+  ComponentServiceManager()
     ..addServiceProvider(mainServiceProvider)
     ..addServiceProvider(authServiceProvider)
     ..addServiceProvider(appServiceProvider)
@@ -53,8 +56,8 @@ void main() async {
     ..addServiceProvider(servicesServiceProvider)
     ..addServiceProvider(profileServiceProvider);
 
-  runApp(ProviderScope(
-    parent: globalContainer,
+  runApp(UncontrolledProviderScope(
+    container: globalContainer,
     child: MyApp(),
   ));
 }
@@ -71,9 +74,6 @@ class MyApp extends HookConsumerWidget {
           noMoreText: 'No more',
           failedText: 'Failed',
           messageText: 'Last updated at %T',
-          textStyle: TextStyle(color: Color(0XFFAECAE5), fontSize: 14),
-          messageStyle: TextStyle(color: Color(0XFFAECAE5), fontSize: 12),
-          iconTheme: IconThemeData(color: Color(0XFFAECAE5)),
           backgroundColor: Colors.transparent,
         );
     EasyRefresh.defaultFooterBuilder = () => const ClassicFooter(
@@ -87,9 +87,6 @@ class MyApp extends HookConsumerWidget {
           showMessage: false,
           triggerOffset: 50,
           iconDimension: 22,
-          textStyle: TextStyle(color: Color(0XFFAECAE5), fontSize: 14),
-          messageStyle: TextStyle(color: Color(0XFFAECAE5), fontSize: 12),
-          iconTheme: IconThemeData(color: Color(0XFFAECAE5)),
           backgroundColor: Colors.transparent,
         );
 
@@ -104,7 +101,7 @@ class MyApp extends HookConsumerWidget {
         usePenetrate: false,
       )
       ..loading = SmartConfigLoading(
-        backDismiss: true,
+        // SmartConfigCustomSmartConfigCustomSmartConfigCustom
         clickMaskDismiss: true,
       )
       ..toast = SmartConfigToast(
@@ -117,6 +114,27 @@ class MyApp extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final themeMode = ref.watch(themeProvider);
 
+    //根据主题配置对应的状态栏
+    int? darkModel = SPUtil.getInt(AppConstant.storageDarkModel, defValue: 0);
+    late SystemUiOverlayStyle systemUiOverlayStyle;
+
+      //根据SP存入的暗色模式,指定全局页面的状态栏,导航栏等设置
+      switch (darkModel) {
+        case 1:
+          Log.d("main.dart - 指定亮色模式");
+          systemUiOverlayStyle = ThemeConfig.systemUiOverlayStyleLightThemeBlack;
+          break;
+        case 2:
+          Log.d("main.dart - 指定暗色模式");
+          systemUiOverlayStyle = ThemeConfig.systemUiOverlayStyleDarkTheme;
+          break;
+        default:
+          systemUiOverlayStyle = ThemeConfig.getSystemUiOverlayStyleByTheme(context);
+          break;
+      }
+
+    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
+
     useEffect(() {
       // 组件挂载时执行
       WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -125,13 +143,10 @@ class MyApp extends HookConsumerWidget {
       });
       return () {
         // 组件卸载时执行
+        globalContainer.dispose();
       };
     }, []);
 
-    //设置全局的状态栏文本样式
-    SystemChrome.setSystemUIOverlayStyle(ThemeConfig.systemUiOverlayStyleLightThemeWhite);
-
-    //路由管理,状态管理,依赖管理一切都始于GetMaterialApp
     return KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) {
       return KeyboardDismissOnTap(
         dismissOnCapturedTaps: false,
@@ -141,13 +156,13 @@ class MyApp extends HookConsumerWidget {
 
           //主题配置
           theme: ThemeData(
-            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
+            colorScheme: ColorScheme.fromSeed(seedColor: AppColorsTheme.colorPrimary),
             useMaterial3: false,
           ).copyWith(extensions: [
             AppColorsTheme.light(),
           ]),
           darkTheme: ThemeData(
-            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, brightness: Brightness.dark),
+            colorScheme: ColorScheme.fromSeed(seedColor: AppColorsTheme.colorPrimary, brightness: Brightness.dark),
             useMaterial3: false,
           ).copyWith(extensions: [
             AppColorsTheme.dark(),

+ 9 - 25
app/lib/modules/splash/page/splash_page.dart

@@ -1,12 +1,10 @@
+import 'package:app/main.dart';
 import 'package:app/router/page/app_page_router.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:cs_resources/generated/assets.dart';
-import 'package:router/componentRouter/app_service.dart';
-import 'package:router/path/router_path.dart';
 import 'package:shared/utils/size_config.dart';
-import 'package:widgets/dialog/custom_error_widget.dart';
-import 'package:widgets/ext/ex_widget.dart';
 import 'package:widgets/my_load_image.dart';
 import 'package:auto_route/annotations.dart';
 import '../vm/splash_view_model.dart';
@@ -23,27 +21,13 @@ class SplashPage extends HookConsumerWidget {
    final viewModel = ref.watch(splashViewModelProvider.notifier)..setContext(context);
 
     return Scaffold(
-      body: Container(
-        decoration: const BoxDecoration(
-          gradient: LinearGradient(
-            colors: [
-              Color(0xFF091D44),
-              Color(0xFF245A8A),
-              Color(0xFF7F7CEC),
-            ],
-            begin: Alignment.topCenter,
-            end: Alignment.bottomCenter,
-          ),
-        ),
-        child:  Center(
-          child: const MyAssetImage(
-            Assets.assetsYyBusinessTopLogo,
-            width: 166,
-            height: 67.5,
-            fit: BoxFit.contain,
-          ).onTap((){
-            context.router.pushNamed(RouterPath.main);
-          }),
+      backgroundColor: context.appColors.backgroundWhite,
+      body: const Center(
+        child: MyAssetImage(
+          Assets.assetsYyHomeSplash,
+          width: 129,
+          height: 56.5,
+          fit: BoxFit.contain,
         ),
       ),
     );

+ 3 - 6
app/lib/modules/splash/vm/splash_view_model.dart

@@ -47,18 +47,15 @@ class SplashViewModel extends _$SplashViewModel {
     // String routerName = await AppChannel.getNativeRouterName();
     // Log.d('SplashController - 查询原生平台有没有保存需要跳转的子路由:$routerName');
 
-    String? token = SPUtil.getString(AppConstant.storageToken);
-    final userConfigService = UserConfigService.getInstance();
-    userConfigService.setToken(token);
+    UserConfigService.getInstance();
 
-    if (UserConfigService.getState().haslogin == true) {
-      //去Attendance页面签到
+    if (UserConfigService.getState().hasLogin == true) {
       Log.d("已经登录,去首页页面");
       ComponentServiceManager().mainService.startMainPage();
     } else {
       //去登录页面
       Log.d("没有登录,去登录页面");
-      ComponentServiceManager().mainService.startMainPage();
+      ComponentServiceManager().authService.startAndPopAllLoginPage();
     }
   }
 }

+ 6 - 0
app/lib/router/component/app_component_service_impl.dart

@@ -3,6 +3,7 @@
  */
 import 'package:app/router/page/app_page_router.dart';
 import 'package:auto_route/src/router/controller/routing_controller.dart';
+import 'package:plugin_basic/provider/app_config/app_config_service.dart';
 import 'package:router/componentRouter/app_service.dart';
 
 class AppComponentServiceImpl extends AppService {
@@ -10,4 +11,9 @@ class AppComponentServiceImpl extends AppService {
   RootStackRouter getAppRouter() {
     return appRouter;
   }
+
+  @override
+  bool isDeviceFullScreen() {
+    return AppConfigService.getState().isFullScreenDevice;
+  }
 }

+ 1 - 4
app/lib/router/component/app_service_provider.dart

@@ -1,15 +1,12 @@
-import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 import 'package:router/componentRouter/app_service.dart';
-import 'package:router/componentRouter/main_service.dart';
 
 import 'app_component_service_impl.dart';
 
-
 part 'app_service_provider.g.dart';
 
 @Riverpod(keepAlive: true)
 AppService appService(Ref ref) {
   return AppComponentServiceImpl();
-}
+}

+ 23 - 11
melos.yaml

@@ -44,47 +44,55 @@ scripts:
         - build_runner
 
   build_runner_auth:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_auth" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_auth" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in auth module
 
+  build_runner_plugin_basic:
+    run: cd "$MELOS_ROOT_PATH/packages/cs_plugin_basic" && flutter pub run build_runner build --delete-conflicting-outputs
+    description: Run `dart run build_runner build` in cs_plugin_basic module
+
   build_runner_community:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_community" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_community" && flutter pub run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in community module
 
   build_runner_facility:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_facility" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_facility" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in facility module
 
   build_runner_form:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_form" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_form" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in form module
 
   build_runner_main:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_main" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_main" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in main module
 
   build_runner_notice_board:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_notice_board" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_notice_board" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in notice_board module
 
   build_runner_payment:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_payment" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_payment" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in payment module
 
   build_runner_profile:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_profile" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_profile" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in profile module
 
   build_runner_property:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_property" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_property" && flutter pub run build_runner build --delete-conflicting-outputs --verbose
+    description: Run `dart run build_runner build` in property module
+
+  watch_runner_property:
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_property" && flutter pub run build_runner watch --delete-conflicting-outputs --verbose
     description: Run `dart run build_runner build` in property module
 
   build_runner_rewards:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_rewards" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_rewards" && dart run build_runner build --delete-conflicting-outputs
     description: Run `dart run build_runner build` in rewards module
 
   build_runner_services:
-    run: cd "$MELOS_ROOT_PATH/packages/cpt_services" && dart run build_runner build
+    run: cd "$MELOS_ROOT_PATH/packages/cpt_services" && flutter pub run build_runner build --delete-conflicting-outputs --verbose
     description: Run `dart run build_runner build` in services module
 
   clean_all:
@@ -107,6 +115,10 @@ scripts:
     run: cd "$MELOS_ROOT_PATH/app" && flutter build apk
     description: 编译安卓Release的APK格式安装包.
 
+  build_aab:
+    run: cd "$MELOS_ROOT_PATH/app" && flutter build appbundle
+    description: 编译安卓Release的APK格式安装包.
+
   resource_get:
     run: |
       cd "$MELOS_ROOT_PATH/packages/cs_domain/" && flutter pub get

+ 4 - 0
packages/cpt_auth/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 264 - 0
packages/cpt_auth/lib/modules/auth_login/auth_login_page.dart

@@ -0,0 +1,264 @@
+import 'package:cpt_auth/modules/auth_login/auth_login_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/dialog/country_code_selecter.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/shatter/custom_check_box.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'auth_login_view_model.dart';
+
+/*
+ * 用户的登录页面
+ */
+@RoutePage()
+class AuthLoginPage extends HookConsumerWidget {
+  const AuthLoginPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.navigate(const AuthLoginPageRoute());
+    } else {
+      appRouter.navigate(const AuthLoginPageRoute());
+    }
+  }
+
+  //替换之前的页面
+  static void startAndPopAll({BuildContext? context}) {
+    if (context != null) {
+      context.router.popUntilRoot();
+      context.router.replace(const AuthLoginPageRoute());
+    } else {
+      appRouter.popUntilRoot();
+      appRouter.replace(const AuthLoginPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(authLoginViewModelProvider.notifier);
+    final state = ref.watch(authLoginViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, "", showBackButton: false),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部Logo
+              const MyAssetImage(
+                Assets.assetsYyHomeSplash,
+                width: 109.5,
+                height: 48,
+              ).marginOnly(top: 45, bottom: 45),
+
+              // 登录表单 - 账号
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  //封装的国家选择控件
+                  CountryCodeSelector(
+                    onChanged: (countryCode) {
+                      viewModel.saveCountryCode(countryCode);
+                    },
+                  ).marginOnly(left: 15),
+
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "account",
+                    fillBGColor: Colors.transparent,
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.done,
+                    errorText: state.accountErrorText,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                    },
+                  ).expanded(),
+                ],
+              ).decorated(
+                color: context.appColors.authFiledBG,
+                borderRadius: BorderRadius.circular(5.0),
+              ),
+
+              // 登录表单 - 密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                marginTop: 15,
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: !state.pwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.doLogin();
+                },
+              ),
+
+              //登录按钮
+              MyButton(
+                onPressed: viewModel.doLogin,
+                text: S.current.login,
+                enable: state.isLoginBtnEnable,
+                textColor: Colors.white,
+                disabledTextColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                disabledBackgroundColor: context.appColors.btnBgDefault.withOpacity(0.2),
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 20, bottom: 23),
+
+              //忘记密码
+              MyTextView(
+                S.current.forgot_password,
+                isFontMedium: true,
+                fontSize: 16,
+                textColor: context.appColors.textPrimary,
+                onClick: viewModel.gotoForgotPage,
+              ),
+
+              //创建账户-注册
+              MyButton(
+                onPressed: viewModel.gotoSignUpPage,
+                text: S.current.create_new_yy_home_account,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 28),
+
+              //同意协议
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  MyAssetImage(
+                    state.isAgreeTerms ? Assets.baseServiceCheckBoxChecked : Assets.baseServiceCheckBoxUncheck,
+                    color: state.isAgreeTerms ? context.appColors.textPrimary : Colors.transparent,
+                    width: 15.5,
+                    height: 15.5,
+                  ).onTap(viewModel.switchAgreeTerms, padding: 10),
+                  RichText(
+                    text: TextSpan(
+                      children: [
+                        TextSpan(
+                          text: S.current.agree_to,
+                          style: TextStyle(color: context.appColors.textDarkGray, fontWeight: FontWeight.w500, fontSize: 15), // 灰色文本
+                        ),
+                        const TextSpan(
+                          text: " ",
+                        ),
+                        TextSpan(
+                          text: S.current.terms_of_service,
+                          style: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15), // 蓝色文本
+                          recognizer: TapGestureRecognizer()..onTap = viewModel.gotoTermsPage,
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ).marginOnly(top: 30, bottom: 30),
+
+              //结束
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框 账号与密码
+  Widget _buildInputLayout(
+    BuildContext context,
+    LoginState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    Color? fillBGColor,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: fillBGColor ?? context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 73 - 0
packages/cpt_auth/lib/modules/auth_login/auth_login_state.dart

@@ -0,0 +1,73 @@
+/*
+ * 登录的状态类
+ */
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class LoginState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? accountErrorText;
+  String? passwordErrorText;
+
+  String? countryCode;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  //登录按钮是否可用
+  bool isLoginBtnEnable;
+
+  bool isAgreeTerms;
+
+  // ===================================  Begin  ↓  ===================================
+
+  LoginState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.accountErrorText,
+    this.passwordErrorText,
+    this.pwdVisibility = false,
+    this.isLoginBtnEnable = false,
+    this.isAgreeTerms = false,
+    this.countryCode,
+  }) : formData = formData ??
+            {
+              'account': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.mobile_phone,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+            };
+
+  LoginState copyWith({
+    String? accountErrorText,
+    String? passwordErrorText,
+    String? account,
+    String? password,
+    bool? pwdVisibility,
+    bool? isLoginBtnEnable,
+    bool? isAgreeTerms,
+    String? countryCode,
+  }) {
+    return LoginState(
+      formData: this.formData,
+      accountErrorText: accountErrorText,
+      passwordErrorText: passwordErrorText,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      isLoginBtnEnable: isLoginBtnEnable ?? this.isLoginBtnEnable,
+      isAgreeTerms: isAgreeTerms ?? this.isLoginBtnEnable,
+      countryCode: countryCode ?? this.countryCode,
+    );
+  }
+}

+ 150 - 0
packages/cpt_auth/lib/modules/auth_login/auth_login_view_model.dart

@@ -0,0 +1,150 @@
+import 'package:cpt_auth/modules/sign_up/sign_up_page.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:router/componentRouter/component_service_manager.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import '../forgot_input/forgot_input_page.dart';
+import 'auth_login_state.dart';
+
+part 'auth_login_view_model.g.dart';
+
+@riverpod
+class AuthLoginViewModel extends _$AuthLoginViewModel {
+  late final AuthRepository authRepository;
+
+  @override
+  LoginState build() {
+    authRepository = ref.read(authRepositoryProvider);
+    final state = LoginState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //切换是否隐藏密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  //执行用户的登录
+  void doLogin() async {
+    state = state.copyWith(accountErrorText: null, passwordErrorText: null);
+
+    final FocusNode accountFocusNode = state.formData['account']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final TextEditingController accountController = state.formData['account']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+
+    accountFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+
+    final account = accountController.text;
+    final password = passwordController.text;
+
+    Log.d('当前待提交的 账号:$account password:$password');
+
+    if (Utils.isEmpty(account)) {
+      state = state.copyWith(accountErrorText: "Phone cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    final result = await authRepository.authLogin("${state.countryCode}$account", password);
+
+    //请求成功去首页
+    if (result.isSuccess) {
+      UserConfigService.getInstance().setToken(token: result.data?.token, userName: result.data?.name);
+      ComponentServiceManager().mainService.startMainPage();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  //去协议页面
+  void gotoTermsPage() {
+    ToastEngine.show("去协议页面");
+  }
+
+  //去忘记密码页面
+  void gotoForgotPage() {
+    ForgotInputPage.startInstance();
+  }
+
+  //去注册页面
+  void gotoSignUpPage() {
+    SignUpPage.startInstance();
+  }
+
+  //切换同意按钮
+  void switchAgreeTerms() {
+    state = state.copyWith(isAgreeTerms: !state.isAgreeTerms);
+  }
+
+  //初始化监听
+  void initListener(LoginState initState) {
+    final FocusNode accountFocusNode = initState.formData['account']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final TextEditingController accountController = initState.formData['account']!['controller'];
+    final TextEditingController passwordController = initState.formData['password']!['controller'];
+
+    accountFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (accountFocusNode.hasFocus) {
+        state = state.copyWith(accountErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    //文本框的监听输入
+    accountController.addListener(() {
+      _updateButtonState(accountController, passwordController);
+    });
+
+    passwordController.addListener(() {
+      _updateButtonState(accountController, passwordController);
+    });
+  }
+
+  //当 Account 和 Password 都有值才能按钮可用
+  void _updateButtonState(TextEditingController accountController, TextEditingController passwordController) {
+    state = state.copyWith(isLoginBtnEnable: accountController.text.isNotEmpty && passwordController.text.isNotEmpty);
+  }
+
+  //销毁资源
+  void onDispose(LoginState initState) {
+    final FocusNode accountFocusNode = initState.formData['account']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final TextEditingController accountController = initState.formData['account']!['controller'];
+    final TextEditingController passwordController = initState.formData['password']!['controller'];
+    accountFocusNode.dispose();
+    passwordFocusNode.dispose();
+    accountController.dispose();
+    passwordController.dispose();
+    Log.d("LoginViewModel 销毁 onDispose");
+  }
+
+  //保存当前选择的国家区号
+  void saveCountryCode(String? countryCode) {
+    state = state.copyWith(countryCode: countryCode);
+    Log.d("saveCountryCode:${state.countryCode}");
+  }
+}

+ 3 - 3
packages/cpt_auth/lib/modules/auth_login/vm/auth_login_view_model.g.dart

@@ -7,12 +7,12 @@ part of 'auth_login_view_model.dart';
 // **************************************************************************
 
 String _$authLoginViewModelHash() =>
-    r'10a37f5f6fead251c1df197123d10ce07fe0594c';
+    r'a3e88f9aa351b658190e033c598003af7c274212';
 
 /// See also [AuthLoginViewModel].
 @ProviderFor(AuthLoginViewModel)
 final authLoginViewModelProvider =
-    AutoDisposeNotifierProvider<AuthLoginViewModel, void>.internal(
+    AutoDisposeNotifierProvider<AuthLoginViewModel, LoginState>.internal(
   AuthLoginViewModel.new,
   name: r'authLoginViewModelProvider',
   debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -22,6 +22,6 @@ final authLoginViewModelProvider =
   allTransitiveDependencies: null,
 );
 
-typedef _$AuthLoginViewModel = AutoDisposeNotifier<void>;
+typedef _$AuthLoginViewModel = AutoDisposeNotifier<LoginState>;
 // ignore_for_file: type=lint
 // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 0 - 34
packages/cpt_auth/lib/modules/auth_login/page/Profile_edit_page.dart

@@ -1,34 +0,0 @@
-
-import 'package:flutter/material.dart';
-import 'package:auto_route/auto_route.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:router/ext/auto_router_extensions.dart';
-
-import '../../../router/page/auth_page_router.dart';
-import '../vm/auth_login_view_model.dart';
-
-@RoutePage()
-class AuthLoginPage extends HookConsumerWidget {
-  const AuthLoginPage({Key? key}) : super(key: key);
-
-  //启动当前页面
-  static void startInstance({BuildContext? context}) {
-    if (context != null) {
-      context.router.push(const AuthLoginPageRoute());
-    } else {
-      appRouter.push(const AuthLoginPageRoute());
-    }
-  }
-
-  @override
-  Widget build(BuildContext context, WidgetRef ref) {
-    final _viewModel = ref.read(authLoginViewModelProvider.notifier);
-
-    return Scaffold(
-      appBar: AppBar(title: Text("登录")),
-      body: Center(
-        child: Text("用户登录页面"),
-      ),
-    );
-  }
-}

+ 0 - 14
packages/cpt_auth/lib/modules/auth_login/vm/auth_login_view_model.dart

@@ -1,14 +0,0 @@
-
-import 'package:riverpod_annotation/riverpod_annotation.dart';
-
-part 'auth_login_view_model.g.dart';
-
-@riverpod
-class AuthLoginViewModel extends _$AuthLoginViewModel {
-
-  @override
-  void build(){
-
-  }
-
-}

+ 117 - 0
packages/cpt_auth/lib/modules/estate_upload_success/estate_upload_success_page.dart

@@ -0,0 +1,117 @@
+import 'package:cpt_auth/modules/sing_up_success/sign_up_success_view_model.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import '../../router/page/auth_page_router.dart';
+import 'estate_upload_success_view_model.dart';
+
+@RoutePage()
+class EstateUploadSuccessPage extends HookConsumerWidget {
+  const EstateUploadSuccessPage({Key? key}) : super(key: key);
+
+  //登录页面进入
+  static void startPop2Login() {
+    appRouter.pushAndPopUntil(
+      const EstateUploadSuccessPageRoute(),
+      predicate: (route) {
+        return route.settings.name == "AuthLoginPageRoute";
+      },
+    );
+  }
+
+  //Me页面进入
+  static void startPop2Estate() {
+    appRouter.pushAndPopUntil(
+      const EstateUploadSuccessPageRoute(),
+      predicate: (route) {
+        return route.settings.name == "MyEstatePageRoute";
+      },
+    );
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(estateUploadSuccessViewModelProvider.notifier);
+    final userConfig = UserConfigService.getState(ref: ref);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 18),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  children: [
+                    //顶部图片
+                    const MyAssetImage(
+                      Assets.authYyHomeSuccess,
+                      width: 264,
+                      height: 180,
+                    ).alignCenter().marginOnly(top: 12),
+
+                    MyTextView(
+                      S.current.thank_you_name(userConfig.userName ?? ""),
+                      fontSize: 23,
+                      marginTop: 30,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.estate_upload_success_desc,
+                      fontSize: 15,
+                      marginTop: 25,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                  ],
+                )).expanded(),
+            Row(
+              children: [
+                MyButton(
+                  onPressed: viewModel.callEmail,
+                  text: S.current.send_email,
+                  textColor: Colors.white,
+                  backgroundColor: context.appColors.btnBgDefault,
+                  fontWeight: FontWeight.w500,
+                  type: ClickType.throttle,
+                  fontSize: 16,
+                  minHeight: 50,
+                  radius: 5,
+                ).expanded(),
+                MyButton(
+                  onPressed: viewModel.callPhone,
+                  text: S.current.call_phone,
+                  textColor: Colors.white,
+                  backgroundColor: context.appColors.btnBgDefault,
+                  fontWeight: FontWeight.w500,
+                  type: ClickType.throttle,
+                  fontSize: 16,
+                  minHeight: 50,
+                  radius: 5,
+                ).marginOnly(left: 10).expanded(),
+              ],
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 22 - 0
packages/cpt_auth/lib/modules/estate_upload_success/estate_upload_success_view_model.dart

@@ -0,0 +1,22 @@
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../select_estate/select_estate_page.dart';
+
+part 'estate_upload_success_view_model.g.dart';
+
+@riverpod
+class EstateUploadSuccessViewModel extends _$EstateUploadSuccessViewModel {
+  @override
+  void build() {}
+
+  //拨打电话
+  void callPhone() {
+
+  }
+
+  //发送邮件
+  void callEmail() {
+
+  }
+
+}

+ 27 - 0
packages/cpt_auth/lib/modules/estate_upload_success/estate_upload_success_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'estate_upload_success_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$estateUploadSuccessViewModelHash() =>
+    r'b59a8d4df107b2a6b269bf899884ac09dd9e3122';
+
+/// See also [EstateUploadSuccessViewModel].
+@ProviderFor(EstateUploadSuccessViewModel)
+final estateUploadSuccessViewModelProvider =
+    AutoDisposeNotifierProvider<EstateUploadSuccessViewModel, void>.internal(
+  EstateUploadSuccessViewModel.new,
+  name: r'estateUploadSuccessViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$estateUploadSuccessViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$EstateUploadSuccessViewModel = AutoDisposeNotifier<void>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 184 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_page.dart

@@ -0,0 +1,184 @@
+import 'package:cpt_auth/modules/auth_login/auth_login_page.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+import 'package:plugin_basic/dialog/country_code_selecter.dart';
+import '../../router/page/auth_page_router.dart';
+import 'forgot_input_state.dart';
+import 'forgot_input_view_model.dart';
+
+@RoutePage()
+class ForgotInputPage extends HookConsumerWidget {
+  const ForgotInputPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const ForgotInputPageRoute());
+    } else {
+      appRouter.push(const ForgotInputPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(forgotInputViewModelProvider.notifier);
+    final state = ref.watch(forgotInputViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authForgotInputEmailPhoneImg,
+                width: 260,
+                height: 170,
+              ).marginOnly(top: 25, bottom: 29),
+
+              //提示文本
+              MyTextView(
+                S.current.forgot_text,
+                fontSize: 24,
+                marginBottom: 23,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              //邮箱输入框
+              _buildInputLayout(
+                context,
+                state,
+                "email",
+                textInputType: TextInputType.emailAddress,
+                textInputAction: TextInputAction.next,
+                errorText: state.emailErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['phone']!['focusNode']);
+                },
+              ),
+
+              //电话号码
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  //封装的国家选择控件
+                  CountryCodeSelector(
+                    onChanged: (countryCode) {
+                     viewModel.saveCountryCode(countryCode);
+                    },
+                  ).marginOnly(left: 15),
+
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "phone",
+                    fillBGColor: Colors.transparent,
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.done,
+                    errorText: state.phoneErrorText,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      viewModel.submitInput();
+                    },
+                  ).expanded(),
+                ],
+              )
+                  .decorated(
+                    color: context.appColors.authFiledBG,
+                    borderRadius: BorderRadius.circular(5.0),
+                  )
+                  .marginOnly(top: 15),
+
+              MyButton(
+                onPressed: viewModel.submitInput,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 25, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    ForgotInputState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    Color? fillBGColor,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: fillBGColor ?? context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 54 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_state.dart

@@ -0,0 +1,54 @@
+/*
+ * 登录的状态类
+ */
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class ForgotInputState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? emailErrorText;
+  String? phoneErrorText;
+
+  String? countryCode; //用户选择的区号
+
+  // ===================================  Begin  ↓  ===================================
+
+  ForgotInputState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.emailErrorText,
+    this.phoneErrorText,
+    this.countryCode,
+  }) : formData = formData ??
+            {
+              'email': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.email,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'phone': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.mobile_phone,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+            };
+
+  ForgotInputState copyWith({
+    String? emailErrorText,
+    String? phoneErrorText,
+    String? countryCode,
+  }) {
+    return ForgotInputState(
+      formData: this.formData,
+      emailErrorText: emailErrorText,
+      phoneErrorText: phoneErrorText,
+      countryCode: countryCode ?? this.countryCode,
+    );
+  }
+}

+ 84 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.dart

@@ -0,0 +1,84 @@
+import 'package:cpt_auth/modules/forgot_verify/forgot_verify_page.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'forgot_input_state.dart';
+
+part 'forgot_input_view_model.g.dart';
+
+@riverpod
+class ForgotInputViewModel extends _$ForgotInputViewModel {
+  @override
+  ForgotInputState build() {
+    final state = ForgotInputState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //提交忘记密码表单,发送验证码
+  void submitInput() {
+    state = state.copyWith(emailErrorText: null, phoneErrorText: null);
+
+    final FocusNode emailFocusNode = state.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = state.formData['phone']!['focusNode'];
+    final TextEditingController emailController = state.formData['email']!['controller'];
+    final TextEditingController phoneController = state.formData['phone']!['controller'];
+
+    emailFocusNode.unfocus();
+    phoneFocusNode.unfocus();
+
+    final email = emailController.text;
+    final phone = phoneController.text;
+
+    Log.d('当前待提交的 邮箱:${email} 电话:${phone} 最终的电话:${state.countryCode}$phone');
+
+    if (Utils.isEmpty(phone)) {
+      state = state.copyWith(phoneErrorText: "Phone cannot be empty!");
+      return;
+    }
+
+    ForgotVerifyPage.startInstance(phone: "${state.countryCode}$phone");
+  }
+
+  //初始化监听
+  void initListener(ForgotInputState initState) {
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+
+    emailFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (emailFocusNode.hasFocus) {
+        state = state.copyWith(emailErrorText: null);
+      }
+    });
+
+    phoneFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (phoneFocusNode.hasFocus) {
+        state = state.copyWith(phoneErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(ForgotInputState initState) {
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+    emailFocusNode.dispose();
+    phoneFocusNode.dispose();
+    Log.d("ForgotInputViewModel 销毁 onDispose");
+  }
+
+  //保存当前选择的国家区号
+  void saveCountryCode(String? countryCode) {
+    state = state.copyWith(countryCode: countryCode);
+    Log.d("saveCountryCode:${state.countryCode}");
+  }
+
+}

+ 27 - 0
packages/cpt_auth/lib/modules/forgot_input/forgot_input_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'forgot_input_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$forgotInputViewModelHash() =>
+    r'd3f941cb656ba085b920cfb68cc80c597e5b97f1';
+
+/// See also [ForgotInputViewModel].
+@ProviderFor(ForgotInputViewModel)
+final forgotInputViewModelProvider = AutoDisposeNotifierProvider<
+    ForgotInputViewModel, ForgotInputState>.internal(
+  ForgotInputViewModel.new,
+  name: r'forgotInputViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$forgotInputViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ForgotInputViewModel = AutoDisposeNotifier<ForgotInputState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 237 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_page.dart

@@ -0,0 +1,237 @@
+import 'package:cpt_auth/modules/forgot_verify/forgot_verify_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'forgot_verify_view_model.dart';
+
+@RoutePage()
+class ForgotVerifyPage extends HookConsumerWidget {
+  final String? phone;
+
+  const ForgotVerifyPage({Key? key, required this.phone}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context, required String? phone}) {
+    if (context != null) {
+      context.router.push(ForgotVerifyPageRoute(phone: phone));
+    } else {
+      appRouter.push(ForgotVerifyPageRoute(phone: phone));
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(forgotVerifyViewModelProvider.notifier);
+    final state = ref.watch(forgotVerifyViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authForgotResetPasswordImg,
+                width: 260,
+                height: 170,
+              ).marginOnly(top: 25, bottom: 29),
+
+              //提示文本
+              MyTextView(
+                S.current.mobile_phone,
+                fontSize: 15,
+                marginBottom: 23,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyTextView(
+                phone ?? "-",
+                fontSize: 23,
+                marginBottom: 23,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              // 表单 - 验证码
+              _buildInputLayout(
+                context,
+                state,
+                "code",
+                textInputType: TextInputType.number,
+                textInputAction: TextInputAction.next,
+                errorText: state.codeErrorText,
+                showRightIcon: true,
+                rightWidget: MyTextView(
+                  state.isCounting ? "${state.countdownTime} s" : S.current.get_code,
+                  textAlign: TextAlign.center,
+                  textColor: context.appColors.textPrimary,
+                  fontSize: 15,
+                  paddingRight: 5,
+                  isFontMedium: true,
+                  onClick: state.isCounting ? null : () => viewModel.showVerifyCodedDialog(phone),
+                ).paddingOnly(top: 15, bottom: 15),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 重置密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                marginTop: 15,
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                textInputAction: TextInputAction.next,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: !state.pwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['confirm_password']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 确认密码
+              _buildInputLayout(
+                context,
+                state,
+                "confirm_password",
+                marginTop: 15,
+                obscureText: !state.confirmPwdVisibility,
+                errorText: state.confirmPasswordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: !state.confirmPwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchConfirmPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.submitVerify(phone);
+                },
+              ),
+
+              MyButton(
+                onPressed: (){
+                  viewModel.submitVerify(phone);
+                },
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 25, bottom: 30),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    ForgotVerifyState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 78 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_state.dart

@@ -0,0 +1,78 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class ForgotVerifyState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? codeErrorText;
+  String? passwordErrorText;
+  String? confirmPasswordErrorText;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  bool confirmPwdVisibility;
+
+  //获取验证码的倒计时
+  bool isCounting;
+  int countdownTime;
+
+  // ===================================  Begin  ↓  ===================================
+
+  ForgotVerifyState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.pwdVisibility = false,
+    this.confirmPwdVisibility = false,
+    this.isCounting = false,
+    this.countdownTime = 0,
+    this.codeErrorText,
+    this.passwordErrorText,
+    this.confirmPasswordErrorText,
+  }) : formData = formData ??
+            {
+              'code': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.verification_code,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.reset_password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+              'confirm_password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.confirm_password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+            };
+
+  ForgotVerifyState copyWith({
+    String? codeErrorText,
+    String? passwordErrorText,
+    String? confirmPasswordErrorText,
+    bool? pwdVisibility,
+    bool? confirmPwdVisibility,
+    bool? isCounting,
+    int? countdownTime,
+  }) {
+    return ForgotVerifyState(
+      formData: this.formData,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      confirmPwdVisibility: confirmPwdVisibility ?? this.confirmPwdVisibility,
+      isCounting: isCounting ?? this.isCounting,
+      countdownTime: countdownTime ?? this.countdownTime,
+      codeErrorText: codeErrorText,
+      passwordErrorText: passwordErrorText,
+      confirmPasswordErrorText: confirmPasswordErrorText,
+    );
+  }
+}

+ 216 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.dart

@@ -0,0 +1,216 @@
+import 'dart:async';
+
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/dialog/verify_code_dialog.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import '../auth_login/auth_login_page.dart';
+import 'forgot_verify_state.dart';
+
+part 'forgot_verify_view_model.g.dart';
+
+@riverpod
+class ForgotVerifyViewModel extends _$ForgotVerifyViewModel with DioCancelableMixin {
+  late final AuthRepository _authRepository;
+
+  @override
+  ForgotVerifyState build() {
+    _authRepository = ref.read(authRepositoryProvider);
+
+    final state = ForgotVerifyState();
+    initListener(state);
+
+    registerCancellation(callback: () {
+      onDispose(state);
+    });
+
+    return state;
+  }
+
+  //提交重置密码的校验
+  void submitVerify(String? phone) async {
+    state = state.copyWith(codeErrorText: null, passwordErrorText: null, confirmPasswordErrorText: null);
+
+    if (Utils.isEmpty(phone)) {
+      ToastEngine.show("Mobile Phone Error");
+      return;
+    }
+
+    final FocusNode codeFocusNode = state.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = state.formData['confirm_password']!['focusNode'];
+
+    final TextEditingController codeController = state.formData['code']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+    final TextEditingController confirmPasswordController = state.formData['confirm_password']!['controller'];
+
+    codeFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+    confirmPasswordFocusNode.unfocus();
+
+    final code = codeController.text;
+    final password = passwordController.text;
+    final confirmPassword = confirmPasswordController.text;
+
+    Log.d('当前待提交的 code:$code password:$password confirmPassword:$confirmPassword');
+
+    if (Utils.isEmpty(code)) {
+      state = state.copyWith(codeErrorText: "Verification Code cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(confirmPassword)) {
+      state = state.copyWith(confirmPasswordErrorText: "Confirm Password cannot be empty!");
+      return;
+    }
+
+    if (confirmPassword != password) {
+      state = state.copyWith(confirmPasswordErrorText: "Password mismatch, please check password");
+      return;
+    }
+
+    //执行密码登录
+    final result = await _authRepository.forgotPassword(
+      smsCode: code,
+      countryCode: Utils.getMobileCode(phone ?? ""),
+      phone: Utils.getRealMobile(phone ?? ""),
+      password: password,
+      confirmPassword: confirmPassword,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess(S.current.successful);
+      gotoLoginPage();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  //去登录页面
+  void gotoLoginPage() {
+    AuthLoginPage.startInstance();
+  }
+
+  //切换隐藏显示密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  void switchConfirmPwdVisibility() {
+    state = state.copyWith(confirmPwdVisibility: !state.confirmPwdVisibility);
+  }
+
+  // 验证弹窗
+  showVerifyCodedDialog(String? phone) {
+    if (Utils.isEmpty(phone)) {
+      ToastEngine.show("Mobile Phone Error");
+      return;
+    }
+
+    DialogEngine.show(
+      onDismiss: () {},
+      widget: VerifyCodeDialog(
+        confirmAction: (key, code) {
+          //发送短信验证码
+          sendSMS(phone!, key, code);
+        },
+      ),
+    );
+  }
+
+  /// 调用接口发送短信验证码
+  void sendSMS(String phone, String? key, String? code) async {
+    final result = await _authRepository.sendSMS(
+      countryCode: Utils.getMobileCode(phone ?? ""),
+      phone: Utils.getRealMobile(phone ?? ""),
+      captchaKey: key,
+      captchaValue: code,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess(S.current.send_sms_successful);
+      _startCountDown();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  Timer? countdownTimer;
+
+  /// 开启倒计时
+  void _startCountDown() {
+    //60秒倒计时
+    state = state.copyWith(isCounting: true, countdownTime: 60);
+
+    //每秒的倒计时
+    countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+      int time = state.countdownTime;
+      Log.d('倒计时-->$time');
+      if (time > 0) {
+        time--;
+        state = state.copyWith(isCounting: true, countdownTime: time);
+      } else {
+        state = state.copyWith(isCounting: false, countdownTime: 0);
+        countdownTimer?.cancel(); // 取消计时器
+      }
+    });
+  }
+
+  //初始化监听
+  void initListener(ForgotVerifyState initState) {
+    final FocusNode codeFocusNode = initState.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+
+    codeFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (codeFocusNode.hasFocus) {
+        state = state.copyWith(codeErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    confirmPasswordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (confirmPasswordFocusNode.hasFocus) {
+        state = state.copyWith(confirmPasswordErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(ForgotVerifyState initState) {
+    final FocusNode codeFocusNode = initState.formData['code']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+    codeFocusNode.dispose();
+    passwordFocusNode.dispose();
+    confirmPasswordFocusNode.dispose();
+
+    countdownTimer?.cancel();
+
+    Log.d("ForgotVerifyViewModel 销毁 onDispose");
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/forgot_verify/forgot_verify_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'forgot_verify_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$forgotVerifyViewModelHash() =>
+    r'8b8ca850496d1176fa0eb77104d3fedf026e3bc3';
+
+/// See also [ForgotVerifyViewModel].
+@ProviderFor(ForgotVerifyViewModel)
+final forgotVerifyViewModelProvider = AutoDisposeNotifierProvider<
+    ForgotVerifyViewModel, ForgotVerifyState>.internal(
+  ForgotVerifyViewModel.new,
+  name: r'forgotVerifyViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$forgotVerifyViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$ForgotVerifyViewModel = AutoDisposeNotifier<ForgotVerifyState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 61 - 0
packages/cpt_auth/lib/modules/select_estate/attach_input_widget.dart

@@ -0,0 +1,61 @@
+import 'package:cpt_auth/modules/select_estate/select_estate_view_model.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+
+//输入框的布局,单独抽取为了做Attach弹窗
+class AttachInputWidget extends HookConsumerWidget {
+  final void Function(BuildContext context, String value) onChanged;
+
+  AttachInputWidget({
+    required this.onChanged,
+  });
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final state = ref.watch(selectEstateViewModelProvider);
+
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        "estate",
+        key: key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        "",
+        hintText: state.formData['estate']!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData['estate']!['controller'],
+        focusNode: state.formData['estate']!['focusNode'],
+        margin: const EdgeInsets.only(top: 0),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: TextInputType.text,
+        textInputAction: TextInputAction.done,
+        onChanged: (k, v) {
+          onChanged.call(context, v);
+        },
+        changeActionType: ClickType.debounce,
+        changeActionMilliseconds: 500,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: false,
+        errorText: null,
+        showLeftIcon: true,
+        showRightIcon: false,
+        rightWidget: null,
+      ),
+    );
+  }
+}

+ 74 - 0
packages/cpt_auth/lib/modules/select_estate/dialog/attach_search_dialog.dart

@@ -0,0 +1,74 @@
+import 'package:cpt_auth/modules/auth_login/auth_login_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:domain/entity/id_name_entity.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../select_estate_view_model.dart';
+
+//输入文本框
+class AttachSearchDialog extends HookConsumerWidget {
+  final ValueChanged<IdNameEntity?> onSelected;
+
+  AttachSearchDialog({
+    required this.onSelected,
+  });
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final state = ref.watch(selectEstateViewModelProvider);
+
+    return Container(
+      margin: const EdgeInsets.symmetric(horizontal: 38),
+      padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
+      decoration: BoxDecoration(
+        color: context.appColors.whiteBG,
+        borderRadius: BorderRadius.circular(5.0), // 5个圆角
+        boxShadow: [
+          BoxShadow(
+            color: const Color(0xFF656565).withOpacity(0.1), // 阴影颜色,并且设置透明度
+            offset: const Offset(0, 1.5), // 阴影的偏移量
+            blurRadius: 2.5, // 模糊半径
+            spreadRadius: 1.5, // 扩散半径
+          ),
+        ],
+      ),
+      constraints: const BoxConstraints(
+        maxHeight: 200, // 设置最大高度
+      ),
+      child: ListView.builder(
+        padding: EdgeInsets.zero,
+        shrinkWrap: true,
+        physics: const AlwaysScrollableScrollPhysics(),
+        itemCount: state.estateList?.length ?? 0,
+        itemBuilder: (context, index) {
+          final item = state.estateList?[index];
+          return MyTextView(
+            item?.name ?? "",
+            fontSize: 15,
+            paddingTop: 10,
+            paddingBottom: 10,
+            isFontRegular: true,
+            onClick: () {
+              onSelected(item); // 选中回调
+              onCancel();
+            },
+            textColor: context.appColors.textBlack,
+          );
+        },
+      ),
+    );
+  }
+}
+
+//取消弹框
+void onCancel() async {
+  SmartDialog.dismiss();
+}

+ 172 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_page.dart

@@ -0,0 +1,172 @@
+import 'dart:async';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:plugin_basic/basic_export.dart';
+import '../../router/page/auth_page_router.dart';
+import 'attach_input_widget.dart';
+import 'select_estate_view_model.dart';
+
+@RoutePage()
+class SelectEstatePage extends HookConsumerWidget with WidgetsBindingObserver {
+  SelectEstateViewModel? viewModel;
+  bool _isKeyboardVisible = false; //软件是否展示
+  double _previousBottom = 0; // 用于保存上一个 Insets
+  Function()? throttledShowDropDownDialog; // 定义防抖函数
+  bool isVisible = true; //页面是否可见
+
+  SelectEstatePage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(SelectEstatePageRoute());
+    } else {
+      appRouter.push(SelectEstatePageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    viewModel = ref.watch(selectEstateViewModelProvider.notifier);
+    final state = ref.watch(selectEstateViewModelProvider);
+
+    // 初始化防抖函数
+    throttledShowDropDownDialog ??= throttle(() {
+      viewModel?.showDropDownDialog(needReLocation: true);
+    }) as Function()?;
+
+    useEffect(() {
+      // 组件挂载时执行 - 执行接口请求
+      WidgetsBinding.instance.addObserver(this);
+      return () {
+        // 组件卸载时执行
+        WidgetsBinding.instance.removeObserver(this);
+      };
+    }, []);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.yy_home_accounts),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 38),
+        width: double.infinity,
+        child: VisibilityDetector(
+          key: const Key('select-estate-page'),
+          onVisibilityChanged: (VisibilityInfo info) {
+            // 检测页面是否可见
+            isVisible = info.visibleFraction > 0;
+            Log.d("检测页面是否可见:$isVisible");
+          },
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              SingleChildScrollView(
+                  scrollDirection: Axis.vertical,
+                  physics: const BouncingScrollPhysics(),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.center,
+                    children: [
+                      //顶部图片
+                      const MyAssetImage(
+                        Assets.authChooseEstateBuilding,
+                        width: 267,
+                        height: 158,
+                      ).marginOnly(top: 28, bottom: 38),
+
+                      MyTextView(
+                        S.current.estate_or_building_name,
+                        fontSize: 23,
+                        marginBottom: 20,
+                        textAlign: TextAlign.center,
+                        isFontMedium: true,
+                        textColor: context.appColors.textBlack,
+                      ),
+
+                      //输入资产的名称
+                      AttachInputWidget(
+                        onChanged: (context, value) {
+                          viewModel?.searchEstate(context, value);
+                        },
+                      ),
+
+                      MyTextView(
+                        S.current.estate_name_desc,
+                        fontSize: 15,
+                        marginTop: 19,
+                        isFontMedium: true,
+                        textColor: context.appColors.textBlack,
+                      ),
+                    ],
+                  )).expanded(),
+              MyButton(
+                onPressed: (){
+                  isVisible = false;
+                  viewModel?.submitEstate();
+                },
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 50, bottom: 50),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  @override
+  void didChangeMetrics() {
+    //页面不可见时不走逻辑
+    if (!isVisible) return;
+
+    // 当键盘弹出或收起时,这里会被调用
+    final currentInsets = WidgetsBinding.instance.window.viewInsets;
+
+    if (currentInsets.bottom > 0 && _previousBottom == 0) {
+      //键盘已弹出
+      _isKeyboardVisible = true;
+    } else if (currentInsets.bottom == 0 && _previousBottom > 0) {
+      //键盘已收起
+      _isKeyboardVisible = false;
+
+      //软键盘收起的时候,重新布局Attach下拉选弹窗
+      throttledShowDropDownDialog?.call();
+    }
+
+    // 更新上一个 Insets
+    _previousBottom = currentInsets.bottom;
+  }
+
+  //防抖 throttle 函数
+  Function throttle(void Function() callback, [int milliseconds = 500]) {
+    bool _isAllowed = true;
+    Timer? _throttleTimer;
+    return () {
+      if (!_isAllowed) return;
+      _isAllowed = false;
+      callback();
+      _throttleTimer?.cancel();
+      _throttleTimer = Timer(Duration(milliseconds: milliseconds), () {
+        _isAllowed = true;
+      });
+    };
+  }
+}

+ 59 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_state.dart

@@ -0,0 +1,59 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:domain/entity/id_name_entity.dart';
+import 'package:flutter/material.dart';
+
+class SelectEstateState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  List<IdNameEntity>? estateList;  //第一步的下拉选房产数据源
+
+  IdNameEntity? selectedEstate; //第一步数据:已经选中的房产
+
+  String? block; //第二步数据:已经选中的Block
+  String? unit; //第二步数据:已经选中的单元
+  String? room; //第二步数据:已经选中的单元-房间
+
+  String? type; //第三步数据:已经选中的类型
+
+  // ===================================  Begin  ↓  ===================================
+
+  SelectEstateState({
+    this.estateList,
+    this.selectedEstate,
+    this.block,
+    this.unit,
+    this.room,
+    this.type,
+    Map<String, Map<String, dynamic>>? formData,
+  }) : formData = formData ??
+            {
+              'estate': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.type_here,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+            };
+
+  SelectEstateState copyWith({
+    double? remainingSpace,
+    List<IdNameEntity>? estateList,
+    IdNameEntity? selectedEstate,
+    String? block,
+    String? unit,
+    String? room,
+    String? type,
+  }) {
+    return SelectEstateState(
+      estateList: estateList ?? this.estateList,
+      selectedEstate: selectedEstate ?? this.selectedEstate,
+      block: block ?? this.block,
+      unit: unit ?? this.unit,
+      room: room ?? this.room,
+      type: type ?? this.type,
+      formData: this.formData,
+    );
+  }
+}

+ 91 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.dart

@@ -0,0 +1,91 @@
+import 'package:cpt_auth/modules/select_estate/select_estate_state.dart';
+import 'package:cpt_auth/modules/select_unit/select_unit_page.dart';
+import 'package:domain/entity/id_name_entity.dart';
+import 'package:domain/repository/profile_repository.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+
+import 'dialog/attach_search_dialog.dart';
+
+part 'select_estate_view_model.g.dart';
+
+@riverpod
+class SelectEstateViewModel extends _$SelectEstateViewModel with DioCancelableMixin {
+  late final ProfileRepository _profileRepository;
+  BuildContext? _targetContext;
+
+  @override
+  SelectEstateState build() {
+    _profileRepository = ref.read(profileRepositoryProvider);
+    final state = SelectEstateState();
+    registerCancellation();
+    return state;
+  }
+
+  /// 提交并进入下一步
+  void submitEstate() {
+    if (state.selectedEstate == null) {
+      ToastEngine.show("Select Estate First");
+      return;
+    }
+
+    SelectUnitPage.startInstance();
+  }
+
+  /// 关键字搜索房产
+  void searchEstate(BuildContext targetContext, String? keyword) async {
+    _targetContext = targetContext;
+    final result = await _profileRepository.searchEstate(keyword: keyword, cancelToken: cancelToken);
+
+    if (result.isSuccess) {
+      final list = result.list;
+
+      if (list != null && list.isNotEmpty) {
+        state = state.copyWith(estateList: list);
+        //尝试展示下拉选选项
+        showDropDownDialog();
+      }
+    } else {
+      Log.e(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  //Attach下拉选
+  void showDropDownDialog({bool needReLocation = false}) {
+    if (_targetContext == null) return;
+    if (state.estateList == null || state.estateList!.isEmpty) return;
+
+    if (needReLocation) {
+      Log.d("需要重新定位弹出");
+      DialogEngine.dismiss(tag: 'estate');
+    }
+    if (!DialogEngine.checkIsExist(tag: 'estate') || needReLocation) {
+      DialogEngine.showAttach(
+        tag: 'estate',
+        targetContext: _targetContext!,
+        position: DialogPosition.bottom,
+        widget: AttachSearchDialog(
+          onSelected: (item) {
+            state = state.copyWith(selectedEstate: item);
+            final TextEditingController controller = state.formData['estate']!['controller'];
+            controller.text = item?.name ?? "";
+          },
+        ),
+      );
+    }
+  }
+
+  /// 保存第二步的单元等信息
+  void saveUnit({required String block, required String unit, required String room}) {
+    state = state.copyWith(block: block, unit: unit, room: room);
+  }
+
+  /// 保存第三步的角色信息
+  void saveRoleType({required String type}) {
+    state = state.copyWith(type: type);
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/select_estate/select_estate_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'select_estate_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$selectEstateViewModelHash() =>
+    r'aba0d6d52228a8b0cdfb7fb1e4fb1009fb33df94';
+
+/// See also [SelectEstateViewModel].
+@ProviderFor(SelectEstateViewModel)
+final selectEstateViewModelProvider = AutoDisposeNotifierProvider<
+    SelectEstateViewModel, SelectEstateState>.internal(
+  SelectEstateViewModel.new,
+  name: r'selectEstateViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$selectEstateViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SelectEstateViewModel = AutoDisposeNotifier<SelectEstateState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 64 - 0
packages/cpt_auth/lib/modules/select_role/screen_owner.dart

@@ -0,0 +1,64 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ScreenOwner extends StatelessWidget {
+  const ScreenOwner({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      scrollDirection: Axis.vertical,
+      physics: const BouncingScrollPhysics(),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            S.current.who_are_owners,
+            fontSize: 23.5,
+            marginTop: 25,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc1,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc2,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc3,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc4,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.owners_desc5,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 57 - 0
packages/cpt_auth/lib/modules/select_role/screen_tenant.dart

@@ -0,0 +1,57 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:widgets/my_text_view.dart';
+
+class ScreenTenant extends StatelessWidget {
+  const ScreenTenant({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return SingleChildScrollView(
+      scrollDirection: Axis.vertical,
+      physics: const BouncingScrollPhysics(),
+      child: Column(
+        mainAxisSize: MainAxisSize.max,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyTextView(
+            S.current.who_are_tenants,
+            fontSize: 23.5,
+            marginTop: 25,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc1,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc2,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc3,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+          MyTextView(
+            S.current.tenants_desc4,
+            fontSize: 15,
+            marginTop: 22,
+            isFontMedium: true,
+            textColor: context.appColors.textBlack,
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 115 - 0
packages/cpt_auth/lib/modules/select_role/select_role_page.dart

@@ -0,0 +1,115 @@
+import 'package:cpt_auth/modules/select_role/screen_owner.dart';
+import 'package:cpt_auth/modules/select_role/screen_tenant.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'select_role_view_model.dart';
+
+@RoutePage()
+class SelectRolePage extends HookConsumerWidget {
+  const SelectRolePage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const SelectRolePageRoute());
+    } else {
+      appRouter.push(const SelectRolePageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(selectRoleViewModelProvider.notifier);
+    final state = ref.watch(selectRoleViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 15),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            MyTextView(
+              S.current.owner_or_tenant,
+              fontSize: 23.5,
+              marginTop: 25,
+              marginBottom: 25,
+              textAlign: TextAlign.center,
+              isFontMedium: true,
+              textColor: context.appColors.textBlack,
+            ),
+
+            //顶部的Tab切换
+            Row(
+              children: [
+                MyTextView(
+                  S.current.owner,
+                  textColor: state.selectedIndex == 0 ? context.appColors.tabTextSelectedPrimary : context.appColors.tabTextUnSelectedPrimary,
+                  fontSize: 16,
+                  isFontMedium: true,
+                  paddingTop: 16,
+                  paddingBottom: 16,
+                  cornerRadius: 5,
+                  textAlign: TextAlign.center,
+                  onClick: () {
+                    viewModel.selectRole(0);
+                  },
+                  backgroundColor: state.selectedIndex == 0 ? context.appColors.tabBgSelectedPrimary : context.appColors.tabBgUnSelectedPrimary,
+                ).expanded(),
+                MyTextView(
+                  S.current.tenant,
+                  textColor: state.selectedIndex == 1 ? context.appColors.tabTextSelectedPrimary : context.appColors.tabTextUnSelectedPrimary,
+                  fontSize: 16,
+                  marginLeft: 15,
+                  paddingTop: 16,
+                  paddingBottom: 16,
+                  textAlign: TextAlign.center,
+                  isFontMedium: true,
+                  cornerRadius: 5,
+                  onClick: () {
+                    viewModel.selectRole(1);
+                  },
+                  backgroundColor: state.selectedIndex == 1 ? context.appColors.tabBgSelectedPrimary : context.appColors.tabBgUnSelectedPrimary,
+                ).expanded(),
+              ],
+            ),
+
+            IndexedStack(
+              index: state.selectedIndex,
+              children: const [
+                ScreenOwner(),
+                ScreenTenant(),
+              ],
+            ).expanded(),
+
+            //底部的按钮
+            MyButton(
+              onPressed: viewModel.submitRole,
+              text: S.current.next,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 15 - 0
packages/cpt_auth/lib/modules/select_role/select_role_state.dart

@@ -0,0 +1,15 @@
+class SelectRoleState {
+  int selectedIndex;
+
+  SelectRoleState({
+    this.selectedIndex = 0,
+  });
+
+  SelectRoleState copyWith({
+    int? selectedIndex,
+  }) {
+    return SelectRoleState(
+      selectedIndex: selectedIndex ?? this.selectedIndex,
+    );
+  }
+}

+ 30 - 0
packages/cpt_auth/lib/modules/select_role/select_role_view_model.dart

@@ -0,0 +1,30 @@
+import 'package:cpt_auth/modules/select_role/select_role_state.dart';
+import 'package:cpt_auth/modules/tenant_doc/tenant_doc_page.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../select_estate/select_estate_state.dart';
+import '../select_estate/select_estate_view_model.dart';
+
+part 'select_role_view_model.g.dart';
+
+@riverpod
+class SelectRoleViewModel extends _$SelectRoleViewModel {
+  late final SelectEstateViewModel _selectEstateViewModel;
+
+  @override
+  SelectRoleState build() {
+    _selectEstateViewModel = ref.watch(selectEstateViewModelProvider.notifier);
+    return SelectRoleState();
+  }
+
+  //进入下一步
+  void submitRole() {
+    _selectEstateViewModel.saveRoleType(type: "${state.selectedIndex + 1}");
+    TenantDocPage.startInstance();
+  }
+
+  //选择角色
+  void selectRole(int index) {
+    state = state.copyWith(selectedIndex: index);
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/select_role/select_role_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'select_role_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$selectRoleViewModelHash() =>
+    r'dcf0c9060d9661a5428df0fa2136cc138dc6050d';
+
+/// See also [SelectRoleViewModel].
+@ProviderFor(SelectRoleViewModel)
+final selectRoleViewModelProvider =
+    AutoDisposeNotifierProvider<SelectRoleViewModel, SelectRoleState>.internal(
+  SelectRoleViewModel.new,
+  name: r'selectRoleViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$selectRoleViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SelectRoleViewModel = AutoDisposeNotifier<SelectRoleState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 250 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_page.dart

@@ -0,0 +1,250 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:domain/entity/id_name_entity.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'select_unit_state.dart';
+import 'select_unit_view_model.dart';
+
+@RoutePage()
+class SelectUnitPage extends HookConsumerWidget {
+
+  const SelectUnitPage({Key? key,}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const SelectUnitPageRoute());
+    } else {
+      appRouter.push(const SelectUnitPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(selectUnitViewModelProvider.notifier);
+    final state = ref.watch(selectUnitViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, S.current.yy_home_accounts),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 20),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    //顶部图片
+                    const MyAssetImage(
+                      Assets.authSignUpUnitImg,
+                      width: 266.5,
+                      height: 162,
+                    ).marginOnly(top: 28, bottom: 18),
+
+                    Row(
+                      mainAxisSize: MainAxisSize.min,
+                      children: [
+                        //街区
+                        Column(
+                          crossAxisAlignment: CrossAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              S.current.block,
+                              marginBottom: 9,
+                              textColor: context.appColors.textBlack,
+                              fontSize: 16,
+                              isFontMedium: true,
+                            ),
+
+                            // 表单 - 街区
+                            _buildInputLayout(
+                              context,
+                              state,
+                              "block",
+                              textInputType: TextInputType.text,
+                              textInputAction: TextInputAction.next,
+                              onSubmit: (formKey, value) {
+                                state.formData[formKey]!['focusNode'].unfocus();
+                                FocusScope.of(context).requestFocus(state.formData['unit']!['focusNode']);
+                              },
+                            ).constrained(width: 88),
+                          ],
+                        ),
+
+                        MyTextView(
+                          "#",
+                          marginTop: 20,
+                          marginLeft: 8.5,
+                          marginRight: 8.5,
+                          textColor: context.appColors.textBlack,
+                          fontSize: 16,
+                          isFontMedium: true,
+                        ),
+
+                        //单元
+                        Column(
+                          crossAxisAlignment: CrossAxisAlignment.start,
+                          children: [
+                            MyTextView(
+                              S.current.unit_number,
+                              marginBottom: 9,
+                              textColor: context.appColors.textBlack,
+                              fontSize: 16,
+                              isFontMedium: true,
+                            ),
+                            Row(
+                              children: [
+                                // 表单 - 单元
+                                _buildInputLayout(
+                                  context,
+                                  state,
+                                  "unit",
+                                  textInputAction: TextInputAction.next,
+                                  onSubmit: (formKey, value) {
+                                    state.formData[formKey]!['focusNode'].unfocus();
+                                    FocusScope.of(context).requestFocus(state.formData['room']!['focusNode']);
+                                  },
+                                ).constrained(width: 83),
+
+                                MyTextView(
+                                  "-",
+                                  textColor: context.appColors.textBlack,
+                                  marginLeft: 4,
+                                  marginRight: 4,
+                                  isFontMedium: true,
+                                ),
+
+                                // 表单 - 房号
+                                _buildInputLayout(
+                                  context,
+                                  state,
+                                  "room",
+                                  textInputAction: TextInputAction.done,
+                                  onSubmit: (formKey, value) {
+                                    state.formData[formKey]!['focusNode'].unfocus();
+                                  },
+                                ).constrained(width: 83),
+                              ],
+                            ),
+                          ],
+                        ),
+                      ],
+                    ),
+
+                    MyTextView(
+                      S.current.block_desc,
+                      fontSize: 15,
+                      marginTop: 25,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.block_example,
+                      fontSize: 15,
+                      marginTop: 20,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.block_example_desc,
+                      fontSize: 15,
+                      marginTop: 20,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                  ],
+                )).expanded(),
+            MyButton(
+              onPressed: (){
+                viewModel.submitUnit();
+              },
+              text: S.current.next,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    SelectUnitState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.number,
+    String? errorText,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 35 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_state.dart

@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+
+class SelectUnitState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  // ===================================  Begin  ↓  ===================================
+
+  SelectUnitState({
+    Map<String, Map<String, dynamic>>? formData,
+  }) : formData = formData ??
+            {
+              'block': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': '',
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'unit': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': '',
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'room': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': '',
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+            };
+}

+ 75 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.dart

@@ -0,0 +1,75 @@
+import 'package:cpt_auth/modules/select_estate/select_estate_view_model.dart';
+import 'package:cpt_auth/modules/select_role/select_role_page.dart';
+import 'package:cpt_auth/modules/select_unit/select_unit_state.dart';
+import 'package:domain/repository/profile_repository.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/util.dart';
+
+import '../select_estate/select_estate_state.dart';
+
+part 'select_unit_view_model.g.dart';
+
+@riverpod
+class SelectUnitViewModel extends _$SelectUnitViewModel with DioCancelableMixin {
+  late final ProfileRepository _profileRepository;
+  late final SelectEstateViewModel _selectEstateViewModel;
+  late final SelectEstateState _selectEstateState;
+
+  @override
+  SelectUnitState build() {
+    _profileRepository = ref.read(profileRepositoryProvider);
+    _selectEstateViewModel = ref.watch(selectEstateViewModelProvider.notifier);
+    _selectEstateState = ref.watch(selectEstateViewModelProvider);
+
+    final state = SelectUnitState();
+    registerCancellation();
+    return state;
+  }
+
+  /// 提交选择的街道与单元
+  void submitUnit() async {
+    final FocusNode blockFocusNode = state.formData['block']!['focusNode'];
+    final FocusNode unitFocusNode = state.formData['unit']!['focusNode'];
+    final FocusNode roomFocusNode = state.formData['room']!['focusNode'];
+
+    blockFocusNode.unfocus();
+    unitFocusNode.unfocus();
+    roomFocusNode.unfocus();
+
+    final TextEditingController blockController = state.formData['block']!['controller'];
+    final TextEditingController unitController = state.formData['unit']!['controller'];
+    final TextEditingController roomController = state.formData['room']!['controller'];
+
+    final block = blockController.text;
+    final unit = unitController.text;
+    final room = roomController.text;
+
+    if (Utils.isEmpty(block)) {
+      ToastEngine.show("Block cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(unit)) {
+      ToastEngine.show("Unit Number cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(room)) {
+      ToastEngine.show("Unit Number cannot be empty!");
+      return;
+    }
+
+    final result = await _profileRepository.estateJoinCheck(estateId: _selectEstateState.selectedEstate?.id, block: block, unit: "$unit-$room");
+
+    if (result.isSuccess) {
+      //存入内存数据
+      _selectEstateViewModel.saveUnit(block: block, unit: unit, room: room);
+      SelectRolePage.startInstance();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/select_unit/select_unit_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'select_unit_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$selectUnitViewModelHash() =>
+    r'04669c8553c295069d6cc1fb0f7e771cbbc72672';
+
+/// See also [SelectUnitViewModel].
+@ProviderFor(SelectUnitViewModel)
+final selectUnitViewModelProvider =
+    AutoDisposeNotifierProvider<SelectUnitViewModel, SelectUnitState>.internal(
+  SelectUnitViewModel.new,
+  name: r'selectUnitViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$selectUnitViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SelectUnitViewModel = AutoDisposeNotifier<SelectUnitState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 302 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_page.dart

@@ -0,0 +1,302 @@
+import 'package:cpt_auth/modules/sign_up/sign_up_state.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/dialog/country_code_selecter.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_field.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_view_model.dart';
+
+@RoutePage()
+class SignUpPage extends HookConsumerWidget {
+  const SignUpPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(const SignUpPageRoute());
+    } else {
+      appRouter.push(const SignUpPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(signUpViewModelProvider.notifier);
+    final state = ref.watch(signUpViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authSignUpInputImg,
+                width: 260,
+                height: 170,
+              ).marginOnly(top: 22, bottom: 34),
+
+              // 表单 - 名
+              _buildInputLayout(
+                context,
+                state,
+                "first_name",
+                textInputType: TextInputType.text,
+                textInputAction: TextInputAction.next,
+                errorText: state.firstNameErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['last_name']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 姓
+              _buildInputLayout(
+                context,
+                state,
+                "last_name",
+                marginTop: 15,
+                textInputType: TextInputType.text,
+                textInputAction: TextInputAction.next,
+                errorText: state.lastNameErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['email']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 邮箱
+              _buildInputLayout(
+                context,
+                state,
+                "email",
+                marginTop: 15,
+                textInputType: TextInputType.emailAddress,
+                textInputAction: TextInputAction.next,
+                errorText: state.emailErrorText,
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['phone']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 电话
+              Row(
+                mainAxisSize: MainAxisSize.max,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  //封装的国家选择控件
+                  CountryCodeSelector(
+                    onChanged: (countryCode) {
+                      viewModel.saveCountryCode(countryCode);
+                    },
+                  ).marginOnly(left: 15),
+
+                  //电话输入框
+                  _buildInputLayout(
+                    context,
+                    state,
+                    "phone",
+                    fillBGColor: Colors.transparent,
+                    textInputType: TextInputType.number,
+                    textInputAction: TextInputAction.done,
+                    errorText: state.phoneErrorText,
+                    onSubmit: (formKey, value) {
+                      state.formData[formKey]!['focusNode'].unfocus();
+                      FocusScope.of(context).requestFocus(state.formData['password']!['focusNode']);
+                    },
+                  ).expanded(),
+                ],
+              )
+                  .decorated(
+                color: context.appColors.authFiledBG,
+                borderRadius: BorderRadius.circular(5.0),
+              )
+                  .marginOnly(top: 15),
+
+              // 表单 - 重置密码
+              _buildInputLayout(
+                context,
+                state,
+                "password",
+                marginTop: 15,
+                obscureText: !state.pwdVisibility,
+                errorText: state.passwordErrorText,
+                textInputAction: TextInputAction.next,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: !state.pwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  FocusScope.of(context).requestFocus(state.formData['confirm_password']!['focusNode']);
+                },
+              ),
+
+              // 表单 - 确认密码
+              _buildInputLayout(
+                context,
+                state,
+                "confirm_password",
+                marginTop: 15,
+                obscureText: !state.confirmPwdVisibility,
+                errorText: state.confirmPasswordErrorText,
+                showRightIcon: true,
+                rightWidget: IconButton(
+                  highlightColor: Colors.transparent,
+                  splashColor: Colors.transparent,
+                  icon: !state.confirmPwdVisibility
+                      ? const MyAssetImage(
+                          Assets.authPasswordHide,
+                          width: 22.5,
+                          height: 16.5,
+                        )
+                      : const MyAssetImage(
+                          Assets.authPasswordShow,
+                          width: 22.5,
+                          height: 16.5,
+                        ),
+                  onPressed: () {
+                    viewModel.switchConfirmPwdVisibility();
+                  },
+                ),
+                onSubmit: (formKey, value) {
+                  state.formData[formKey]!['focusNode'].unfocus();
+                  viewModel.submitSignUp();
+                },
+              ),
+
+              MyButton(
+                onPressed: viewModel.submitSignUp,
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 25, bottom: 25),
+
+              //同意协议
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  MyAssetImage(
+                    state.isAgreeTerms ? Assets.baseServiceCheckBoxChecked : Assets.baseServiceCheckBoxUncheck,
+                    color: state.isAgreeTerms ? context.appColors.textPrimary : Colors.transparent,
+                    width: 15.5,
+                    height: 15.5,
+                  ).onTap(viewModel.switchAgreeTerms, padding: 10),
+                  RichText(
+                    text: TextSpan(
+                      children: [
+                        TextSpan(
+                          text: S.current.agree_to,
+                          style: TextStyle(color: context.appColors.textDarkGray, fontWeight: FontWeight.w500, fontSize: 15), // 灰色文本
+                        ),
+                        const TextSpan(
+                          text: " ",
+                        ),
+                        TextSpan(
+                          text: S.current.terms_of_service,
+                          style: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15), // 蓝色文本
+                          recognizer: TapGestureRecognizer()..onTap = viewModel.gotoTermsPage,
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ).marginOnly(bottom: 35),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 输入框
+  Widget _buildInputLayout(
+    BuildContext context,
+    SignUpState state,
+    String key, {
+    double marginTop = 0,
+    bool? showRightIcon = false, //是否展示右侧的布局
+    Widget? rightWidget, //右侧的布局
+    TextInputType textInputType = TextInputType.text,
+    String? errorText,
+    Color? fillBGColor,
+    bool obscureText = false,
+    TextInputAction textInputAction = TextInputAction.done,
+    Function? onSubmit,
+  }) {
+    return IgnoreKeyboardDismiss(
+      child: MyTextField(
+        key,
+        fillBackgroundColor: fillBGColor ?? context.appColors.authFiledBG,
+        state.formData[key]!['value'],
+        hintText: state.formData[key]!['hintText'],
+        hintStyle: TextStyle(
+          color: context.appColors.authFiledHint,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        controller: state.formData[key]!['controller'],
+        focusNode: state.formData[key]!['focusNode'],
+        margin: EdgeInsets.only(top: marginTop),
+        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
+        showDivider: false,
+        height: 44,
+        style: TextStyle(
+          color: context.appColors.authFiledText,
+          fontSize: 16.0,
+          fontWeight: FontWeight.w500,
+        ),
+        inputType: textInputType,
+        textInputAction: textInputAction,
+        onSubmit: onSubmit,
+        cursorColor: context.appColors.authFiledText,
+        obscureText: obscureText,
+        errorText: errorText,
+        showLeftIcon: true,
+        showRightIcon: showRightIcon,
+        rightWidget: rightWidget,
+      ),
+    );
+  }
+}

+ 111 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_state.dart

@@ -0,0 +1,111 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+
+class SignUpState {
+  //表单的校验与数据
+  final Map<String, Map<String, dynamic>> formData;
+
+  //表单的错误信息展示
+  String? firstNameErrorText;
+  String? lastNameErrorText;
+  String? emailErrorText;
+  String? phoneErrorText;
+  String? passwordErrorText;
+  String? confirmPasswordErrorText;
+
+  String? countryCode;
+
+  //是否明文展示密码
+  bool pwdVisibility;
+
+  bool confirmPwdVisibility;
+
+  bool isAgreeTerms;
+
+  // ===================================  Begin  ↓  ===================================
+
+  SignUpState({
+    Map<String, Map<String, dynamic>>? formData,
+    this.pwdVisibility = false,
+    this.confirmPwdVisibility = false,
+    this.isAgreeTerms = false,
+    this.firstNameErrorText,
+    this.lastNameErrorText,
+    this.emailErrorText,
+    this.phoneErrorText,
+    this.passwordErrorText,
+    this.confirmPasswordErrorText,
+    this.countryCode,
+  }) : formData = formData ??
+            {
+              'first_name': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.first_name,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'last_name': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.last_name,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'email': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.email,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'phone': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.mobile_phone,
+                'focusNode': FocusNode(),
+                'obsecure': false,
+              },
+              'password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+              'confirm_password': {
+                'value': '',
+                'controller': TextEditingController(),
+                'hintText': S.current.confirm_password,
+                'focusNode': FocusNode(),
+                'obsecure': true,
+              },
+            };
+
+  SignUpState copyWith({
+    String? firstNameErrorText,
+    String? lastNameErrorText,
+    String? emailErrorText,
+    String? phoneErrorText,
+    String? passwordErrorText,
+    String? confirmPasswordErrorText,
+    String? countryCode,
+    bool? pwdVisibility,
+    bool? confirmPwdVisibility,
+    bool? isAgreeTerms,
+  }) {
+    return SignUpState(
+      formData: this.formData,
+      isAgreeTerms: isAgreeTerms ?? this.isAgreeTerms,
+      pwdVisibility: pwdVisibility ?? this.pwdVisibility,
+      confirmPwdVisibility: confirmPwdVisibility ?? this.confirmPwdVisibility,
+      firstNameErrorText: firstNameErrorText,
+      lastNameErrorText: lastNameErrorText,
+      emailErrorText: emailErrorText,
+      phoneErrorText: phoneErrorText,
+      passwordErrorText: passwordErrorText,
+      confirmPasswordErrorText: confirmPasswordErrorText,
+      countryCode: countryCode ?? this.countryCode,
+    );
+  }
+}

+ 219 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.dart

@@ -0,0 +1,219 @@
+import 'package:cpt_auth/modules/sing_up_verify/sign_up_verify_page.dart';
+import 'package:flutter/material.dart';
+import 'package:plugin_basic/dialog/verify_code_dialog.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'sign_up_state.dart';
+
+part 'sign_up_view_model.g.dart';
+
+@riverpod
+class SignUpViewModel extends _$SignUpViewModel {
+  @override
+  SignUpState build() {
+    final state = SignUpState();
+    initListener(state);
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //提交注册表单
+  void submitSignUp() {
+    state = state.copyWith(
+      firstNameErrorText: null,
+      lastNameErrorText: null,
+      emailErrorText: null,
+      phoneErrorText: null,
+      passwordErrorText: null,
+      confirmPasswordErrorText: null,
+    );
+
+    final FocusNode firstNameFocusNode = state.formData['first_name']!['focusNode'];
+    final FocusNode lastNameFocusNode = state.formData['last_name']!['focusNode'];
+    final FocusNode emailFocusNode = state.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = state.formData['phone']!['focusNode'];
+    final FocusNode passwordFocusNode = state.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = state.formData['confirm_password']!['focusNode'];
+
+    firstNameFocusNode.unfocus();
+    lastNameFocusNode.unfocus();
+    emailFocusNode.unfocus();
+    phoneFocusNode.unfocus();
+    passwordFocusNode.unfocus();
+    confirmPasswordFocusNode.unfocus();
+
+    final TextEditingController firstNameController = state.formData['first_name']!['controller'];
+    final TextEditingController lastNameController = state.formData['last_name']!['controller'];
+    final TextEditingController emailController = state.formData['email']!['controller'];
+    final TextEditingController phoneController = state.formData['phone']!['controller'];
+    final TextEditingController passwordController = state.formData['password']!['controller'];
+    final TextEditingController confirmPasswordController = state.formData['confirm_password']!['controller'];
+
+    final firstName = firstNameController.text;
+    final lastName = lastNameController.text;
+    final email = emailController.text;
+    final phone = phoneController.text;
+    final password = passwordController.text;
+    final confirmPassword = confirmPasswordController.text;
+
+    Log.d('当前待提交的 firstName:$firstName lastName:$lastName email:$email phone:$phone password:$password confirmPassword:$confirmPassword');
+
+    if (Utils.isEmpty(firstName)) {
+      state = state.copyWith(firstNameErrorText: "First Name cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(lastName)) {
+      state = state.copyWith(lastNameErrorText: "Last Name cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(email)) {
+      state = state.copyWith(emailErrorText: "Email cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(phone)) {
+      state = state.copyWith(phoneErrorText: "Email cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(password)) {
+      state = state.copyWith(passwordErrorText: "Password cannot be empty!");
+      return;
+    }
+
+    if (Utils.isEmpty(confirmPassword)) {
+      state = state.copyWith(confirmPasswordErrorText: "Confirm Password cannot be empty!");
+      return;
+    }
+
+    if (confirmPassword != password) {
+      state = state.copyWith(confirmPasswordErrorText: "Password mismatch, please check password");
+      return;
+    }
+
+
+    //短信验证码之前的图片验证码弹窗
+    DialogEngine.show(
+      onDismiss: () {},
+      widget: VerifyCodeDialog(
+        confirmAction: (key, code) {
+          //去新页面发送短信验证码并校验和注册用户
+          SignUpVerifyPage.startInstance(
+            firstName: firstName,
+            lastName: lastName,
+            email: email,
+            phone: "${state.countryCode}$phone",
+            password: password,
+            confirmPassword: confirmPassword,
+            verifyKey: key,
+            verifyCode: code,
+          );
+        },
+      ),
+    );
+
+  }
+
+  //切换隐藏显示密码
+  void switchPwdVisibility() {
+    state = state.copyWith(pwdVisibility: !state.pwdVisibility);
+  }
+
+  void switchConfirmPwdVisibility() {
+    state = state.copyWith(confirmPwdVisibility: !state.confirmPwdVisibility);
+  }
+
+  //切换同意按钮
+  void switchAgreeTerms() {
+    state = state.copyWith(isAgreeTerms: !state.isAgreeTerms);
+  }
+
+  void gotoTermsPage() {
+    ToastEngine.show("去协议页面");
+  }
+
+  //初始化监听
+  void initListener(SignUpState initState) {
+    final FocusNode firstNameFocusNode = initState.formData['first_name']!['focusNode'];
+    final FocusNode lastNameFocusNode = initState.formData['last_name']!['focusNode'];
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+
+    firstNameFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (firstNameFocusNode.hasFocus) {
+        state = state.copyWith(firstNameErrorText: null);
+      }
+    });
+
+    lastNameFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (lastNameFocusNode.hasFocus) {
+        state = state.copyWith(lastNameErrorText: null);
+      }
+    });
+
+    emailFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (emailFocusNode.hasFocus) {
+        state = state.copyWith(emailErrorText: null);
+      }
+    });
+
+    phoneFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (phoneFocusNode.hasFocus) {
+        state = state.copyWith(phoneErrorText: null);
+      }
+    });
+
+    passwordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (passwordFocusNode.hasFocus) {
+        state = state.copyWith(passwordErrorText: null);
+      }
+    });
+
+    confirmPasswordFocusNode.addListener(() {
+      // 获取焦点的时候清空错误文本
+      if (confirmPasswordFocusNode.hasFocus) {
+        state = state.copyWith(confirmPasswordErrorText: null);
+      }
+    });
+  }
+
+  //销毁资源
+  void onDispose(SignUpState initState) {
+    final FocusNode firstNameFocusNode = initState.formData['first_name']!['focusNode'];
+    final FocusNode lastNameFocusNode = initState.formData['last_name']!['focusNode'];
+    final FocusNode emailFocusNode = initState.formData['email']!['focusNode'];
+    final FocusNode phoneFocusNode = initState.formData['phone']!['focusNode'];
+    final FocusNode passwordFocusNode = initState.formData['password']!['focusNode'];
+    final FocusNode confirmPasswordFocusNode = initState.formData['confirm_password']!['focusNode'];
+    firstNameFocusNode.dispose();
+    lastNameFocusNode.dispose();
+    emailFocusNode.dispose();
+    phoneFocusNode.dispose();
+    passwordFocusNode.dispose();
+    confirmPasswordFocusNode.dispose();
+
+    Log.d("SignUpViewModel 销毁 onDispose");
+  }
+
+  //保存当前选择的国家区号
+  void saveCountryCode(String? countryCode) {
+    state = state.copyWith(countryCode: countryCode);
+    Log.d("saveCountryCode:${state.countryCode}");
+  }
+
+}

+ 26 - 0
packages/cpt_auth/lib/modules/sign_up/sign_up_view_model.g.dart

@@ -0,0 +1,26 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'sign_up_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$signUpViewModelHash() => r'47850a34e661455a7b752fc95c1ddb0ba30c3b4c';
+
+/// See also [SignUpViewModel].
+@ProviderFor(SignUpViewModel)
+final signUpViewModelProvider =
+    AutoDisposeNotifierProvider<SignUpViewModel, SignUpState>.internal(
+  SignUpViewModel.new,
+  name: r'signUpViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$signUpViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SignUpViewModel = AutoDisposeNotifier<SignUpState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 107 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_page.dart

@@ -0,0 +1,107 @@
+import 'package:cpt_auth/modules/sing_up_success/sign_up_success_view_model.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import '../../router/page/auth_page_router.dart';
+
+@RoutePage()
+class SignUpSuccessPage extends HookConsumerWidget {
+  const SignUpSuccessPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance() {
+    appRouter.pushAndPopUntil(
+      const SignUpSuccessPageRoute(),
+      predicate: (route) {
+        return route.settings.name == AuthLoginPageRoute.name;
+      },
+    );
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(signUpSuccessViewModelProvider.notifier);
+    final userConfig = UserConfigService.getState(ref: ref);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 20),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  children: [
+                    //顶部图片
+                    const MyAssetImage(
+                      Assets.authYyHomeSuccess,
+                      width: 264,
+                      height: 180,
+                    ).alignCenter().marginOnly(top: 12),
+
+                    MyTextView(
+                      S.current.welcome_name(userConfig.userName ?? ""),
+                      fontSize: 23,
+                      marginTop: 30,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.sign_up_success_txt1,
+                      fontSize: 15,
+                      marginTop: 25,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.sign_up_success_txt2,
+                      fontSize: 15,
+                      marginTop: 20,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+
+                    MyTextView(
+                      S.current.sign_up_success_txt3,
+                      fontSize: 15,
+                      marginTop: 20,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                  ],
+                )).expanded(),
+            MyButton(
+              onPressed: viewModel.gotoSelectEstatePage,
+              text: S.current.get_started,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 16 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.dart

@@ -0,0 +1,16 @@
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../select_estate/select_estate_page.dart';
+
+part 'sign_up_success_view_model.g.dart';
+
+@riverpod
+class SignUpSuccessViewModel extends _$SignUpSuccessViewModel {
+  @override
+  void build() {}
+
+  //去选择社区的页面
+  void gotoSelectEstatePage() {
+    SelectEstatePage.startInstance();
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/sing_up_success/sign_up_success_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'sign_up_success_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$signUpSuccessViewModelHash() =>
+    r'7c137417584f468299cce7cf59fc08a28f038227';
+
+/// See also [SignUpSuccessViewModel].
+@ProviderFor(SignUpSuccessViewModel)
+final signUpSuccessViewModelProvider =
+    AutoDisposeNotifierProvider<SignUpSuccessViewModel, void>.internal(
+  SignUpSuccessViewModel.new,
+  name: r'signUpSuccessViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$signUpSuccessViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SignUpSuccessViewModel = AutoDisposeNotifier<void>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 236 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_page.dart

@@ -0,0 +1,236 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:pinput/pinput.dart';
+import '../../router/page/auth_page_router.dart';
+import 'sign_up_verify_view_model.dart';
+
+@RoutePage()
+class SignUpVerifyPage extends HookConsumerWidget {
+  final String firstName;
+  final String lastName;
+  final String email;
+  final String fullPhone;
+  final String password;
+  final String confirmPassword;
+  final String? verifyKey;
+  final String? verifyCode;
+
+  const SignUpVerifyPage({
+    Key? key,
+    required this.firstName,
+    required this.lastName,
+    required this.email,
+    required this.fullPhone,
+    required this.password,
+    required this.confirmPassword,
+    required this.verifyKey,
+    required this.verifyCode,
+  }) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({
+    required String firstName,
+    required String lastName,
+    required String email,
+    required String phone,
+    required String password,
+    required String confirmPassword,
+    required String? verifyKey,
+    required String? verifyCode,
+  }) {
+    appRouter.push(SignUpVerifyPageRoute(
+      firstName: firstName,
+      lastName: lastName,
+      email: email,
+      phone: phone,
+      password: password,
+      confirmPassword: confirmPassword,
+      verifyKey: verifyKey,
+      verifyCode: verifyCode,
+    ));
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.watch(signUpVerifyViewModelProvider.notifier);
+    final state = ref.watch(signUpVerifyViewModelProvider);
+
+    useEffect(() {
+      Future.microtask(() {
+        //默认进来就发短信
+        viewModel.sendSMS(fullPhone, verifyKey, verifyCode);
+      });
+      return () {
+      };
+    }, []);
+
+    String? smsCode;  //已输入的短信验证码
+    const length = 6;
+    final borderColor = context.appColors.textPrimary;
+    const errorColor = Colors.redAccent;
+    final fillColor = context.appColors.authFiledBG;
+    final defaultPinTheme = PinTheme(
+      width: 50,
+      height: 50,
+      textStyle: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15),
+      decoration: BoxDecoration(
+        color: fillColor,
+        borderRadius: BorderRadius.circular(5),
+        border: Border.all(color: Colors.transparent),
+      ),
+    );
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(context, ""),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: SingleChildScrollView(
+        scrollDirection: Axis.vertical,
+        physics: const BouncingScrollPhysics(),
+        child: Container(
+          margin: const EdgeInsets.symmetric(horizontal: 38),
+          width: double.infinity,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              //顶部图片
+              const MyAssetImage(
+                Assets.authSmsVerifyImg,
+                width: 260,
+                height: 180,
+              ).marginOnly(top: 12, bottom: 24),
+
+              MyTextView(
+                S.current.sign_up_verify_txt,
+                fontSize: 15,
+                marginBottom: 24,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              SizedBox(
+                height: 50,
+                child: Pinput(
+                  length: length,
+                  controller: state.controller,
+                  focusNode: state.focusNode,
+                  defaultPinTheme: defaultPinTheme,
+                  onCompleted: (pin) {
+                    smsCode = pin;
+                    viewModel.verifySignUpInput(
+                      firstName: firstName,
+                      lastName: lastName,
+                      email: email,
+                      fullPhone: fullPhone,
+                      password: password,
+                      confirmPassword: confirmPassword,
+                      smsCode: pin,
+                    );
+                  },
+                  focusedPinTheme: defaultPinTheme.copyWith(
+                    height: 50,
+                    width: 50,
+                    decoration: defaultPinTheme.decoration!.copyWith(
+                      border: Border.all(color: borderColor),
+                    ),
+                  ),
+                  errorPinTheme: defaultPinTheme.copyWith(
+                    decoration: BoxDecoration(
+                      color: errorColor,
+                      borderRadius: BorderRadius.circular(5),
+                    ),
+                  ),
+                ),
+              ),
+
+              MyTextView(
+                S.current.did_not_receive,
+                fontSize: 15,
+                marginTop: 24,
+                marginBottom: 16,
+                textAlign: TextAlign.center,
+                isFontMedium: true,
+                textColor: context.appColors.textBlack,
+              ),
+
+              MyButton(
+                onPressed: () {
+                  if (!state.isCounting) {
+                    viewModel.showVerifyCodedDialog(fullPhone);
+                  }
+                },
+                text: state.isCounting ? "${state.countdownTime} s" : S.current.resend_code,
+                textColor: Colors.white,
+                backgroundColor: state.isCounting ? context.appColors.disEnableGray : context.appColors.orangeBG,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 42,
+                radius: 5,
+              ).constrained(width: 140),
+
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  RichText(
+                    text: TextSpan(
+                      children: [
+                        TextSpan(
+                          text: S.current.you_have,
+                          style: TextStyle(color: context.appColors.textBlack, fontWeight: FontWeight.w500, fontSize: 15),
+                        ),
+                        TextSpan(
+                          text: " 2 ",
+                          style: TextStyle(color: context.appColors.textPrimary, fontWeight: FontWeight.w500, fontSize: 15),
+                        ),
+                        TextSpan(
+                          text: S.current.tries_left,
+                          style: TextStyle(color: context.appColors.textBlack, fontWeight: FontWeight.w500, fontSize: 15),
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ).marginOnly(top: 16),
+
+              MyButton(
+                onPressed: () {
+                  viewModel.verifySignUpInput(
+                    firstName: firstName,
+                    lastName: lastName,
+                    email: email,
+                    fullPhone: fullPhone,
+                    password: password,
+                    confirmPassword: confirmPassword,
+                    smsCode: smsCode,
+                  );
+                },
+                text: S.current.next,
+                textColor: Colors.white,
+                backgroundColor: context.appColors.btnBgDefault,
+                fontWeight: FontWeight.w500,
+                type: ClickType.throttle,
+                fontSize: 16,
+                minHeight: 50,
+                radius: 5,
+              ).marginOnly(top: 50, bottom: 50),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 30 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_state.dart

@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+
+class SignUpVerifyState {
+  final TextEditingController controller;
+  final FocusNode focusNode;
+
+  // 获取验证码的倒计时
+  bool isCounting;
+  int countdownTime;
+
+  SignUpVerifyState({
+    this.isCounting = false,
+    this.countdownTime = 0,
+    TextEditingController? controller,
+    FocusNode? focusNode,
+  })  : controller = controller ?? TextEditingController(),
+        focusNode = focusNode ?? FocusNode();
+
+  SignUpVerifyState copyWith({
+    bool? isCounting,
+    int? countdownTime,
+  }) {
+    return SignUpVerifyState(
+      isCounting: isCounting ?? this.isCounting,
+      countdownTime: countdownTime ?? this.countdownTime,
+      controller: this.controller,
+      focusNode: this.focusNode,
+    );
+  }
+}

+ 128 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.dart

@@ -0,0 +1,128 @@
+import 'dart:async';
+
+import 'package:cpt_auth/modules/sing_up_success/sign_up_success_page.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:domain/repository/auth_repository.dart';
+import 'package:plugin_basic/dialog/verify_code_dialog.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:plugin_platform/engine/dialog/dialog_engine.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:shared/utils/util.dart';
+
+import 'sign_up_verify_state.dart';
+
+part 'sign_up_verify_view_model.g.dart';
+
+@riverpod
+class SignUpVerifyViewModel extends _$SignUpVerifyViewModel {
+  late final AuthRepository authRepository;
+
+  @override
+  SignUpVerifyState build() {
+    authRepository = ref.read(authRepositoryProvider);
+
+    final state = SignUpVerifyState();
+    ref.onDispose(() {
+      onDispose(state);
+    });
+    return state;
+  }
+
+  //校验SMS短信验证码,并完成用户注册
+  void verifySignUpInput({
+    required String firstName,
+    required String lastName,
+    required String email,
+    required String fullPhone,
+    required String password,
+    required String confirmPassword,
+    required String? smsCode,
+  }) async {
+    if (Utils.isEmpty(smsCode)) {
+      ToastEngine.show('Enter SMS Code');
+      return;
+    }
+
+    final result = await authRepository.userRegister(
+      firstName: firstName,
+      lastName: lastName,
+      email: email,
+      countryCode: Utils.getMobileCode(fullPhone),
+      phone: Utils.getRealMobile(fullPhone),
+      password: password,
+      confirmPassword: confirmPassword,
+      smsCode: smsCode,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess(S.current.successful);
+      //存入Token
+      UserConfigService.getInstance().setToken(token: result.data?.token, userName: result.data?.name);
+      //去成功页面
+      SignUpSuccessPage.startInstance();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  //销毁资源
+  void onDispose(SignUpVerifyState initState) {
+    state.controller.dispose();
+    state.focusNode.dispose();
+    countdownTimer?.cancel();
+  }
+
+  //展示验证码的弹窗
+  showVerifyCodedDialog(String fullPhone) {
+    DialogEngine.show(
+      onDismiss: () {},
+      widget: VerifyCodeDialog(
+        confirmAction: (key, code) {
+          //发送短信验证码
+          sendSMS(fullPhone, key, code);
+        },
+      ),
+    );
+  }
+
+  /// 调用接口发送短信验证码
+  void sendSMS(String fullPhone, String? key, String? code) async {
+    final result = await authRepository.sendSMS(
+      countryCode: Utils.getMobileCode(fullPhone),
+      phone: Utils.getRealMobile(fullPhone),
+      captchaKey: key,
+      captchaValue: code,
+    );
+
+    if (result.isSuccess) {
+      NotifyEngine.showSuccess(S.current.send_sms_successful);
+      _startCountDown();
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+
+  Timer? countdownTimer;
+
+  /// 开启倒计时
+  void _startCountDown() {
+    //60秒倒计时
+    state = state.copyWith(isCounting: true, countdownTime: 60);
+
+    //每秒的倒计时
+    countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+      int time = state.countdownTime;
+      Log.d('倒计时-->$time');
+      if (time > 0) {
+        time--;
+        state = state.copyWith(isCounting: true, countdownTime: time);
+      } else {
+        state = state.copyWith(isCounting: false, countdownTime: 0);
+        countdownTimer?.cancel(); // 取消计时器
+      }
+    });
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/sing_up_verify/sign_up_verify_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'sign_up_verify_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$signUpVerifyViewModelHash() =>
+    r'ad1cb9a410068dac7bf1311d777f3fe0baa1a938';
+
+/// See also [SignUpVerifyViewModel].
+@ProviderFor(SignUpVerifyViewModel)
+final signUpVerifyViewModelProvider = AutoDisposeNotifierProvider<
+    SignUpVerifyViewModel, SignUpVerifyState>.internal(
+  SignUpVerifyViewModel.new,
+  name: r'signUpVerifyViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$signUpVerifyViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$SignUpVerifyViewModel = AutoDisposeNotifier<SignUpVerifyState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 99 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_page.dart

@@ -0,0 +1,99 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:widgets/my_button.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:plugin_platform/platform_export.dart';
+
+import '../../router/page/auth_page_router.dart';
+import 'tenant_doc_view_model.dart';
+
+@RoutePage()
+class TenantDocPage extends HookConsumerWidget {
+  TenantDocPage({Key? key}) : super(key: key);
+
+  //启动当前页面
+  static void startInstance({BuildContext? context}) {
+    if (context != null) {
+      context.router.push(TenantDocPageRoute());
+    } else {
+      appRouter.push(TenantDocPageRoute());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final viewModel = ref.read(tenantDocViewModelProvider.notifier);
+    final state = ref.watch(tenantDocViewModelProvider);
+
+    return Scaffold(
+      appBar: MyAppBar.appBar(
+        context,
+        "",
+      ),
+      backgroundColor: context.appColors.backgroundDefault,
+      body: Container(
+        padding: const EdgeInsets.symmetric(horizontal: 15),
+        width: double.infinity,
+        child: Column(
+          mainAxisSize: MainAxisSize.max,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            SingleChildScrollView(
+                scrollDirection: Axis.vertical,
+                physics: const BouncingScrollPhysics(),
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    MyTextView(
+                      S.current.upload_documents,
+                      fontSize: 23.5,
+                      marginTop: 23,
+                      marginBottom: 21,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    MyTextView(
+                      state.type == "1" ? S.current.upload_doc_owner_desc : S.current.upload_doc_tenant_desc,
+                      fontSize: 15,
+                      marginBottom: 24,
+                      isFontMedium: true,
+                      textColor: context.appColors.textBlack,
+                    ),
+                    ImageNineGrid(
+                      isSelectEnable: true,
+                      maxImages: 3,
+                      spacing: 10,
+                      aspectRatio: 108 / 80,
+                      initialImages: [],
+                      onImagesChanged: (list) {
+                        viewModel.setDocList(list);
+                      },
+                    ),
+                  ],
+                )).expanded(),
+
+            //底部的按钮
+            MyButton(
+              onPressed: viewModel.submitDoc,
+              text: S.current.next,
+              textColor: Colors.white,
+              backgroundColor: context.appColors.btnBgDefault,
+              fontWeight: FontWeight.w500,
+              type: ClickType.throttle,
+              fontSize: 16,
+              minHeight: 50,
+              radius: 5,
+            ).marginOnly(top: 50, bottom: 50, left: 18, right: 18),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 20 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_state.dart

@@ -0,0 +1,20 @@
+class TenantDocState {
+  final List<String> docList;
+
+  final String type;
+
+  const TenantDocState({
+    required this.docList,
+    required this.type,
+  });
+
+  TenantDocState copyWith({
+    List<String>? docList,
+    String? type,
+  }) {
+    return TenantDocState(
+      docList: docList ?? this.docList,
+      type: type ?? this.type,
+    );
+  }
+}

+ 69 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.dart

@@ -0,0 +1,69 @@
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:domain/repository/profile_repository.dart';
+import 'package:plugin_basic/constants/app_constant.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:router/componentRouter/component_service_manager.dart';
+import 'package:shared/utils/event_bus.dart';
+import 'package:shared/utils/log_utils.dart';
+
+import '../estate_upload_success/estate_upload_success_page.dart';
+import '../select_estate/select_estate_state.dart';
+import '../select_estate/select_estate_view_model.dart';
+import 'tenant_doc_state.dart';
+
+part 'tenant_doc_view_model.g.dart';
+
+@riverpod
+class TenantDocViewModel extends _$TenantDocViewModel {
+  late final ProfileRepository _profileRepository;
+  late final SelectEstateState _selectEstateState;
+
+  @override
+  TenantDocState build() {
+    _profileRepository = ref.read(profileRepositoryProvider);
+    _selectEstateState = ref.watch(selectEstateViewModelProvider);
+
+    return TenantDocState(docList: [], type: _selectEstateState.type ?? "1");
+  }
+
+  //设置已选中的文件数组
+  void setDocList(List<String> list) {
+    state = state.copyWith(docList: list);
+  }
+
+  //提交文件
+  void submitDoc() async {
+    if (state.docList.isEmpty) {
+      ToastEngine.show("Select Documents First");
+      return;
+    }
+
+    final result = await _profileRepository.joinEstateUnit(
+      estateId: _selectEstateState.selectedEstate?.id,
+      block: _selectEstateState.block,
+      unit: "${_selectEstateState.unit}-${_selectEstateState.room}",
+      type: _selectEstateState.type,
+      paths: state.docList,
+    );
+
+    if (result.isSuccess) {
+      //如果有用户信息,说明是在Me页面添加的
+      if (UserConfigService.getState().user != null) {
+        //刷新Estate页面
+        bus.emit(AppConstant.eventEstateRefresh, true);
+        //去成功页面
+        Log.d("Estate页面");
+        EstateUploadSuccessPage.startPop2Estate();
+      } else {
+        Log.d("登录页面");
+        //如果没有用户信息,说明是在登录页面添加的。
+        EstateUploadSuccessPage.startPop2Login();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
+  }
+}

+ 27 - 0
packages/cpt_auth/lib/modules/tenant_doc/tenant_doc_view_model.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'tenant_doc_view_model.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$tenantDocViewModelHash() =>
+    r'92e8fc2e3d5b1e9f7a709ca06a17adb6a28c76ff';
+
+/// See also [TenantDocViewModel].
+@ProviderFor(TenantDocViewModel)
+final tenantDocViewModelProvider =
+    AutoDisposeNotifierProvider<TenantDocViewModel, TenantDocState>.internal(
+  TenantDocViewModel.new,
+  name: r'tenantDocViewModelProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$tenantDocViewModelHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+typedef _$TenantDocViewModel = AutoDisposeNotifier<TenantDocState>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 8 - 6
packages/cpt_auth/lib/router/component/auth_component_service.dart

@@ -1,13 +1,12 @@
 /*
  * Auth 组件的组件路由
  */
-import 'package:flutter/cupertino.dart';
 import 'package:router/componentRouter/auth_service.dart';
-import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 
-import '../../modules/auth_login/page/Profile_edit_page.dart';
+import '../../modules/auth_login/auth_login_page.dart';
+import '../../modules/select_estate/select_estate_page.dart';
 
 part 'auth_component_service.g.dart';
 
@@ -17,15 +16,18 @@ AuthService authService(Ref ref) {
 }
 
 class AuthComponentService extends AuthService {
-
   @override
   void startLoginPage() {
     AuthLoginPage.startInstance();
   }
 
   @override
-  void startResetPasswordPage() {
-
+  void startSelectEstatePage() {
+    SelectEstatePage.startInstance();
   }
 
+  @override
+  void startAndPopAllLoginPage() {
+    AuthLoginPage.startAndPopAll();
+  }
 }

+ 23 - 2
packages/cpt_auth/lib/router/page/auth_page_router.dart

@@ -1,9 +1,21 @@
 import 'package:auto_route/auto_route.dart';
+import 'package:domain/entity/id_name_entity.dart';
 import 'package:flutter/material.dart';
 import 'package:router/ext/auto_router_extensions.dart';
 import 'package:router/path/router_path.dart';
 
-import '../../modules/auth_login/page/Profile_edit_page.dart';
+import '../../modules/auth_login/auth_login_page.dart';
+import '../../modules/forgot_input/forgot_input_page.dart';
+import '../../modules/forgot_verify/forgot_verify_page.dart';
+import '../../modules/sign_up/sign_up_page.dart';
+import '../../modules/sing_up_verify/sign_up_verify_page.dart';
+import '../../modules/sing_up_success/sign_up_success_page.dart';
+import '../../modules/select_estate/select_estate_page.dart';
+import '../../modules/select_unit/select_unit_page.dart';
+import '../../modules/select_role/select_role_page.dart';
+import '../../modules/tenant_doc/tenant_doc_page.dart';
+import '../../modules/estate_upload_success/estate_upload_success_page.dart';
+
 
 
 part 'auth_page_router.gr.dart';
@@ -15,7 +27,16 @@ part 'auth_page_router.gr.dart';
 class AuthPageRouter extends _$AuthPageRouter {
   @override
   List<AutoRoute> get routes => [
-
     CustomRoute(page: AuthLoginPageRoute.page, path: RouterPath.authLogin, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: ForgotInputPageRoute.page, path: RouterPath.authForgotInput, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: ForgotVerifyPageRoute.page, path: RouterPath.authForgotVerify, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SignUpPageRoute.page, path: RouterPath.authSignUp, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SignUpVerifyPageRoute.page, path: RouterPath.authSignUpVerify, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SignUpSuccessPageRoute.page, path: RouterPath.authSignUpSuccess, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SelectEstatePageRoute.page, path: RouterPath.authSelectEstate, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SelectUnitPageRoute.page, path: RouterPath.authSelectUnit, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: SelectRolePageRoute.page, path: RouterPath.authSelectRole, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: TenantDocPageRoute.page, path: RouterPath.authTenantDoc, transitionsBuilder: applySlideTransition),
+    CustomRoute(page: EstateUploadSuccessPageRoute.page, path: RouterPath.authEstateUploadSuccess, transitionsBuilder: applySlideTransition),
   ];
 }

+ 333 - 1
packages/cpt_auth/lib/router/page/auth_page_router.gr.dart

@@ -20,7 +20,86 @@ abstract class _$AuthPageRouter extends RootStackRouter {
         routeData: routeData,
         child: const AuthLoginPage(),
       );
-    }
+    },
+    EstateUploadSuccessPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const EstateUploadSuccessPage(),
+      );
+    },
+    ForgotInputPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const ForgotInputPage(),
+      );
+    },
+    ForgotVerifyPageRoute.name: (routeData) {
+      final args = routeData.argsAs<ForgotVerifyPageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: ForgotVerifyPage(
+          key: args.key,
+          phone: args.phone,
+        ),
+      );
+    },
+    SelectEstatePageRoute.name: (routeData) {
+      final args = routeData.argsAs<SelectEstatePageRouteArgs>(
+          orElse: () => const SelectEstatePageRouteArgs());
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: SelectEstatePage(key: args.key),
+      );
+    },
+    SelectRolePageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SelectRolePage(),
+      );
+    },
+    SelectUnitPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SelectUnitPage(),
+      );
+    },
+    SignUpPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SignUpPage(),
+      );
+    },
+    SignUpSuccessPageRoute.name: (routeData) {
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: const SignUpSuccessPage(),
+      );
+    },
+    SignUpVerifyPageRoute.name: (routeData) {
+      final args = routeData.argsAs<SignUpVerifyPageRouteArgs>();
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: SignUpVerifyPage(
+          key: args.key,
+          firstName: args.firstName,
+          lastName: args.lastName,
+          email: args.email,
+          fullPhone: args.phone,
+          password: args.password,
+          confirmPassword: args.confirmPassword,
+          verifyKey: args.verifyKey,
+          verifyCode: args.verifyCode,
+        ),
+      );
+    },
+    TenantDocPageRoute.name: (routeData) {
+      final args = routeData.argsAs<TenantDocPageRouteArgs>(
+          orElse: () => const TenantDocPageRouteArgs());
+      return AutoRoutePage<dynamic>(
+        routeData: routeData,
+        child: TenantDocPage(key: args.key),
+      );
+    },
   };
 }
 
@@ -37,3 +116,256 @@ class AuthLoginPageRoute extends PageRouteInfo<void> {
 
   static const PageInfo<void> page = PageInfo<void>(name);
 }
+
+/// generated route for
+/// [EstateUploadSuccessPage]
+class EstateUploadSuccessPageRoute extends PageRouteInfo<void> {
+  const EstateUploadSuccessPageRoute({List<PageRouteInfo>? children})
+      : super(
+          EstateUploadSuccessPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'EstateUploadSuccessPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [ForgotInputPage]
+class ForgotInputPageRoute extends PageRouteInfo<void> {
+  const ForgotInputPageRoute({List<PageRouteInfo>? children})
+      : super(
+          ForgotInputPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'ForgotInputPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [ForgotVerifyPage]
+class ForgotVerifyPageRoute extends PageRouteInfo<ForgotVerifyPageRouteArgs> {
+  ForgotVerifyPageRoute({
+    Key? key,
+    required String? phone,
+    List<PageRouteInfo>? children,
+  }) : super(
+          ForgotVerifyPageRoute.name,
+          args: ForgotVerifyPageRouteArgs(
+            key: key,
+            phone: phone,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'ForgotVerifyPageRoute';
+
+  static const PageInfo<ForgotVerifyPageRouteArgs> page =
+      PageInfo<ForgotVerifyPageRouteArgs>(name);
+}
+
+class ForgotVerifyPageRouteArgs {
+  const ForgotVerifyPageRouteArgs({
+    this.key,
+    required this.phone,
+  });
+
+  final Key? key;
+
+  final String? phone;
+
+  @override
+  String toString() {
+    return 'ForgotVerifyPageRouteArgs{key: $key, phone: $phone}';
+  }
+}
+
+/// generated route for
+/// [SelectEstatePage]
+class SelectEstatePageRoute extends PageRouteInfo<SelectEstatePageRouteArgs> {
+  SelectEstatePageRoute({
+    Key? key,
+    List<PageRouteInfo>? children,
+  }) : super(
+          SelectEstatePageRoute.name,
+          args: SelectEstatePageRouteArgs(key: key),
+          initialChildren: children,
+        );
+
+  static const String name = 'SelectEstatePageRoute';
+
+  static const PageInfo<SelectEstatePageRouteArgs> page =
+      PageInfo<SelectEstatePageRouteArgs>(name);
+}
+
+class SelectEstatePageRouteArgs {
+  const SelectEstatePageRouteArgs({this.key});
+
+  final Key? key;
+
+  @override
+  String toString() {
+    return 'SelectEstatePageRouteArgs{key: $key}';
+  }
+}
+
+/// generated route for
+/// [SelectRolePage]
+class SelectRolePageRoute extends PageRouteInfo<void> {
+  const SelectRolePageRoute({List<PageRouteInfo>? children})
+      : super(
+          SelectRolePageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SelectRolePageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SelectUnitPage]
+class SelectUnitPageRoute extends PageRouteInfo<void> {
+  const SelectUnitPageRoute({List<PageRouteInfo>? children})
+      : super(
+          SelectUnitPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SelectUnitPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SignUpPage]
+class SignUpPageRoute extends PageRouteInfo<void> {
+  const SignUpPageRoute({List<PageRouteInfo>? children})
+      : super(
+          SignUpPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SignUpPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SignUpSuccessPage]
+class SignUpSuccessPageRoute extends PageRouteInfo<void> {
+  const SignUpSuccessPageRoute({List<PageRouteInfo>? children})
+      : super(
+          SignUpSuccessPageRoute.name,
+          initialChildren: children,
+        );
+
+  static const String name = 'SignUpSuccessPageRoute';
+
+  static const PageInfo<void> page = PageInfo<void>(name);
+}
+
+/// generated route for
+/// [SignUpVerifyPage]
+class SignUpVerifyPageRoute extends PageRouteInfo<SignUpVerifyPageRouteArgs> {
+  SignUpVerifyPageRoute({
+    Key? key,
+    required String firstName,
+    required String lastName,
+    required String email,
+    required String phone,
+    required String password,
+    required String confirmPassword,
+    required String? verifyKey,
+    required String? verifyCode,
+    List<PageRouteInfo>? children,
+  }) : super(
+          SignUpVerifyPageRoute.name,
+          args: SignUpVerifyPageRouteArgs(
+            key: key,
+            firstName: firstName,
+            lastName: lastName,
+            email: email,
+            phone: phone,
+            password: password,
+            confirmPassword: confirmPassword,
+            verifyKey: verifyKey,
+            verifyCode: verifyCode,
+          ),
+          initialChildren: children,
+        );
+
+  static const String name = 'SignUpVerifyPageRoute';
+
+  static const PageInfo<SignUpVerifyPageRouteArgs> page =
+      PageInfo<SignUpVerifyPageRouteArgs>(name);
+}
+
+class SignUpVerifyPageRouteArgs {
+  const SignUpVerifyPageRouteArgs({
+    this.key,
+    required this.firstName,
+    required this.lastName,
+    required this.email,
+    required this.phone,
+    required this.password,
+    required this.confirmPassword,
+    required this.verifyKey,
+    required this.verifyCode,
+  });
+
+  final Key? key;
+
+  final String firstName;
+
+  final String lastName;
+
+  final String email;
+
+  final String phone;
+
+  final String password;
+
+  final String confirmPassword;
+
+  final String? verifyKey;
+
+  final String? verifyCode;
+
+  @override
+  String toString() {
+    return 'SignUpVerifyPageRouteArgs{key: $key, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, password: $password, confirmPassword: $confirmPassword, verifyKey: $verifyKey, verifyCode: $verifyCode}';
+  }
+}
+
+/// generated route for
+/// [TenantDocPage]
+class TenantDocPageRoute extends PageRouteInfo<TenantDocPageRouteArgs> {
+  TenantDocPageRoute({
+    Key? key,
+    List<PageRouteInfo>? children,
+  }) : super(
+          TenantDocPageRoute.name,
+          args: TenantDocPageRouteArgs(key: key),
+          initialChildren: children,
+        );
+
+  static const String name = 'TenantDocPageRoute';
+
+  static const PageInfo<TenantDocPageRouteArgs> page =
+      PageInfo<TenantDocPageRouteArgs>(name);
+}
+
+class TenantDocPageRouteArgs {
+  const TenantDocPageRouteArgs({this.key});
+
+  final Key? key;
+
+  @override
+  String toString() {
+    return 'TenantDocPageRouteArgs{key: $key}';
+  }
+}

+ 3 - 0
packages/cpt_auth/pubspec.yaml

@@ -48,6 +48,9 @@ dependencies:
   # Hooks 简化 Riverpod 获取
   hooks_riverpod: ^2.5.1
 
+  # 验证码的插件
+  pinput: ^5.0.0
+
 dev_dependencies:
   flutter_test:
     sdk: flutter

+ 4 - 0
packages/cpt_community/devtools_options.yaml

@@ -0,0 +1,4 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
+  - provider: true

+ 139 - 0
packages/cpt_community/lib/components/comments_dialog.dart

@@ -0,0 +1,139 @@
+import 'package:flutter/material.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/no_shadow_scroll_behavior.dart';
+import 'package:widgets/widget_export.dart';
+
+class CommentsDialog extends StatelessWidget {
+  String? title;
+  Widget? Function(BuildContext)? messageBuilder;
+  VoidCallback confirmAction;
+  VoidCallback? cancelAction;
+  bool isShowCancelBtn;
+  bool isShowConfirmBtn;
+  String? confirmTxt;
+  String? cancelTxt;
+  double? height = 0.0;
+  double? minHeight = 0.0;
+  double? maxHeight;
+  double? maxWidth = 300;
+
+  CommentsDialog({
+    this.title,
+    Widget Function(BuildContext)? this.messageBuilder,
+    required this.confirmAction,
+    this.cancelAction,
+    this.isShowCancelBtn = true,
+    this.isShowConfirmBtn = true,
+    this.confirmTxt,
+    this.cancelTxt,
+    height,
+    minHeight,
+    maxHeight,
+    maxWidth,
+    Key? key,
+  }): height = height ?? 0.0,
+        minHeight = minHeight ?? 0.0,
+        maxHeight = maxHeight ?? 5000,
+        maxWidth = maxWidth ?? 500,
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
+
+    return Container(
+        width: double.infinity,
+        constraints: BoxConstraints(
+          minHeight: minHeight!,
+          maxHeight: maxHeight!,
+          maxWidth: maxWidth!,
+          minWidth: 0.0,
+        ),
+        decoration: BoxDecoration(
+          color: context.appColors.whiteSecondBG,
+          borderRadius: const BorderRadius.only(
+            topLeft: Radius.circular(0),
+            topRight: Radius.circular(0),
+          ),
+        ),
+        child: SingleChildScrollView(
+            reverse: true, // 反向滚动,确保输入框在键盘上方
+            child: Column(
+                mainAxisSize: MainAxisSize.min,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                    messageBuilder!(context) ?? Container(), // 处理 null 情况
+                    Visibility(
+                      visible: isShowConfirmBtn && isShowCancelBtn,
+                      child: Row(
+                        children: [
+                          const SizedBox(width: 18),
+                          Visibility(
+                            visible: isShowCancelBtn,
+                            child: Expanded(
+                              flex: 1,
+                              child: InkWell(
+                                onTap: () {
+                                  onCancel();
+                                  cancelAction?.call();
+                                },
+                                child: MyTextView(
+                                  cancelTxt ?? S.current.no,
+                                  fontSize: 16,
+                                  isFontMedium: true,
+                                  paddingTop: 13,
+                                  marginRight: 15,
+                                  paddingBottom: 13,
+                                  textAlign: TextAlign.center,
+                                  textColor: Colors.white,
+                                  backgroundColor: context.appColors.orangeBG,
+                                  cornerRadius: 7,
+                                ),
+                              ),
+                            ),
+                          ),
+                          Expanded(
+                            flex: 1,
+                            child: Visibility(
+                              visible: isShowConfirmBtn,
+                              child: InkWell(
+                                onTap: () async {
+                                  onCancel();
+                                  confirmAction();
+                                },
+                                child: MyTextView(
+                                  confirmTxt ?? S.current.yes,
+                                  fontSize: 16,
+                                  paddingTop: 13,
+                                  paddingBottom: 13,
+                                  isFontMedium: true,
+                                  textAlign: TextAlign.center,
+                                  textColor: Colors.white,
+                                  backgroundColor: context.appColors.btnBgDefault,
+                                  cornerRadius: 7,
+                                ),
+                              ),
+                            ),
+                          ),
+                          const SizedBox(width: 18),
+                        ],
+                      ).marginOnly(bottom: 30, top: 28),
+                    ),
+                ],
+            ),
+        ).marginOnly(
+          bottom: keyboardHeight + 10,
+          top: 5,
+        ),
+      );
+  }
+
+  // 取消弹框
+  void onCancel() async {
+    SmartDialog.dismiss();
+  }
+}
+   

+ 254 - 0
packages/cpt_community/lib/components/garage_card.dart

@@ -0,0 +1,254 @@
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/ext_dart.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+import '../modules/garage/for_sale/for_sale_vm.dart';
+
+// 'id':1,
+// 'goods_img':  'https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500',
+// 'title': 'Electronic keyboard',
+// 'price': '\$66',
+// 'isCollection': true,
+// 'collection_num': '12',
+// 'publisher': 'William Jefferson',
+// 'publish_time': 'June 17,2016 at 7:23 p.m.',
+// 'publisher_avator': Assets.communityCamera,'
+
+// 定义一个 使用场景的 枚举
+enum GarageCardUseType {
+  // 默认
+  forSale,
+  forRent,
+  myPostsForSale,
+  myPostsForRent,
+}
+
+class GarageCard extends StatelessWidget {
+  GarageCardUseType? useType;
+  Map<String, dynamic> itemObj;
+  double? cardHeight;
+  final Function()? onTap;
+  final Function(dynamic)? onClickColleciotn;
+
+  GarageCard({
+    Key? key,
+    this.useType = GarageCardUseType.forSale,
+    required this.itemObj,
+    this.onTap,
+    this.onClickColleciotn,
+    double? cardHeight,
+  }) : super(key: key) {
+    this.cardHeight ??= 214;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    List? card_resources = itemObj.getValue<List>("resources", []) ?? [];
+    String card_img = card_resources.length > 0 ? card_resources[0] : "";
+    String card_title = itemObj.getValue("title", "");
+    int card_price = itemObj.getValue("price", "");
+    String card_created_at = itemObj.getValue("created_at", "");
+    Map<String, dynamic>? card_account = itemObj.getValue<Map<String, dynamic>>("account", null);
+    String card_avatar = card_account?['avatar'] ?? "";
+    String card_publish_name = card_account?['name'] ?? "";
+    bool card_liked = itemObj.getValue("liked", false);
+    int card_likes_count = itemObj.getValue("likes_count", 0);
+    return Column(
+        mainAxisSize: MainAxisSize.max,
+        children: [
+          // 图片
+          Row(
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              Expanded(
+                child: ClipRRect(
+                  borderRadius: const BorderRadius.only(
+                    topLeft: Radius.circular(8),
+                    topRight: Radius.circular(8),
+                  ),
+                  child: MyLoadImage(
+                    card_img,
+                    width: 166.5,
+                    height: 102.5.ap,
+                    isCircle: false,
+                    fit: BoxFit.cover,
+                  ),
+                ),
+              ),
+            ],
+          ),
+
+          // 标题
+          Padding(
+            padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 5),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Expanded(
+                  child: MyTextView(
+                    card_title,
+                    maxLines: 1,
+                    isTextEllipsis: true,
+                    textAlign: TextAlign.left,
+                    textColor: context.appColors.textBlack,
+                    fontSize: 16,
+                    isFontRegular: true,
+                  ),
+                ),
+              ],
+            ),
+          ),
+
+          // 价格 及 收藏
+          Padding(
+            padding: const EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 0),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceAround,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                Expanded(
+                  child: MyTextView(
+                    '$card_price',
+                    maxLines: 1,
+                    isTextEllipsis: true,
+                    textAlign: TextAlign.start,
+                    textColor: AppColorsTheme.colorPrimary,
+                    fontSize: 18,
+                    isFontMedium: true,
+                  ),
+                ),
+                // 动态的 收藏数
+                CollectionWidget(
+                  collectionNum: card_likes_count,
+                  isCollection: card_liked,
+                  onClickColleciotn: onClickColleciotn,
+                ),
+              ],
+            ),
+          ),
+
+          // 发布人信息
+          Expanded(
+            child: Padding(
+                padding: EdgeInsets.only(
+                  left: (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent) ? 10 : 0,
+                  right: 10,
+                  bottom: (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent) ? 10.5 :0,
+                ),
+                child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [
+                  if (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent)
+                    MyLoadImage(
+                      card_avatar,
+                      width: 30,
+                      height: 30,
+                      isCircle: true,
+                    )
+                  else
+                    const SizedBox.shrink(),
+                  Expanded(
+                      child: Column(
+                          mainAxisAlignment: MainAxisAlignment.center,
+                          crossAxisAlignment: CrossAxisAlignment.start,
+                          mainAxisSize: MainAxisSize.min,
+                          children: [
+                        if (useType == GarageCardUseType.forSale || useType == GarageCardUseType.forRent)
+                          MyTextView(
+                            card_publish_name,
+                            maxLines: 1,
+                            isTextEllipsis: true,
+                            textAlign: TextAlign.start,
+                            marginLeft: 13,
+                            fontSize: 12,
+                            textColor: ColorUtils.string2Color('#2956B7'),
+                            isFontRegular: true,
+                          )
+                        else
+                          const SizedBox.shrink(),
+                        MyTextView(
+                          card_created_at,
+                          maxLines: 1,
+                          isTextEllipsis: true,
+                          textAlign: TextAlign.start,
+                          marginLeft: 13,
+                          marginTop: 2.5,
+                          marginBottom: 2.5,
+                          fontSize: 10,
+                          textColor: context.appColors.textBlack,
+                          isFontRegular: true,
+                        ),
+                      ])),
+                ])),
+          )
+        ],
+      ).constrained(height: cardHeight);
+
+  }
+}
+
+class CollectionWidget extends HookConsumerWidget {
+  int collectionNum = 0;
+  bool isCollection = false;
+  final Function(dynamic)? onClickColleciotn;
+
+  CollectionWidget({
+    Key? key,
+    required this.collectionNum,
+    required this.isCollection,
+    this.onClickColleciotn,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final collectionNumState = useState(collectionNum);
+    final isCollectionState = useState(isCollection);
+    return Container(
+        width: 60,
+        height: 30,
+        alignment: Alignment.center,
+        // decoration: BoxDecoration(
+        //   color: ColorUtils.string2Color('#E5E5E5'),
+        //   borderRadius: BorderRadius.circular(15),
+        // ),
+        child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
+          MyTextView(
+            '${collectionNumState.value}',
+            textColor: context.appColors.textBlack,
+            fontSize: 14,
+            isFontRegular: true,
+            marginRight: 7,
+          ),
+          MyLoadImage(
+            isCollectionState.value ? Assets.communityLikeActive : Assets.communityLike,
+            width: 14.1,
+            height: 14,
+          )
+        ]
+            // 点击 收餐/取消收藏
+            ).onTap(() async {
+          // Log.d("点击了收藏按钮  ${isCollectionState.value}");
+          // ToastEngine.show("点击了收藏按钮 ${isCollectionState.value}");
+          final result = await onClickColleciotn?.call(isCollectionState.value);
+          if (result != null && result) {
+            isCollectionState.value = !isCollectionState.value;
+            collectionNumState.value =
+                (collectionNumState.value + (isCollectionState.value ? 1 : -1)) < 0 ? 0 : (collectionNumState.value + (isCollectionState.value ? 1 : -1));
+            if (isCollectionState.value) {
+              // ToastEngine.show("Collect Success");
+            } else {
+              // ToastEngine.show("Cancel Collect Success");
+            }
+          }
+        }));
+  }
+}

+ 85 - 0
packages/cpt_community/lib/components/newfeed_card_header.dart

@@ -0,0 +1,85 @@
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+// 'id':1,
+// 'avator': Assets.communityCamera,
+// 'title': 'William Jefferson',
+// 'isFollow': false,
+// 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+// 'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+// 'time': 'June 17,2016 at 7:23 p.m.',
+// 'likeno': 12
+
+class NewsFeedCardHeader extends StatelessWidget {
+  final String title;
+  final String avator;
+  final String time;
+  final VoidCallback? onTap;
+
+  const NewsFeedCardHeader({
+    Key? key,
+    required this.title,
+    required this.avator,
+    required this.time,
+    this.onTap,
+  }) : super(key: key);
+
+
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.only(left: 16,right: 60,),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          MyLoadImage(
+            avator,
+            width: 45,
+            height: 45,
+            isCircle: true,
+            fit: BoxFit.cover,
+          ).onTap(() {
+            // 点击头像
+            onTap?.call();
+          }),
+          Expanded(
+            child: Container(
+              padding: const EdgeInsets.only(left:15, right: 40),
+              // color: Colors.red,
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.start,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  MyTextView(
+                    title,
+                    isFontMedium: true,
+                    fontSize: 18,
+                    textColor: context.appColors.textBlack,
+                    maxLines: 1,
+                    isTextEllipsis: true,
+                  ),
+                  MyTextView(
+                    time,
+                    isFontRegular: true,
+                    fontSize: 13,
+                    marginTop: 8,
+                    textColor: ColorUtils.string2Color('#767676'),
+                    maxLines: 1,
+                    isTextEllipsis: true,
+                  ),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 161 - 0
packages/cpt_community/lib/components/newsfeed_card_content.dart

@@ -0,0 +1,161 @@
+import 'dart:math';
+
+import 'package:auto_route/src/route/page_route_info.dart';
+import 'package:cpt_community/components/comments_dialog.dart';
+import 'package:cpt_community/modules/community/community_page.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:plugin_basic/modules/global_web_page.dart';
+import 'package:plugin_basic/modules/preview_photo_page.dart';
+import 'package:plugin_basic/router/basic_page_router.dart';
+import 'package:plugin_platform/engine/image/image_preview.dart';
+import 'package:router/componentRouter/component_service_manager.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/goto_page.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+
+class NewsFeedCardContent extends StatelessWidget {
+  final String content;
+  final List<dynamic>? imageUrls;
+  int? rowMaxImageNum=3;
+  int? textMaxLines = 3;
+  bool? isPreviewImage = true;
+  NewsFeedCardContent({
+    Key? key,
+    required this.content,
+    required List<dynamic> this.imageUrls,
+    this.rowMaxImageNum = 3,
+    this.textMaxLines = 3,
+    this.isPreviewImage = true,
+  }) : super(key: key);
+
+
+  @override
+  Widget build(BuildContext context) {
+    List imageUrlsList = [];
+    if(imageUrls != null && imageUrls!.isNotEmpty){
+      // dart 取出 imageUrls 里面的前 rowMaxImageNum 张图片
+      imageUrlsList = imageUrls!.take(rowMaxImageNum!).toList();
+    }
+    int totalImageCount = imageUrls!.length;
+    int otherImageCount = imageUrls!.length - rowMaxImageNum!;
+    int rowActualImageCount = imageUrls!.length > rowMaxImageNum! ? rowMaxImageNum! : imageUrls!.length;
+    return LayoutBuilder(
+        builder: (BuildContext context, BoxConstraints constraints) {
+          final maxHeight = constraints.maxHeight;
+          final minHeight = constraints.minHeight;
+          final maxWidth = constraints.maxWidth;
+          // Log.d("---maxHeight-----$maxHeight-- $minHeight  $maxWidth--");
+          return Container(
+            width: double.infinity,
+            // color: Colors.red,
+            padding: const EdgeInsets.symmetric(horizontal: 15),
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  MyTextView(
+                    content,
+                    textColor: context.appColors.textBlack,
+                    fontSize: 15,
+                    maxLines: textMaxLines,
+                    isTextEllipsis: true,
+                  ),
+                  const SizedBox(height: 12),
+                  // 图片
+                  _buildImageSection(
+                      context,
+                      isPreviewImage!,
+                      maxWidth - 15,
+                      imageUrlsList,
+                      imageUrls,
+                      totalImageCount,
+                      otherImageCount,
+                      rowMaxImageNum!
+                  ),
+                ]
+            )
+          );
+      }
+    );
+  }
+
+
+  Widget _buildImageSection(BuildContext context,bool isPreviewImage, double maxWidth, List? imageUrls,List? totalImageUrls, int totalImageCount, int otherImageCount, int rowMaxImageNum) {
+    if (imageUrls != null && imageUrls!.isNotEmpty) {
+      final imageCount = imageUrls.length;
+      return SizedBox(
+        width: maxWidth,
+        height: 87,
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: List.generate(
+              imageUrls.length,
+              (index) => Container(
+                height: 87,
+                  width: (totalImageCount < rowMaxImageNum)? maxWidth/rowMaxImageNum - 16 : maxWidth/imageCount - 16,
+                margin: EdgeInsets.only(right: (index!=imageCount-1) ? 16 : 0 ),
+                color: ColorUtils.string2Color("#F2F3F6"),
+                child:Stack(
+                    children: [
+                      Hero(
+                        tag: imageUrls[index],
+                        child: MyLoadImage(
+                          imageUrls[index],
+                          height: 87,
+                          // width: maxWidth/imageCount - 16,
+                          width: (totalImageCount < rowMaxImageNum)? maxWidth/rowMaxImageNum - 16 : maxWidth/imageCount - 16,
+                          fit: BoxFit.cover,
+                          // fit:BoxFit.fitWidth,
+                          cornerRadius: 5,
+                          onClick: (){
+                            if(isPreviewImage){
+                              // 点击图片预览
+                              // 过滤掉非字符串类型的元素
+                              List<String> filteredImages = totalImageUrls?.whereType<String>().toList() ?? [];
+                              // PreviewPhotoPage.startInstance(context: context, images: filteredImages, position: index);
+                              // context.appRouter.push(PreviewPhotoPageRoute(images: filteredImages, position: index));
+                              // ImagePreviewEngine.multipleImagePreview(
+                              //     context,
+                              //     filteredImages,
+                              //     heroes: List.generate(filteredImages.length, (index) => filteredImages[index]),
+                              //     onLongPressAction: (url) {}
+                              // );
+                              GotoPage.pushPageByFade(
+                                  context: context,
+                                  targetPage:
+                                  PreviewPhotoPage(images: filteredImages, position: index),
+                              );
+                            }
+                          },
+                        ),
+                      ),
+                      otherImageCount > 0 && index == imageCount-1 ?
+                        Positioned(
+                        bottom: 0,
+                        right: 0,
+                        child: Container(
+                          padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
+                          color: context.appColors.textBlack.withOpacity(0.5),
+                          child: MyTextView(
+                            "+$otherImageCount",
+                            textColor: Colors.white,
+                            fontSize: 12,
+                          ),
+                        ),
+                      ): const SizedBox.shrink(),
+                    ]
+                  )
+              ),
+          )
+        )
+            );
+      } else {
+        return const SizedBox.shrink();
+      }
+    }
+}

+ 147 - 0
packages/cpt_community/lib/components/newsfeed_card_footer.dart

@@ -0,0 +1,147 @@
+import 'package:cpt_community/components/newfeed_card_header.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/my_like_button.dart';
+
+import '../modules/community/community_page.dart';
+
+// 'id':1,
+// 'avator': Assets.communityCamera,
+// 'title': 'William Jefferson',
+// 'isFollow': false,
+// 'content': 'She said YES and our lives changed.Thank you all for coming to my propose today.We hope everyone can ……[More]',
+// 'imageUrls': ['https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500','https://img2.baidu.com/it/u=3489233687,2364672159&fm=253&fmt=auto&app=120&f=JPEG?w=507&h=500'],
+// 'time': 'June 17,2016 at 7:23 p.m.',
+// 'likeno': 12
+
+class NewsFeedCardFooter extends HookConsumerWidget {
+  final bool isLike;
+  int? likes_count = 0;
+  int? comments_count = 0;
+  final VoidCallback? onLike;
+  final VoidCallback? onComment;
+  final VoidCallback? onShare;
+  final bool showShare;
+  final GlobalKey<MyLikeButtonState> likeButtonKey;
+
+  NewsFeedCardFooter({
+    Key? key,
+    GlobalKey<MyLikeButtonState>? likeButtonKey,
+    required this.isLike,
+    this.onLike,
+    this.likes_count = 0,
+    this.comments_count = 0,
+    this.onComment,
+    this.onShare,
+    this.showShare = false,
+  })  : likeButtonKey = likeButtonKey ?? GlobalKey<MyLikeButtonState>(),
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final _isLike = useState(isLike);
+    final _likes_count = useState(likes_count);
+    final _comments_count = useState(comments_count);
+
+    return Container(
+        height: 40,
+        width: double.infinity,
+        // padding: const EdgeInsets.symmetric(horizontal: 16),
+        child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [
+          Expanded(
+            child: Container(
+              padding: const EdgeInsets.all(10),
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  MyLikeButton(
+                    key: likeButtonKey,
+                    isLiked: _isLike.value,
+                    isCustomIcon: true,
+                    onLike: () {
+                      Log.d('点击了like button');
+                      onLike?.call();
+                    },
+                  ),
+                  // 动态的
+                  MyTextView(
+                    S.current.like_count(_likes_count.value?.toString() ?? "0"),
+                    textColor: ColorUtils.string2Color('#767676'),
+                    fontSize: 14,
+                    isFontRegular: true,
+                    textAlign: TextAlign.left,
+                    marginLeft: 8,
+                  )
+                ],
+              ),
+            ).onTap(() {
+              final state = likeButtonKey.currentState;
+              state?.triggerTap();
+            }),
+          ),
+          Expanded(
+            child: Container(
+              padding: const EdgeInsets.all(8),
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  const MyAssetImage(
+                    Assets.communityComments,
+                    width: 16,
+                    height: 16,
+                  ),
+                  MyTextView(
+                    S.current.comment_count(comments_count?.toString() ?? "0"),
+                    textColor: ColorUtils.string2Color('#767676'),
+                    fontSize: 14,
+                    isFontRegular: true,
+                    textAlign: TextAlign.left,
+                    marginLeft: 8,
+                  ),
+                ],
+              ),
+            ).onTap(() {
+              // Log.d("点击了comments   ${tabsRouterKey.currentState?.controller?.activeIndex}");
+              // tabsRouterKey.currentState?.controller?.setActiveIndex(2);
+              onComment?.call();
+            }),
+          ),
+          if (showShare)
+            Expanded(
+              child: Container(
+                padding: const EdgeInsets.all(8),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: [
+                    const MyAssetImage(
+                      Assets.communityShare,
+                      width: 16,
+                      height: 16,
+                    ),
+                    MyTextView(
+                      S.current.share,
+                      textColor: ColorUtils.string2Color('#767676'),
+                      fontSize: 14,
+                      isFontRegular: true,
+                      textAlign: TextAlign.left,
+                      marginLeft: 8,
+                    ),
+                  ],
+                ),
+              ).onTap(() {
+                Log.d("点击了share");
+                onShare?.call();
+              }),
+            ),
+        ]));
+  }
+}

+ 392 - 0
packages/cpt_community/lib/modules/community/community_page.dart

@@ -0,0 +1,392 @@
+
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:plugin_basic/provider/user_config/user_config_service.dart';
+import 'package:router/ext/auto_router_extensions.dart';
+import 'package:shared/utils/color_utils.dart';
+import 'package:shared/utils/log_utils.dart';
+import 'package:widgets/my_load_image.dart';
+import 'package:widgets/ext/ex_widget.dart';
+import 'package:widgets/my_text_view.dart';
+import 'package:widgets/my_appbar.dart';
+import 'package:cs_resources/theme/app_colors_theme.dart';
+import 'package:widgets/widget_export.dart';
+
+import '../../router/page/community_page_router.dart';
+import '../my_posts/my_posts_page.dart';
+import 'newsfeed_tabs.dart';
+import 'community_vm.dart';
+import 'customSilverHeaderTabs.dart';
+
+final tabsRouterKey = GlobalKey<AutoTabsRouterState>();
+final GlobalKey<ExtendedNestedScrollViewState> extendedNestedScrollViewKey =
+GlobalKey<ExtendedNestedScrollViewState>();
+@RoutePage()
+class CommunityPage extends HookConsumerWidget with WidgetsBindingObserver {
+    CommunityPage({Key? key}) : super(key: key);
+
+    //启动当前页面
+    static void startInstance({BuildContext? context}) {
+      if (context != null) {
+        context.router.push(const CommunityPageRoute());
+      } else {
+        appRouter.push(const CommunityPageRoute());
+      }
+    }
+
+    bool _isKeyboardVisible = false;
+
+    void handlerNestedScrollViewScroll({double? outerOffset, double? innerOffset, bool? isOuterScrollAnimated=false, bool? isInnerScrollAnimated=false}){
+
+      if(outerOffset !=null){
+        if(isOuterScrollAnimated!){
+          extendedNestedScrollViewKey.currentState?.outerController.animateTo(
+            outerOffset,
+            duration: const Duration(seconds: 1),
+            curve: Curves.easeIn,
+          );
+        }else {
+          extendedNestedScrollViewKey.currentState?.outerController.jumpTo(
+            outerOffset,
+          );
+        }
+      }
+
+      if(innerOffset !=null){
+        extendedNestedScrollViewKey.currentState?.innerPositions.forEach((position) {
+          if(isInnerScrollAnimated!){
+            position.animateTo(innerOffset,
+                duration: Duration(seconds: 1), curve: Curves.easeIn);
+          }else {
+            position.jumpTo(innerOffset);
+          }
+        });
+      }
+    }
+
+    @override
+    void didChangeMetrics() {
+      final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom;
+      final newValue = bottomInset > 0.0;
+      if (_isKeyboardVisible != newValue) {
+          _isKeyboardVisible = newValue;
+        if (_isKeyboardVisible) {
+          handlerNestedScrollViewScroll(innerOffset: 0.0,);
+          print("键盘已显示");
+        } else {
+          WidgetsBinding.instance.removeObserver(this);
+          print("键盘已隐藏");
+        }
+      }
+    }
+
+    @override
+    Widget build(BuildContext context, WidgetRef ref) {
+        final vm = ref.read(communityVmProvider.notifier);
+        final state = ref.watch(communityVmProvider);
+
+        useEffect(() {
+          // 监听窗口
+          WidgetsBinding.instance.addObserver(this);
+        }, []);
+
+
+
+        useEffect((){
+          Log.d("CommunityPage initState");
+          // 延迟监听
+          WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
+            if(tabsRouterKey.currentState?.controller != null){
+              tabsRouterKey.currentState?.controller?.addListener((){
+                vm.tabsRouterChange();
+              });
+            }
+          });
+
+          return (){
+            Log.d("CommunityPage dispose");
+            WidgetsBinding.instance.removeObserver(this);
+            tabsRouterKey.currentState?.controller?.removeListener(vm.tabsRouterChange);
+          };
+        },[]);
+
+        return Scaffold(
+            appBar: MyAppBar.searchAppBar(
+              context,
+              value: vm.getCurrentQueryParams('keyword'),
+              actions: [
+                 const MyAssetImage(
+                  Assets.communityLikeActive,
+                  width: 21.5,
+                  height: 21.5,
+                ).onTap((){
+                  vm.handlerClickNavbarLikeBtn(context);
+                 }),
+                SizedBox(width: state.currentCategoryIdx ==0 ? 15:20),
+                state.currentCategoryIdx ==1 ?
+                  const MyAssetImage(
+                    Assets.communityFillterIcon,
+                    width: 21,
+                    height: 21,
+                  ).onTap((){
+                    vm.handlerClickNavbarFilterBtn(context);
+                  }) : const SizedBox.shrink(),
+                const SizedBox(width: 15),
+              ],
+              backgroundColor: context.appColors.backgroundWhite,
+              onSearch: (value) {
+                vm.handlerSearch(value);
+              }
+            ),
+          backgroundColor: context.appColors.backgroundDefault,
+          body:  ExtendedNestedScrollView(
+            key: extendedNestedScrollViewKey,
+              onlyOneScrollInBody: true,
+            headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
+              return [
+                // SliverPersistentHeader(
+                //   delegate: CustomSliverPersistentHeaderDelegate(
+                //     maxHeight: 180,
+                //     minHeight: 180,
+                //     child: _buildTopSection(context, ref, vm, state),
+                //   ),
+                //   pinned: false,
+                // ),
+                // top 组件,转换为 Sliver 组件
+                SliverToBoxAdapter(
+                  child: _buildTopSection(context, ref, vm, state),
+                ),
+              ];
+            },
+            body: Column(
+              mainAxisSize: MainAxisSize.max,
+              children: [
+                Expanded(
+                    child: AutoTabsRouter.pageView(
+                      key: tabsRouterKey,
+                      routes: const [
+                        NewsPageRoute(),
+                        FollowingPageRoute(),
+                        ForyouPageRoute(),
+                        ForsalePageRoute(),
+                        ForrentPageRoute(),
+                      ],
+                      builder: (context, child, pageController) {
+                            final tabsRouter = AutoTabsRouter.of(context);
+                        return Column(
+                          children: [
+                            _buildTabsSection(context, ref, tabsRouter, vm, state),
+                            _buildPostSection(context, ref, vm, state),
+                            Expanded(
+                                child: child
+                            ),
+                          ],
+                        );
+                      },
+                    )
+                )
+              ]
+            )
+          )
+        );
+    }
+
+
+     Widget _buildTopSection(BuildContext context, WidgetRef ref, vm, state) {
+        final topSectionsData = vm.topSectionsData;
+        // final currentPageIdx = tabsRouterKey.currentState?.controller?.activeIndex ?? 0;
+        int curTagIdx = 0;
+        int currentPageIdx = state.currentPageViewIdx;
+        int newsfeedTabCount = state.newsFeedTabsList.length;
+        if(currentPageIdx >= newsfeedTabCount){
+          curTagIdx = 1;
+        }else {
+          curTagIdx = 0;
+        }
+        return Container(
+          color: context.appColors.whiteBG,
+          padding: const EdgeInsets.only(top: 0, bottom: 10),
+          child: Center(
+            child: Row(
+              mainAxisSize: MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: List.generate(topSectionsData.length, (index) {
+                final item = topSectionsData[index];
+                return Column(
+                  children: [
+                    Container(
+                      width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
+                      height: 70,
+                      decoration: BoxDecoration(
+                        shape: BoxShape.circle, // 设置为圆形
+                        color: ColorUtils.string2Color("#F0F8FF"),
+                        boxShadow: index == curTagIdx ? [
+                          BoxShadow(
+                            color: context.appColors.tabLightBlueShadow, // 设置阴影颜色
+                            blurRadius: 5, // 设置模糊半径
+                            spreadRadius: 0.05, // 控制阴影扩散
+                            offset: const Offset(0, 4), // 设置阴影偏移量
+                          ),] : [],// 未选中时无阴影,
+                      ),
+                      child: MyAssetImage(
+                        item['icon'],
+                        width: MediaQuery.of(context).size.width / topSectionsData.length - 36,
+                        // width: 70,
+                        height: 70,
+                      ).onTap(() {
+                          vm.handlerSwitchNewsfeedOrGaragesale(index, context, null);
+                        },
+                        type: ClickType.throttle,
+                      ),
+                    ),
+                    SizedBox.fromSize(size: const Size(0, 9)),
+                    MyTextView(
+                      item['title'],
+                      fontSize: 15,
+                      textColor: index == curTagIdx ? context.appColors.textPrimary: context.appColors.textBlack,
+                      textAlign: TextAlign.center,
+                      isFontMedium: true,
+                    ),
+                  ],
+                ).marginOnly(left: 18, right: 18, top: 10, bottom: 10);
+              }),
+            ),
+          ),
+        );
+    }
+
+     Widget _buildTabsSection(BuildContext context, WidgetRef ref, tabsRouter, vm, state){
+       int currentPageIndex = tabsRouter!.activeIndex ?? 0;
+       int newsfeedTabCount = state.newsFeedTabsList.length;
+
+       List<String> tabsList;
+       if(currentPageIndex < newsfeedTabCount){
+         // news feed
+         tabsList = state.newsFeedTabsList ?? [];
+       }else {
+         tabsList = state.garageSaleTabsList ?? [];
+       }
+       return Container(
+        width: double.infinity,
+        color: ColorUtils.string2Color('#F2F3F6'),
+        child: Center(
+          child: NewsfeedTabs(
+            key: UniqueKey(),
+            tabsList: tabsList,
+              tabsRouter: tabsRouter,
+            margin: EdgeInsets.only(top: 14,bottom: 12, left: 20, right: 20),
+            onClickAction:(Map<String, dynamic>? params){
+              if (params != null) {
+                // 解构 params
+                final int? currentCatgoryIdx = params['currentCatgoryIdx'] as int?;
+                final int? tabIdx = params['tabIdx'] as int?;
+                vm.handlerChangeTab(tabIdx, tabsRouter, currentCatgoryIdx);
+              }
+            }
+          ),
+        ),
+      );
+    }
+
+      Widget _buildPostSection(BuildContext context, WidgetRef ref, vm, state){
+        int currentPageIndex = state.currentPageViewIdx;
+        int newsfeedTabCount = state.newsFeedTabsList.length;
+        if(currentPageIndex < newsfeedTabCount){
+          // news feed
+          return  _buildNewsFeedPost(context, ref, vm, state);
+        }else {
+          return  _buildGarageSalePost(context, ref, vm, state);
+        }
+
+      }
+
+      Widget _buildNewsFeedPost(BuildContext context, WidgetRef ref, vm, state){
+        final userConfig = UserConfigService.getState(ref: ref);
+        return Container(
+          height: 65.5,
+          width: double.infinity,
+          padding: const EdgeInsets.only(left: 20, right: 20),
+          color: Colors.white,
+          child: Row(
+            children: [
+              // const MyAssetImage(Assets.communityNewsFeed, width: 45,height: 45,),
+              MyLoadImage(
+                userConfig.user?.avatar,
+                width: 45,
+                height: 45,
+                isCircle: true,
+                fit: BoxFit.fill,
+              ),
+              Expanded(
+                child: MyTextView(
+                  S.current.what_on_your_mind,
+                  textColor: context.appColors.textBlack,
+                  fontSize: 15,
+                  marginLeft: 15,
+                  alignment: Alignment.centerLeft,
+                  textAlign: TextAlign.left,
+                  backgroundColor: ColorUtils.string2Color('#ffffff'),
+                  maxLines: 1,
+                  isFontMedium: true,
+                ),
+              ),
+              const MyAssetImage(
+                Assets.communityCamera,
+                width: 21,
+                height: 16.5,
+              ),
+            ],
+          ).onTap((){
+            vm.handlerGotoNewsfeedPost(context);
+          }),
+        );
+      }
+
+      Widget _buildGarageSalePost(BuildContext context, WidgetRef ref, vm, state){
+        final userConfig = UserConfigService.getState(ref: ref);
+        return Container(
+          height: 65.5,
+          width: double.infinity,
+          padding: const EdgeInsets.only(left: 20, right: 20),
+          color: Colors.white,
+          child: Row(
+            children: [
+              MyLoadImage(
+                userConfig.user?.avatar,
+                width: 45,
+                height: 45,
+                isCircle: true,
+                fit: BoxFit.fill,
+              ),
+              Expanded(
+                child: MyTextView(
+                  S.current.sell_item,
+                  textColor: context.appColors.textBlack,
+                  fontSize: 15,
+                  marginLeft: 15,
+                  alignment: Alignment.centerLeft,
+                  textAlign: TextAlign.left,
+                  backgroundColor: ColorUtils.string2Color('#ffffff'),
+                  maxLines: 1,
+                  isFontMedium: true,
+                ),
+              ),
+              const MyAssetImage(
+                Assets.communityCamera,
+                width: 21,
+                height: 16.5,
+              ),
+            ],
+          ).onTap((){
+            vm.handlerGotoGaragePost(context);
+          }),
+        );
+      }
+}
+

+ 17 - 0
packages/cpt_community/lib/modules/community/community_pageview_idx_data.dart

@@ -0,0 +1,17 @@
+
+class CommunityPageViewIdxData {
+  static const int news = 0;
+  static const int following = 1;
+  static const int forYou = 2;
+  static const int forSale = 3;
+  static const int forRent = 4;
+
+
+  static Map<int, String> get values => {
+    0: "news",
+    1: "following",
+    2: "forYou",
+    3: "forSale",
+    4: "forRent",
+  };
+}

+ 80 - 0
packages/cpt_community/lib/modules/community/community_state.dart

@@ -0,0 +1,80 @@
+import 'package:cpt_community/modules/community/community_page.dart';
+import 'package:cs_resources/generated/assets.dart';
+import 'package:cs_resources/generated/l10n.dart';
+
+class CommunityVmState {
+  List<Map<String, dynamic>>? topSectionsData;
+  List<String>? newsFeedTabsList;
+  List<String>? garageSaleTabsList;
+  List<Map<String, dynamic>>? garageCategoryList = []; // garageCategoryList
+  int currentCategoryIdx = 0; // 0: news feed, 1: garage sale
+  int currentPageViewIdx = 0;
+  int lastNewsfeedTabIdx = 0; // 上一次newsfeed 的 tabIdx
+  int lastGarageTabIdx = 0; // 上一次garagesale 的 tabIdx
+  dynamic? tabsRouter;
+  dynamic? pageController;
+
+  CommunityVmState({
+    List<Map<String, dynamic>>? topSectionsData,
+    required this.currentPageViewIdx,
+    currentCategoryIdx,
+    lastNewsfeedTabIdx,
+    lastGarageTabIdx,
+    newsFeedTabsList,
+    garageSaleTabsList,
+    List<Map<String, dynamic>>? garageCategoryList,
+    this.tabsRouter,
+    this.pageController,
+  })  : topSectionsData = topSectionsData ??
+            [
+              {
+                "title": S.current.news_feed,
+                "icon": Assets.communityNews,
+              },
+              {
+                "title": S.current.garage_sale,
+                "icon": Assets.communityGarageSale,
+              },
+            ],
+        currentCategoryIdx = currentCategoryIdx ?? 0,
+        lastGarageTabIdx = lastGarageTabIdx ?? 0,
+        lastNewsfeedTabIdx = lastNewsfeedTabIdx ?? 0,
+        newsFeedTabsList = newsFeedTabsList ??
+            [
+              S.current.newsfeed_news,
+              S.current.newsfeed_following,
+              S.current.for_you,
+            ],
+        garageSaleTabsList = garageSaleTabsList ??
+            [
+              S.current.for_sale,
+              S.current.for_rent,
+            ],
+        garageCategoryList = garageCategoryList ?? [];
+
+  CommunityVmState copyWith({
+    List<Map<String, dynamic>>? topSectionsData,
+    List<String>? newsFeedTabsList,
+    List<String>? garageSaleTabsList,
+    int? currentCategoryIdx,
+    int? currentPageViewIdx,
+    int? lastNewsfeedTabIdx,
+    int? lastGarageTabIdx,
+    List<Map<String, dynamic>>? garageCategoryList,
+    dynamic? tabsRouter,
+    dynamic? pageController,
+  }) {
+    return CommunityVmState(
+      topSectionsData: topSectionsData ?? this.topSectionsData,
+      newsFeedTabsList: newsFeedTabsList ?? this.newsFeedTabsList,
+      garageSaleTabsList: garageSaleTabsList ?? this.garageSaleTabsList,
+      currentCategoryIdx: currentCategoryIdx ?? this.currentCategoryIdx,
+      currentPageViewIdx: currentPageViewIdx ?? this.currentPageViewIdx,
+      lastNewsfeedTabIdx: lastNewsfeedTabIdx ?? this.lastNewsfeedTabIdx,
+      lastGarageTabIdx: lastGarageTabIdx ?? this.lastGarageTabIdx,
+      garageCategoryList: garageCategoryList ?? this.garageCategoryList,
+      tabsRouter: tabsRouter ?? this.tabsRouter,
+      pageController: pageController ?? this.pageController,
+    );
+  }
+}

+ 0 - 0
packages/cpt_community/lib/modules/community/community_vm.dart


部分文件因为文件数量过多而无法显示