1、项目toast替换
2、添加注销账号功能
3、部分代码优化
This commit is contained in:
shenzuqiang 2026-03-10 15:38:29 +08:00
parent 5278050b1c
commit 85d03e0390
31 changed files with 682 additions and 147 deletions

View File

@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-03-09T06:18:00.411600700Z">
<DropdownSelection timestamp="2026-03-10T06:53:45.140671700Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=JRBI89BIE6AI5TG6" />
<DeviceId pluginId="PhysicalDevice" identifier="serial=d927cac5" />
</handle>
</Target>
</DropdownSelection>

View File

@ -129,7 +129,7 @@ dependencies {
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core.splashscreen)
implementation(libs.multidex)
// implementation(libs.multidex)
// Compose 依赖
implementation(platform(libs.androidx.compose.bom))

View File

@ -1,18 +1,7 @@
package com.img.rabbit
import android.app.Activity
import android.app.ActivityManager
import android.app.Application
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.webkit.WebView
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.multidex.MultiDex
import com.bumptech.glide.Glide
import com.img.rabbit.utils.NetworkMonitor
import com.g.gysdk.GYManager
@ -61,7 +50,7 @@ class BaseApplication : Application() {
}
override fun attachBaseContext(base: android.content.Context?) {
MultiDex.install(base)
// MultiDex.install(base)
super.attachBaseContext(base)
}

View File

