Просмотр исходного кода

feat: 邀请收益与兑换页面优化

gogs 1 месяц назад
Родитель
Сommit
485c53d137
88 измененных файлов с 5175 добавлено и 74 удалено
  1. 23 0
      module_base/src/main/java/com/yc/module_base/model/InviteEarnings.kt
  2. 16 1
      module_base/src/main/java/com/yc/module_base/net/RequestInterceptor.kt
  3. 1 0
      module_me/build.gradle
  4. 20 4
      module_me/src/main/AndroidManifest.xml
  5. 73 0
      module_me/src/main/java/com/mita/module_me/api/ShareInviteService.kt
  6. 17 0
      module_me/src/main/java/com/mita/module_me/model/InviteRelationship.kt
  7. 11 0
      module_me/src/main/java/com/mita/module_me/model/InviteShareConfig.kt
  8. 13 0
      module_me/src/main/java/com/mita/module_me/model/ShareApiResponse.kt
  9. 47 0
      module_me/src/main/java/com/mita/module_me/model/ShareBankModels.kt
  10. 54 0
      module_me/src/main/java/com/mita/module_me/model/ShareBillModels.kt
  11. 9 0
      module_me/src/main/java/com/mita/module_me/model/ShareInviteBindBody.kt
  12. 29 0
      module_me/src/main/java/com/mita/module_me/model/ShareInvitedUsers.kt
  13. 15 0
      module_me/src/main/java/com/mita/module_me/model/ShareSummary.kt
  14. 13 0
      module_me/src/main/java/com/mita/module_me/model/ShareUserProfile.kt
  15. 6 1
      module_me/src/main/java/com/mita/module_me/view/MeFragment.kt
  16. 169 0
      module_me/src/main/java/com/mita/module_me/view/invite/AddBankCardActivity.kt
  17. 104 0
      module_me/src/main/java/com/mita/module_me/view/invite/AddBankCardViewModel.kt
  18. 54 0
      module_me/src/main/java/com/mita/module_me/view/invite/BillDetailVH.kt
  19. 12 0
      module_me/src/main/java/com/mita/module_me/view/invite/BillRowItem.kt
  20. 81 0
      module_me/src/main/java/com/mita/module_me/view/invite/ExchangeActivity.kt
  21. 76 0
      module_me/src/main/java/com/mita/module_me/view/invite/ExchangeViewModel.kt
  22. 122 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteBindingActivity.kt
  23. 118 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteBindingViewModel.kt
  24. 282 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteEarningsActivity.kt
  25. 16 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteEarningsVH.kt
  26. 520 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteEarningsViewModel.kt
  27. 36 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteRulesActivity.kt
  28. 303 0
      module_me/src/main/java/com/mita/module_me/view/invite/InviteShareDialog.kt
  29. 7 0
      module_me/src/main/java/com/mita/module_me/view/invite/LoadMoreRowItem.kt
  30. 26 0
      module_me/src/main/java/com/mita/module_me/view/invite/LoadMoreVH.kt
  31. 40 0
      module_me/src/main/java/com/mita/module_me/view/invite/ShareInviteRepository.kt
  32. 139 0
      module_me/src/main/java/com/mita/module_me/view/invite/WithdrawActivity.kt
  33. 99 0
      module_me/src/main/java/com/mita/module_me/view/invite/WithdrawViewModel.kt
  34. 86 0
      module_me/src/main/java/com/mita/module_me/view/widget/TwoLineImageView.java
  35. 0 66
      module_me/src/main/java/com/mita/module_me/view/widget/TwoLineImageView.kt
  36. BIN
      module_me/src/main/res/drawable/banner_scroll_bg.png
  37. BIN
      module_me/src/main/res/drawable/copy_link.png
  38. 5 0
      module_me/src/main/res/drawable/debug_stroke_green.xml
  39. 9 0
      module_me/src/main/res/drawable/ic_arrow_right_gray.xml
  40. BIN
      module_me/src/main/res/drawable/ic_empty_data.png
  41. 170 0
      module_me/src/main/res/drawable/ic_launcher_background.xml
  42. 30 0
      module_me/src/main/res/drawable/ic_launcher_foreground.xml
  43. BIN
      module_me/src/main/res/drawable/ic_toast_icon.png
  44. BIN
      module_me/src/main/res/drawable/invite_bg.png
  45. BIN
      module_me/src/main/res/drawable/invite_binding_bottom_bg.png
  46. BIN
      module_me/src/main/res/drawable/invite_binding_card_bg.png
  47. 8 0
      module_me/src/main/res/drawable/module_me_bg_rule_edge.xml
  48. 6 0
      module_me/src/main/res/drawable/module_me_bg_rules_card.xml
  49. 6 0
      module_me/src/main/res/drawable/module_me_bg_topbar_btn_dark.xml
  50. BIN
      module_me/src/main/res/drawable/qq.png
  51. BIN
      module_me/src/main/res/drawable/save_to_photo_album.png
  52. 9 0
      module_me/src/main/res/drawable/sp_circle_solid_ff6d00.xml
  53. 9 0
      module_me/src/main/res/drawable/sp_circle_stroke_999.xml
  54. 6 0
      module_me/src/main/res/drawable/sp_r10_stroke_f56a05_white.xml
  55. 10 0
      module_me/src/main/res/drawable/sp_r12_dash_white_60.xml
  56. 5 0
      module_me/src/main/res/drawable/sp_r12_ff5722.xml
  57. 5 0
      module_me/src/main/res/drawable/sp_r12_white_30.xml
  58. 5 0
      module_me/src/main/res/drawable/sp_r16_ff5722.xml
  59. 6 0
      module_me/src/main/res/drawable/sp_r16_stroke_ff5722_white.xml
  60. 9 0
      module_me/src/main/res/drawable/sp_r16_stroke_ff9800.xml
  61. 5 0
      module_me/src/main/res/drawable/sp_r20_f56a05.xml
  62. 6 0
      module_me/src/main/res/drawable/sp_r20_stroke_f56a05_white.xml
  63. 5 0
      module_me/src/main/res/drawable/sp_r20_white.xml
  64. 5 0
      module_me/src/main/res/drawable/sp_r24_ff9800.xml
  65. 9 0
      module_me/src/main/res/drawable/sp_r24_gradient_ff9800_ff5722.xml
  66. 6 0
      module_me/src/main/res/drawable/sp_r4_solid_e3f2fd.xml
  67. 5 0
      module_me/src/main/res/drawable/sp_r6_solid_f56a05.xml
  68. 6 0
      module_me/src/main/res/drawable/sp_r6_stroke_f56a05.xml
  69. 5 0
      module_me/src/main/res/drawable/sp_red_dot_6.xml
  70. BIN
      module_me/src/main/res/drawable/weixin.png
  71. BIN
      module_me/src/main/res/drawable/winxin_comment.png
  72. 76 0
      module_me/src/main/res/layout/module_me_activity_add_bank_card.xml
  73. 163 0
      module_me/src/main/res/layout/module_me_activity_exchange.xml
  74. 226 0
      module_me/src/main/res/layout/module_me_activity_invite_binding.xml
  75. 654 0
      module_me/src/main/res/layout/module_me_activity_invite_earnings.xml
  76. 126 0
      module_me/src/main/res/layout/module_me_activity_invite_rules.xml
  77. 297 0
      module_me/src/main/res/layout/module_me_activity_withdraw.xml
  78. 275 0
      module_me/src/main/res/layout/module_me_dialog_invite_share.xml
  79. 17 1
      module_me/src/main/res/layout/module_me_fragment_me.xml
  80. 85 0
      module_me/src/main/res/layout/module_me_item_bill_detail.xml
  81. 61 0
      module_me/src/main/res/layout/module_me_item_invite_earnings.xml
  82. 24 0
      module_me/src/main/res/layout/module_me_item_load_more.xml
  83. 29 0
      module_me/src/main/res/layout/module_me_row_bank_input_bank.xml
  84. 29 0
      module_me/src/main/res/layout/module_me_row_bank_input_card.xml
  85. 30 0
      module_me/src/main/res/layout/module_me_row_bank_input_id.xml
  86. 29 0
      module_me/src/main/res/layout/module_me_row_bank_input_name.xml
  87. 26 0
      module_me/src/main/res/layout/module_me_tab_invite_earnings.xml
  88. 41 1
      umlibrary/src/main/java/com/bmkj/umlibrary/ShareManager.kt

+ 23 - 0
module_base/src/main/java/com/yc/module_base/model/InviteEarnings.kt

@@ -0,0 +1,23 @@
+package com.yc.module_base.model
+
+import android.os.Parcelable
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class InviteEarnings(
+    val inviteCode: String,
+    val isBind: Boolean,
+    val points: Long,
+    val todayEarnings: Long,
+    val totalEarnings: Long,
+    val totalInvites: Int,
+    val list: List<InviteItem>
+) : Parcelable
+
+@Parcelize
+data class InviteItem(
+    val name: String,
+    val id: String,
+    val contribution: String,
+    val date: String
+) : Parcelable

+ 16 - 1
module_base/src/main/java/com/yc/module_base/net/RequestInterceptor.kt

@@ -71,6 +71,21 @@ class RequestInterceptor() : com.yc.networklibrary.remote.interceptor.RequestInt
         val apiPath = "${url.scheme}://${url.host}${url.encodedPath}".trim()
         /*服务端的接口地址*/
         var serverPath = "${url.scheme}://${url.host}/".trim()
+        Log.d(TAG, "intercept url: ${url}")
+        if (url.encodedPath.startsWith("/api/share/app/share/")) {
+            val localUrl = url.newBuilder()
+                .scheme("http")
+                .host("192.168.0.112")
+                .port(8880)
+                .build()
+            newRequestBuilder.url(localUrl)
+            serverPath = "${localUrl.scheme}://${localUrl.host}/".trim()
+            Log.d(TAG, "rewrite to local: ${localUrl}")
+            val token = LiveSession.getUserToken()
+            if (token.isNotEmpty()) {
+                newRequestBuilder.header("token", token)
+            }
+        }
         /*如果请求的不是服务端的接口,不加密*/
         if (!serverPath.startsWith(URL_BASE)) {
             return chain.proceed(newRequestBuilder.build())
@@ -147,4 +162,4 @@ class RequestInterceptor() : com.yc.networklibrary.remote.interceptor.RequestInt
         }
     }
 
-}
+}

+ 1 - 0
module_me/build.gradle

@@ -8,4 +8,5 @@ android {
 dependencies {
 
     api project(":module_base")
+    implementation "com.google.zxing:core:3.5.3"
 }

+ 20 - 4
module_me/src/main/AndroidManifest.xml

@@ -94,10 +94,6 @@
             android:name=".view.rechargerecord.RechargeRecordActivity"
             android:launchMode="singleTask"
             android:screenOrientation="portrait"></activity>
-        <activity
-            android:name=".view.accountsecurity.AccountSecurityActivity"
-            android:launchMode="singleTask"
-            android:screenOrientation="portrait"></activity>
         <activity
             android:name=".view.roommanage.RoomManageActivity"
             android:launchMode="singleTask"
@@ -119,6 +115,26 @@
             android:screenOrientation="portrait"
             android:launchMode="singleTask">
         </activity>
+        <activity
+            android:name=".view.invite.InviteBindingActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".view.invite.InviteEarningsActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".view.invite.WithdrawActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".view.invite.AddBankCardActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".view.invite.ExchangeActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait" />        
     </application>
 
 </manifest>

+ 73 - 0
module_me/src/main/java/com/mita/module_me/api/ShareInviteService.kt

@@ -0,0 +1,73 @@
+package com.mita.module_me.api
+
+import com.mita.module_me.model.InviteRelationship
+import com.mita.module_me.model.ShareBankCard
+import com.mita.module_me.model.ShareBillListData
+import com.mita.module_me.model.ShareBindBankCardBody
+import com.mita.module_me.model.ShareInviteBindBody
+import com.mita.module_me.model.ShareInvitedUsersData
+import com.mita.module_me.model.SharePointFlowListData
+import com.mita.module_me.model.ShareUserProfile
+import com.mita.module_me.model.ShareSummary
+import com.mita.module_me.model.ShareApiResponse
+import com.mita.module_me.model.ShareExchangeBody
+import com.mita.module_me.model.ShareWithdrawBody
+import retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.POST
+import retrofit2.http.Query
+
+interface ShareInviteService {
+    @GET("api/share/app/share/relationship")
+    suspend fun getInviteRelationship(): ShareApiResponse<InviteRelationship?>
+
+    @GET("api/share/app/share/summary")
+    suspend fun getShareSummary(): ShareApiResponse<ShareSummary?>
+
+    @GET("api/share/app/share/invited")
+    suspend fun getInvitedUsers(
+        @Query("page") page: Int,
+        @Query("page_size") pageSize: Int
+    ): ShareApiResponse<ShareInvitedUsersData?>
+
+    @GET("api/share/app/share/user")
+    suspend fun getUserProfile(@Query("user_id") userId: String): ShareApiResponse<ShareUserProfile?>
+
+    @POST("api/share/app/share/relationship")
+    suspend fun createInviteRelationship(
+        @Body body: ShareInviteBindBody
+    ): ShareApiResponse<InviteRelationship?>
+
+    @GET("api/share/app/share/bill/list")
+    suspend fun getBillList(
+        @Query("user_id") userId: String,
+        @Query("page") page: Int,
+        @Query("page_size") pageSize: Int,
+        @Query("type") type: String
+    ): ShareApiResponse<ShareBillListData?>
+
+    @GET("api/share/app/share/flow/list")
+    suspend fun getPointFlowList(
+        @Query("user_id") userId: String,
+        @Query("page") page: Int,
+        @Query("page_size") pageSize: Int
+    ): ShareApiResponse<SharePointFlowListData?>
+
+    @GET("api/share/app/share/bank/card")
+    suspend fun getBankCard(@Query("user_id") userId: String): ShareApiResponse<ShareBankCard?>
+
+    @POST("api/share/app/share/bank/bind")
+    suspend fun bindBankCard(@Body body: ShareBindBankCardBody): ShareApiResponse<ShareBankCard?>
+
+    @POST("api/share/app/share/bank/withdraw")
+    suspend fun withdraw(@Body body: ShareWithdrawBody): ShareApiResponse<Any?>
+
+    @POST("api/share/app/share/exchange")
+    suspend fun exchange(@Body body: ShareExchangeBody): ShareApiResponse<Any?>
+
+    @GET("api/share/app/share/invite/config")
+    suspend fun getInviteShareConfig(
+        @Query("platform") platform: String = "all"
+    ): ShareApiResponse<Map<String, String>?>
+
+}

+ 17 - 0
module_me/src/main/java/com/mita/module_me/model/InviteRelationship.kt

@@ -0,0 +1,17 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class InviteRelationship(
+    @SerializedName("id")
+    val id: Long?,
+    @SerializedName("inviter_id")
+    val inviterId: String?,
+    @SerializedName("inviter_nickname")
+    val inviterNickname: String?,
+    @SerializedName("invitee_id")
+    val inviteeId: String?,
+    @SerializedName("created_at")
+    val createdAt: String?
+)
+

+ 11 - 0
module_me/src/main/java/com/mita/module_me/model/InviteShareConfig.kt

@@ -0,0 +1,11 @@
+package com.mita.module_me.model
+
+data class InviteShareConfig(
+    val inviteBaseUrl: String,
+    val shareTitle: String,
+    val shareDesc: String,
+    val shareIcon: String,
+    val posterAppName: String,
+    val posterSubtitle: String
+)
+

+ 13 - 0
module_me/src/main/java/com/mita/module_me/model/ShareApiResponse.kt

@@ -0,0 +1,13 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareApiResponse<T>(
+    @SerializedName("code")
+    val code: Int?,
+    @SerializedName(value = "message", alternate = ["msg", "error"])
+    val message: String?,
+    @SerializedName("data")
+    val data: T?
+)
+

+ 47 - 0
module_me/src/main/java/com/mita/module_me/model/ShareBankModels.kt

@@ -0,0 +1,47 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareBankCard(
+    @SerializedName("id")
+    val id: Int?,
+    @SerializedName("user_id")
+    val userId: Long?,
+    @SerializedName("real_name")
+    val realName: String?,
+    @SerializedName("id_card")
+    val idCard: String?,
+    @SerializedName("card_number")
+    val cardNumber: String?,
+    @SerializedName("bank_name")
+    val bankName: String?
+)
+
+data class ShareBindBankCardBody(
+    @SerializedName("user_id")
+    val userId: Long,
+    @SerializedName("real_name")
+    val realName: String,
+    @SerializedName("id_card")
+    val idCard: String,
+    @SerializedName("card_number")
+    val cardNumber: String,
+    @SerializedName("bank_name")
+    val bankName: String
+)
+
+data class ShareWithdrawBody(
+    @SerializedName("user_id")
+    val userId: Long,
+    @SerializedName("amount")
+    val amount: Long
+)
+
+data class ShareExchangeBody(
+    @SerializedName("user_id")
+    val userId: Long,
+    @SerializedName("points")
+    val points: Double,
+    @SerializedName("flow_type")
+    val flowType: Int
+)

+ 54 - 0
module_me/src/main/java/com/mita/module_me/model/ShareBillModels.kt

@@ -0,0 +1,54 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareBillListData(
+    @SerializedName("list")
+    val list: List<ShareBillItem>?,
+    @SerializedName("total")
+    val total: Int?,
+    @SerializedName("total_amount")
+    val totalAmount: String?
+)
+
+data class ShareBillItem(
+    @SerializedName("order_id")
+    val orderId: Long?,
+    @SerializedName("amount")
+    val amount: Double?,
+    @SerializedName("points")
+    val points: Double?,
+    @SerializedName("type")
+    val type: Int?,
+    @SerializedName("from_user")
+    val fromUser: String?,
+    @SerializedName("from_user_id")
+    val fromUserId: String?,
+    @SerializedName("from_user_pic")
+    val fromUserPic: String?,
+    @SerializedName("time")
+    val time: String?
+)
+
+data class SharePointFlowListData(
+    @SerializedName("list")
+    val list: List<SharePointFlow>?,
+    @SerializedName("total")
+    val total: Int?,
+    @SerializedName("total_points")
+    val totalPoints: Double?
+)
+
+data class SharePointFlow(
+    @SerializedName("id")
+    val id: Long?,
+    @SerializedName("points")
+    val points: Double?,
+    @SerializedName("flow_type")
+    val flowType: Int?,
+    @SerializedName("remark")
+    val remark: String?,
+    @SerializedName("created_at")
+    val createdAt: String?
+)
+

+ 9 - 0
module_me/src/main/java/com/mita/module_me/model/ShareInviteBindBody.kt

@@ -0,0 +1,9 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareInviteBindBody(
+    @SerializedName("inviter_id")
+    val inviterId: String
+)
+

+ 29 - 0
module_me/src/main/java/com/mita/module_me/model/ShareInvitedUsers.kt

@@ -0,0 +1,29 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareInvitedUsersData(
+    @SerializedName("list")
+    val list: List<ShareInvitedUser>?,
+    @SerializedName("total")
+    val total: Int?,
+    @SerializedName("page")
+    val page: Int?,
+    @SerializedName("page_size")
+    val pageSize: Int?
+)
+
+data class ShareInvitedUser(
+    @SerializedName("user_id")
+    val userId: String?,
+    @SerializedName("nick_name")
+    val nickName: String?,
+    @SerializedName("avatar")
+    val avatar: String?,
+    @SerializedName("created_at")
+    val createdAt: String?,
+    @SerializedName("points")
+    val points: String?,
+    @SerializedName("contribution")
+    val contribution: String?
+)

+ 15 - 0
module_me/src/main/java/com/mita/module_me/model/ShareSummary.kt

@@ -0,0 +1,15 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareSummary(
+    @SerializedName("balance")
+    val balance: Double?,
+    @SerializedName("today_earnings")
+    val todayEarnings: Double?,
+    @SerializedName("total_earnings")
+    val totalEarnings: Double?,
+    @SerializedName("invite_count")
+    val inviteCount: Int?
+)
+

+ 13 - 0
module_me/src/main/java/com/mita/module_me/model/ShareUserProfile.kt

@@ -0,0 +1,13 @@
+package com.mita.module_me.model
+
+import com.google.gson.annotations.SerializedName
+
+data class ShareUserProfile(
+    @SerializedName("user_id")
+    val userId: Long?,
+    @SerializedName("nick_name")
+    val nickName: String?,
+    @SerializedName("head_pic")
+    val headPic: String?
+)
+

+ 6 - 1
module_me/src/main/java/com/mita/module_me/view/MeFragment.kt

@@ -19,6 +19,7 @@ import com.mita.module_me.view.dialog.MysteryMenDialog
 import com.mita.module_me.view.dressup.DressShopCenterActivity
 import com.mita.module_me.view.dressup.MyDressActivity
 import com.mita.module_me.view.dressup.shop.prettyId.PrettyIdListActivity
+import com.mita.module_me.view.invite.InviteEarningsActivity
 import com.mita.module_me.view.setting.PopupWindowRight
 import com.mita.module_me.view.setting.SettingActivity
 import com.mita.module_me.view.useredit.UserEditActivity
@@ -74,6 +75,7 @@ import kotlinx.android.synthetic.main.module_me_fragment_me.cl_level
 import kotlinx.android.synthetic.main.module_me_fragment_me.guizu
 import kotlinx.android.synthetic.main.module_me_fragment_me.ivAvatar
 import kotlinx.android.synthetic.main.module_me_fragment_me.ivFrame
+import kotlinx.android.synthetic.main.module_me_fragment_me.ivInvitePoster
 import kotlinx.android.synthetic.main.module_me_fragment_me.ivUserAvatar
 import kotlinx.android.synthetic.main.module_me_fragment_me.iv_set
 import kotlinx.android.synthetic.main.module_me_fragment_me.lianghao
@@ -223,6 +225,9 @@ class MeFragment : BaseFragment<MeViewModel>() {
         iv_set.setOnClickListener {
             context?.launchActivity<SettingActivity> { }
         }
+        ivInvitePoster.setOnClickListener {
+            startActivity(Intent(activity, InviteEarningsActivity::class.java))
+        }
     }
 
     private val adapter by threadUnsafeLazy {
@@ -437,4 +442,4 @@ class MeFragment : BaseFragment<MeViewModel>() {
             0
         )
     }
-}
+}

+ 169 - 0
module_me/src/main/java/com/mita/module_me/view/invite/AddBankCardActivity.kt

@@ -0,0 +1,169 @@
+package com.mita.module_me.view.invite
+
+import android.app.Activity
+import android.text.InputFilter
+import android.text.Spanned
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.mita.module_me.R
+import com.yc.baselibrary.ext.toast
+import com.yc.baselibrary.view.base.BaseActivity
+import kotlinx.android.synthetic.main.module_me_activity_add_bank_card.*
+import kotlinx.android.synthetic.main.module_me_row_bank_input_bank.*
+import kotlinx.android.synthetic.main.module_me_row_bank_input_card.*
+import kotlinx.android.synthetic.main.module_me_row_bank_input_id.*
+import kotlinx.android.synthetic.main.module_me_row_bank_input_name.*
+
+class AddBankCardActivity : BaseActivity<AddBankCardViewModel>() {
+
+    override fun initView() {
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
+            val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
+            clTopBar.setPadding(
+                clTopBar.paddingLeft,
+                top,
+                clTopBar.paddingRight,
+                clTopBar.paddingBottom
+            )
+            insets
+        }
+        ViewCompat.requestApplyInsets(rootLayout)
+
+        ivBack.setOnClickListener { finish() }
+
+        etRealName.filters = arrayOf(InputFilter.LengthFilter(20))
+        etBankName.filters = arrayOf(InputFilter.LengthFilter(50))
+        etCardNumber.filters = arrayOf(
+            InputFilter.LengthFilter(19),
+            DigitsOnlyFilter()
+        )
+        etIdCard.filters = arrayOf(
+            InputFilter.LengthFilter(18),
+            IdCardFilter()
+        )
+
+        btnSave.setOnClickListener {
+            val name = etRealName.text?.toString().orEmpty().trim()
+            val idCard = etIdCard.text?.toString().orEmpty().trim()
+            val cardNo = etCardNumber.text?.toString().orEmpty().trim()
+            val bankName = etBankName.text?.toString().orEmpty().trim()
+
+            if (name.isBlank()) {
+                toast("请输入姓名")
+                return@setOnClickListener
+            }
+            if (name.length > 20) {
+                toast("姓名最多20个字符")
+                return@setOnClickListener
+            }
+            if (idCard.isBlank()) {
+                toast("请输入身份证号")
+                return@setOnClickListener
+            }
+            if (idCard.length != 18) {
+                toast("身份证号长度不正确")
+                return@setOnClickListener
+            }
+            if (!viewModel.isValidChinaIDCard(idCard)) {
+                toast("身份证号码格式不正确")
+                return@setOnClickListener
+            }
+            if (cardNo.isBlank()) {
+                toast("请输入银行卡号")
+                return@setOnClickListener
+            }
+            if (cardNo.length !in 16..19) {
+                toast("银行卡号长度不正确")
+                return@setOnClickListener
+            }
+            if (bankName.isBlank()) {
+                toast("请输入所属银行")
+                return@setOnClickListener
+            }
+            if (bankName.length > 50) {
+                toast("所属银行最多50个字符")
+                return@setOnClickListener
+            }
+
+            viewModel.saveBankCard(
+                realName = name,
+                idCard = idCard,
+                cardNumber = cardNo,
+                bankName = bankName
+            )
+        }
+    }
+
+    override fun observe() {
+        viewModel.bankCard.observe(this) { card ->
+            if (card != null) {
+                if (!card.realName.isNullOrBlank()) etRealName.setText(card.realName)
+                if (!card.idCard.isNullOrBlank()) etIdCard.setText(card.idCard)
+                if (!card.cardNumber.isNullOrBlank()) etCardNumber.setText(card.cardNumber)
+                if (!card.bankName.isNullOrBlank()) etBankName.setText(card.bankName)
+            }
+        }
+
+        viewModel.saveSuccess.observe(this) { ok ->
+            if (ok == true) {
+                setResult(Activity.RESULT_OK)
+                toast("保存成功")
+                finish()
+            }
+        }
+
+        viewModel.errorMsg.observe(this) { msg ->
+            if (!msg.isNullOrBlank()) toast(msg)
+        }
+
+        viewModel.stateModel.observe(this) { type ->
+            val loading = type == 1
+            btnSave.isEnabled = !loading
+            btnSave.alpha = if (loading) 0.7f else 1f
+        }
+    }
+
+    override fun ready() {
+        viewModel.loadExisting()
+    }
+
+    override fun getLayoutId() = R.layout.module_me_activity_add_bank_card
+}
+
+private class DigitsOnlyFilter : InputFilter {
+    override fun filter(
+        source: CharSequence?,
+        start: Int,
+        end: Int,
+        dest: Spanned?,
+        dstart: Int,
+        dend: Int
+    ): CharSequence? {
+        if (source.isNullOrEmpty()) return null
+        for (i in start until end) {
+            val c = source[i]
+            if (!c.isDigit()) return ""
+        }
+        return null
+    }
+}
+
+private class IdCardFilter : InputFilter {
+    override fun filter(
+        source: CharSequence?,
+        start: Int,
+        end: Int,
+        dest: Spanned?,
+        dstart: Int,
+        dend: Int
+    ): CharSequence? {
+        if (source.isNullOrEmpty()) return null
+        for (i in start until end) {
+            val c = source[i]
+            val ok = c.isDigit() || c == 'x' || c == 'X'
+            if (!ok) return ""
+        }
+        return null
+    }
+}

+ 104 - 0
module_me/src/main/java/com/mita/module_me/view/invite/AddBankCardViewModel.kt

@@ -0,0 +1,104 @@
+package com.mita.module_me.view.invite
+
+import android.app.Application
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.SavedStateHandle
+import com.mita.module_me.model.ShareBankCard
+import com.mita.module_me.model.ShareBindBankCardBody
+import com.yc.baselibrary.net.exception.Errors
+import com.yc.baselibrary.net.exception.LoadingType
+import com.yc.baselibrary.view.base.BaseVm
+import com.yc.module_base.LiveSession
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+class AddBankCardViewModel(
+    app: Application,
+    map: MutableMap<String, Any?>,
+    savedStateHandle: SavedStateHandle
+) : BaseVm(app, map, savedStateHandle) {
+
+    private val shareInviteRepository: ShareInviteRepository by lazy { ShareInviteRepository() }
+
+    val bankCard = MutableLiveData<ShareBankCard?>()
+    val saveSuccess = MutableLiveData<Boolean>()
+    val errorMsg = MutableLiveData<String>()
+
+    fun loadExisting() {
+        requestMix(
+            block = {
+                val userId = LiveSession.getUserId().toString()
+                val res = shareInviteRepository.getBankCard(userId)
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取银行卡失败")
+                }
+                res.data
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success { bankCard.value = it }
+                error {
+                    Log.e("AddBankCardViewModel", "loadExisting error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "获取银行卡失败"
+                }
+            }
+        )
+    }
+
+    fun saveBankCard(realName: String, idCard: String, cardNumber: String, bankName: String) {
+        requestMix(
+            block = {
+                val body = ShareBindBankCardBody(
+                    userId = LiveSession.getUserId(),
+                    realName = realName,
+                    idCard = idCard,
+                    cardNumber = cardNumber,
+                    bankName = bankName
+                )
+                val res = shareInviteRepository.bindBankCard(body)
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "保存失败")
+                }
+                res.data
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    bankCard.value = it
+                    saveSuccess.value = true
+                }
+                error {
+                    Log.e("AddBankCardViewModel", "saveBankCard error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "保存失败"
+                }
+            }
+        )
+    }
+
+    fun isValidChinaIDCard(id: String): Boolean {
+        if (id.length != 18) return false
+        val idPattern = Regex("^\\d{17}[\\dXx]$")
+        if (!id.matches(idPattern)) return false
+
+        try {
+            val birthday = id.substring(6, 14)
+            val sdf = SimpleDateFormat("yyyyMMdd", Locale.getDefault())
+            sdf.isLenient = false
+            sdf.parse(birthday)
+        } catch (_: Exception) {
+            return false
+        }
+
+        val weights = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
+        val validateCodes = charArrayOf('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
+        var sum = 0
+        for (i in 0 until 17) {
+            sum += (id[i] - '0') * weights[i]
+        }
+        val remainder = sum % 11
+        val lastChar = id[17].uppercaseChar()
+        return lastChar == validateCodes[remainder]
+    }
+}
+

+ 54 - 0
module_me/src/main/java/com/mita/module_me/view/invite/BillDetailVH.kt

@@ -0,0 +1,54 @@
+package com.mita.module_me.view.invite
+
+import android.graphics.Color
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.mita.module_me.R
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+import kotlinx.android.synthetic.main.module_me_item_bill_detail.view.*
+
+class BillDetailVH(parent: ViewGroup) : BaseViewHolder<BillRowItem>(
+    LayoutInflater.from(parent.context).inflate(R.layout.module_me_item_bill_detail, parent, false)
+) {
+    override fun setViewData(
+        context: android.content.Context,
+        item: BillRowItem,
+        position: Int,
+        count: Int
+    ) {
+        val titleText = if (item.type == 1 && !item.fromUserId.isNullOrBlank()) {
+            "${item.title} ID:${item.fromUserId}"
+        } else {
+            item.title
+        }
+        itemView.tvTitle.text = titleText
+        itemView.tvSubTitle.text = item.subTitle.orEmpty()
+
+        if (!item.remark.isNullOrBlank()) {
+            itemView.tvRemark.visibility = View.VISIBLE
+            itemView.tvRemark.text = item.remark
+        } else {
+            itemView.tvRemark.visibility = View.GONE
+        }
+
+        val amountText: String
+        val amountColor: Int
+        if (item.type == 1) {
+            amountText = "+${item.amount}"
+            amountColor = Color.parseColor("#333333")
+        } else {
+            if (item.title == "提现返还") {
+                amountText = "+${item.amount}"
+                amountColor = Color.parseColor("#4CAF50")
+            } else {
+                amountText = "-${item.amount}"
+                amountColor = Color.parseColor("#FFFF6D00")
+            }
+        }
+        itemView.tvAmount.text = amountText
+        itemView.tvAmount.setTextColor(amountColor)
+        itemView.tvDate.text = item.date
+    }
+}
+

+ 12 - 0
module_me/src/main/java/com/mita/module_me/view/invite/BillRowItem.kt

@@ -0,0 +1,12 @@
+package com.mita.module_me.view.invite
+
+data class BillRowItem(
+    val title: String,
+    val subTitle: String?,
+    val amount: String,
+    val date: String,
+    val type: Int,
+    val remark: String?,
+    val fromUserId: String?
+)
+

+ 81 - 0
module_me/src/main/java/com/mita/module_me/view/invite/ExchangeActivity.kt

@@ -0,0 +1,81 @@
+package com.mita.module_me.view.invite
+
+import android.app.Activity
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.mita.module_me.R
+import com.yc.baselibrary.ext.toast
+import com.yc.baselibrary.view.base.BaseActivity
+import kotlinx.android.synthetic.main.module_me_activity_exchange.*
+
+class ExchangeActivity : BaseActivity<ExchangeViewModel>() {
+
+    override fun initView() {
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
+            val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
+            clTopBar.setPadding(
+                clTopBar.paddingLeft,
+                top,
+                clTopBar.paddingRight,
+                clTopBar.paddingBottom
+            )
+            insets
+        }
+        ViewCompat.requestApplyInsets(rootLayout)
+
+        ivBack.setOnClickListener { finish() }
+
+        btnAll.setOnClickListener {
+            val points = viewModel.balancePoints.value ?: 0.0
+            etAmount.setText(viewModel.formatBalance(points))
+            etAmount.setSelection(etAmount.text?.length ?: 0)
+        }
+
+        btnExchange.setOnClickListener {
+            val points = etAmount.text?.toString()?.trim()?.toDoubleOrNull()
+            if (points == null || points <= 0) {
+                toast("请输入有效的兑换金额")
+                return@setOnClickListener
+            }
+            val balance = viewModel.balancePoints.value ?: 0.0
+            if (points > balance) {
+                toast("积分不足")
+                return@setOnClickListener
+            }
+            viewModel.submitExchange(points)
+        }
+    }
+
+    override fun observe() {
+        viewModel.balancePoints.observe(this) { points ->
+            tvBalance.text = "积分:${viewModel.formatBalance(points)}(1积分=1金币)"
+        }
+
+        viewModel.submitSuccess.observe(this) { ok ->
+            if (ok == true) {
+                setResult(Activity.RESULT_OK)
+                toast("兑换成功")
+                etAmount.setText("")
+                viewModel.loadData()
+            }
+        }
+
+        viewModel.errorMsg.observe(this) { msg ->
+            if (!msg.isNullOrBlank()) toast(msg)
+        }
+
+        viewModel.stateModel.observe(this) { type ->
+            val loading = type == 1
+            btnExchange.isEnabled = !loading
+            btnExchange.alpha = if (loading) 0.7f else 1f
+            progressBar.visibility = if (loading) View.VISIBLE else View.GONE
+        }
+    }
+
+    override fun ready() {
+        viewModel.loadData()
+    }
+
+    override fun getLayoutId() = R.layout.module_me_activity_exchange
+}

+ 76 - 0
module_me/src/main/java/com/mita/module_me/view/invite/ExchangeViewModel.kt

@@ -0,0 +1,76 @@
+package com.mita.module_me.view.invite
+
+import android.app.Application
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.SavedStateHandle
+import com.mita.module_me.model.ShareExchangeBody
+import com.yc.baselibrary.net.exception.Errors
+import com.yc.baselibrary.net.exception.LoadingType
+import com.yc.baselibrary.view.base.BaseVm
+import com.yc.module_base.LiveSession
+
+class ExchangeViewModel(
+    app: Application,
+    map: MutableMap<String, Any?>,
+    savedStateHandle: SavedStateHandle
+) : BaseVm(app, map, savedStateHandle) {
+
+    private val shareInviteRepository: ShareInviteRepository by lazy { ShareInviteRepository() }
+
+    val balancePoints = MutableLiveData<Double>()
+    val submitSuccess = MutableLiveData<Boolean>()
+    val errorMsg = MutableLiveData<String>()
+
+    fun loadData() {
+        requestMix(
+            block = {
+                val summaryRes = shareInviteRepository.getShareSummary()
+                if (summaryRes.code != 200) {
+                    throw Errors.ErrorException(summaryRes.code ?: -1, summaryRes.message ?: "获取积分失败")
+                }
+                summaryRes.data?.balance ?: 0.0
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success { balancePoints.value = it }
+                error {
+                    Log.e("ExchangeViewModel", "loadData error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "加载失败"
+                }
+            }
+        )
+    }
+
+    fun submitExchange(points: Double) {
+        submitSuccess.value = false
+        requestMix(
+            block = {
+                val res = shareInviteRepository.exchange(
+                    ShareExchangeBody(
+                        userId = LiveSession.getUserId(),
+                        points = points,
+                        flowType = 1
+                    )
+                )
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "兑换失败")
+                }
+                true
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success { submitSuccess.value = true }
+                error {
+                    Log.e("ExchangeViewModel", "submitExchange error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "兑换失败"
+                }
+            }
+        )
+    }
+
+    fun formatBalance(value: Double): String {
+        return if (value % 1.0 == 0.0) String.format("%.0f", value) else String.format("%.2f", value)
+    }
+}
+

+ 122 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteBindingActivity.kt

@@ -0,0 +1,122 @@
+package com.mita.module_me.view.invite
+
+import android.app.Activity
+import android.view.View
+import com.mita.module_me.R
+import android.util.Log
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.yc.baselibrary.ext.toast
+import com.yc.baselibrary.view.base.BaseActivity
+import kotlinx.android.synthetic.main.module_me_activity_invite_binding.*
+
+class InviteBindingActivity : BaseActivity<InviteBindingViewModel>() {
+
+    private var boundChanged: Boolean = false
+
+    override fun initView() {
+        this.window.decorView.systemUiVisibility =
+            View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
+            val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
+            rootLayout.setPadding(
+                rootLayout.paddingLeft,
+                top,
+                rootLayout.paddingRight,
+                rootLayout.paddingBottom
+            )
+            insets
+        }
+        ViewCompat.requestApplyInsets(rootLayout)
+
+        ivBack.setOnClickListener { finish() }
+
+        btnNext.setOnClickListener {
+            val code = etInviteCode.text.toString()
+            if (code.isBlank()) {
+                toast("请输入邀请码")
+                return@setOnClickListener
+            }
+            viewModel.checkInviter(code)
+        }
+
+        btnCancel.setOnClickListener {
+            clInputState.visibility = View.VISIBLE
+            clConfirmState.visibility = View.GONE
+        }
+
+        btnConfirm.setOnClickListener {
+            val inviterId = viewModel.inviterInfo.value?.userId?.toInt() ?: 0
+            viewModel.confirmBind(inviterId)
+        }
+
+        btnBack.setOnClickListener { finish() }
+    }
+
+    override fun finish() {
+        if (boundChanged) {
+            setResult(Activity.RESULT_OK)
+        }
+        super.finish()
+    }
+
+    override fun observe() {
+        viewModel.inviteRelationship.observe(this) { relationship ->
+            //输出relationship
+            Log.d("InviteBindingActivity", "observe: $relationship")
+            if (relationship != null) {
+                clInputState.visibility = View.GONE
+                clConfirmState.visibility = View.VISIBLE
+                tvInviterName.text = "绑定人:${relationship.inviterNickname ?: ""}"
+                tvInviterId.text = "绑定ID:${relationship.inviterId ?: ""}"
+                tvSuccessHint.visibility = View.GONE
+                llActionButtons.visibility = View.GONE
+                btnBack.visibility = View.VISIBLE
+            }
+        }
+
+        viewModel.inviterInfo.observe(this) { user ->
+            if (user != null) {
+                Log.d("InviteBindingActivity", "observe inviterInfo: $user")
+                clInputState.visibility = View.GONE
+                clConfirmState.visibility = View.VISIBLE
+                tvInviterName.text = "绑定人:${user.nickName ?: ""}"
+                tvInviterId.text = "绑定ID:${user.userId}"
+                tvSuccessHint.visibility = View.GONE
+                llActionButtons.visibility = View.VISIBLE
+                btnConfirm.isEnabled = true
+                btnBack.visibility = View.GONE
+            }
+        }
+
+        viewModel.bindSuccess.observe(this) { success ->
+            if (success) {
+                Log.d("InviteBindingActivity", "observe bindSuccess: $success")
+                toast("绑定成功!")
+                boundChanged = true
+                tvSuccessHint.visibility = View.GONE
+                llActionButtons.visibility = View.GONE
+                btnBack.visibility = View.VISIBLE
+            }
+        }
+
+        viewModel.errorMsg.observe(this) { msg ->
+            if (msg.isNotEmpty()) {
+                Log.e("InviteBindingActivity", "observe error: $msg")
+                toast(msg)
+            }
+        }
+
+        viewModel.stateModel.observe(this) { type ->
+            progressBar.visibility = if (type == 1) View.VISIBLE else View.GONE
+        }
+    }
+
+    override fun ready() {
+        viewModel.getInviteRelationship()
+        Log.d("InviteBindingActivity getInviteRelationship", "ready: ")
+    }
+
+    override fun getLayoutId() = R.layout.module_me_activity_invite_binding
+}

+ 118 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteBindingViewModel.kt

@@ -0,0 +1,118 @@
+package com.mita.module_me.view.invite
+
+import android.app.Application
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.SavedStateHandle
+import com.mita.module_me.model.InviteRelationship
+import com.yc.baselibrary.net.exception.Errors
+import com.yc.baselibrary.net.exception.LoadingType
+import com.yc.baselibrary.view.base.BaseVm
+import com.yc.module_base.CommonRepository
+import com.yc.module_base.LiveSession
+import com.yc.module_base.model.User
+import com.mita.module_me.model.ShareInviteBindBody
+import com.mita.module_me.model.ShareUserProfile
+
+class InviteBindingViewModel(
+    app: Application,
+    map: MutableMap<String, Any?>,
+    savedStateHandle: SavedStateHandle
+) : BaseVm(app, map, savedStateHandle) {
+
+    private val commonRepository: CommonRepository by lazy { CommonRepository() }
+    private val shareInviteRepository: ShareInviteRepository by lazy { ShareInviteRepository() }
+    
+    val inviterInfo = MutableLiveData<User?>()
+    val inviteRelationship = MutableLiveData<InviteRelationship?>()
+    val bindSuccess = MutableLiveData<Boolean>()
+    val errorMsg = MutableLiveData<String>()
+
+    fun getInviteRelationship() {
+        requestMix(
+            block = {
+                val res = shareInviteRepository.getInviteRelationship()
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取绑定关系失败")
+                }
+                res.data
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    Log.d("InviteBindingViewModel", "getInviteRelationship success: $it")
+                    inviteRelationship.value = it
+                }
+                error {
+                    Log.e("InviteBindingViewModel", "getInviteRelationship error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "获取绑定关系失败"
+                }
+            }
+        )
+    }
+
+    fun checkInviter(inviteCode: String) {
+        val code = inviteCode.toLongOrNull()
+        if (code == null) {
+            errorMsg.value = "请输入有效的邀请码"
+            return
+        }
+        if (code == LiveSession.getUserId()) {
+            errorMsg.value = "不能绑定自己"
+            return
+        }
+
+        requestMix(
+            block = {
+                val res = shareInviteRepository.getUserProfile(inviteCode)
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取用户信息失败")
+                }
+                res.data
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success { profile: ShareUserProfile? ->
+                    if (profile?.userId == null || profile.userId <= 0) {
+                        errorMsg.value = "用户不存在"
+                        return@success
+                    }
+                    inviterInfo.value = User(
+                        userId = profile.userId,
+                        nickName = profile.nickName,
+                        picture = profile.headPic
+                    )
+                }
+                error {
+                    Log.e("InviteBinding", "checkInviter error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "获取用户信息失败"
+                }
+            }
+        )
+    }
+
+    fun confirmBind(inviterId: Int) {
+        requestMix(
+            block = {
+                val res = shareInviteRepository.createInviteRelationship(
+                    ShareInviteBindBody(inviterId = inviterId.toString())
+                )
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "绑定失败")
+                }
+                res.data
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    inviteRelationship.value = it
+                    bindSuccess.value = true
+                }
+                error {
+                    Log.e("InviteBinding", "confirmBind error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "绑定失败"
+                }
+            }
+        )
+    }
+}

+ 282 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteEarningsActivity.kt

@@ -0,0 +1,282 @@
+package com.mita.module_me.view.invite
+import android.util.Log
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.Typeface
+import android.view.View
+import android.view.ViewTreeObserver
+import android.widget.TextView
+import java.text.NumberFormat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.mita.module_me.R
+import com.google.android.material.tabs.TabLayout
+import com.yc.baselibrary.adapter.viewHolder.MutableAdapter
+import com.yc.baselibrary.ext.showDialogBottom2
+import com.yc.baselibrary.ext.toast
+import com.yc.baselibrary.utils.ComponentUtil
+import com.yc.baselibrary.view.base.BaseActivity
+import kotlinx.android.synthetic.main.module_me_activity_invite_earnings.*
+
+import com.yc.module_base.model.InviteItem
+
+class InviteEarningsActivity : BaseActivity<InviteEarningsViewModel>() {
+
+    private companion object {
+        private const val REQUEST_EXCHANGE = 1001
+        private const val REQUEST_WITHDRAW = 1002
+        private const val REQUEST_BINDING = 1003
+    }
+
+    private val mAdapter by lazy { 
+        MutableAdapter(viewModel.list).apply {
+            addVhDelegate(InviteItem::class.java, ::InviteEarningsVH)
+            addVhDelegate(BillRowItem::class.java, ::BillDetailVH)
+            addVhDelegate(LoadMoreRowItem::class.java, ::LoadMoreVH)
+        }
+    }
+    
+    private var currentTabIndex = 0
+    private var currentBillType = 1
+
+    override fun initView() {
+        this.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
+            val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
+            clTopBar.setPadding(
+                clTopBar.paddingLeft,
+                top,
+                clTopBar.paddingRight,
+                clTopBar.paddingBottom
+            )
+            insets
+        }
+        ViewCompat.requestApplyInsets(rootLayout)
+
+        ivBack.setOnClickListener { finish() }
+        tvRule.setOnClickListener { startActivity(Intent(this, InviteRulesActivity::class.java)) }
+        btnCopy.setOnClickListener {
+            val code = tvInviteCode.text?.toString().orEmpty()
+            if (code.isBlank()) {
+                toast("邀请码为空")
+                return@setOnClickListener
+            }
+            if (ComponentUtil.copyStr(this, code)) {
+                toast("已复制")
+            } else {
+                toast("复制失败")
+            }
+        }
+       
+        btnExchange.setOnClickListener {
+            startActivityForResult(Intent(this, ExchangeActivity::class.java), REQUEST_EXCHANGE)
+        }
+        btnWithdraw.setOnClickListener {
+            startActivityForResult(Intent(this, WithdrawActivity::class.java), REQUEST_WITHDRAW)
+        }
+        btnInviteNow.setOnClickListener {
+            viewModel.requestInviteShareConfig()
+        }
+
+        recyclerView.layoutManager = LinearLayoutManager(this)
+        recyclerView.adapter = mAdapter
+        recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+                if (dy <= 0) return
+                val lm = recyclerView.layoutManager as? LinearLayoutManager ?: return
+                val lastPos = lm.findLastVisibleItemPosition()
+                if (lastPos >= mAdapter.itemCount - 2) {
+                    viewModel.loadMore()
+                }
+            }
+        })
+
+        if (tabLayout.tabCount == 0) {
+            tabLayout.addTab(createTab("我的邀请", selected = true))
+            tabLayout.addTab(createTab("账单明细", selected = false))
+            tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+                override fun onTabSelected(tab: TabLayout.Tab) {
+                    setTabSelected(tab, true)
+                    currentTabIndex = tab.position
+                    billHeader.visibility = if (currentTabIndex == 1) View.VISIBLE else View.GONE
+                    viewModel.selectTab(currentTabIndex)
+                }
+
+                override fun onTabUnselected(tab: TabLayout.Tab) {
+                    setTabSelected(tab, false)
+                }
+
+                override fun onTabReselected(tab: TabLayout.Tab) {
+                    setTabSelected(tab, true)
+                }
+            })
+        }
+
+        llBillIncome.setOnClickListener {
+            if (currentTabIndex != 1) return@setOnClickListener
+            currentBillType = 1
+            applyBillTypeUi(currentBillType)
+            viewModel.setBillType(1)
+        }
+        llBillExpense.setOnClickListener {
+            if (currentTabIndex != 1) return@setOnClickListener
+            currentBillType = 2
+            applyBillTypeUi(currentBillType)
+            viewModel.setBillType(2)
+        }
+        applyBillTypeUi(currentBillType)
+
+        tvHeaderTitle.viewTreeObserver.addOnGlobalLayoutListener(object :
+            ViewTreeObserver.OnGlobalLayoutListener {
+            override fun onGlobalLayout() {
+                if (tvMoreInvite.width <= 0) return
+                val text = tvHeaderTitle.text?.toString().orEmpty()
+                val idx = text.indexOf("丰厚")
+                val center = if (idx >= 0 && idx + 2 <= text.length) {
+                    val layout = tvHeaderTitle.layout
+                    if (layout != null && tvHeaderTitle.lineCount > 1) {
+                        val line = layout.getLineForOffset(idx)
+                        val lineLeft = layout.getLineLeft(line)
+                        val start = lineLeft + layout.getPrimaryHorizontal(idx)
+                        val end = lineLeft + layout.getPrimaryHorizontal(idx + 2)
+                        (start + end) / 2f
+                    } else {
+                        val paint = tvHeaderTitle.paint
+                        val fullWidth = paint.measureText(text)
+                        val prefixWidth = paint.measureText(text.substring(0, idx))
+                        val targetWidth = paint.measureText(text.substring(idx, idx + 2))
+                        val contentWidth =
+                            tvHeaderTitle.width - tvHeaderTitle.paddingLeft - tvHeaderTitle.paddingRight
+                        val startX =
+                            tvHeaderTitle.paddingLeft + (contentWidth - fullWidth) / 2f + prefixWidth
+                        startX + targetWidth / 2f
+                    }
+                } else {
+                    tvHeaderTitle.width / 2f
+                }
+                val maxX = (tvHeaderTitle.width - tvMoreInvite.width).toFloat()
+                tvMoreInvite.translationX = (center - tvMoreInvite.width / 2f).coerceIn(0f, maxX)
+                tvHeaderTitle.viewTreeObserver.removeOnGlobalLayoutListener(this)
+            }
+        })
+    }
+
+    private fun createTab(text: String, selected: Boolean): TabLayout.Tab {
+        val tab = tabLayout.newTab()
+        val view = layoutInflater.inflate(R.layout.module_me_tab_invite_earnings, tabLayout, false)
+        view.findViewById<TextView>(R.id.tvTab).text = text
+        tab.customView = view
+        setTabSelected(tab, selected)
+        return tab
+    }
+
+    private fun setTabSelected(tab: TabLayout.Tab, selected: Boolean) {
+        val tv = tab.customView?.findViewById<TextView>(R.id.tvTab) ?: return
+        tv.setTypeface(tv.typeface, if (selected) Typeface.BOLD else Typeface.NORMAL)
+        tv.setTextColor(if (selected) Color.parseColor("#D84315") else Color.parseColor("#999999"))
+        tab.customView?.findViewById<View>(R.id.vIndicator)?.visibility =
+            if (selected) View.VISIBLE else View.INVISIBLE
+    }
+
+    override fun observe() {
+        viewModel.earningsData.observe(this) { data ->
+            if (data != null) {
+                Log.d("InviteEarningsActivity", "observe data: $data")
+                tvInviteCode.text = data.inviteCode.ifBlank { "" }
+                tvBindingStatus.text = if (data.isBind) "已绑定" else "未绑定"
+                val nf = NumberFormat.getIntegerInstance()
+                tvPoints.text = nf.format(data.points)
+                tvTodayEarnings.text = nf.format(data.todayEarnings)
+                tvTotalEarnings.text = nf.format(data.totalEarnings)
+                tvTotalInvites.text = nf.format(data.totalInvites)
+            }
+        }
+
+        viewModel.inviteRelationship.observe(this) { relationship ->
+            if (relationship == null) {
+                tvBindingTitle.visibility = View.VISIBLE
+                tvBoundInviter.visibility = View.GONE
+                ivBindArrow.visibility = View.VISIBLE
+                clBinding.isClickable = true
+                clBinding.setOnClickListener {
+                    startActivityForResult(Intent(this, InviteBindingActivity::class.java), REQUEST_BINDING)
+                }
+            } else {
+                tvBindingTitle.visibility = View.GONE
+                tvBoundInviter.visibility = View.VISIBLE
+                ivBindArrow.visibility = View.GONE
+                clBinding.isClickable = false
+                clBinding.setOnClickListener(null)
+                val nick = relationship.inviterNickname.orEmpty()
+                val id = relationship.inviterId.orEmpty()
+                tvBoundInviter.text = "邀请人:${nick}(ID:${id})"
+            }
+        }
+
+        viewModel.errorMsg.observe(this) { msg ->
+            if (msg.isNotEmpty()) {
+                Log.e("InviteEarningsActivity", "observe error: $msg")
+                toast(msg)
+            }
+        }
+
+        viewModel.billTotal.observe(this) { total ->
+            tvBillTotal.text = total.ifBlank { "0" }
+        }
+
+        viewModel.listEmpty.observe(this) { isEmpty ->
+            emptyView.visibility = if (isEmpty == true) View.VISIBLE else View.GONE
+            recyclerView.visibility = if (isEmpty == true) View.GONE else View.VISIBLE
+        }
+
+        viewModel.stateModel.observe(this) { type ->
+            progressBar.visibility = if (type == 1) View.VISIBLE else View.GONE
+        }
+
+        viewModel.inviteShareConfig.observe(this) { cfg ->
+            if (cfg != null) {
+                showDialogBottom2(InviteShareDialog(this, cfg))
+                viewModel.inviteShareConfig.value = null
+            }
+        }
+    }
+
+    override fun ready() {
+        viewModel.getInviteEarnings()
+        viewModel.selectTab(0)
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if ((requestCode == REQUEST_EXCHANGE || requestCode == REQUEST_WITHDRAW || requestCode == REQUEST_BINDING) && resultCode == RESULT_OK) {
+            viewModel.getInviteEarnings()
+            viewModel.selectTab(currentTabIndex)
+        }
+    }
+
+
+    override fun getLayoutId() = R.layout.module_me_activity_invite_earnings
+
+    private fun applyBillTypeUi(type: Int) {
+        if (type == 1) {
+            flIncomeDot.background = resources.getDrawable(R.drawable.sp_circle_solid_ff6d00, theme)
+            tvIncomeCheck.visibility = View.VISIBLE
+            tvIncomeLabel.setTextColor(Color.parseColor("#333333"))
+
+            flExpenseDot.background = resources.getDrawable(R.drawable.sp_circle_stroke_999, theme)
+            tvExpenseCheck.visibility = View.GONE
+            tvExpenseLabel.setTextColor(Color.parseColor("#666666"))
+        } else {
+            flIncomeDot.background = resources.getDrawable(R.drawable.sp_circle_stroke_999, theme)
+            tvIncomeCheck.visibility = View.GONE
+            tvIncomeLabel.setTextColor(Color.parseColor("#666666"))
+
+            flExpenseDot.background = resources.getDrawable(R.drawable.sp_circle_solid_ff6d00, theme)
+            tvExpenseCheck.visibility = View.VISIBLE
+            tvExpenseLabel.setTextColor(Color.parseColor("#333333"))
+        }
+    }
+}

+ 16 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteEarningsVH.kt

@@ -0,0 +1,16 @@
+package com.mita.module_me.view.invite
+
+import android.view.ViewGroup
+import com.mita.module_me.R
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+import com.yc.module_base.model.InviteItem
+import kotlinx.android.synthetic.main.module_me_item_invite_earnings.view.*
+
+class InviteEarningsVH(parent: ViewGroup) : BaseViewHolder<InviteItem>(android.view.LayoutInflater.from(parent.context).inflate(R.layout.module_me_item_invite_earnings, parent, false)) {
+    override fun setViewData(context: android.content.Context, item: InviteItem, position: Int, count: Int) {
+        itemView.tvName.text = item.name
+        itemView.tvId.text = "ID: ${item.id}"
+        itemView.tvContribution.text = item.contribution
+        itemView.tvDate.text = item.date
+    }
+}

+ 520 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteEarningsViewModel.kt

@@ -0,0 +1,520 @@
+package com.mita.module_me.view.invite
+
+import android.app.Application
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.SavedStateHandle
+import com.mita.module_me.model.InviteRelationship
+import com.mita.module_me.model.InviteShareConfig
+import com.mita.module_me.model.ShareBillItem
+import com.mita.module_me.model.SharePointFlow
+import com.mita.module_me.model.ShareInvitedUser
+import com.yc.baselibrary.net.exception.Errors
+import com.yc.baselibrary.net.exception.LoadingType
+import com.yc.baselibrary.adapter.observable.ObservableAdapterList
+import com.yc.baselibrary.view.base.BaseVm
+import com.yc.module_base.model.InviteEarnings
+import com.yc.module_base.model.InviteItem
+import com.yc.module_base.LiveSession
+
+class InviteEarningsViewModel(
+    app: Application,
+    map: MutableMap<String, Any?>,
+    savedStateHandle: SavedStateHandle
+) : BaseVm(app, map, savedStateHandle) {
+
+    private companion object {
+        private const val PAGE_SIZE = 20
+    }
+
+    private val shareInviteRepository: ShareInviteRepository by lazy { ShareInviteRepository() }
+
+    val earningsData = MutableLiveData<InviteEarnings?>()
+    val inviteRelationship = MutableLiveData<InviteRelationship?>()
+    val billTotal = MutableLiveData<String>()
+    val listEmpty = MutableLiveData<Boolean>()
+    val list = ObservableAdapterList<Any>()
+    val errorMsg = MutableLiveData<String>()
+    val inviteShareConfig = MutableLiveData<InviteShareConfig?>()
+
+    private var inviteListCache: MutableList<InviteItem> = mutableListOf()
+    private var currentTab: Int = 0
+    private var currentBillType: Int = 1
+    private var cachedInviteShareConfig: InviteShareConfig? = null
+
+    private var invitePage: Int = 1
+    private var inviteEndReached: Boolean = false
+    private var inviteLoadingMore: Boolean = false
+
+    private var incomePage: Int = 1
+    private var incomeEndReached: Boolean = false
+    private var incomeLoadingMore: Boolean = false
+    private var incomeCache: MutableList<BillRowItem> = mutableListOf()
+    private var incomeTotalCache: String = "0"
+
+    private var expensePage: Int = 1
+    private var expenseEndReached: Boolean = false
+    private var expenseLoadingMore: Boolean = false
+    private var expenseCache: MutableList<BillRowItem> = mutableListOf()
+    private var expenseTotalCache: String = "0"
+
+    data class InviteEarningsResult(
+        val earnings: InviteEarnings,
+        val relationship: InviteRelationship?
+    )
+
+    fun getInviteEarnings() {
+        Log.d("InviteEarningsViewModelx", "getInviteEarnings: ")
+        requestMix(
+            block = {
+                val relationshipRes = shareInviteRepository.getInviteRelationship()
+                if (relationshipRes.code != 200) {
+                    throw Errors.ErrorException(
+                        relationshipRes.code ?: -1,
+                        relationshipRes.message ?: "获取绑定关系失败"
+                    )
+                }
+
+                val summaryRes = shareInviteRepository.getShareSummary()
+                if (summaryRes.code != 200) {
+                    throw Errors.ErrorException(summaryRes.code ?: -1, summaryRes.message ?: "获取邀请收益失败")
+                }
+                val summary = summaryRes.data
+
+                val invitedRes = shareInviteRepository.getInvitedUsers(page = 1, pageSize = PAGE_SIZE)
+                if (invitedRes.code != 200) {
+                    throw Errors.ErrorException(invitedRes.code ?: -1, invitedRes.message ?: "获取邀请列表失败")
+                }
+
+                val invitedUsers: List<ShareInvitedUser> = invitedRes.data?.list.orEmpty()
+                val inviteItems = mapInvitedUsers(invitedUsers)
+
+                InviteEarningsResult(
+                    earnings = InviteEarnings(
+                        inviteCode = LiveSession.getUserId().toString(),
+                        isBind = relationshipRes.data != null,
+                        points = (summary?.balance ?: 0.0).toLong(),
+                        todayEarnings = (summary?.todayEarnings ?: 0.0).toLong(),
+                        totalEarnings = (summary?.totalEarnings ?: 0.0).toLong(),
+                        totalInvites = summary?.inviteCount ?: 0,
+                        list = inviteItems
+                    ),
+                    relationship = relationshipRes.data
+                )
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    earningsData.value = it.earnings
+                    inviteRelationship.value = it.relationship
+                    inviteListCache = it.earnings.list.toMutableList()
+                    invitePage = 1
+                    inviteEndReached = inviteListCache.size < PAGE_SIZE
+                    if (currentTab == 0) {
+                        list.setNewData(inviteListCache)
+                        listEmpty.value = inviteListCache.isEmpty()
+                    }
+                }
+                error {
+                    Log.e("InviteEarningsViewModel", "getInviteEarnings error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "获取邀请收益失败"
+                }
+            }
+        )
+    }
+
+    fun selectTab(tabIndex: Int) {
+        currentTab = tabIndex
+        if (tabIndex == 0) {
+            list.setNewData(inviteListCache)
+            listEmpty.value = inviteListCache.isEmpty()
+            return
+        }
+        if (currentBillType == 1 && incomeCache.isNotEmpty()) {
+            billTotal.value = incomeTotalCache
+            list.setNewData(incomeCache)
+            listEmpty.value = incomeCache.isEmpty()
+            return
+        }
+        if (currentBillType == 2 && expenseCache.isNotEmpty()) {
+            billTotal.value = expenseTotalCache
+            list.setNewData(expenseCache)
+            listEmpty.value = expenseCache.isEmpty()
+            return
+        }
+        loadBillDetails(currentBillType)
+    }
+
+    fun setBillType(type: Int) {
+        currentBillType = type
+        if (currentTab == 1) {
+            if (type == 1 && incomeCache.isNotEmpty()) {
+                billTotal.value = incomeTotalCache
+                list.setNewData(incomeCache)
+                listEmpty.value = incomeCache.isEmpty()
+                return
+            }
+            if (type == 2 && expenseCache.isNotEmpty()) {
+                billTotal.value = expenseTotalCache
+                list.setNewData(expenseCache)
+                listEmpty.value = expenseCache.isEmpty()
+                return
+            }
+            loadBillDetails(type)
+        }
+    }
+
+    fun loadMore() {
+        if (currentTab == 0) {
+            loadMoreInvites()
+            return
+        }
+        loadMoreBills(currentBillType)
+    }
+
+    fun requestInviteShareConfig() {
+        cachedInviteShareConfig?.let {
+            inviteShareConfig.value = it
+            return
+        }
+        requestMix(
+            block = {
+                val res = shareInviteRepository.getInviteShareConfig()
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取分享配置失败")
+                }
+                val map = res.data.orEmpty()
+                val cfg = InviteShareConfig(
+                    inviteBaseUrl = map["invite_base_url"] ?: "https://share.sweetee.live/invite/",
+                    shareTitle = map["share_title"] ?: "甜咖直播",
+                    shareDesc = map["share_desc"] ?: "邀请好友领取丰厚礼",
+                    shareIcon = map["share_icon"] ?: "",
+                    posterAppName = map["poster_app_name"] ?: "甜咖直播",
+                    posterSubtitle = map["poster_subtitle"] ?: "邀请您加入"
+                )
+                cfg
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    cachedInviteShareConfig = it
+                    inviteShareConfig.value = it
+                }
+                error {
+                    Log.e("InviteEarningsViewModel", "requestInviteShareConfig error: ${it.message}", it)
+                    val fallback = InviteShareConfig(
+                        inviteBaseUrl = "https://share.sweetee.live/invite/",
+                        shareTitle = "甜咖直播",
+                        shareDesc = "邀请好友领取丰厚礼",
+                        shareIcon = "",
+                        posterAppName = "甜咖直播",
+                        posterSubtitle = "邀请您加入"
+                    )
+                    cachedInviteShareConfig = fallback
+                    inviteShareConfig.value = fallback
+                    errorMsg.value = it.message ?: "获取分享配置失败"
+                }
+            }
+        )
+    }
+
+    private fun loadBillDetails(type: Int) {
+        val userId = LiveSession.getUserId().toString()
+        requestMix(
+            block = {
+                if (type == 1) {
+                    val res = shareInviteRepository.getBillList(
+                        userId = userId,
+                        page = 1,
+                        pageSize = PAGE_SIZE,
+                        type = "1"
+                    )
+                    if (res.code != 200) {
+                        throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取账单明细失败")
+                    }
+                    val data = res.data
+                    val items = data?.list.orEmpty().map { it.toIncomeRow() }
+                    val endReached = data?.list.orEmpty().size < PAGE_SIZE
+                    Triple(items, data?.totalAmount ?: "0", endReached)
+                } else {
+                    val res = shareInviteRepository.getPointFlowList(
+                        userId = userId,
+                        page = 1,
+                        pageSize = PAGE_SIZE
+                    )
+                    if (res.code != 200) {
+                        throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取账单明细失败")
+                    }
+                    val data = res.data
+                    val filtered = data?.list.orEmpty().filter { flow ->
+                        val t = flow.flowType ?: 0
+                        t == 1 || t == 2 || t == 3
+                    }
+                    val items = filtered.map { it.toExpenseRow() }
+                    val total = data?.totalPoints ?: 0.0
+                    val endReached = data?.list.orEmpty().size < PAGE_SIZE
+                    Triple(items, formatNumber(total), endReached)
+                }
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    val items = it.first
+                    val total = it.second
+                    val endReached = it.third
+                    billTotal.value = total
+                    if (type == 1) {
+                        incomeCache = items.toMutableList()
+                        incomeTotalCache = total
+                        incomePage = 1
+                        incomeEndReached = endReached
+                    } else {
+                        expenseCache = items.toMutableList()
+                        expenseTotalCache = total
+                        expensePage = 1
+                        expenseEndReached = endReached
+                    }
+                    list.setNewData(items)
+                    listEmpty.value = items.isEmpty()
+                }
+                error {
+                    Log.e("InviteEarningsViewModel", "loadBillDetails error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "获取账单明细失败"
+                }
+            }
+        )
+    }
+
+    private fun loadMoreInvites() {
+        if (inviteEndReached || inviteLoadingMore) return
+        val nextPage = invitePage + 1
+        inviteLoadingMore = true
+        showLoadMoreFooter()
+        requestMix(
+            block = {
+                val res = shareInviteRepository.getInvitedUsers(page = nextPage, pageSize = PAGE_SIZE)
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取邀请列表失败")
+                }
+                val invitedUsers = res.data?.list.orEmpty()
+                val items = mapInvitedUsers(invitedUsers)
+                val endReached = invitedUsers.size < PAGE_SIZE
+                Pair(items, endReached)
+            },
+            build = {
+                setLoadingType(LoadingType.NONE)
+                success {
+                    hideLoadMoreFooter()
+                    val items = it.first
+                    val endReached = it.second
+                    invitePage = nextPage
+                    inviteEndReached = endReached
+                    if (items.isNotEmpty()) {
+                        inviteListCache.addAll(items)
+                        if (currentTab == 0) {
+                            list.addAll(list.size, items)
+                            listEmpty.value = false
+                        }
+                    }
+                    inviteLoadingMore = false
+                    if (inviteEndReached && currentTab == 0) {
+                        showNoMoreFooter()
+                    }
+                }
+                error {
+                    hideLoadMoreFooter()
+                    errorMsg.value = it.message ?: "获取邀请列表失败"
+                    inviteLoadingMore = false
+                }
+            }
+        )
+    }
+
+    private fun loadMoreBills(type: Int) {
+        if (type == 1) {
+            if (incomeEndReached || incomeLoadingMore) return
+        } else {
+            if (expenseEndReached || expenseLoadingMore) return
+        }
+
+        val userId = LiveSession.getUserId().toString()
+        val nextPage = if (type == 1) incomePage + 1 else expensePage + 1
+        if (type == 1) incomeLoadingMore = true else expenseLoadingMore = true
+        showLoadMoreFooter()
+
+        requestMix(
+            block = {
+                if (type == 1) {
+                    val res = shareInviteRepository.getBillList(
+                        userId = userId,
+                        page = nextPage,
+                        pageSize = PAGE_SIZE,
+                        type = "1"
+                    )
+                    if (res.code != 200) {
+                        throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取账单明细失败")
+                    }
+                    val data = res.data
+                    val raw = data?.list.orEmpty()
+                    val items = raw.map { it.toIncomeRow() }
+                    val endReached = raw.size < PAGE_SIZE
+                    Triple(items, data?.totalAmount ?: "0", endReached)
+                } else {
+                    val res = shareInviteRepository.getPointFlowList(
+                        userId = userId,
+                        page = nextPage,
+                        pageSize = PAGE_SIZE
+                    )
+                    if (res.code != 200) {
+                        throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取账单明细失败")
+                    }
+                    val data = res.data
+                    val raw = data?.list.orEmpty()
+                    val filtered = raw.filter { flow ->
+                        val t = flow.flowType ?: 0
+                        t == 1 || t == 2 || t == 3
+                    }
+                    val items = filtered.map { it.toExpenseRow() }
+                    val total = data?.totalPoints ?: 0.0
+                    val endReached = raw.size < PAGE_SIZE
+                    Triple(items, formatNumber(total), endReached)
+                }
+            },
+            build = {
+                setLoadingType(LoadingType.NONE)
+                success {
+                    hideLoadMoreFooter()
+                    val items = it.first
+                    val total = it.second
+                    val endReached = it.third
+                    if (currentTab == 1 && currentBillType == type) {
+                        billTotal.value = total
+                    }
+                    if (type == 1) {
+                        incomeTotalCache = total
+                        incomePage = nextPage
+                        incomeEndReached = endReached
+                        if (items.isNotEmpty()) {
+                            incomeCache.addAll(items)
+                        }
+                    } else {
+                        expenseTotalCache = total
+                        expensePage = nextPage
+                        expenseEndReached = endReached
+                        if (items.isNotEmpty()) {
+                            expenseCache.addAll(items)
+                        }
+                    }
+                    if (currentTab == 1 && currentBillType == type) {
+                        if (items.isNotEmpty()) {
+                            list.addAll(list.size, items)
+                            listEmpty.value = false
+                        }
+                    }
+                    if (type == 1) incomeLoadingMore = false else expenseLoadingMore = false
+                    val end = if (type == 1) incomeEndReached else expenseEndReached
+                    if (end && currentTab == 1 && currentBillType == type) {
+                        showNoMoreFooter()
+                    }
+                }
+                error {
+                    hideLoadMoreFooter()
+                    errorMsg.value = it.message ?: "获取账单明细失败"
+                    if (type == 1) incomeLoadingMore = false else expenseLoadingMore = false
+                }
+            }
+        )
+    }
+
+    private fun showLoadMoreFooter() {
+        if (list.lastOrNull() is LoadMoreRowItem) return
+        list.addAll(list.size, listOf(LoadMoreRowItem(isLoading = true)))
+    }
+
+    private fun hideLoadMoreFooter() {
+        val last = list.lastOrNull()
+        if (last is LoadMoreRowItem) {
+            list.removeAt(list.size - 1)
+        }
+    }
+
+    private fun showNoMoreFooter() {
+        hideLoadMoreFooter()
+        list.addAll(list.size, listOf(LoadMoreRowItem(isLoading = false, isNoMore = true)))
+    }
+
+    private fun mapInvitedUsers(invitedUsers: List<ShareInvitedUser>): List<InviteItem> {
+        return invitedUsers.map {
+            val contributionStr = it.points ?: it.contribution ?: "0"
+            InviteItem(
+                name = it.nickName.orEmpty(),
+                id = it.userId.orEmpty(),
+                contribution = contributionStr,
+                date = it.createdAt.orEmpty()
+            )
+        }
+    }
+
+    private fun ShareBillItem.toIncomeRow(): BillRowItem {
+        val pointsVal = points ?: 0.0
+        val pointsStr = formatNumber(pointsVal)
+        val sub = if (type == 1) "一级贡献" else "二级贡献"
+        return BillRowItem(
+            title = fromUser ?: "未知用户",
+            subTitle = sub,
+            amount = pointsStr,
+            date = time.orEmpty(),
+            type = 1,
+            remark = null,
+            fromUserId = fromUserId
+        )
+    }
+
+    private fun SharePointFlow.toExpenseRow(): BillRowItem {
+        val t = flowType ?: 0
+        val title = when (t) {
+            1 -> "兑换"
+            2 -> "提现"
+            3 -> "提现返还"
+            else -> "其他"
+        }
+        val sub = if (t == 3) "返还积分" else "消耗积分"
+        val p = points ?: 0.0
+        val amount = if (t == 3) {
+            formatNumber(kotlin.math.abs(p))
+        } else {
+            formatNumber(kotlin.math.abs(p))
+        }
+        return BillRowItem(
+            title = title,
+            subTitle = sub,
+            amount = amount,
+            date = formatIsoDate(createdAt.orEmpty()),
+            type = 2,
+            remark = remark,
+            fromUserId = null
+        )
+    }
+
+    private fun formatNumber(value: Double): String {
+        return if (value % 1.0 == 0.0) String.format("%.0f", value) else String.format("%.2f", value)
+    }
+
+    private fun formatIsoDate(isoDate: String): String {
+        try {
+            val inputFormat = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", java.util.Locale.getDefault())
+            val outputFormat = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", java.util.Locale.getDefault())
+            val date = inputFormat.parse(isoDate)
+            return if (date != null) outputFormat.format(date) else isoDate
+        } catch (_: Exception) {
+            try {
+                val inputFormat2 = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", java.util.Locale.getDefault())
+                val outputFormat = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", java.util.Locale.getDefault())
+                val date = inputFormat2.parse(isoDate)
+                return if (date != null) outputFormat.format(date) else isoDate
+            } catch (_: Exception) {
+                return isoDate
+            }
+        }
+    }
+}

+ 36 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteRulesActivity.kt

@@ -0,0 +1,36 @@
+package com.mita.module_me.view.invite
+
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.mita.module_me.R
+import com.yc.baselibrary.view.base.BaseActivity
+import com.yc.baselibrary.view.base.BaseVm
+import kotlinx.android.synthetic.main.module_me_activity_invite_rules.*
+
+class InviteRulesActivity : BaseActivity<BaseVm>() {
+
+    override fun getLayoutId() = R.layout.module_me_activity_invite_rules
+
+    override fun initView() {
+        this.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
+            val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
+            clTopBar.setPadding(
+                clTopBar.paddingLeft,
+                top,
+                clTopBar.paddingRight,
+                clTopBar.paddingBottom
+            )
+            insets
+        }
+        ViewCompat.requestApplyInsets(rootLayout)
+
+        ivBack.setOnClickListener { finish() }
+    }
+
+    override fun ready() {
+    }
+}
+

+ 303 - 0
module_me/src/main/java/com/mita/module_me/view/invite/InviteShareDialog.kt

@@ -0,0 +1,303 @@
+package com.mita.module_me.view.invite
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.ContentValues
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Typeface
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.provider.MediaStore
+import android.view.View
+import androidx.core.graphics.drawable.toBitmap
+import com.bmkj.umlibrary.ShareManager
+import com.google.zxing.BarcodeFormat
+import com.google.zxing.EncodeHintType
+import com.google.zxing.MultiFormatWriter
+import com.google.zxing.common.BitMatrix
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
+import com.hunliji.hlj_dialog.xpopup.core.BottomPopupView
+import com.mita.module_me.R
+import com.mita.module_me.model.InviteShareConfig
+import com.yc.baselibrary.ext.setSafeListener
+import com.yc.baselibrary.ext.toast
+import com.yc.module_base.LiveSession
+import kotlinx.android.synthetic.main.module_me_dialog_invite_share.view.*
+import java.util.EnumMap
+import java.util.Hashtable
+
+class InviteShareDialog(
+    context: Context,
+    private val cfg: InviteShareConfig
+) : BottomPopupView(context) {
+
+    override fun getImplLayoutId() = R.layout.module_me_dialog_invite_share
+
+    private val inviteCode: String = LiveSession.getUserId().toString()
+    private val inviteLink: String = run {
+        val base = cfg.inviteBaseUrl.trim()
+        val normalized = if (base.endsWith("/")) base else "$base/"
+        normalized + inviteCode
+    }
+
+    private var qrBitmap: Bitmap? = null
+
+    override fun onCreate() {
+        super.onCreate()
+
+        initTabs()
+        initLinkShare()
+        initPosterShare()
+    }
+
+    private fun initTabs() {
+        switchTab(0)
+        tvTabLink.setSafeListener { switchTab(0) }
+        tvTabPoster.setSafeListener { switchTab(1) }
+    }
+
+    private fun switchTab(index: Int) {
+        val isLink = index == 0
+        tvTabLink.isSelected = isLink
+        tvTabPoster.isSelected = !isLink
+        vTabLinkIndicator.visibility = if (isLink) View.VISIBLE else View.INVISIBLE
+        vTabPosterIndicator.visibility = if (isLink) View.INVISIBLE else View.VISIBLE
+        llLinkShare.visibility = if (isLink) View.VISIBLE else View.GONE
+        llPosterShare.visibility = if (isLink) View.GONE else View.VISIBLE
+    }
+
+    private fun initLinkShare() {
+        ivShareWeixin.setSafeListener {
+            startWeChatShare(isCircle = false)
+            dismiss()
+        }
+        ivShareMoments.setSafeListener {
+            startWeChatShare(isCircle = true)
+            dismiss()
+        }
+        ivShareQq.setSafeListener {
+            val shareText = "来${cfg.shareTitle}一起玩!邀请链接: $inviteLink"
+            shareTextToPackage("com.tencent.mobileqq", shareText, "未安装QQ")
+        }
+        ivShareCopy.setSafeListener {
+            val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+            clipboard.setPrimaryClip(ClipData.newPlainText("Invite Link", inviteLink))
+            toast("复制成功,快去邀请好友吧")
+            dismiss()
+        }
+    }
+
+    private fun initPosterShare() {
+        tvInviteCode.text = "邀请码:$inviteCode"
+
+        val bg = ivPosterBg.drawable?.toBitmap()
+        if (bg != null) {
+            ivPosterBg.setImageBitmap(bg)
+        } else {
+            ivPosterBg.setImageResource(R.drawable.invite_bg)
+        }
+
+        qrBitmap = generateQrCode(inviteLink, 1024, R.drawable.ic_toast_icon)
+        ivQr.setImageBitmap(qrBitmap)
+
+        btnSavePoster.setSafeListener {
+            val bitmap = createPosterBitmap()
+            if (bitmap == null) {
+                toast("保存失败")
+                return@setSafeListener
+            }
+            val uri = saveBitmapToGallery(context, bitmap, "Tianka_Invite_$inviteCode.png")
+            if (uri != null) {
+                toast("已保存到相册")
+                dismiss()
+            } else {
+                toast("保存失败")
+            }
+        }
+    }
+
+    private fun shareTextToPackage(packageName: String, text: String, notInstalledToast: String) {
+        val intent = Intent(Intent.ACTION_SEND).apply {
+            type = "text/plain"
+            putExtra(Intent.EXTRA_TEXT, text)
+            setPackage(packageName)
+        }
+        try {
+            context.startActivity(intent)
+            dismiss()
+        } catch (_: Exception) {
+            toast(notInstalledToast)
+        }
+    }
+
+    private fun startWeChatShare(isCircle: Boolean) {
+        val activity = context as? android.app.Activity ?: return
+        val icon = cfg.shareIcon.trim()
+        val title = cfg.shareTitle
+        val desc = cfg.shareDesc
+        val listener = object : ShareManager.ThreeListener {
+            override fun onResult() {
+                toast("分享成功")
+            }
+
+            override fun onError() {
+                toast("分享失败")
+            }
+
+            override fun onCancel() {
+                toast("分享取消")
+            }
+        }
+
+        if (icon.startsWith("http://") || icon.startsWith("https://")) {
+            if (isCircle) {
+                ShareManager.getConfig(context).startWxCircle(activity, icon, title, desc, inviteLink, listener)
+            } else {
+                ShareManager.getConfig(context).startWxShare(activity, icon, title, desc, inviteLink, listener)
+            }
+        } else {
+            if (isCircle) {
+                ShareManager.getConfig(context).startWxCircle(activity, R.drawable.ic_toast_icon, title, desc, inviteLink, listener)
+            } else {
+                ShareManager.getConfig(context).startWxShare(activity, R.drawable.ic_toast_icon, title, desc, inviteLink, listener)
+            }
+        }
+    }
+
+    private fun createPosterBitmap(): Bitmap? {
+        val width = 1080
+        val height = 1620
+        val bgBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.invite_bg) ?: return null
+        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(bitmap)
+
+        val srcRect = Rect(0, 0, bgBitmap.width, bgBitmap.height)
+        val scale = maxOf(width.toFloat() / bgBitmap.width, height.toFloat() / bgBitmap.height)
+        val scaledWidth = (bgBitmap.width * scale).toInt()
+        val scaledHeight = (bgBitmap.height * scale).toInt()
+        val left = (width - scaledWidth) / 2
+        val top = (height - scaledHeight) / 2
+        val destRect = Rect(left, top, left + scaledWidth, top + scaledHeight)
+        canvas.drawBitmap(bgBitmap, srcRect, destRect, null)
+
+        val paint = Paint().apply {
+            color = android.graphics.Color.WHITE
+            textAlign = Paint.Align.CENTER
+            isAntiAlias = true
+        }
+
+        paint.textSize = 100f
+        paint.typeface = Typeface.DEFAULT_BOLD
+        canvas.drawText(cfg.posterAppName, width / 2f, 180f, paint)
+
+        paint.textSize = 60f
+        paint.typeface = Typeface.DEFAULT
+        canvas.drawText(cfg.posterSubtitle, width / 2f, 280f, paint)
+
+        val qrSize = 760
+        val qr = qrBitmap ?: generateQrCode(inviteLink, 1024, R.drawable.ic_toast_icon)
+        if (qr != null) {
+            val qrLeft = (width - qrSize) / 2
+            val qrTop = height - 390 - qrSize
+
+            val qrBgPaint = Paint().apply { color = android.graphics.Color.WHITE; isAntiAlias = true }
+            val radius = 30f
+            val qrRectF = RectF(qrLeft.toFloat(), qrTop.toFloat(), (qrLeft + qrSize).toFloat(), (qrTop + qrSize).toFloat())
+            canvas.drawRoundRect(qrRectF, radius, radius, qrBgPaint)
+
+            val padding = 26
+            val qrDestRect = Rect(qrLeft + padding, qrTop + padding, qrLeft + qrSize - padding, qrTop + qrSize - padding)
+            canvas.drawBitmap(qr, null, qrDestRect, null)
+        }
+
+        paint.textSize = 70f
+        paint.typeface = Typeface.DEFAULT_BOLD
+        paint.color = android.graphics.Color.parseColor("#BDBDBD")
+        canvas.drawText("邀请码:$inviteCode", width / 2f, height - 150f, paint)
+
+        return bitmap
+    }
+
+    private fun generateQrCode(content: String, size: Int, logoResId: Int): Bitmap? {
+        return try {
+            val hints = EnumMap<EncodeHintType, Any>(EncodeHintType::class.java).apply {
+                put(EncodeHintType.CHARACTER_SET, "UTF-8")
+                put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H)
+                put(EncodeHintType.MARGIN, 1)
+            }
+            val bitMatrix: BitMatrix = MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, size, size, hints)
+            val pixels = IntArray(size * size)
+            for (y in 0 until size) {
+                val offset = y * size
+                for (x in 0 until size) {
+                    pixels[offset + x] = if (bitMatrix[x, y]) 0xFF000000.toInt() else 0xFFFFFFFF.toInt()
+                }
+            }
+            val qrBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
+            qrBitmap.setPixels(pixels, 0, size, 0, 0, size, size)
+
+            val logo = BitmapFactory.decodeResource(context.resources, logoResId)
+            if (logo != null) addLogo(qrBitmap, logo) else qrBitmap
+        } catch (_: Exception) {
+            null
+        }
+    }
+
+    private fun addLogo(qrBitmap: Bitmap, logo: Bitmap): Bitmap {
+        val srcWidth = qrBitmap.width
+        val srcHeight = qrBitmap.height
+        val logoWidth = logo.width
+        val logoHeight = logo.height
+
+        val scaleFactor = srcWidth * 1.0f / 5 / logoWidth
+        var bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888)
+        try {
+            val canvas = Canvas(bitmap)
+            canvas.drawBitmap(qrBitmap, 0f, 0f, null)
+            canvas.scale(scaleFactor, scaleFactor, srcWidth / 2f, srcHeight / 2f)
+            canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2f, (srcHeight - logoHeight) / 2f, null)
+            canvas.save()
+            canvas.restore()
+        } catch (_: Exception) {
+            bitmap = qrBitmap
+        }
+        return bitmap
+    }
+
+    private fun saveBitmapToGallery(context: Context, bitmap: Bitmap, displayName: String): Uri? {
+        val resolver = context.contentResolver
+        val values = ContentValues().apply {
+            put(MediaStore.Images.Media.DISPLAY_NAME, displayName)
+            put(MediaStore.Images.Media.MIME_TYPE, "image/png")
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Tianka")
+                put(MediaStore.Images.Media.IS_PENDING, 1)
+            }
+        }
+        val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) ?: return null
+        return try {
+            resolver.openOutputStream(uri)?.use { out ->
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
+            }
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                val done = ContentValues().apply { put(MediaStore.Images.Media.IS_PENDING, 0) }
+                resolver.update(uri, done, null, null)
+            }
+            uri
+        } catch (_: Exception) {
+            try {
+                resolver.delete(uri, null, null)
+            } catch (_: Exception) {
+            }
+            null
+        }
+    }
+}

+ 7 - 0
module_me/src/main/java/com/mita/module_me/view/invite/LoadMoreRowItem.kt

@@ -0,0 +1,7 @@
+package com.mita.module_me.view.invite
+
+data class LoadMoreRowItem(
+    val isLoading: Boolean,
+    val isNoMore: Boolean = false
+)
+

+ 26 - 0
module_me/src/main/java/com/mita/module_me/view/invite/LoadMoreVH.kt

@@ -0,0 +1,26 @@
+package com.mita.module_me.view.invite
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.mita.module_me.R
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+import kotlinx.android.synthetic.main.module_me_item_load_more.view.*
+
+class LoadMoreVH(parent: ViewGroup) : BaseViewHolder<LoadMoreRowItem>(
+    LayoutInflater.from(parent.context).inflate(R.layout.module_me_item_load_more, parent, false)
+) {
+    override fun setViewData(context: android.content.Context, item: LoadMoreRowItem, position: Int, count: Int) {
+        if (item.isNoMore) {
+            itemView.progressBar.visibility = View.GONE
+            itemView.tvText.text = "没有更多了"
+        } else if (item.isLoading) {
+            itemView.progressBar.visibility = View.VISIBLE
+            itemView.tvText.text = "加载中..."
+        } else {
+            itemView.progressBar.visibility = View.GONE
+            itemView.tvText.text = ""
+        }
+    }
+}
+

+ 40 - 0
module_me/src/main/java/com/mita/module_me/view/invite/ShareInviteRepository.kt

@@ -0,0 +1,40 @@
+package com.mita.module_me.view.invite
+
+import com.mita.module_me.api.ShareInviteService
+import com.mita.module_me.model.ShareExchangeBody
+import com.yc.baselibrary.view.base.BaseRepository
+
+class ShareInviteRepository : BaseRepository() {
+    private val shareInviteService: ShareInviteService by lazyRetrofit()
+
+    suspend fun getInviteRelationship() = shareInviteService.getInviteRelationship()
+
+    suspend fun getShareSummary() = shareInviteService.getShareSummary()
+
+    suspend fun getInvitedUsers(page: Int, pageSize: Int) =
+        shareInviteService.getInvitedUsers(page = page, pageSize = pageSize)
+
+    suspend fun getUserProfile(userId: String) = shareInviteService.getUserProfile(userId)
+
+    suspend fun createInviteRelationship(body: com.mita.module_me.model.ShareInviteBindBody) =
+        shareInviteService.createInviteRelationship(body)
+
+    suspend fun getBillList(userId: String, page: Int, pageSize: Int, type: String) =
+        shareInviteService.getBillList(userId = userId, page = page, pageSize = pageSize, type = type)
+
+    suspend fun getPointFlowList(userId: String, page: Int, pageSize: Int) =
+        shareInviteService.getPointFlowList(userId = userId, page = page, pageSize = pageSize)
+
+    suspend fun getBankCard(userId: String) = shareInviteService.getBankCard(userId)
+
+    suspend fun bindBankCard(body: com.mita.module_me.model.ShareBindBankCardBody) =
+        shareInviteService.bindBankCard(body)
+
+    suspend fun withdraw(body: com.mita.module_me.model.ShareWithdrawBody) =
+        shareInviteService.withdraw(body)
+
+    suspend fun exchange(body: ShareExchangeBody) = shareInviteService.exchange(body)
+
+    suspend fun getInviteShareConfig(platform: String = "all") =
+        shareInviteService.getInviteShareConfig(platform)
+}

+ 139 - 0
module_me/src/main/java/com/mita/module_me/view/invite/WithdrawActivity.kt

@@ -0,0 +1,139 @@
+package com.mita.module_me.view.invite
+
+import android.app.Activity
+import android.content.Intent
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import com.mita.module_me.R
+import com.yc.baselibrary.ext.toast
+import com.yc.baselibrary.view.base.BaseActivity
+import kotlinx.android.synthetic.main.module_me_activity_withdraw.*
+import java.text.NumberFormat
+
+class WithdrawActivity : BaseActivity<WithdrawViewModel>() {
+
+    private companion object {
+        private const val REQUEST_ADD_BANK_CARD = 2001
+    }
+
+    override fun initView() {
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
+            val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
+            clTopBar.setPadding(
+                clTopBar.paddingLeft,
+                top,
+                clTopBar.paddingRight,
+                clTopBar.paddingBottom
+            )
+            insets
+        }
+        ViewCompat.requestApplyInsets(rootLayout)
+
+        ivBack.setOnClickListener { finish() }
+
+        llBankCard.setOnClickListener {
+            startActivityForResult(Intent(this, AddBankCardActivity::class.java), REQUEST_ADD_BANK_CARD)
+        }
+
+        btnAll.setOnClickListener {
+            val points = viewModel.balancePoints.value ?: 0.0
+            val maxAmountYuan = (points / 10000.0).toInt()
+            etAmount.setText(if (maxAmountYuan > 0) maxAmountYuan.toString() else "0")
+            etAmount.setSelection(etAmount.text?.length ?: 0)
+        }
+
+        btnConfirm.setOnClickListener {
+            val card = viewModel.bankCard.value
+            if (card == null) {
+                toast("请先添加银行卡")
+                startActivityForResult(Intent(this, AddBankCardActivity::class.java), REQUEST_ADD_BANK_CARD)
+                return@setOnClickListener
+            }
+
+            val amountYuan = etAmount.text?.toString()?.trim()?.toLongOrNull() ?: 0L
+            if (amountYuan <= 0) {
+                toast("请输入提现金额")
+                return@setOnClickListener
+            }
+            if (amountYuan % 100L != 0L) {
+                toast("提现金额必须是100的整数倍")
+                return@setOnClickListener
+            }
+
+            val pointsRequired = amountYuan * 10000.0
+            val points = viewModel.balancePoints.value ?: 0.0
+            if (pointsRequired > points) {
+                toast("余额不足")
+                return@setOnClickListener
+            }
+
+            val amountFen = amountYuan * 100L
+            viewModel.submitWithdraw(amountFen)
+        }
+    }
+
+    override fun observe() {
+        viewModel.balancePoints.observe(this) { points ->
+            val nf = NumberFormat.getIntegerInstance()
+            val availableYuan = (points / 10000.0).toInt()
+            tvAvailable.text = "可提现金额: ${availableYuan}元"
+            tvPointsLeft.text = "剩余积分: ${nf.format(points.toLong())}"
+        }
+
+        viewModel.bankCard.observe(this) { card ->
+            if (card == null) {
+                tvBankName.text = "请添加银行卡"
+                tvBankNo.visibility = View.GONE
+                tvAddHint.visibility = View.VISIBLE
+            } else {
+                tvBankName.text = card.bankName.orEmpty()
+                val no = card.cardNumber.orEmpty()
+                val masked = if (no.length > 4) {
+                    "**** **** **** " + no.takeLast(4)
+                } else {
+                    no
+                }
+                tvBankNo.text = masked
+                tvBankNo.visibility = View.VISIBLE
+                tvAddHint.visibility = View.GONE
+            }
+        }
+
+        viewModel.submitSuccess.observe(this) { success ->
+            if (success == true) {
+                setResult(Activity.RESULT_OK)
+                toast("申请提交成功")
+                finish()
+            }
+        }
+
+        viewModel.errorMsg.observe(this) { msg ->
+            if (!msg.isNullOrBlank()) toast(msg)
+        }
+
+        viewModel.stateModel.observe(this) { type ->
+            val loading = type == 1
+            btnConfirm.isEnabled = !loading
+            btnConfirm.alpha = if (loading) 0.7f else 1f
+        }
+    }
+
+    override fun ready() {
+        viewModel.loadData()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        viewModel.refreshBankCard()
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (requestCode == REQUEST_ADD_BANK_CARD && resultCode == RESULT_OK) {
+            viewModel.refreshBankCard()
+        }
+    }
+
+    override fun getLayoutId() = R.layout.module_me_activity_withdraw
+}

+ 99 - 0
module_me/src/main/java/com/mita/module_me/view/invite/WithdrawViewModel.kt

@@ -0,0 +1,99 @@
+package com.mita.module_me.view.invite
+
+import android.app.Application
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.SavedStateHandle
+import com.mita.module_me.model.ShareBankCard
+import com.mita.module_me.model.ShareWithdrawBody
+import com.yc.baselibrary.net.exception.Errors
+import com.yc.baselibrary.net.exception.LoadingType
+import com.yc.baselibrary.view.base.BaseVm
+import com.yc.module_base.LiveSession
+
+class WithdrawViewModel(
+    app: Application,
+    map: MutableMap<String, Any?>,
+    savedStateHandle: SavedStateHandle
+) : BaseVm(app, map, savedStateHandle) {
+
+    private val shareInviteRepository: ShareInviteRepository by lazy { ShareInviteRepository() }
+
+    val balancePoints = MutableLiveData<Double>()
+    val bankCard = MutableLiveData<ShareBankCard?>()
+    val submitSuccess = MutableLiveData<Boolean>()
+    val errorMsg = MutableLiveData<String>()
+
+    fun loadData() {
+        requestMix(
+            block = {
+                val summaryRes = shareInviteRepository.getShareSummary()
+                if (summaryRes.code != 200) {
+                    throw Errors.ErrorException(summaryRes.code ?: -1, summaryRes.message ?: "获取积分失败")
+                }
+                val userId = LiveSession.getUserId().toString()
+                val bankRes = shareInviteRepository.getBankCard(userId)
+                if (bankRes.code != 200) {
+                    throw Errors.ErrorException(bankRes.code ?: -1, bankRes.message ?: "获取银行卡失败")
+                }
+                Pair(summaryRes.data?.balance ?: 0.0, bankRes.data)
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success {
+                    balancePoints.value = it.first
+                    bankCard.value = it.second
+                }
+                error {
+                    Log.e("WithdrawViewModel", "loadData error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "加载失败"
+                }
+            }
+        )
+    }
+
+    fun refreshBankCard() {
+        requestMix(
+            block = {
+                val userId = LiveSession.getUserId().toString()
+                val res = shareInviteRepository.getBankCard(userId)
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "获取银行卡失败")
+                }
+                res.data
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success { bankCard.value = it }
+                error {
+                    Log.e("WithdrawViewModel", "refreshBankCard error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "获取银行卡失败"
+                }
+            }
+        )
+    }
+
+    fun submitWithdraw(amountFen: Long) {
+        requestMix(
+            block = {
+                val userId = LiveSession.getUserId()
+                val res = shareInviteRepository.withdraw(
+                    ShareWithdrawBody(userId = userId, amount = amountFen)
+                )
+                if (res.code != 200) {
+                    throw Errors.ErrorException(res.code ?: -1, res.message ?: "提交失败")
+                }
+                true
+            },
+            build = {
+                setLoadingType(LoadingType.SIMPLE)
+                success { submitSuccess.value = true }
+                error {
+                    Log.e("WithdrawViewModel", "submitWithdraw error: ${it.message}", it)
+                    errorMsg.value = it.message ?: "提交失败"
+                }
+            }
+        )
+    }
+}
+

+ 86 - 0
module_me/src/main/java/com/mita/module_me/view/widget/TwoLineImageView.java

@@ -0,0 +1,86 @@
+package com.mita.module_me.view.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+import com.mita.module_me.R;
+
+public class TwoLineImageView extends RelativeLayout {
+
+    private ImageView ivIcon;
+    private TextView tvBottomText;
+    private String bottomText;
+    private int iconResId = -1;
+
+    public TwoLineImageView(Context context) {
+        this(context, null);
+    }
+
+    public TwoLineImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TwoLineImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initAttrs(context, attrs);
+        initView(context);
+    }
+
+    private void initAttrs(Context context, AttributeSet attrs) {
+        if (attrs == null) {
+            return;
+        }
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TwoLineImageView);
+        iconResId = typedArray.getResourceId(R.styleable.TwoLineImageView_lineIcon, -1);
+        bottomText = typedArray.getString(R.styleable.TwoLineImageView_lineText);
+        typedArray.recycle();
+    }
+
+    private void initView(Context context) {
+        View.inflate(context, R.layout.module_me_layout_tew_line_image_item, this);
+        tvBottomText = findViewById(R.id.tvText);
+        ivIcon = findViewById(R.id.ivIcon);
+        if (bottomText != null && bottomText.length() > 0) {
+            tvBottomText.setText(bottomText);
+        }
+        if (iconResId != -1) {
+            ivIcon.setImageResource(iconResId);
+        }
+    }
+
+    public void setContent(String content) {
+        if (tvBottomText != null) {
+            tvBottomText.setText(content);
+        }
+    }
+
+    public void setImage(String url) {
+        if (ivIcon == null) {
+            return;
+        }
+        int sizePx = dpToPx(40);
+        Glide.with(getContext())
+                .load(url)
+                .apply(new RequestOptions().override(sizePx, sizePx))
+                .into(ivIcon);
+    }
+
+    public void setImageResource(int resourceId) {
+        if (ivIcon != null) {
+            ivIcon.setImageResource(resourceId);
+        }
+    }
+
+    private int dpToPx(float dp) {
+        float density = getResources().getDisplayMetrics().density;
+        return (int) (dp * density + 0.5f);
+    }
+}
+

+ 0 - 66
module_me/src/main/java/com/mita/module_me/view/widget/TwoLineImageView.kt

@@ -1,66 +0,0 @@
-package com.mita.module_me.view.widget
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.widget.ImageView
-import android.widget.RelativeLayout
-import android.widget.TextView
-import com.mita.module_me.R
-import com.xueyu.kotlinextlibrary.dp
-import com.yc.baselibrary.ext.loadImage
-
-/** Created by yc on 2021/2/21
- **/
-class TwoLineImageView @JvmOverloads constructor(
-    private val mContext: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0
-) : RelativeLayout(mContext, attrs, defStyleAttr) {
-
-    private lateinit var ivIcon: ImageView
-    private lateinit var tvBottomText: TextView
-    private var bottomText: String? = null
-    private var icon: Int? = null
-
-    init {
-        val attr = context.obtainStyledAttributes(attrs, R.styleable.TwoLineImageView)
-        icon = attr.getResourceId(R.styleable.TwoLineImageView_lineIcon, -1)
-        bottomText = attr.getString(R.styleable.TwoLineImageView_lineText)
-        attr.recycle()
-    }
-
-    private fun initView() {
-        //加载布局
-        View.inflate(mContext, R.layout.module_me_layout_tew_line_image_item, this)
-
-        tvBottomText = findViewById(R.id.tvText)
-        ivIcon = findViewById(R.id.ivIcon)
-        if (!bottomText.isNullOrEmpty()) {
-            tvBottomText.text = bottomText
-        }
-        if (icon != -1) {
-            icon?.let { ivIcon.setImageResource(it) }
-        }
-    }
-
-    init {
-        initView()
-    }
-
-    fun setContent(content: String) {
-        tvBottomText.text = content
-    }
-
-    fun setImage(url: String) {
-        ivIcon.loadImage(
-            width = 40.dp,
-            height = 40.dp,
-            url = url
-        )
-    }
-
-    fun setImageResource(resourceId: Int) {
-        ivIcon.setImageResource(resourceId)
-    }
-}

BIN
module_me/src/main/res/drawable/banner_scroll_bg.png


BIN
module_me/src/main/res/drawable/copy_link.png


+ 5 - 0
module_me/src/main/res/drawable/debug_stroke_green.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#00000000" />
+    <stroke android:width="1dp" android:color="#00FF00" />
+</shape>

+ 9 - 0
module_me/src/main/res/drawable/ic_arrow_right_gray.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#999999"
+        android:pathData="M8.59,16.59L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.59Z" />
+</vector>

BIN
module_me/src/main/res/drawable/ic_empty_data.png


+ 170 - 0
module_me/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 30 - 0
module_me/src/main/res/drawable/ic_launcher_foreground.xml

@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>

BIN
module_me/src/main/res/drawable/ic_toast_icon.png


BIN
module_me/src/main/res/drawable/invite_bg.png


BIN
module_me/src/main/res/drawable/invite_binding_bottom_bg.png


BIN
module_me/src/main/res/drawable/invite_binding_card_bg.png


+ 8 - 0
module_me/src/main/res/drawable/module_me_bg_rule_edge.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <corners
+        android:bottomLeftRadius="15dp"
+        android:topLeftRadius="15dp" />
+    <solid android:color="#33000000" />
+</shape>
+

+ 6 - 0
module_me/src/main/res/drawable/module_me_bg_rules_card.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <corners android:radius="12dp" />
+    <solid android:color="#FFF9F5" />
+</shape>
+

+ 6 - 0
module_me/src/main/res/drawable/module_me_bg_topbar_btn_dark.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <corners android:radius="20dp" />
+    <solid android:color="#1A000000" />
+</shape>
+

BIN
module_me/src/main/res/drawable/qq.png


BIN
module_me/src/main/res/drawable/save_to_photo_album.png


+ 9 - 0
module_me/src/main/res/drawable/sp_circle_solid_ff6d00.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="#FFFF6D00" />
+    <stroke
+        android:width="1dp"
+        android:color="#FFFF6D00" />
+</shape>
+

+ 9 - 0
module_me/src/main/res/drawable/sp_circle_stroke_999.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="@android:color/transparent" />
+    <stroke
+        android:width="1dp"
+        android:color="#999999" />
+</shape>
+

+ 6 - 0
module_me/src/main/res/drawable/sp_r10_stroke_f56a05_white.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="10dp" />
+    <solid android:color="@color/white" />
+    <stroke android:width="2dp" android:color="#F56A05" />
+</shape>

+ 10 - 0
module_me/src/main/res/drawable/sp_r12_dash_white_60.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="12dp" />
+    <solid android:color="#00000000" />
+    <stroke
+        android:width="1dp"
+        android:color="#99FFFFFF"
+        android:dashWidth="6dp"
+        android:dashGap="4dp" />
+</shape>

+ 5 - 0
module_me/src/main/res/drawable/sp_r12_ff5722.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="12dp" />
+    <solid android:color="#FF5722" />
+</shape>

+ 5 - 0
module_me/src/main/res/drawable/sp_r12_white_30.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="12dp" />
+    <solid android:color="#33FFFFFF" />
+</shape>

+ 5 - 0
module_me/src/main/res/drawable/sp_r16_ff5722.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="16dp" />
+    <solid android:color="#FF5722" />
+</shape>

+ 6 - 0
module_me/src/main/res/drawable/sp_r16_stroke_ff5722_white.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="16dp" />
+    <solid android:color="@color/white" />
+    <stroke android:width="1dp" android:color="#FF5722" />
+</shape>

+ 9 - 0
module_me/src/main/res/drawable/sp_r16_stroke_ff9800.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="16dp" />
+    <solid android:color="@android:color/transparent" />
+    <stroke
+        android:width="1dp"
+        android:color="#FFFF9800" />
+</shape>
+

+ 5 - 0
module_me/src/main/res/drawable/sp_r20_f56a05.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="20dp" />
+    <solid android:color="#F56A05" />
+</shape>

+ 6 - 0
module_me/src/main/res/drawable/sp_r20_stroke_f56a05_white.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="20dp" />
+    <solid android:color="@color/white" />
+    <stroke android:width="1dp" android:color="#F56A05" />
+</shape>

+ 5 - 0
module_me/src/main/res/drawable/sp_r20_white.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="20dp" />
+    <solid android:color="#FFFFFFFF" />
+</shape>

+ 5 - 0
module_me/src/main/res/drawable/sp_r24_ff9800.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="24dp" />
+    <solid android:color="#FFFF9800" />
+</shape>

+ 9 - 0
module_me/src/main/res/drawable/sp_r24_gradient_ff9800_ff5722.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="24dp" />
+    <gradient
+        android:angle="0"
+        android:startColor="#FFFF9800"
+        android:endColor="#FFFF5722" />
+</shape>
+

+ 6 - 0
module_me/src/main/res/drawable/sp_r4_solid_e3f2fd.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="4dp" />
+    <solid android:color="#E3F2FD" />
+</shape>
+

+ 5 - 0
module_me/src/main/res/drawable/sp_r6_solid_f56a05.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="4dp" />
+    <solid android:color="#FFF56A05" />
+</shape>

+ 6 - 0
module_me/src/main/res/drawable/sp_r6_stroke_f56a05.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="4dp" />
+    <solid android:color="#00000000" />
+    <stroke android:width="1dp" android:color="#FFF56A05" />
+</shape>

+ 5 - 0
module_me/src/main/res/drawable/sp_red_dot_6.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="#FFFF0000" />
+</shape>

BIN
module_me/src/main/res/drawable/weixin.png


BIN
module_me/src/main/res/drawable/winxin_comment.png


+ 76 - 0
module_me/src/main/res/layout/module_me_activity_add_bank_card.xml

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#F5F5F5">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/clTopBar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="@color/white"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="12dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <ImageView
+            android:id="@+id/ivBack"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:src="@drawable/ic_back"
+            android:tint="#333333"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="添加银行卡"
+            android:textColor="#333333"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="12dp"
+        android:background="@color/white"
+        android:orientation="vertical"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/clTopBar">
+
+        <include layout="@layout/module_me_row_bank_input_name" />
+        <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#EEEEEE" />
+        <include layout="@layout/module_me_row_bank_input_id" />
+        <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#EEEEEE" />
+        <include layout="@layout/module_me_row_bank_input_card" />
+        <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#EEEEEE" />
+        <include layout="@layout/module_me_row_bank_input_bank" />
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/btnSave"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:layout_margin="16dp"
+        android:background="@drawable/sp_r24_gradient_ff9800_ff5722"
+        android:text="保存信息"
+        android:textColor="@color/white"
+        android:textSize="16sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 163 - 0
module_me/src/main/res/layout/module_me_activity_exchange.xml

@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/ivBg"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:scaleType="centerCrop"
+        android:src="@drawable/invite_bg"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/clTopBar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <ImageView
+            android:id="@+id/ivBack"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_marginStart="16dp"
+            android:background="@drawable/module_me_bg_topbar_btn_dark"
+            android:padding="6dp"
+            android:src="@drawable/ic_back"
+            android:tint="@color/white"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/tvTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="兑换金币"
+            android:textColor="@color/white"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="@id/ivBack"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="@id/ivBack" />
+
+        <!-- <TextView
+            android:id="@+id/tvRule"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="16dp"
+            android:background="@drawable/sp_r12_white_30"
+            android:paddingHorizontal="12dp"
+            android:paddingVertical="4dp"
+            android:text="规则"
+            android:textColor="@color/white"
+            android:textSize="12sp"
+            app:layout_constraintBottom_toBottomOf="@id/tvTitle"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/tvTitle" /> -->
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <LinearLayout
+        android:id="@+id/card"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="24dp"
+        android:layout_marginTop="44dp"
+        android:background="@drawable/sp_r14_white"
+        android:gravity="center_horizontal"
+        android:orientation="vertical"
+        android:padding="24dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/clTopBar">
+
+        <TextView
+            android:id="@+id/tvCardTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="兑换金币"
+            android:textColor="#D84315"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="¥"
+                android:textColor="#333333"
+                android:textSize="24sp"
+                android:textStyle="bold" />
+
+            <EditText
+                android:id="@+id/etAmount"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="12dp"
+                android:layout_weight="1"
+                android:background="@android:color/transparent"
+                android:hint="请输入兑换金额"
+                android:inputType="numberDecimal"
+                android:textColor="#333333"
+                android:textSize="14sp" />
+
+            <Button
+                android:id="@+id/btnAll"
+                android:layout_width="56dp"
+                android:layout_height="30dp"
+                android:background="@drawable/sp_r16_stroke_ff9800"
+                android:text="全部"
+                android:textColor="#FF9800"
+                android:textSize="12sp" />
+        </LinearLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginTop="8dp"
+            android:background="#EEEEEE" />
+
+        <TextView
+            android:id="@+id/tvBalance"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:text="积分:0(1积分=1金币)"
+            android:textColor="#999999"
+            android:textSize="12sp" />
+
+        <Button
+            android:id="@+id/btnExchange"
+            android:layout_width="match_parent"
+            android:layout_height="48dp"
+            android:layout_marginTop="28dp"
+            android:background="@drawable/sp_r24_gradient_ff9800_ff5722"
+            android:text="兑换"
+            android:textColor="@color/white"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+        <ProgressBar
+            android:id="@+id/progressBar"
+            style="?android:attr/progressBarStyleSmall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:visibility="gone" />
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 226 - 0
module_me/src/main/res/layout/module_me_activity_invite_binding.xml

@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/invite_bg">
+
+    <ImageView
+        android:id="@+id/ivBottomOverlay"
+        android:layout_width="match_parent"
+        android:layout_height="220dp"
+        android:src="@drawable/invite_binding_bottom_bg"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <ImageView
+        android:id="@+id/ivBack"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:padding="12dp"
+        android:src="@drawable/ic_back"
+        android:tint="@color/white"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/tvTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="好友绑定"
+        android:textColor="@color/white"
+        android:textSize="18sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="@id/ivBack"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@id/ivBack" />
+
+    <!-- Central Card -->
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/clCard"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="24dp"
+        android:layout_marginTop="120dp"
+        android:background="@drawable/sp_r10_white"
+        android:padding="24dp"
+        app:layout_constraintTop_toBottomOf="@id/tvTitle">
+
+        <!-- Input State -->
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/clInputState"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="visible"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <TextView
+                android:id="@+id/tvCardTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="好友绑定"
+                android:textColor="#E91E63"
+                android:textSize="20sp"
+                android:textStyle="bold"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/tvCardDesc"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
+                android:gravity="center"
+                android:lineSpacingExtra="4dp"
+                android:text="绑定成功后,系统将赠送该好友您充值金额的4%同等价值积分作为奖励。积分可提现,绑定请慎重。"
+                android:textColor="#A16850"
+                android:textSize="14sp"
+                app:layout_constraintTop_toBottomOf="@id/tvCardTitle" />
+
+            <EditText
+                android:id="@+id/etInviteCode"
+                android:layout_width="178dp"
+                android:layout_height="36dp"
+                android:layout_marginTop="24dp"
+                android:background="@drawable/sp_r10_stroke_f56a05_white"
+                android:gravity="center"
+                android:hint="输入邀请码进行绑定"
+                android:inputType="number"
+                android:maxLength="10"
+                android:paddingHorizontal="8dp"
+                android:textColor="@color/black"
+                android:textColorHint="#CCCCCC"
+                android:textSize="16sp"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/tvCardDesc" />
+
+            <Button
+                android:id="@+id/btnNext"
+                android:layout_width="140dp"
+                android:layout_height="48dp"
+                android:layout_marginTop="32dp"
+                android:background="@drawable/sp_r24_ff9800"
+                android:text="下一步"
+                android:textColor="@color/white"
+                android:textSize="16sp"
+                android:textStyle="bold"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/etInviteCode" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <!-- Confirm State -->
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/clConfirmState"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <TextView
+                android:id="@+id/tvInviterName"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:text="绑定人:"
+                android:textColor="#A16850"
+                android:textSize="16sp"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/tvInviterId"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:text="绑定ID:"
+                android:textColor="#A16850"
+                android:textSize="16sp"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/tvInviterName" />
+
+            <TextView
+                android:id="@+id/tvSuccessHint"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="30dp"
+                android:text="恭喜,绑定成功!"
+                android:textColor="#E91E63"
+                android:textSize="24sp"
+                android:textStyle="bold"
+                android:visibility="gone"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/tvInviterId" />
+
+            <LinearLayout
+                android:id="@+id/llActionButtons"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="30dp"
+                android:orientation="horizontal"
+                app:layout_constraintTop_toBottomOf="@id/tvInviterId">
+
+                <Button
+                    android:id="@+id/btnCancel"
+                    android:layout_width="0dp"
+                    android:layout_height="40dp"
+                    android:layout_weight="1"
+                    android:layout_marginEnd="8dp"
+                    android:background="@drawable/sp_r20_stroke_f56a05_white"
+                    android:text="取消"
+                    android:textColor="#F56A05"
+                    android:textSize="14sp" />
+
+                <Button
+                    android:id="@+id/btnConfirm"
+                    android:layout_width="0dp"
+                    android:layout_height="40dp"
+                    android:layout_weight="1"
+                    android:layout_marginStart="8dp"
+                    android:background="@drawable/sp_r20_f56a05"
+                    android:text="确认绑定"
+                    android:textColor="@color/white"
+                    android:textSize="14sp" />
+            </LinearLayout>
+
+            <Button
+                android:id="@+id/btnBack"
+                android:layout_width="match_parent"
+                android:layout_height="40dp"
+                android:layout_marginTop="30dp"
+                android:background="@drawable/sp_r20_f56a05"
+                android:text="返回"
+                android:textColor="@color/white"
+                android:textSize="14sp"
+                android:visibility="gone"
+                app:layout_constraintTop_toBottomOf="@id/tvInviterId"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <ProgressBar
+        android:id="@+id/progressBar"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 654 - 0
module_me/src/main/res/layout/module_me_activity_invite_earnings.xml

@@ -0,0 +1,654 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/ivBg"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:scaleType="centerCrop"
+        android:src="@drawable/invite_bg"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/clTopBar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:paddingBottom="0dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <ImageView
+            android:id="@+id/ivBack"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_marginStart="16dp"
+            android:background="@drawable/module_me_bg_topbar_btn_dark"
+            android:padding="6dp"
+            android:src="@drawable/ic_back"
+            android:tint="@color/white"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/tvTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="邀请收益"
+            android:textColor="@color/white"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="@id/ivBack"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="@id/ivBack" />
+
+        <TextView
+            android:id="@+id/tvRule"
+            android:layout_width="56dp"
+            android:layout_height="29dp"
+            android:layout_marginEnd="0dp"
+            android:background="@drawable/module_me_bg_rule_edge"
+            android:gravity="center"
+            android:text="规则"
+            android:textColor="@color/white"
+            android:textSize="12sp"
+            app:layout_constraintBottom_toBottomOf="@id/tvTitle"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/tvTitle" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.core.widget.NestedScrollView
+        android:id="@+id/scrollView"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:fillViewport="true"
+        app:layout_constraintBottom_toTopOf="@+id/btnInviteNow"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/clTopBar">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingBottom="16dp">
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="16dp"
+                android:layout_marginTop="0dp"
+                android:background="@drawable/sp_r12_dash_white_60"
+                android:padding="0dp">
+
+                <TextView
+                    android:id="@+id/tvMoreInvite"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginBottom="4dp"
+                    android:background="@drawable/sp_r10_stroke_f56a05_white"
+                    android:paddingHorizontal="10dp"
+                    android:paddingVertical="3dp"
+                    android:text="多邀多得"
+                    android:textColor="#F56A05"
+                    android:textSize="12sp"
+                    app:layout_constraintBottom_toTopOf="@id/tvHeaderTitle"
+                    app:layout_constraintStart_toStartOf="@id/tvHeaderTitle" />
+
+                <TextView
+                    android:id="@+id/tvHeaderTitle"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="30dp"
+                    android:layout_marginBottom="8dp"
+                    android:gravity="center"
+                    android:text="邀请好友领取丰厚现金"
+                    android:textColor="@color/white"
+                    android:textSize="22sp"
+                    android:textStyle="bold"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <FrameLayout
+                    android:id="@+id/llReward"
+                    android:layout_width="0dp"
+                    android:layout_height="100dp"
+                    android:layout_marginTop="0dp"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/tvHeaderTitle">
+
+                    <ImageView
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:scaleType="fitXY"
+                        android:src="@drawable/banner_scroll_bg" />
+
+                    <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"
+                        
+                        android:gravity="center"
+                        android:orientation="vertical"
+                        android:paddingTop="0dp"
+                        android:paddingBottom="18dp"
+                        android:paddingHorizontal="12dp">
+
+                        <LinearLayout
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:gravity="center"
+                            android:orientation="horizontal">
+
+                            <TextView
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="邀请的一级用户可获得充值收益"
+                                android:textColor="#A16850"
+                                android:textSize="12sp" />
+
+                            <TextView
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginStart="4dp"
+                                android:text="4%"
+                                android:textColor="#FF3B30"
+                                android:textSize="14sp"
+                                android:textStyle="bold" />
+                        </LinearLayout>
+
+                        <LinearLayout
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="6dp"
+                            android:gravity="center"
+                            android:orientation="horizontal">
+
+                            <TextView
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="一级用户邀请二级用户可获得充值收益"
+                                android:textColor="#A16850"
+                                android:textSize="12sp" />
+
+                            <TextView
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginStart="4dp"
+                                android:text="1%"
+                                android:textColor="#FF3B30"
+                                android:textSize="14sp"
+                                android:textStyle="bold" />
+                        </LinearLayout>
+                    </LinearLayout>
+                </FrameLayout>
+
+                <LinearLayout
+                    android:id="@+id/llInviteCode"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="0dp"
+                    android:gravity="center"
+                    android:orientation="horizontal"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/llReward">
+
+                    <TextView
+                        android:id="@+id/tvInviteCodeLabel"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="我的专属邀请码:"
+                        android:textColor="@color/white"
+                        android:textSize="14sp" />
+
+                    <TextView
+                        android:id="@+id/tvInviteCode"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="6dp"
+                        android:text=""
+                        android:textColor="@color/white"
+                        android:textSize="16sp"
+                        android:textStyle="bold"/>
+
+                    <Button
+                        android:id="@+id/btnCopy"
+                        android:layout_width="48dp"
+                        android:layout_height="24dp"
+                        android:layout_marginStart="10dp"
+                        android:background="@drawable/sp_r12_white_30"
+                        android:text="复制"
+                        android:textColor="@color/white"
+                        android:textSize="12sp" />
+                </LinearLayout>
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:id="@+id/clBinding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="16dp"
+                android:layout_marginTop="12dp"
+                android:background="@drawable/sp_r10_white"
+                android:padding="16dp">
+
+                <TextView
+                    android:id="@+id/tvBindingTitle"
+                    android:layout_width="wrap_content"
+                    android:layout_height="20dp"
+                    android:layout_marginStart="17dp"
+                    android:layout_marginTop="8dp"
+                    android:text="绑定关系"
+                    android:textColor="#FF5E2D11"
+                    android:textSize="14sp"
+                    android:textFontWeight="400"
+                    android:fontFamily="@font/app_font"
+                    android:gravity="start|top"
+                    android:alpha="1"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:id="@+id/tvBindingStatus"
+                    android:layout_width="wrap_content"
+                    android:layout_height="20dp"
+                    android:layout_marginTop="8dp"
+                    android:textColor="#FFAB8E7B"
+                    android:textSize="14sp"
+                    android:textFontWeight="400"
+                    android:fontFamily="@font/app_font"
+                    android:gravity="start|top"
+                    android:alpha="1"
+                    app:layout_constraintEnd_toStartOf="@+id/vBindDot"
+                    app:layout_constraintTop_toTopOf="parent"
+                    android:text="未绑定" />
+
+                <View
+                    android:id="@+id/vBindDot"
+                    android:layout_width="6dp"
+                    android:layout_height="6dp"
+                    android:layout_marginStart="4dp"
+                    android:layout_marginEnd="8dp"
+                    android:alpha="1"
+                    android:background="@drawable/sp_red_dot_6"
+                    app:layout_constraintBottom_toBottomOf="@id/tvBindingStatus"
+                    app:layout_constraintEnd_toStartOf="@+id/ivBindArrow"
+                    app:layout_constraintTop_toTopOf="@id/tvBindingStatus" />
+
+                <ImageView
+                    android:id="@+id/ivBindArrow"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:src="@drawable/ic_arrow_right_gray"
+                    app:layout_constraintBottom_toBottomOf="@id/tvBindingStatus"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintTop_toTopOf="@id/tvBindingStatus" />
+
+                <TextView
+                    android:id="@+id/tvBoundInviter"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="10dp"
+                    android:textColor="#FF5E2D11"
+                    android:textSize="13sp"
+                    android:visibility="gone"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/tvBindingTitle"
+                    android:text="邀请人:" />
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:id="@+id/clEarnings"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="16dp"
+                android:layout_marginTop="12dp"
+                android:background="@drawable/sp_r10_white"
+                android:padding="16dp">
+
+                <TextView
+                    android:id="@+id/tvPointsLabel"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/sp_r20_white"
+                    android:elevation="2dp"
+                    android:paddingHorizontal="10dp"
+                    android:paddingVertical="2dp"
+                    android:text="积分余额"
+                    android:textColor="@color/black"
+                    android:textSize="14sp"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:id="@+id/tvPoints"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dp"
+                    android:textColor="#FF3B30"
+                    android:textSize="24sp"
+                    android:textStyle="bold"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toBottomOf="@id/tvPointsLabel"
+                    android:text="0" />
+
+                <Button
+                    android:id="@+id/btnExchange"
+                    android:layout_width="62dp"
+                    android:layout_height="30dp"
+                    android:background="@drawable/sp_r6_stroke_f56a05"
+                    android:text="兑换"
+                    android:textColor="#FFF56A05"
+                    android:textSize="14sp"
+                    android:layout_marginRight="10dp"
+                    app:layout_constraintBottom_toBottomOf="@id/tvPoints"
+                    app:layout_constraintEnd_toStartOf="@id/btnWithdraw"
+                    app:layout_constraintTop_toTopOf="@id/tvPoints" />
+
+                <Button
+                    android:id="@+id/btnWithdraw"
+                    android:layout_width="62dp"
+                    android:layout_height="30dp"
+                    android:background="@drawable/sp_r6_solid_f56a05"
+                    android:text="提现"
+                    android:textColor="@color/white"
+                    android:textSize="14sp"
+                    app:layout_constraintBottom_toBottomOf="@id/tvPoints"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintTop_toTopOf="@id/tvPoints" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:orientation="horizontal"
+                    app:layout_constraintTop_toBottomOf="@id/tvPoints">
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:gravity="center"
+                        android:orientation="vertical">
+
+                        
+
+                        <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="4dp"
+                            android:text="今日收益"
+                            android:textColor="#999999"
+                            android:textSize="12sp" />
+                            <TextView
+                            android:id="@+id/tvTodayEarnings"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:textColor="@color/black"
+                            android:textSize="16sp"
+                            android:text="0" />
+                    </LinearLayout>
+
+                    <View
+                        android:layout_width="1dp"
+                        android:layout_height="match_parent"
+                        android:background="#EEEEEE" />
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:gravity="center"
+                        android:orientation="vertical">
+
+                      
+
+                        <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="4dp"
+                            android:text="累计收益"
+                            android:textColor="#999999"
+                            android:textSize="12sp" />
+                              <TextView
+                            android:id="@+id/tvTotalEarnings"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:textColor="@color/black"
+                            android:textSize="16sp"
+                            android:text="0" />
+                    </LinearLayout>
+
+                    <View
+                        android:layout_width="1dp"
+                        android:layout_height="match_parent"
+                        android:background="#EEEEEE" />
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:gravity="center"
+                        android:orientation="vertical">
+
+                       
+
+                        <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="4dp"
+                            android:text="累计邀请人次"
+                            android:textColor="#999999"
+                            android:textSize="12sp" />
+                             <TextView
+                            android:id="@+id/tvTotalInvites"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:textColor="@color/black"
+                            android:textSize="16sp"
+                            android:text="0" />
+                    </LinearLayout>
+                </LinearLayout>
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="500dp"
+                android:layout_marginHorizontal="16dp"
+                android:layout_marginTop="12dp"
+                android:background="@drawable/sp_r10_white"
+                android:orientation="vertical"
+                android:padding="16dp">
+
+                <com.google.android.material.tabs.TabLayout
+                    android:id="@+id/tabLayout"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:tabIndicatorHeight="0dp" />
+
+                <androidx.constraintlayout.widget.ConstraintLayout
+                    android:id="@+id/billHeader"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="12dp"
+                    android:visibility="gone">
+
+                    <LinearLayout
+                        android:id="@+id/llBillIncome"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="horizontal"
+                        android:gravity="center_vertical"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toTopOf="parent"
+                        app:layout_constraintBottom_toBottomOf="parent">
+
+                        <FrameLayout
+                            android:id="@+id/flIncomeDot"
+                            android:layout_width="16dp"
+                            android:layout_height="16dp"
+                            android:background="@drawable/sp_circle_stroke_999">
+
+                            <TextView
+                                android:id="@+id/tvIncomeCheck"
+                                android:layout_width="match_parent"
+                                android:layout_height="match_parent"
+                                android:gravity="center"
+                                android:text="✓"
+                                android:textColor="@color/white"
+                                android:textSize="10sp"
+                                android:visibility="gone" />
+                        </FrameLayout>
+
+                        <TextView
+                            android:id="@+id/tvIncomeLabel"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginStart="4dp"
+                            android:text="收益"
+                            android:textColor="#333333"
+                            android:textSize="14sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:id="@+id/llBillExpense"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="16dp"
+                        android:orientation="horizontal"
+                        android:gravity="center_vertical"
+                        app:layout_constraintStart_toEndOf="@id/llBillIncome"
+                        app:layout_constraintTop_toTopOf="@id/llBillIncome"
+                        app:layout_constraintBottom_toBottomOf="@id/llBillIncome">
+
+                        <FrameLayout
+                            android:id="@+id/flExpenseDot"
+                            android:layout_width="16dp"
+                            android:layout_height="16dp"
+                            android:background="@drawable/sp_circle_stroke_999">
+
+                            <TextView
+                                android:id="@+id/tvExpenseCheck"
+                                android:layout_width="match_parent"
+                                android:layout_height="match_parent"
+                                android:gravity="center"
+                                android:text="✓"
+                                android:textColor="@color/white"
+                                android:textSize="10sp"
+                                android:visibility="gone" />
+                        </FrameLayout>
+
+                        <TextView
+                            android:id="@+id/tvExpenseLabel"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginStart="4dp"
+                            android:text="支出"
+                            android:textColor="#666666"
+                            android:textSize="14sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="horizontal"
+                        android:gravity="center_vertical"
+                        app:layout_constraintEnd_toEndOf="parent"
+                        app:layout_constraintTop_toTopOf="parent"
+                        app:layout_constraintBottom_toBottomOf="parent">
+
+                        <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="合计: "
+                            android:textColor="#FFFF6D00"
+                            android:textSize="14sp" />
+
+                        <TextView
+                            android:id="@+id/tvBillTotal"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="0"
+                            android:textColor="#FFFF6D00"
+                            android:textSize="18sp"
+                            android:textStyle="bold" />
+                    </LinearLayout>
+                </androidx.constraintlayout.widget.ConstraintLayout>
+
+                <FrameLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"
+                    android:layout_marginTop="16dp">
+
+                    <androidx.recyclerview.widget.RecyclerView
+                        android:id="@+id/recyclerView"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:nestedScrollingEnabled="true" />
+
+                    <LinearLayout
+                        android:id="@+id/emptyView"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:orientation="vertical"
+                        android:gravity="center"
+                        android:visibility="gone">
+
+                        <ImageView
+                            android:layout_width="120dp"
+                            android:layout_height="120dp"
+                            android:src="@drawable/ic_empty_data" />
+
+                        <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="16dp"
+                            android:text="暂无数据"
+                            android:textColor="#999999"
+                            android:textSize="14sp" />
+                    </LinearLayout>
+                </FrameLayout>
+            </LinearLayout>
+        </LinearLayout>
+    </androidx.core.widget.NestedScrollView>
+
+    <Button
+        android:id="@+id/btnInviteNow"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:layout_marginHorizontal="32dp"
+        android:layout_marginBottom="24dp"
+        android:background="@drawable/sp_r16_ff5722"
+        android:text="立即邀请"
+        android:textColor="@color/white"
+        android:textSize="16sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <ProgressBar
+        android:id="@+id/progressBar"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 126 - 0
module_me/src/main/res/layout/module_me_activity_invite_rules.xml

@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/ivBg"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:scaleType="centerCrop"
+        android:src="@drawable/invite_bg"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/clTopBar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <ImageView
+            android:id="@+id/ivBack"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_marginStart="16dp"
+            android:background="@drawable/module_me_bg_topbar_btn_dark"
+            android:padding="6dp"
+            android:src="@drawable/ic_back"
+            android:tint="@color/white"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/tvTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="邀请收益规则"
+            android:textColor="@color/white"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="@id/ivBack"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="@id/ivBack" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <LinearLayout
+        android:id="@+id/cardContainer"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginHorizontal="16dp"
+        android:layout_marginTop="20dp"
+        android:layout_marginBottom="32dp"
+        android:background="@drawable/module_me_bg_rules_card"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/clTopBar">
+
+        <androidx.core.widget.NestedScrollView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fillViewport="true"
+            android:padding="20dp">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:lineSpacingExtra="8dp"
+                    android:text="点&quot;邀请收益&quot;右下角的二维码或者微信等分享链接,别人扫码下载自动绑定你的名下,你额外获得他充值额的4%积分,他的下级充值,你获得其充值额的1%积分。积分满百元可提现。"
+                    android:textColor="#A16850"
+                    android:textSize="15sp" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:lineSpacingExtra="8dp"
+                    android:text="用户也可以手动输入邀请码绑定,让用户点&quot;我的&quot;→&quot;账户安全&quot;→&quot;绑定关系&quot;输入邀请码即可。"
+                    android:textColor="#A16850"
+                    android:textSize="15sp" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:lineSpacingExtra="8dp"
+                    android:text="每个人的邀请码就是自己的甜咖ID号。"
+                    android:textColor="#A16850"
+                    android:textSize="15sp" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:lineSpacingExtra="8dp"
+                    android:text="举例:A邀请B,B邀请C,那么C充值100元,B得一级分享收益4元,A得二级分享收益1元。"
+                    android:textColor="#A16850"
+                    android:textSize="15sp" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:lineSpacingExtra="8dp"
+                    android:text="注意:绑定关系一旦生效,暂时无法解除。具体联系官方客服"
+                    android:textColor="#A16850"
+                    android:textSize="15sp" />
+            </LinearLayout>
+        </androidx.core.widget.NestedScrollView>
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
+

+ 297 - 0
module_me/src/main/res/layout/module_me_activity_withdraw.xml

@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/rootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#F5F5F5">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/clTopBar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="@color/white"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="12dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <ImageView
+            android:id="@+id/ivBack"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:src="@drawable/ic_back"
+            android:tint="#333333"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/tvTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="提现"
+            android:textColor="#333333"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.core.widget.NestedScrollView
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:fillViewport="true"
+        app:layout_constraintBottom_toTopOf="@id/btnConfirm"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/clTopBar">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:padding="16dp">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@drawable/sp_r10_white"
+                android:orientation="vertical"
+                android:padding="16dp">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="提现金额"
+                    android:textColor="#999999"
+                    android:textSize="14sp" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal">
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="¥"
+                        android:textColor="#333333"
+                        android:textSize="24sp"
+                        android:textStyle="bold" />
+
+                    <EditText
+                        android:id="@+id/etAmount"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="8dp"
+                        android:layout_weight="1"
+                        android:background="@android:color/transparent"
+                        android:hint="请输入提现金额"
+                        android:inputType="number"
+                        android:maxLines="1"
+                        android:textColor="#333333"
+                        android:textSize="16sp" />
+
+                    <TextView
+                        android:id="@+id/btnAll"
+                        android:layout_width="wrap_content"
+                        android:layout_height="32dp"
+                        android:layout_marginStart="8dp"
+                        android:background="@drawable/sp_r16_stroke_ff9800"
+                        android:gravity="center"
+                        android:paddingHorizontal="12dp"
+                        android:text="全部"
+                        android:textColor="#FF9800"
+                        android:textSize="12sp" />
+                </LinearLayout>
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:layout_marginTop="12dp"
+                    android:background="#EEEEEE" />
+
+                <TextView
+                    android:id="@+id/tvAvailable"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="12dp"
+                    android:text="可提现金额: 0元"
+                    android:textColor="#333333"
+                    android:textSize="14sp"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/tvPointsLeft"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dp"
+                    android:text="剩余积分: 0"
+                    android:textColor="#999999"
+                    android:textSize="12sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="4dp"
+                    android:text="提现比例: 10000积分=1元"
+                    android:textColor="#FF9800"
+                    android:textSize="12sp" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                android:background="@drawable/sp_r10_white"
+                android:orientation="vertical"
+                android:padding="16dp">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="提现到"
+                    android:textColor="#333333"
+                    android:textSize="14sp"
+                    android:textStyle="bold" />
+
+                <LinearLayout
+                    android:id="@+id/llBankCard"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal">
+
+                    <FrameLayout
+                        android:layout_width="32dp"
+                        android:layout_height="32dp"
+                        android:background="@drawable/sp_r4_solid_e3f2fd">
+
+                        <TextView
+                            android:layout_width="match_parent"
+                            android:layout_height="match_parent"
+                            android:gravity="center"
+                            android:text="卡"
+                            android:textColor="#1976D2"
+                            android:textSize="14sp"
+                            android:textStyle="bold" />
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="12dp"
+                        android:layout_weight="1"
+                        android:orientation="vertical">
+
+                        <TextView
+                            android:id="@+id/tvBankName"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="请添加银行卡"
+                            android:textColor="#999999"
+                            android:textSize="16sp" />
+
+                        <TextView
+                            android:id="@+id/tvBankNo"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="2dp"
+                            android:text=""
+                            android:textColor="#999999"
+                            android:textSize="14sp"
+                            android:visibility="gone" />
+                    </LinearLayout>
+
+                    <TextView
+                        android:id="@+id/tvAddHint"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="去添加"
+                        android:textColor="#999999"
+                        android:textSize="14sp"
+                        android:visibility="gone" />
+
+                    <ImageView
+                        android:layout_width="16dp"
+                        android:layout_height="16dp"
+                        android:layout_marginStart="4dp"
+                        android:src="@drawable/ic_arrow_right_gray" />
+                </LinearLayout>
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="24dp"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="提现说明"
+                    android:textColor="#666666"
+                    android:textSize="14sp"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dp"
+                    android:text="1.每笔提现手续费为提现额的6%"
+                    android:textColor="#999999"
+                    android:textSize="12sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="2.整百及整百的倍数方可提现"
+                    android:textColor="#999999"
+                    android:textSize="12sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="3.每天可提现1次"
+                    android:textColor="#999999"
+                    android:textSize="12sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="4.每个id每天提现上限额为1亿积分"
+                    android:textColor="#999999"
+                    android:textSize="12sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="5.法定节假日不打款"
+                    android:textColor="#999999"
+                    android:textSize="12sp" />
+            </LinearLayout>
+        </LinearLayout>
+    </androidx.core.widget.NestedScrollView>
+
+    <Button
+        android:id="@+id/btnConfirm"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:layout_margin="16dp"
+        android:background="@drawable/sp_r24_gradient_ff9800_ff5722"
+        android:text="确认提现"
+        android:textColor="@color/white"
+        android:textSize="16sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 275 - 0
module_me/src/main/res/layout/module_me_dialog_invite_share.xml

@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/sp_r20_white"
+    android:orientation="vertical"
+    android:paddingBottom="20dp"
+    android:paddingTop="10dp">
+
+    <View
+        android:layout_width="36dp"
+        android:layout_height="4dp"
+        android:layout_gravity="center_horizontal"
+        android:background="@drawable/sp_r16_000000_23" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="14dp"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingHorizontal="14dp">
+
+            <TextView
+                android:id="@+id/tvTabLink"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="链接分享"
+                android:textColor="#333333"
+                android:textSize="16sp"
+                android:textStyle="bold" />
+
+            <View
+                android:id="@+id/vTabLinkIndicator"
+                android:layout_width="20dp"
+                android:layout_height="3dp"
+                android:layout_marginTop="6dp"
+                android:background="@drawable/sp_r6_solid_f56a05" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingHorizontal="14dp">
+
+            <TextView
+                android:id="@+id/tvTabPoster"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="海报分享"
+                android:textColor="#999999"
+                android:textSize="16sp"
+                android:textStyle="bold" />
+
+            <View
+                android:id="@+id/vTabPosterIndicator"
+                android:layout_width="20dp"
+                android:layout_height="3dp"
+                android:layout_marginTop="6dp"
+                android:background="@drawable/sp_r6_solid_f56a05"
+                android:visibility="invisible" />
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/llLinkShare"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="18dp"
+        android:gravity="center"
+        android:orientation="horizontal"
+        android:paddingHorizontal="12dp">
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <ImageView
+                android:id="@+id/ivShareWeixin"
+                android:layout_width="52dp"
+                android:layout_height="52dp"
+                android:src="@drawable/weixin" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:text="微信"
+                android:textColor="#666666"
+                android:textSize="12sp" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <ImageView
+                android:id="@+id/ivShareMoments"
+                android:layout_width="52dp"
+                android:layout_height="52dp"
+                android:src="@drawable/winxin_comment" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:text="朋友圈"
+                android:textColor="#666666"
+                android:textSize="12sp" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <ImageView
+                android:id="@+id/ivShareQq"
+                android:layout_width="52dp"
+                android:layout_height="52dp"
+                android:src="@drawable/qq" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:text="QQ"
+                android:textColor="#666666"
+                android:textSize="12sp" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/ivShareCopy"
+                android:layout_width="52dp"
+                android:layout_height="52dp"
+                android:background="@drawable/sp_r24_ff9800"
+                android:padding="12dp">
+
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:src="@drawable/copy_link"
+                    android:tint="@color/white" />
+            </FrameLayout>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:text="复制链接"
+                android:textColor="#666666"
+                android:textSize="12sp" />
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/llPosterShare"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="12dp"
+        android:gravity="center_horizontal"
+        android:orientation="vertical"
+        android:paddingHorizontal="12dp"
+        android:visibility="gone">
+
+        <FrameLayout
+            android:layout_width="300dp"
+            android:layout_height="440dp"
+            android:layout_marginTop="8dp"
+            android:background="@drawable/sp_r10_shadow_d0d0d0"
+            android:elevation="6dp">
+
+            <ImageView
+                android:id="@+id/ivPosterBg"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scaleType="centerCrop"
+                android:src="@drawable/invite_bg" />
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center_horizontal"
+                android:orientation="vertical"
+                android:paddingTop="32dp"
+                android:paddingBottom="74dp">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="甜咖直播"
+                    android:textColor="@color/white"
+                    android:textSize="20sp"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="6dp"
+                    android:text="邀请您加入"
+                    android:textColor="@color/white"
+                    android:textSize="12sp" />
+
+                <View
+                    android:layout_width="1dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1" />
+
+                <FrameLayout
+                    android:layout_width="184dp"
+                    android:layout_height="184dp"
+                    android:background="@drawable/sp_r20_white"
+                    android:padding="8dp">
+
+                    <ImageView
+                        android:id="@+id/ivQr"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:scaleType="fitXY" />
+                </FrameLayout>
+
+                <TextView
+                    android:id="@+id/tvInviteCode"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:text="邀请码:"
+                    android:textColor="#BDBDBD"
+                    android:textSize="16sp"
+                    android:textStyle="bold"
+                    android:shadowColor="#33000000"
+                    android:shadowDx="0"
+                    android:shadowDy="1"
+                    android:shadowRadius="2" />
+            </LinearLayout>
+        </FrameLayout>
+
+        <Button
+            android:id="@+id/btnSavePoster"
+            android:layout_width="300dp"
+            android:layout_height="48dp"
+            android:layout_marginTop="16dp"
+            android:background="@drawable/sp_r24_ff9800"
+            android:drawablePadding="8dp"
+            android:paddingHorizontal="16dp"
+            android:text="保存到相册"
+            android:textColor="@color/white"
+            android:textSize="16sp"
+            android:textStyle="bold" />
+    </LinearLayout>
+
+</LinearLayout>

+ 17 - 1
module_me/src/main/res/layout/module_me_fragment_me.xml

@@ -513,6 +513,22 @@
                                 tools:ignore="UseAppTint" />
                         </androidx.constraintlayout.widget.ConstraintLayout>
 
+                        <!-- 邀请关注 -->
+                        
+                        <ImageView
+                            android:id="@+id/ivInvitePoster"
+                            android:layout_width="match_parent"
+                            android:layout_height="80dp"
+                            android:layout_marginHorizontal="@dimen/dp_16"
+                            android:layout_marginTop="@dimen/dp_16"
+                            android:background="@drawable/sp_r10_white"
+                            android:src="@drawable/icon_me_gift_wall_enter_tip"
+                            android:scaleType="fitXY"
+                            app:layout_constraintStart_toStartOf="parent"
+                            app:layout_constraintTop_toBottomOf="@id/clGiftWallEnter" />
+                        
+                        <!-- 邀请关注 end-->
+
                         <LinearLayout
                             android:layout_width="match_parent"
                             android:layout_height="wrap_content"
@@ -525,7 +541,7 @@
                             android:paddingTop="@dimen/dp_12"
                             android:paddingBottom="@dimen/dp_12"
                             app:layout_constraintBottom_toBottomOf="parent"
-                            app:layout_constraintTop_toBottomOf="@id/clGiftWallEnter">
+                            app:layout_constraintTop_toBottomOf="@id/ivInvitePoster">
 
                             <TextView
                                 android:layout_width="wrap_content"

+ 85 - 0
module_me/src/main/res/layout/module_me_item_bill_detail.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingVertical="12dp">
+
+    <TextView
+        android:id="@+id/tvTitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:textColor="#333333"
+        android:textSize="14sp"
+        app:layout_constraintEnd_toStartOf="@id/tvAmount"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="卡里克 ID:584021" />
+
+    <TextView
+        android:id="@+id/tvSubTitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="2dp"
+        android:textColor="#999999"
+        android:textSize="12sp"
+        app:layout_constraintEnd_toStartOf="@id/tvAmount"
+        app:layout_constraintStart_toStartOf="@id/tvTitle"
+        app:layout_constraintTop_toBottomOf="@id/tvTitle"
+        tools:text="一级贡献" />
+
+    <TextView
+        android:id="@+id/tvRemark"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="2dp"
+        android:textColor="#666666"
+        android:textSize="12sp"
+        android:visibility="gone"
+        app:layout_constraintEnd_toStartOf="@id/tvAmount"
+        app:layout_constraintStart_toStartOf="@id/tvTitle"
+        app:layout_constraintTop_toBottomOf="@id/tvSubTitle"
+        tools:text="积分兑换金币"
+        tools:visibility="visible" />
+
+    <TextView
+        android:id="@+id/tvAmount"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="#333333"
+        android:textSize="14sp"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@id/tvTitle"
+        tools:text="+4000000" />
+
+    <TextView
+        android:id="@+id/tvDate"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="2dp"
+        android:textColor="#BBBBBB"
+        android:textSize="10sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tvAmount"
+        tools:text="2026-02-11 18:00:50" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/bottomBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierAllowsGoneWidgets="false"
+        app:barrierDirection="bottom"
+        app:constraint_referenced_ids="tvSubTitle,tvRemark,tvDate" />
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="1dp"
+        android:layout_marginTop="12dp"
+        android:background="#F5F5F5"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/bottomBarrier" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 61 - 0
module_me/src/main/res/layout/module_me_item_invite_earnings.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingVertical="12dp">
+
+    <TextView
+        android:id="@+id/tvName"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="#333333"
+        android:textSize="14sp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="鹿凌晶" />
+
+    <TextView
+        android:id="@+id/tvId"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:textColor="#999999"
+        android:textSize="12sp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/tvName"
+        tools:text="ID: 3253233" />
+
+    <TextView
+        android:id="@+id/tvContributionLabel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="贡献积分:"
+        android:textColor="#333333"
+        android:textSize="14sp"
+        app:layout_constraintEnd_toStartOf="@+id/tvContribution"
+        app:layout_constraintTop_toTopOf="@+id/tvName" />
+
+    <TextView
+        android:id="@+id/tvContribution"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="#FF5722"
+        android:textSize="14sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/tvContributionLabel"
+        tools:text="89318" />
+
+    <TextView
+        android:id="@+id/tvDate"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:textColor="#999999"
+        android:textSize="12sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/tvContribution"
+        tools:text="2063/03/01" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 24 - 0
module_me/src/main/res/layout/module_me_item_load_more.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <ProgressBar
+        android:id="@+id/progressBar"
+        style="?android:attr/progressBarStyleSmall"
+        android:layout_width="18dp"
+        android:layout_height="18dp"
+        android:indeterminate="true"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/tvText"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:textColor="#999999"
+        android:textSize="12sp" />
+</LinearLayout>
+

+ 29 - 0
module_me/src/main/res/layout/module_me_row_bank_input_bank.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingHorizontal="16dp"
+    android:paddingVertical="16dp">
+
+    <TextView
+        android:layout_width="80dp"
+        android:layout_height="wrap_content"
+        android:text="所属银行"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+
+    <EditText
+        android:id="@+id/etBankName"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:background="@android:color/transparent"
+        android:hint="例如:招商银行"
+        android:inputType="text"
+        android:maxLength="50"
+        android:maxLines="1"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+</LinearLayout>

+ 29 - 0
module_me/src/main/res/layout/module_me_row_bank_input_card.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingHorizontal="16dp"
+    android:paddingVertical="16dp">
+
+    <TextView
+        android:layout_width="80dp"
+        android:layout_height="wrap_content"
+        android:text="银行卡号"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+
+    <EditText
+        android:id="@+id/etCardNumber"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:background="@android:color/transparent"
+        android:hint="请填写银行卡号"
+        android:inputType="number"
+        android:maxLength="19"
+        android:maxLines="1"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+</LinearLayout>

+ 30 - 0
module_me/src/main/res/layout/module_me_row_bank_input_id.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingHorizontal="16dp"
+    android:paddingVertical="16dp">
+
+    <TextView
+        android:layout_width="80dp"
+        android:layout_height="wrap_content"
+        android:text="身份证号"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+
+    <EditText
+        android:id="@+id/etIdCard"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:background="@android:color/transparent"
+        android:hint="请填写身份证号码"
+        android:digits="0123456789Xx"
+        android:inputType="text"
+        android:maxLength="18"
+        android:maxLines="1"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+</LinearLayout>

+ 29 - 0
module_me/src/main/res/layout/module_me_row_bank_input_name.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingHorizontal="16dp"
+    android:paddingVertical="16dp">
+
+    <TextView
+        android:layout_width="80dp"
+        android:layout_height="wrap_content"
+        android:text="姓名"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+
+    <EditText
+        android:id="@+id/etRealName"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:background="@android:color/transparent"
+        android:hint="请填写真实姓名"
+        android:inputType="textPersonName"
+        android:maxLength="20"
+        android:maxLines="1"
+        android:textColor="#333333"
+        android:textSize="16sp" />
+</LinearLayout>

+ 26 - 0
module_me/src/main/res/layout/module_me_tab_invite_earnings.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center_horizontal">
+
+    <TextView
+        android:id="@+id/tvTab"
+        android:layout_width="64dp"
+        android:layout_height="23dp"
+        android:alpha="1"
+        android:fontFamily="@font/app_font"
+        android:gravity="center"
+        android:textColor="#999999"
+        android:textSize="16sp" />
+
+    <View
+        android:id="@+id/vIndicator"
+        android:layout_width="20dp"
+        android:layout_height="3dp"
+        android:layout_marginTop="4dp"
+        android:alpha="1"
+        android:background="#D84315"
+        android:visibility="invisible" />
+</LinearLayout>

+ 41 - 1
umlibrary/src/main/java/com/bmkj/umlibrary/ShareManager.kt

@@ -89,6 +89,26 @@ class ShareManager(context: Context) {
             .share();
     }
 
+    fun startWxShare(
+        activity: Activity,
+        iconResId: Int,
+        title: String,
+        des: String,
+        link: String,
+        listener: ThreeListener
+    ) {
+        val web = UMWeb(link);
+        web.title = title//标题
+        web.setThumb(UMImage(activity, iconResId));//缩略图
+        web.description = des;//描述
+        mThreeListener = listener
+        ShareAction(activity)
+            .setPlatform(SHARE_MEDIA.WEIXIN)
+            .withMedia(web)
+            .setCallback(umShareListener)
+            .share();
+    }
+
     fun startWxCircle(
         activity: Activity,
         icon: String,
@@ -109,6 +129,26 @@ class ShareManager(context: Context) {
             .share();
     }
 
+    fun startWxCircle(
+        activity: Activity,
+        iconResId: Int,
+        title: String,
+        des: String,
+        link: String,
+        listener: ThreeListener
+    ) {
+        val web = UMWeb(link);
+        web.title = title//标题
+        web.setThumb(UMImage(activity, iconResId));//缩略图
+        web.description = des;//描述
+        mThreeListener = listener
+        ShareAction(activity)
+            .setPlatform(SHARE_MEDIA.WEIXIN_CIRCLE)
+            .withMedia(web)
+            .setCallback(umShareListener)
+            .share();
+    }
+
 
     interface ThreeListener {
         fun onResult()
@@ -116,4 +156,4 @@ class ShareManager(context: Context) {
         fun onCancel()
     }
 
-}
+}