@ -196,13 +196,7 @@ class MainActivity : ComponentActivity(), LoadingCallback {
showSplash = true
}
if(showSplash){ //|| globalLogout.value == true){
// 全局注销,重新登录
// if(globalLogout.value == true){
// loginViewModel = viewModel()
// loginViewModel.requestUserConfig()
// }
if(showSplash){
val token = PreferenceUtil.getAccessToken()
// 未登录,显示登录页
if (token.isNullOrEmpty() && loginViewModel.userConfigResult.value == null) {
@ -368,7 +362,7 @@ class MainActivity : ComponentActivity(), LoadingCallback {
}
}
// 模拟加载过程,2秒后关闭启动页
// 模拟加载过程,500毫秒后关闭启动页
LaunchedEffect(Unit) {
delay(500L)
splashViewModel.setLoading(false)

View File

@ -1,3 +1,4 @@
package com.img.rabbit.bean.local
data class NotifyBean(var code: String,var message: String)
data class ErrorBean(var code: String,var message: String)

View File

@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@ -71,6 +72,7 @@ import com.g.gysdk.EloginActivityParam
import com.g.gysdk.GYManager
import com.g.gysdk.GYResponse
import com.g.gysdk.GyCallBack
import com.img.rabbit.components.CenterToast
import com.img.rabbit.config.Constants.agreementUrl
import com.img.rabbit.config.Constants.privacyUrl
import com.img.rabbit.pages.toolbar.TitleBar
@ -87,18 +89,6 @@ import org.json.JSONObject
@Composable
fun LoginScreen(navController: NavHostController? = null, generalViewModel: GeneralViewModel, loginViewModel: LoginViewModel, isVisibilityBreak: Boolean) {
val context = LocalContext.current
// val networkStatus by generalViewModel.networkStatus.observeAsState(initial = true)
// var showNetworkDisconnected by remember { mutableStateOf(false) }
//
// // 网络状态监听
// LaunchedEffect(networkStatus) {
// if (!networkStatus) {
// // 网络断开时的处理
// Log.w("NetworkStatus","网络断开")
// }else{
// Log.w("NetworkStatus","网络已连接")
// }
// }
//关于登录的事件监听
LaunchedEffect(Unit) {
@ -120,20 +110,20 @@ fun LoginScreen(navController: NavHostController? = null, generalViewModel: Gene
if (loginViewModel.loginResult.value?.data?.token?.isNotEmpty() == true) {
val loginInfo = loginViewModel.loginResult.value?.data
AppEventBus.post( LoginBindEvent.Login(userId = loginInfo?.user_id?:"", loginType = PreferenceUtil.getLoginType(), isLogin = true, data = loginInfo) )
Toast.makeText(context, "登录成功", Toast.LENGTH_SHORT).show() //提示登录成功
CenterToast.show("登录成功")
if(isVisibilityBreak){
navController?.popBackStack() // 当允许返回上一页时,登录成功后,返回上一页
}
loginViewModel.loginResult.value = null //清理loginState
}else if(loginViewModel.loginResult.value != null && loginViewModel.loginResult.value?.data?.token == null){
Log.w("LoginScreen","登录失败,无有效的Token")
Toast.makeText(context, "登录失败,请重新登录", Toast.LENGTH_SHORT).show()//提示登录成功
CenterToast.show("登录失败,请重新登录")
}
}
Scaffold{
Box(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) {
Image(
painter = painterResource(id = R.mipmap.ic_main_previous_mask),
@ -758,11 +748,7 @@ private fun WxLoginScreen(
//打开微信登录
viewModel.authorWechat(context, generalViewModel.api)
} else {
Toast.makeText(
context,
"请先同意用户协议和隐私政策",
Toast.LENGTH_SHORT
).show()
CenterToast.show("请先同意用户协议和隐私政策")
}
}
) {
@ -934,11 +920,7 @@ private fun AliPayLoginScreen(
// 打开支付宝登录
viewModel.requestAliPayAuthParam(context)
} else {
Toast.makeText(
context,
"请先同意用户协议和隐私政策",
Toast.LENGTH_SHORT
).show()
CenterToast.show("请先同意用户协议和隐私政策")
}
}
) {
@ -1164,7 +1146,7 @@ private fun OtherLoginBar(viewModel: LoginViewModel) {
*/
private fun validatePhoneEmpty(context: Context, viewModel: LoginViewModel, showToast: Boolean = false): Boolean {
if (showToast && viewModel.userName.value.isEmpty()) {
Toast.makeText(context, "请输入手机号", Toast.LENGTH_SHORT).show()
CenterToast.show("请输入手机号")
}
return viewModel.userName.value.isNotEmpty()
}
@ -1175,11 +1157,11 @@ private fun validatePhoneEmpty(context: Context, viewModel: LoginViewModel, show
private fun validateCaptchaLoginEmpty(context: Context, viewModel: LoginViewModel, showToast: Boolean = false): Boolean {
if (showToast) {
if(viewModel.userName.value.isEmpty()){
Toast.makeText(context, "请输入手机号", Toast.LENGTH_SHORT).show()
CenterToast.show("请输入手机号")
}else if(viewModel.captcha.value.isEmpty()){
Toast.makeText(context, "请输入验证码", Toast.LENGTH_SHORT).show()
CenterToast.show("请输入验证码")
}else if(!viewModel.isPolicyAgreement.value){
Toast.makeText(context, "请同意用户协议和隐私政策", Toast.LENGTH_SHORT).show()
CenterToast.show("请同意用户协议和隐私政策")
}
}
return viewModel.userName.value.isNotEmpty() && viewModel.captcha.value.isNotEmpty() && viewModel.isPolicyAgreement.value

View File

@ -2,9 +2,9 @@ package com.img.rabbit.pages
import android.annotation.SuppressLint
import android.util.Log
import android.view.Gravity
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@ -50,6 +50,7 @@ import com.img.rabbit.pages.screen.mine.setting.AboutScreen
import com.img.rabbit.pages.screen.mine.setting.AccountBindScreen
import com.img.rabbit.pages.screen.mine.setting.AccountManagerScreen
import com.img.rabbit.pages.screen.mine.setting.BindScreen
import com.img.rabbit.pages.screen.mine.setting.DeleteAccountScreen
import com.img.rabbit.pages.screen.other.CameraGuideScreen
import com.img.rabbit.route.ScreenRoute
import com.img.rabbit.viewmodel.BindViewModel
@ -133,14 +134,23 @@ fun MainScreen(generalViewModel: GeneralViewModel, loginViewModel: LoginViewMode
Icon(
painter = painterResource(id = iconRes),
contentDescription = item.title,
tint = Color.Unspecified
tint = Color.Unspecified,
modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
){
selectedTab = tabItems[index]
}
)
},
label = { Text(item.title, color = if (selectedTab == tabItems[index]) item.selectedColor else item.normalColor) },
selected = selectedTab == tabItems[index],
onClick = { selectedTab = tabItems[index] },
interactionSource = remember { MutableInteractionSource() },
colors = NavigationBarItemDefaults.colors(
indicatorColor = Color.Transparent
indicatorColor = Color.Transparent, // 去掉选中时的椭圆背景
selectedIconColor = Color.Unspecified,
unselectedIconColor = Color.Unspecified
)
)
}
@ -222,6 +232,9 @@ fun MainScreen(generalViewModel: GeneralViewModel, loginViewModel: LoginViewMode
composable(ScreenRoute.AboutMine.route) {
AboutScreen(navController = navController)
}
composable(ScreenRoute.DeleteAccount.route) {
DeleteAccountScreen(navController = navController)
}
// 登录页面Login
composable(ScreenRoute.Login.route) {

View File

@ -222,13 +222,11 @@ fun UpdateDialog(
) {
// 执行下载更新
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// isStartDown.value = true
onStatusChange(false, false, url)
}else{
when {
// 情况 A已获得权限
storagePermissionState.status.isGranted -> {
// isStartDown.value = true
onStatusChange(false, false, url)
}
// 情况 B需要向用户解释之前拒绝过但未勾选“不再询问”

View File

@ -54,6 +54,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import coil3.compose.AsyncImage
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.pages.LoadingCallback
import com.img.rabbit.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil
@ -90,7 +91,7 @@ fun HomeScreen(
BackHandler(enabled = (currentRoute == ScreenRoute.Home.route)) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > 2000) {
Toast.makeText(context, "再按一次退出应用", Toast.LENGTH_SHORT).show()
CenterToast.show("再按一次退出应用")
lastClickTime = currentTime
} else {
(context as? Activity)?.finish()
@ -470,7 +471,7 @@ fun HomeScreen(
val uniVersion =
PreferenceUtil.getUserConfig()?.config?.uniVersionEntity?.firstOrNull { it.unimp_type == item.type }
if(uniVersion == null){
Toast.makeText(context, "无可用资源...", Toast.LENGTH_SHORT).show()
CenterToast.show("无可用资源...")
return@clickable
}
val uniMpId = uniVersion.unimp_id

View File

@ -48,6 +48,7 @@ import androidx.navigation.compose.rememberNavController
import coil3.compose.AsyncImage
import com.img.rabbit.BuildConfig
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.route.ScreenRoute
@ -81,7 +82,7 @@ fun MineScreen(
BackHandler(enabled = (currentRoute == ScreenRoute.Mine.route)) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > 2000) {
Toast.makeText(context, "再按一次退出应用", Toast.LENGTH_SHORT).show()
CenterToast.show("再按一次退出应用")
lastClickTime = currentTime
} else {
(context as? Activity)?.finish()
@ -194,8 +195,7 @@ fun MineScreen(
userInfo?.user_id
)
clipboard.setPrimaryClip(clip)
Toast.makeText(context, "已复制到剪贴板", Toast.LENGTH_SHORT)
.show()
CenterToast.show("已复制到剪贴板")
}
}
){
@ -388,7 +388,7 @@ fun MineScreen(
interactionSource = remember { MutableInteractionSource() }
) {
if (!generalViewModel.api.isWXAppInstalled) {
Toast.makeText(context, "未安装微信客户端", Toast.LENGTH_SHORT).show()
CenterToast.show("未安装微信客户端")
}else if(userInfo != null){
viewModel.requestServiceLink(context, generalViewModel.api)
}
@ -455,7 +455,7 @@ fun MineScreen(
GlobalStateManager(context).storeGlobalUpdateNotify(true)
}
} else {
Toast.makeText(context, tips, Toast.LENGTH_SHORT).show()
CenterToast.show(tips)
}
}
},

View File

@ -1,12 +1,14 @@
@file:OptIn(ExperimentalPermissionsApi::class)
package com.img.rabbit.pages.screen.make
import android.Manifest
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Canvas
@ -24,6 +26,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
@ -63,10 +66,15 @@ import androidx.compose.ui.unit.toSize
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import coil3.compose.AsyncImage
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.img.rabbit.R
import com.img.rabbit.bean.local.ClothingBean
import com.img.rabbit.bean.local.HairstyleBean
import com.img.rabbit.components.AppearanceType
import com.img.rabbit.components.CenterToast
import com.img.rabbit.components.DrawingBoardPicker
import com.img.rabbit.config.CommonData.clothingForFemales
import com.img.rabbit.config.CommonData.clothingForMans
@ -170,6 +178,14 @@ fun CutoutScreen(navController: NavController) {
val selectedSize = remember { mutableStateOf(resizes.first { it.width == widthParam && it.height == heightParam }) }
val storagePermissionState = rememberPermissionState(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES // Android 13+ 使用具体媒体权限
} else {
Manifest.permission.WRITE_EXTERNAL_STORAGE // 旧版本使用常规存储权限
}
)
// 图片选择启动器
val imagePickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent()
@ -200,7 +216,7 @@ fun CutoutScreen(navController: NavController) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
context.mainExecutor.execute {
isLoading.value = false
Toast.makeText(context, "抠图失败,请重试", Toast.LENGTH_SHORT).show()
CenterToast.show("抠图失败,请重试")
}
}
}
@ -245,21 +261,18 @@ fun CutoutScreen(navController: NavController) {
imagePickerLauncher.launch("image/*")
}
Column(
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4))
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4)).navigationBarsPadding()
) {
TitleBar(navController = navController, paddingValues = it, title = "", showSave = true){
// 保存图片
coroutineScope.launch {
// 从 Layer 捕获 Bitmap
val bitmap = graphicsLayer.toImageBitmap().asAndroidBitmap()
// 保存图片到系统相册(图片已按比例裁剪)
saveCanvasToGallery(context, bitmap, ExportFormat.JPG){fileName, isSuccess ->
if(isSuccess){
Toast.makeText(context, "已保存为: $fileName", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(context, "保存失败", Toast.LENGTH_SHORT).show()
}
}
// // 保存图片到系统相册(图片已按比例裁剪)
// saveCanvasToGallery(context, bitmap, ExportFormat.JPG){fileName, isSuccess ->
// val tips = if(isSuccess){ "已保存为: $fileName" }else{ "保存失败" }
// CenterToast.show(tips)
// }
/*
// 保存图片到系统相册(指定尺寸如果targetWidth与targetHeight比原始值小太多会导致图片模糊)
@ -271,6 +284,33 @@ fun CutoutScreen(navController: NavController) {
}
}
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 保存图片到系统相册(图片已按比例裁剪)
saveCanvasToGallery(context, bitmap, ExportFormat.JPG){fileName, isSuccess ->
val tips = if(isSuccess){ "已保存为: $fileName" }else{ "保存失败" }
CenterToast.show(tips)
}
}else{
when {
// 情况 A已获得权限
storagePermissionState.status.isGranted -> {
// 保存图片到系统相册(图片已按比例裁剪)
saveCanvasToGallery(context, bitmap, ExportFormat.JPG){fileName, isSuccess ->
val tips = if(isSuccess){ "已保存为: $fileName" }else{ "保存失败" }
CenterToast.show(tips)
}
}
// 情况 B需要向用户解释之前拒绝过但未勾选“不再询问”
storagePermissionState.status.shouldShowRationale -> {
storagePermissionState.launchPermissionRequest()
}
// 情况 C从未请求过权限或已被彻底拒绝
else -> {
storagePermissionState.launchPermissionRequest()
}
}
}
}
}

View File

@ -1,7 +1,6 @@
package com.img.rabbit.pages.screen.make
import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
@ -13,6 +12,7 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Scaffold
@ -33,6 +33,7 @@ import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import coil3.compose.AsyncImage
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.components.DrawingBoardFormatPicker
import com.img.rabbit.config.CommonData.formats
import com.img.rabbit.pages.toolbar.TitleBar
@ -66,7 +67,7 @@ fun FormatScreen(navController: NavController) {
imagePickerLauncher.launch("image/*")
}
Column(
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4))
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4)).navigationBarsPadding()
) {
TitleBar(navController = navController, paddingValues = it, title = "", showSave = true) {
val bitmap = selectedImageUri.value?.let {uri->
@ -87,11 +88,8 @@ fun FormatScreen(navController: NavController) {
sourceBitmap = bitmap,
format = format,
onSubmitResult = { fileName, isSuccess ->
if (isSuccess) {
Toast.makeText(context, "已保存至 $fileName", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "保存失败", Toast.LENGTH_SHORT).show()
}
val tips = if (isSuccess) { "已保存至 $fileName" } else { "保存失败" }
CenterToast.show(tips)
}
)
}

View File

@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
@ -56,6 +57,7 @@ import kotlinx.coroutines.withContext
import androidx.compose.ui.graphics.asAndroidBitmap
import com.img.rabbit.components.CenterToast
import io.moyuru.cropify.Cropify
import io.moyuru.cropify.CropifyOption
import io.moyuru.cropify.rememberCropifyState
@ -97,7 +99,7 @@ fun LongImageScreen(navController: NavController) {
mediaPickerLauncher.launch(matisse)
}
Column (
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4))
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4)).navigationBarsPadding()
) {
TitleBar(
navController = navController,
@ -107,11 +109,8 @@ fun LongImageScreen(navController: NavController) {
){
coroutineScope.launch {
saveLongToGallery(context = context, items = imageItems, format = ExportFormat.JPG){ fileName, isSuccess ->
if (isSuccess) {
Toast.makeText(context, "已保存至 $fileName", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "保存失败", Toast.LENGTH_SHORT).show()
}
val tips = if (isSuccess) { "已保存至 $fileName" } else { "保存失败" }
CenterToast.show(tips)
}
}
}

View File

@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
@ -53,6 +54,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize
import androidx.navigation.NavController
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.components.DrawingBoardCertificatePicker
import com.img.rabbit.config.CommonData.resizes
import com.img.rabbit.pages.toolbar.TitleBar
@ -117,7 +119,7 @@ fun ResizeScreen(navController: NavController) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
context.mainExecutor.execute {
isLoading.value = false
Toast.makeText(context, "加载失败,请重试", Toast.LENGTH_SHORT).show()
CenterToast.show("加载失败,请重试")
}
}
}
@ -141,7 +143,7 @@ fun ResizeScreen(navController: NavController) {
}
Column(
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4))
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4)).navigationBarsPadding()
) {
TitleBar(navController = navController, paddingValues = it, title = "", showSave = true){
// 保存图片
@ -150,11 +152,8 @@ fun ResizeScreen(navController: NavController) {
val bitmap = graphicsLayer.toImageBitmap().asAndroidBitmap()
// 保存图片到系统相册(图片已按比例裁剪)
saveCanvasToGallery(context, bitmap, ExportFormat.JPG){fileName, isSuccess ->
if(isSuccess){
Toast.makeText(context, "已保存为: $fileName", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(context, "保存失败", Toast.LENGTH_SHORT).show()
}
val tips = if(isSuccess){ "已保存为: $fileName" }else{ "保存失败" }
CenterToast.show(tips)
}
/*

View File

@ -13,13 +13,16 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -38,6 +41,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.img.rabbit.components.CenterToast
import com.img.rabbit.components.ImagePicker
import com.img.rabbit.pages.toolbar.TitleBar
import com.img.rabbit.viewmodel.FeedbackViewModel
@ -46,17 +50,18 @@ import okhttp3.RequestBody.Companion.toRequestBody
@Composable
fun FeedbackScreen(navController: NavHostController, viewModel: FeedbackViewModel = viewModel()) {
val context = LocalContext.current
val scrollState = rememberScrollState()
LaunchedEffect(viewModel.errorState.value) {
if(viewModel.errorState.value != null){
Toast.makeText(context, viewModel.errorState.value?.message, Toast.LENGTH_SHORT).show()
viewModel.errorState.value?.message?.let { CenterToast.show(it) }
}
viewModel.errorState.value = null
}
Scaffold{
Column(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) {
TitleBar(navController = navController, paddingValues = it, title = "意见反馈")
Box(
@ -67,7 +72,7 @@ fun FeedbackScreen(navController: NavHostController, viewModel: FeedbackViewMode
){
// 意见反馈内容
Column(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().verticalScroll(scrollState)
) {
// 意见反馈类型
Row(
@ -421,6 +426,7 @@ fun FeedbackScreen(navController: NavHostController, viewModel: FeedbackViewMode
)
}
}
Box(modifier = Modifier.fillMaxWidth().height(98.dp))
}
@ -439,7 +445,7 @@ fun FeedbackScreen(navController: NavHostController, viewModel: FeedbackViewMode
interactionSource = remember { MutableInteractionSource() }
) {
if (viewModel.feedbackMore.isEmpty()) {
Toast.makeText(context, "请填写详细问题或意见...", Toast.LENGTH_SHORT).show()
CenterToast.show("请填写详细问题或意见...")
return@clickable
}
// 提交反馈

View File

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
@ -29,7 +30,7 @@ import com.img.rabbit.R
fun OnlineServiceScreen(navController: NavHostController) {
Scaffold{
Column(
modifier = Modifier.fillMaxSize().padding(it)
modifier = Modifier.fillMaxSize().padding(it).navigationBarsPadding()
) {
// 顶部栏
Row(

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
@ -66,7 +67,7 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
Scaffold{
Column(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) {
TitleBar(navController = navController, paddingValues = it, title = "设置")
@ -257,6 +258,48 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
)
}
Image(
painter = painterResource(id = R.mipmap.ic_arrow_right),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.wrapContentSize()
.padding(end = 12.dp)
.align(Alignment.CenterVertically)
)
}
Box(
modifier = Modifier.fillMaxWidth().height(0.5.dp).padding(horizontal = 12.dp).background(
Color(0x4DBBBBBB)
)
)
Row(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
// 跳转注销账号页面
navController.navigate("deleteAccount")
},
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier.weight(1f)
){
Text(
text = "注销账号",
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1A1A1A),
modifier = Modifier
.wrapContentSize()
.padding(start = 8.dp)
)
}
Image(
painter = painterResource(id = R.mipmap.ic_arrow_right),
contentDescription = null,

View File

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
@ -43,14 +44,14 @@ fun AboutScreen(navController: NavHostController) {
Scaffold{
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) {
TitleBar(navController = navController, paddingValues = it, title = "关于我们")
Box(
modifier = Modifier.fillMaxSize().padding(start = 17.dp, end = 17.dp)
){
//TODO 设置内容
// 设置内容
Column(
modifier = Modifier
.fillMaxSize()

View File

@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
@ -45,6 +46,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.pages.toolbar.TitleBar
import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.utils.AppEventBus
@ -66,7 +68,7 @@ fun AccountBindScreen(navController: NavHostController, viewModel: AccountBindVi
LaunchedEffect(viewModel.unBindState.value) {
if(viewModel.unBindState.value != null){
val loginInfo = viewModel.unBindState.value?.data
Toast.makeText(context, "解绑成功!", Toast.LENGTH_SHORT).show()
CenterToast.show("解绑成功!")
AppEventBus.post( LoginBindEvent.Bind(userId = loginInfo?.user_id?:"", loginType = null, isBind = false, data = loginInfo) )
val popped = navController.popBackStack(route = "home", inclusive = true)
@ -80,14 +82,14 @@ fun AccountBindScreen(navController: NavHostController, viewModel: AccountBindVi
}
LaunchedEffect(viewModel.errorState.value) {
if(viewModel.errorState.value != null){
Toast.makeText(context, "解绑失败...", Toast.LENGTH_SHORT).show()
CenterToast.show("解绑失败...")
viewModel.errorState.value = null
}
}
Scaffold{
Box(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
){
Column(
modifier = Modifier.fillMaxSize()

View File

@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
@ -47,6 +48,7 @@ import androidx.navigation.compose.rememberNavController
import coil3.compose.AsyncImage
import com.img.rabbit.R
import com.img.rabbit.bean.response.UserInfoEntity
import com.img.rabbit.components.CenterToast
import com.img.rabbit.pages.dialog.TipsDialog
import com.img.rabbit.pages.toolbar.TitleBar
import com.img.rabbit.provider.storage.PreferenceUtil
@ -76,7 +78,7 @@ fun AccountManagerScreen(navController: NavHostController, viewModel: AccountMan
loginInfo?.let { PreferenceUtil.saveLoginInfo(it) }
PreferenceUtil.saveAccessToken(viewModel.switchState.value?.data?.token)
AppEventBus.post( LoginBindEvent.Login(userId = loginInfo?.user_id?:"", loginType = null, isLogin = true, data = loginInfo) )
Toast.makeText(context, "切换账号成功!", Toast.LENGTH_SHORT).show()
CenterToast.show("切换账号成功!")
//延迟执行,确保返回时,接口已经拿到最新的用户信息
delay(500)
@ -86,14 +88,14 @@ fun AccountManagerScreen(navController: NavHostController, viewModel: AccountMan
}
LaunchedEffect(viewModel.errorState.value) {
if(viewModel.errorState.value != null){
Toast.makeText(context, "切换账号失败...", Toast.LENGTH_SHORT).show()
CenterToast.show("切换账号失败...")
viewModel.errorState.value = null
}
}
Scaffold{
Box(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
){
Image(
painter = painterResource(id = R.mipmap.ic_account_switch_top_mask),

View File

@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@ -65,6 +66,7 @@ import com.g.gysdk.GYManager
import com.g.gysdk.GYResponse
import com.g.gysdk.GyCallBack
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.config.Constants.agreementUrl
import com.img.rabbit.config.Constants.privacyUrl
import com.img.rabbit.pages.toolbar.TitleBar
@ -102,12 +104,12 @@ fun BindScreen(navController: NavHostController, viewModel: BindViewModel = view
LaunchedEffect(viewModel.bindState.value) {
if (viewModel.bindState.value != null && viewModel.bindState.value?.data?.token != null) {
val loginInfo = viewModel.bindState.value?.data
Toast.makeText(context, "绑定成功!", Toast.LENGTH_SHORT).show()
CenterToast.show("绑定成功!")
AppEventBus.post( LoginBindEvent.Bind(userId = loginInfo?.user_id?:"", loginType = null, isBind = true, data = loginInfo) )
navController.popBackStack()
}else if (viewModel.bindState.value != null){
Toast.makeText(context, "绑定失败!", Toast.LENGTH_SHORT).show()
CenterToast.show("绑定失败...")
}
viewModel.bindState.value = null
}
@ -116,9 +118,9 @@ fun BindScreen(navController: NavHostController, viewModel: BindViewModel = view
// 登录成功后,保存 token
LaunchedEffect(viewModel.errorState.value) {
if (viewModel.errorState.value != null) {
Toast.makeText(context, viewModel.errorState.value?.message?: "绑定失败", Toast.LENGTH_SHORT).show()
CenterToast.show(viewModel.errorState.value?.message?: "绑定失败...")
}else if (viewModel.bindState.value != null){
Toast.makeText(context, "绑定失败!", Toast.LENGTH_SHORT).show()
CenterToast.show("绑定失败...")
}
viewModel.errorState.value = null
}
@ -141,7 +143,7 @@ fun BindScreen(navController: NavHostController, viewModel: BindViewModel = view
Scaffold{
Box(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) {
Image(
painter = painterResource(id = R.mipmap.ic_main_previous_mask),
@ -742,7 +744,7 @@ private fun CaptchaBindScreen(context: Context, viewModel: BindViewModel) {
*/
private fun validatePhoneEmpty(context: Context, viewModel: BindViewModel, showToast: Boolean = false): Boolean {
if (showToast && viewModel.userName.value.isEmpty()) {
Toast.makeText(context, "请输入手机号", Toast.LENGTH_SHORT).show()
CenterToast.show("请输入手机号")
}
return viewModel.userName.value.isNotEmpty()
}
@ -753,11 +755,11 @@ private fun validatePhoneEmpty(context: Context, viewModel: BindViewModel, showT
private fun validateCaptchaLoginEmpty(context: Context, viewModel: BindViewModel, showToast: Boolean = false): Boolean {
if (showToast) {
if(viewModel.userName.value.isEmpty()){
Toast.makeText(context, "请输入手机号", Toast.LENGTH_SHORT).show()
CenterToast.show("请输入手机号")
}else if(viewModel.captcha.value.isEmpty()){
Toast.makeText(context, "请输入验证码", Toast.LENGTH_SHORT).show()
CenterToast.show("请输入验证码")
}else if(!viewModel.isPolicyAgreement.value){
Toast.makeText(context, "请同意用户协议和隐私政策", Toast.LENGTH_SHORT).show()
CenterToast.show("请同意用户协议和隐私政策")
}
}
return viewModel.userName.value.isNotEmpty() && viewModel.captcha.value.isNotEmpty() && viewModel.isPolicyAgreement.value
@ -819,11 +821,7 @@ private fun WxBindScreen(
// 打开微信登录
viewModel.requestWithWechatAuth(context, generalViewModel.api)
} else {
Toast.makeText(
context,
"请先同意用户协议和隐私政策",
Toast.LENGTH_SHORT
).show()
CenterToast.show("请先同意用户协议和隐私政策")
}
}
) {

View File

@ -0,0 +1,446 @@
package com.img.rabbit.pages.screen.mine.setting
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.img.rabbit.R
import com.img.rabbit.components.CenterToast
import com.img.rabbit.pages.toolbar.TitleBar
import com.img.rabbit.viewmodel.DeleteAccountViewModel
@Composable
fun DeleteAccountScreen(navController: NavHostController,viewModel: DeleteAccountViewModel = viewModel()) {
val inputTextValue = "我自愿注销本账号"
var isRights by remember { mutableStateOf(false) }//权益
var isDispute by remember { mutableStateOf(false) }//纠纷
var isDuty by remember { mutableStateOf(false) }//责任
var isIrrecoverable by remember { mutableStateOf(false) }//不可恢复
var inputText by remember { mutableStateOf("") }//文字
LaunchedEffect(viewModel.resultState.value) {
if (viewModel.resultState.value != null) {
CenterToast.show("注销成功!")
navController.popBackStack()
}
}
LaunchedEffect(viewModel.errorState.value) {
if (viewModel.errorState.value != null) {
CenterToast.show("注销失败...")
}
}
Scaffold{
Column(
modifier = Modifier.fillMaxSize().navigationBarsPadding().background(Color(0xFFF9F9F9))
) {
TitleBar(navController = navController, paddingValues = it, title = "注销账号")
Box(
modifier = Modifier.fillMaxSize().padding(start = 17.dp, end = 17.dp)
){
// 设置内容
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 16.dp)
.verticalScroll(rememberScrollState())
){
Image(
painter = painterResource(id = R.mipmap.ic_warning),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.size(86.dp).align(Alignment.CenterHorizontally)
)
Text(
text = "为保证你的账号安全,在你提交的注销申请生效前,需要同时满足以下条件:",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.align(Alignment.CenterHorizontally),
color = Color(0xFF3D3D3D)
)
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(top = 16.dp, bottom = 16.dp)
.align(Alignment.CenterHorizontally)
.background(Color(0xFFFFFFFF), shape = RoundedCornerShape(12.dp))
) {
//权益
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
) {
Text(
text = "1.放弃当前的VIP权益",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f),
color = Color(0xFF1A1A1A)
)
Checkbox(
checked = isRights,
onCheckedChange = { isChecked ->
isRights = isChecked
},
modifier = Modifier
.align(Alignment.CenterVertically)
.size(16.dp)
.scale(0.35f)
.padding(start = 6.dp)
.background(
if (isRights) Color(0xFF252525)
else Color.Transparent,
shape = RoundedCornerShape(36.dp)
)
.border(
width = 1.dp,
color = if (isRights) Color(0xFF252525)
else Color(0xFFCCCCCC),
shape = RoundedCornerShape(36.dp)
)
.align(Alignment.CenterVertically),
colors = androidx.compose.material3.CheckboxDefaults.colors(
checkedColor = Color.Transparent, // 隐藏默认背景
uncheckedColor = Color.Transparent, // 隐藏默认背景
checkmarkColor = Color.White
)
)
}
Text(
text = "当前充值的VIP我们不会退款请解除你的自动续费如果有同意请打勾",
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier
.padding(top = 4.dp, start = 30.dp, end = 57.dp),
color = Color(0xFF767676)
)
//纠纷
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
) {
Text(
text = "2.当前账号没有违规及其他的法律纠纷",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f),
color = Color(0xFF1A1A1A)
)
Checkbox(
checked = isDispute,
onCheckedChange = { isChecked ->
isDispute = isChecked
},
modifier = Modifier
.align(Alignment.CenterVertically)
.size(16.dp)
.scale(0.35f)
.padding(start = 6.dp)
.background(
if (isDispute) Color(0xFF252525)
else Color.Transparent,
shape = RoundedCornerShape(36.dp)
)
.border(
width = 1.dp,
color = if (isDispute) Color(0xFF252525)
else Color(0xFFCCCCCC),
shape = RoundedCornerShape(36.dp)
)
.align(Alignment.CenterVertically),
colors = androidx.compose.material3.CheckboxDefaults.colors(
checkedColor = Color.Transparent, // 隐藏默认背景
uncheckedColor = Color.Transparent, // 隐藏默认背景
checkmarkColor = Color.White
)
)
}
Text(
text = "同意请打勾",
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier
.padding(top = 4.dp, start = 30.dp, end = 57.dp),
color = Color(0xFF767676)
)
//责任
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
) {
Text(
text = "3.确认是账号本人进行注销操作,自愿承担后果",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f),
color = Color(0xFF1A1A1A)
)
Checkbox(
checked = isDuty,
onCheckedChange = { isChecked ->
isDuty = isChecked
},
modifier = Modifier
.align(Alignment.CenterVertically)
.size(16.dp)
.scale(0.35f)
.padding(start = 6.dp)
.background(
if (isDuty) Color(0xFF252525)
else Color.Transparent,
shape = RoundedCornerShape(36.dp)
)
.border(
width = 1.dp,
color = if (isDuty) Color(0xFF252525)
else Color(0xFFCCCCCC),
shape = RoundedCornerShape(36.dp)
)
.align(Alignment.CenterVertically),
colors = androidx.compose.material3.CheckboxDefaults.colors(
checkedColor = Color.Transparent, // 隐藏默认背景
uncheckedColor = Color.Transparent, // 隐藏默认背景
checkmarkColor = Color.White
)
)
}
Text(
text = "同意请打勾",
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier
.padding(top = 4.dp, start = 30.dp, end = 57.dp),
color = Color(0xFF767676)
)
//不可恢复
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
) {
Text(
text = "4.账号注销后账号不可登录,不可恢复",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f),
color = Color(0xFF1A1A1A)
)
Checkbox(
checked = isIrrecoverable,
onCheckedChange = { isChecked ->
isIrrecoverable = isChecked
},
modifier = Modifier
.align(Alignment.CenterVertically)
.size(16.dp)
.scale(0.35f)
.padding(start = 6.dp)
.background(
if (isIrrecoverable) Color(0xFF252525)
else Color.Transparent,
shape = RoundedCornerShape(36.dp)
)
.border(
width = 1.dp,
color = if (isIrrecoverable) Color(0xFF252525)
else Color(0xFFCCCCCC),
shape = RoundedCornerShape(36.dp)
)
.align(Alignment.CenterVertically),
colors = androidx.compose.material3.CheckboxDefaults.colors(
checkedColor = Color.Transparent, // 隐藏默认背景
uncheckedColor = Color.Transparent, // 隐藏默认背景
checkmarkColor = Color.White
)
)
}
Text(
text = "同意请打勾",
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier
.padding(top = 4.dp, start = 30.dp, end = 57.dp),
color = Color(0xFF767676)
)
//文字前面意愿
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
) {
Text(
text = "5.请在输入框中输入以下文字",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f),
color = Color(0xFF1A1A1A)
)
Box(modifier = Modifier.size(16.dp))
}
Text(
text = inputTextValue,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier
.padding(top = 4.dp, start = 30.dp, end = 57.dp),
color = Color(0xFFF64545)
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
.background(Color(0xFFF6F6F6), RoundedCornerShape(8.dp)).border(
width = 1.dp,
color = Color(0xFFDEDEDE),
shape = RoundedCornerShape(8.dp)
)
){
CustomTextField(value = inputText, onValueChange = {input-> inputText = input })
}
Box(modifier = Modifier.height(14.dp))
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color(0xFFFF4F4F).copy(
alpha = if(isValid(isRights, isDispute, isDuty, isIrrecoverable, inputText, inputTextValue))1f else 0.5f
), shape = RoundedCornerShape(197.dp))
.clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) {
if(isValid(isRights, isDispute, isDuty, isIrrecoverable, inputText, inputTextValue)){
// 处理确认注销逻辑
viewModel.deleteAccount()
}
}
){
Text(
text = "确认注销",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFFFFFFFF),
modifier = Modifier
.align(Alignment.Center)
)
}
Box(modifier = Modifier.height(56.dp))
}
}
}
}
}
@Composable
fun CustomTextField(
value: String,
onValueChange: (String) -> Unit,
placeholder: String = ""
) {
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color(0xFFF6F6F6), RoundedCornerShape(8.dp))
.padding(horizontal = 16.dp),
// 核心:处理占位符和对齐
decorationBox = { innerTextField ->
Box(
contentAlignment = Alignment.CenterStart, // 垂直居中
modifier = Modifier.fillMaxSize()
) {
if (value.isEmpty()) {
Text(text = placeholder, color = Color.Gray, fontSize = 14.sp)
}
innerTextField() // 实际的文本输入区域
}
},
cursorBrush = SolidColor(Color.Blue), // 自定义光标颜色
textStyle = TextStyle(color = Color.Black, fontSize = 16.sp)
)
}
private fun isValid(isRights: Boolean, isDispute: Boolean, isDuty: Boolean, isIrrecoverable: Boolean, input: String, @Suppress(
"SameParameterValue"
) inputValue: String): Boolean {
return isRights && isDispute && isDuty && isIrrecoverable && input == inputValue
}
@Preview(showBackground = true)
@Composable
private fun PreviewDeleteAccountScreen(){
DeleteAccountScreen(navController = rememberNavController())
}

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
@ -34,7 +35,7 @@ import com.img.rabbit.pages.toolbar.TitleBar
fun CameraGuideScreen(navController: NavController) {
Scaffold {
Box(
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4))
modifier = Modifier.fillMaxSize().background(Color(0xFFF4F4F4)).navigationBarsPadding()
) {
Column(
modifier = Modifier.fillMaxSize(),

View File

@ -34,7 +34,7 @@ import com.img.rabbit.route.ScreenRoute
@Composable
fun TitleBar(navController: NavController?, paddingValues: PaddingValues, title: String? = "", showSave: Boolean = false, showBreak: Boolean = true, onSubmit: (() -> Unit)? = null) {
Box(
modifier = Modifier.fillMaxWidth().padding(paddingValues)
modifier = Modifier.fillMaxWidth().padding(top = paddingValues.calculateTopPadding())
){
Row(
modifier = Modifier

View File

@ -28,5 +28,6 @@ sealed class ScreenRoute(val route: String) {
object BindAccount : ScreenRoute("bindAccount")
object ManagerAccount : ScreenRoute("managerAccount")
object AboutMine : ScreenRoute("aboutMine")
object DeleteAccount : ScreenRoute("deleteAccount")
object NetError : ScreenRoute("netError")
}

View File

@ -1,15 +1,8 @@
package com.img.rabbit.utils
import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.dcloud.android.downloader.domain.DownloadInfo
import com.github.gzuliyujiang.oaid.DeviceIdentifier
import com.google.gson.Gson
import com.img.rabbit.BuildConfig
import com.img.rabbit.bean.response.UniVersionEntity
import com.img.rabbit.components.CenterToast

View File

@ -14,6 +14,7 @@ import com.google.gson.JsonObject
import com.img.rabbit.bean.local.ErrorBean
import com.img.rabbit.bean.local.OnekeyPreLogin
import com.img.rabbit.bean.response.LoginInfoEntity
import com.img.rabbit.components.CenterToast
import com.img.rabbit.pages.screen.mine.setting.BindScreenType
import com.img.rabbit.provider.api.ApiManager
import com.img.rabbit.provider.api.ResultVo
@ -207,14 +208,14 @@ class BindViewModel : BaseViewModel() {
if (isPolicyAgreement.value) {
doWechatAuth(context, wxApi)
}else{
Toast.makeText(context, "请先同意用户协议和隐私政策", Toast.LENGTH_SHORT).show()
CenterToast.show("请先同意用户协议和隐私政策")
}
}
//获取微信授权
private fun doWechatAuth(context: Context, wxApi: IWXAPI) {
if (!wxApi.isWXAppInstalled) {
Toast.makeText(context, "您没有安装微信客户端,请先下载安装", Toast.LENGTH_SHORT).show()
CenterToast.show("您没有安装微信客户端,请先下载安装")
return
}
isBindWxAuthor = true

View File

@ -0,0 +1,32 @@
package com.img.rabbit.viewmodel
import androidx.compose.runtime.mutableStateOf
import com.img.rabbit.bean.local.ErrorBean
import com.img.rabbit.bean.local.NotifyBean
import com.img.rabbit.provider.api.ApiManager
import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.utils.AppEventBus
import com.img.rabbit.utils.LoginBindEvent
class DeleteAccountViewModel : BaseViewModel() {
// 结果状态
val resultState = mutableStateOf<NotifyBean?>(null)
// 错误状态
val errorState = mutableStateOf<ErrorBean?>(null)
fun deleteAccount() {
isLoading.value = true // 开始加载
mLaunch {
val response = ApiManager.serviceVo.userDestroy()
if (response.status) {
resultState.value = NotifyBean(response.code.toString(), response.message.ifEmpty { "已注销!" })
PreferenceUtil.clearLogin()
AppEventBus.post( LoginBindEvent.Login(userId = null, loginType = PreferenceUtil.getLoginType(), isLogin = false, data = null) )
}else{
errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "注销失败..." })
}
isLoading.value = false // 开始加载
}
}
}

View File

@ -4,7 +4,6 @@ import android.app.Activity
import android.content.Context
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.State
@ -21,6 +20,7 @@ import com.img.rabbit.bean.local.OnekeyPreLogin
import com.img.rabbit.bean.local.toAlipayResult
import com.img.rabbit.bean.response.LoginInfoEntity
import com.img.rabbit.bean.response.UserConfigEntity
import com.img.rabbit.components.CenterToast
import com.img.rabbit.provider.api.ApiManager
import com.img.rabbit.provider.api.ResultVo
import com.img.rabbit.provider.storage.GlobalStateManager
@ -270,7 +270,7 @@ class LoginViewModel : BaseViewModel() {
fun authorWechat(context: Context, wxApi:IWXAPI) {
if (isPolicyAgreement.value) {
if (!wxApi.isWXAppInstalled) {
Toast.makeText(context, "您没有安装微信客户端,请先下载安装", Toast.LENGTH_SHORT).show()
CenterToast.show("您没有安装微信客户端,请先下载安装")
return
}
isLoginWxAuthor = true
@ -279,7 +279,7 @@ class LoginViewModel : BaseViewModel() {
req.state = context.packageName + Math.random() * 1000 + "_phone"
wxApi.sendReq(req)
}else{
Toast.makeText(context, "请先同意用户协议和隐私政策", Toast.LENGTH_SHORT).show()
CenterToast.show("请先同意用户协议和隐私政策")
}
}
@ -331,7 +331,7 @@ class LoginViewModel : BaseViewModel() {
private fun authorAlipay(context: Context, authParam: String){
if(authParam.isEmpty()){
Toast.makeText(context, "请先获取支付宝登录授权码", Toast.LENGTH_SHORT).show()
CenterToast.show("请先获取支付宝登录授权码")
return
}
// 发送请求获取支付宝登录授权码
@ -436,13 +436,7 @@ class LoginViewModel : BaseViewModel() {
mLaunch {
val response = ApiManager.serviceVo.logout()
if (response.status) {
//logoutState.value = response
PreferenceUtil.clearLogin()
// setLogin(false)
// 跳转登录页面
// GlobalScope.launch {
// GlobalStateManager(context).storeGlobalLogoutNotify(true)
// }
AppEventBus.post( LoginBindEvent.Login(userId = null, loginType = PreferenceUtil.getLoginType(), isLogin = false, data = null) )
} else {
errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "退出登录失败" })

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -13,7 +13,7 @@ constraintlayout = "2.1.4"
splashscreen = "1.0.1"
datastoreCore = "1.2.0"
datastorePreferences = "1.1.1"
multidex = "2.0.1"
#multidex = "2.0.1"
# Compose Version
composeBom = "2024.05.00"
@ -80,7 +80,7 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
#multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
# Compose dependencies
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }