个人资料

master
pan 3 years ago
parent a38dcf6587
commit 4145e3fbef
  1. 27
      background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  2. 3
      background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt
  3. 3
      background/src/main/java/com/gyf/csams/uikit/CheckForm.kt
  4. 34
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  5. 5
      foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt
  6. 21
      foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt
  7. 12
      foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt
  8. 31
      foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  9. 40
      foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
  10. 66
      foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  11. 88
      foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  12. 1
      foreground/src/main/res/values-en/strings.xml
  13. 1
      foreground/src/main/res/values-zh/strings.xml
  14. 1
      foreground/src/main/res/values/strings.xml
  15. 57
      lib/src/main/java/com/gyf/lib/model/ChoosePhotoViewModel.kt
  16. 61
      lib/src/main/java/com/gyf/lib/model/RefreshViewModel.kt
  17. 83
      lib/src/main/java/com/gyf/lib/uikit/ChoosePhoto.kt
  18. 133
      lib/src/main/java/com/gyf/lib/uikit/Profile.kt
  19. 2
      lib/src/main/java/com/gyf/lib/util/Api.kt
  20. 64
      lib/src/main/java/com/gyf/lib/util/TokenUtil.kt
  21. 1
      lib/src/main/res/values-en/strings.xml
  22. 1
      lib/src/main/res/values-zh/strings.xml
  23. 1
      lib/src/main/res/values/strings.xml

@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -17,16 +19,16 @@ import com.gyf.csams.R
import com.gyf.csams.account.model.LoginViewModel
import com.gyf.csams.main.model.MenuType
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.ManagerInfoVo
import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.*
import com.gyf.lib.util.TokenManager
class MainActivity : BaseActivity() {
override val clientType: ClientType = ClientType.Background
private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -34,17 +36,28 @@ class MainActivity : BaseActivity() {
Body {
val loginViewModel: LoginViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel()
MainColumnFrame(background = { /*TODO*/ }) {
(TokenManager.getPersonInfo() as? ManagerInfoVo)?.let {
MainColumnFrame(background = { }) {
val photoLauncher = choosePhoto.photoLauncher()
val permissionLauncher =
choosePhoto.permissionLauncher(photoLauncher = photoLauncher)
val personInfoData by TokenManager.getPersonInfoData().observeAsState()
personInfoData?.let {
Profile(
modifier = Modifier
.weight(0.3F)
.weight(0.4F)
.padding(10.dp),
personInfoVo = it
personInfoVo = it,
setHeadImg = {
choosePhoto.requestPhoto(
photoLauncher = photoLauncher,
permissionLauncher = permissionLauncher
)
}
)
}
Column(
modifier = Modifier.weight(0.7F),
modifier = Modifier.weight(0.6F),
verticalArrangement = Arrangement.SpaceEvenly
) {

@ -104,7 +104,8 @@ class ManagementOfficerActivity : ComponentActivity() {
.width(200.dp)
.fillMaxHeight()
.border(width = 1.dp, color = MaterialTheme.colors.onBackground),
personInfoVo = it.value
personInfoVo = it.value,
readOnly = true
) {
Logger.i("expanded=$expanded")
Text(

@ -14,7 +14,6 @@ import com.gyf.csams.R
import com.gyf.csams.main.model.BaseAuditViewModel
import com.gyf.csams.module.AuditVo
import com.gyf.csams.module.Duty
import com.gyf.csams.module.ManagerVo
import com.gyf.lib.uikit.*
import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.TokenManager
@ -78,7 +77,7 @@ inline fun <reified VO : AuditVo, reified M : BaseAuditViewModel<VO>> CheckForm(
) {
(TokenManager.getOwnInfo() as? ManagerVo)?.let { it ->
TokenManager.getManagerInfo()?.let { it ->
var confirmDesc: Int? = null
var backDesc: Int? = null
var onConfirm: (() -> Unit)? = null

@ -3,7 +3,6 @@ package com.gyf.csams.account.model
import android.app.Activity
import android.app.Application
import android.os.Build
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@ -11,11 +10,17 @@ import com.google.gson.reflect.TypeToken
import com.gyf.csams.R
import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.account.ui.AccountRoute
import com.gyf.csams.module.*
import com.gyf.csams.module.ApiResponse
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.UserLoginVo
import com.gyf.csams.module.UserRegVo
import com.gyf.lib.model.AbstractLoginViewModel
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.*
import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.Api
import com.gyf.lib.util.HttpCallback
import com.gyf.lib.util.HttpClient
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -204,26 +209,3 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
}
class RefreshViewModel(application: Application) : AndroidViewModel(application) {
private val _refresh = MutableLiveData<Boolean>()
val refresh: LiveData<Boolean> = _refresh
fun refresh() {
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(AccountApi.Refresh),
callback = HttpCallback<UserVo>(action = "刷新个人信息", onSuccess = { it ->
it.body.let {
if (it != null) {
TokenManager.update(it)
}
_refresh.postValue(it != null)
}
}, typeToken = object : TypeToken<ApiResponse<UserVo>>() {}.type),
jsonParam = OnlyToken(clientType = ClientType.Foreground)
)
}
}
}

@ -586,11 +586,8 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
* 提交申请
*
*/
fun apply(callback: (message: String) -> Unit) {
fun apply(associationId: Int, callback: (message: String) -> Unit) {
val associationId = (TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId
?: throw IllegalArgumentException("社团id为空")
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(ActivityApi.Register),

@ -379,21 +379,14 @@ class ActivityDetailActivity : ComponentActivity() {
contentAlignment = Alignment.Center
) {
val photo = rememberCoilPainter(request = Api.buildUrl(vo.url))
Image(
painter = photo,
contentDescription = vo.name,
)
when (photo.loadState) {
is ImageLoadState.Loading -> {
// Display a circular progress indicator whilst loading
CircularProgressIndicator(Modifier.align(Alignment.Center))
}
is ImageLoadState.Error -> {
// If you wish to display some content if the request fails
}
else -> {
}
when (photo.loadState) {
is ImageLoadState.Loading -> CircularProgressIndicator(Modifier.align(Alignment.Center))
is ImageLoadState.Success -> Image(
painter = photo,
contentDescription = vo.name,
)
else -> Text(text = "图片加载失败")
}
}
}

@ -41,6 +41,7 @@ import com.gyf.csams.MainApplication
import com.gyf.csams.MyLocationListener
import com.gyf.csams.R
import com.gyf.csams.activity.model.ApplyActViewModel
import com.gyf.csams.association.ui.AssociationActivity
import com.gyf.csams.module.CheckStatus
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
@ -71,6 +72,15 @@ class ApplyActActivity : AppCompatActivity() {
private lateinit var mLocationClient: LocationClient
private val associationId: Int
get() {
val id = intent.getIntExtra(
AssociationActivity::class.java.name,
0
)
return if (id == 0) throw IllegalArgumentException("不存在社团id,获取失败") else id
}
override fun onStop() {
super.onStop()
mLocationClient.stop()
@ -216,7 +226,7 @@ class ApplyActActivity : AppCompatActivity() {
),
confirmDesc = if (checkInfo?.body != null) R.string.reg_again_btn else R.string.confirm_btn,
) {
model.apply {
model.apply(associationId = associationId) {
scaffoldModel.update(message = it, actionLabel = "返回") {
onBackPressed()
}

@ -4,7 +4,6 @@ import android.app.Application
import android.graphics.Bitmap
import android.net.Uri
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@ -17,6 +16,7 @@ import com.gyf.csams.module.AssociationCheckVo
import com.gyf.csams.module.AssociationRegVo
import com.gyf.csams.module.ClientType
import com.gyf.lib.BuildConfig
import com.gyf.lib.model.ChoosePhotoViewModel
import com.gyf.lib.uikit.AsyncStringForm
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.util.*
@ -24,19 +24,15 @@ import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
class RegAssociationViewModel(application: Application) : AndroidViewModel(application) {
class RegAssociationViewModel(application: Application) : ChoosePhotoViewModel(application) {
val frameDesc = "社团注册资料"
val name = AsyncStringForm(formDesc = "社团名称", textLength = 5)
val desc = AsyncStringForm(formDesc = "社团简介", textLength = 30)
private val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri?> = _picture
private val _fileId = MutableLiveData<Int>()
val fileId: LiveData<Int> = _fileId
@ -51,17 +47,6 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
read()
}
fun setPicture(uri: Uri) {
_picture.value = uri
}
private fun getInputSteam(): InputStream? {
_picture.value?.let {
val resolver = getApplication<MainApplication>().contentResolver
return resolver.openInputStream(it)
}
throw IllegalArgumentException(UNKNOW_ERROR)
}
/**
* 加载历史提交的注册资料可能为空
@ -73,17 +58,17 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
Api.buildUrl(AssociationApi.Read),
HttpCallback<AssociationCheckVo>(action = "加载历史注册资料", onSuccess = { it ->
_checkInfo.postValue(it)
it.body?.let { it ->
name.setValue(it.associationVo.name)
desc.setValue(it.associationVo.desc)
it.body?.let { checkVo ->
name.setValue(checkVo.associationVo.name)
desc.setValue(checkVo.associationVo.desc)
val context = getApplication<Application>()
val request = ImageRequest.Builder(context)
.data("${BuildConfig.SERVER_ADDRESS}/${it.associationVo.logo}")
.data("${BuildConfig.SERVER_ADDRESS}/${checkVo.associationVo.logo}")
.target(
onSuccess = { result ->
it.associationVo.logo.split("/").apply {
checkVo.associationVo.logo.split("/").apply {
File.createTempFile(last(), null, context.cacheDir).apply {
Logger.d("文件路径:${absolutePath}")
FileOutputStream(this).use {
@ -97,7 +82,7 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
.apply {
Logger.i("读取缓存图片")
setPicture(Uri.fromFile(this))
_fileId.postValue(it.fileId)
_fileId.postValue(checkVo.fileId)
}
}

@ -400,45 +400,15 @@ class AssociationActivity : ComponentActivity() {
}
val data by model.data.observeAsState()
val listState = rememberLazyListState()
LazyColumn(state = listState, modifier = modifier) {
LazyColumn(
state = listState, modifier = modifier, verticalArrangement =
Arrangement.spacedBy(space = 10.dp, alignment = Alignment.CenterVertically)
) {
data?.forEach {
item {
Column {
Spacer(modifier = Modifier.height(10.dp))
Row {
val weight = 1F / 3
Spacer(modifier = Modifier.weight(weight))
Card(
modifier = Modifier.weight(weight),
backgroundColor = MaterialTheme.colors.background
) {
Text(text = it.name)
}
Spacer(modifier = Modifier.weight(weight))
}
Spacer(modifier = Modifier.height(10.dp))
Divider(color = MaterialTheme.colors.background)
}
Profile(modifier = Modifier.height(300.dp), personInfoVo = it, readOnly = true)
}
}
item {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
IconButton(onClick = {
model.load(associationId) {
}
}) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_down),
contentDescription = null
)
}
}
}
}
}

@ -1,15 +1,10 @@
package com.gyf.csams.association.ui
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
@ -24,7 +19,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.coil.rememberCoilPainter
import com.gyf.csams.R
@ -43,6 +37,9 @@ import com.orhanobut.logger.Logger
*
*/
class RegAssociationActivity : ComponentActivity() {
private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
@ -111,7 +108,6 @@ class RegAssociationActivity : ComponentActivity() {
}
/**
* 社团Logo
*
@ -123,42 +119,11 @@ class RegAssociationActivity : ComponentActivity() {
scaffoldModel: ScaffoldModel = viewModel(),
modifier: Modifier
) {
val photoIntent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
val uri: Uri? by model.picture.observeAsState()
val resultLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) {
RESULT_OK -> {
it.data?.data?.apply {
Logger.i("uri=$this")
model.setPicture(this)
}
}
}
}
val loadPicture = {
//model.loadPicture(context)
resultLauncher.launch(photoIntent)
}
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// Permission Accepted: Do something
loadPicture()
} else {
// Permission Denied: Do something
scaffoldModel.update(message = getString(R.string.denined_photo_permission))
}
}
val uri: Uri? by model.picture.observeAsState()
val photoLauncher = choosePhoto.photoLauncher(model = model)
val permissionLauncher = choosePhoto.permissionLauncher(photoLauncher = photoLauncher)
Box(contentAlignment = Alignment.Center, modifier = modifier) {
@ -171,19 +136,10 @@ class RegAssociationActivity : ComponentActivity() {
if (uri == null) {
OutlinedButton(onClick = {
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
applicationContext,
Manifest.permission.READ_EXTERNAL_STORAGE
) -> {
// Some works that require permission
loadPicture()
}
else -> {
// Asking for permission
launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
choosePhoto.requestPhoto(
photoLauncher = photoLauncher,
permissionLauncher = permissionLauncher
)
}) {
Text(text = model.picturePlaceHolder)
}
@ -201,7 +157,7 @@ class RegAssociationActivity : ComponentActivity() {
verticalArrangement = Arrangement.SpaceEvenly
) {
IconButton(onClick = {
loadPicture()
choosePhoto.loadPicture(photoLauncher = photoLauncher)
}) {
Image(
painter = painterResource(id = R.drawable.ic_exchange_rate),

@ -33,7 +33,6 @@ import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState
import com.gyf.csams.R
import com.gyf.csams.account.model.AccountViewModel
import com.gyf.csams.account.model.RefreshViewModel
import com.gyf.csams.activity.ui.ActivityDetailActivity
import com.gyf.csams.association.ui.AssociationActivity
import com.gyf.csams.association.ui.RegAssociationActivity
@ -41,6 +40,7 @@ import com.gyf.csams.main.model.*
import com.gyf.csams.message.ui.SysMessageActivity
import com.gyf.csams.module.*
import com.gyf.csams.uikit.*
import com.gyf.lib.model.RefreshViewModel
import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.*
import com.gyf.lib.util.*
@ -55,6 +55,8 @@ class MainActivity : BaseActivity() {
override val clientType: ClientType = ClientType.Foreground
private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this)
@ExperimentalPagerApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -100,23 +102,36 @@ class MainActivity : BaseActivity() {
accountViewModel: AccountViewModel = viewModel(),
navController: NavHostController
) {
MainColumnFrame(
background = { Background(image = BackgroundImage.Center, alpha = 0.5F) },
mainMenu = MainMenu.Center,
nav = navController
) {
Profile(
modifier = Modifier
.weight(0.3F)
.padding(10.dp)
)
val photoLauncher = choosePhoto.photoLauncher()
val permissionLauncher = choosePhoto.permissionLauncher(photoLauncher = photoLauncher)
val personInfoData by TokenManager.getPersonInfoData().observeAsState()
personInfoData?.let {
Profile(
modifier = Modifier
.weight(0.4F)
.padding(10.dp),
setHeadImg = {
choosePhoto.requestPhoto(
photoLauncher = photoLauncher,
permissionLauncher = permissionLauncher
)
},
personInfoVo = it
)
}
Column(
modifier = Modifier.weight(0.7F),
modifier = Modifier.weight(0.6F),
verticalArrangement = Arrangement.SpaceEvenly
) {
(TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId?.let {
TokenManager.getUserInfo()?.associationVo?.associationId?.let {
CenterMenuItem(text = model.myAssociationDesc) {
startActivity(
Intent(
@ -215,8 +230,7 @@ class MainActivity : BaseActivity() {
mainMenu = MainMenu.List,
nav = navController
) {
val associationVo: AssociationVo? =
(TokenManager.getOwnInfo() as? UserVo)?.associationVo
val associationVo = TokenManager.getUserInfo()?.associationVo
if (associationVo == null) {
RegisterAssociation(navController = navController)
}
@ -506,37 +520,31 @@ class MainActivity : BaseActivity() {
.fillMaxWidth()
) {
when (photo.loadState) {
is ImageLoadState.Loading -> {
// Display a circular progress indicator whilst loading
CircularProgressIndicator()
is ImageLoadState.Loading -> CircularProgressIndicator()
is ImageLoadState.Success -> it[page].apply {
Image(
painter = photo,
contentDescription = activityVo.activityName,
modifier = Modifier.clickable(onClick = {
startActivity(
Intent(
this@MainActivity,
ActivityDetailActivity::class.java
).apply {
putExtra(
ActivityDetailActivity::class.java.name,
activityVo.activityId
)
putExtra(
AssociationActivity::class.java.name,
associationVo.associationId
)
})
})
)
}
is ImageLoadState.Error -> {
// If you wish to display some content if the request fails
Logger.e("轮播图加载失败")
}
else ->
it[page].apply {
Image(
painter = photo,
contentDescription = activityVo.activityName,
modifier = Modifier.clickable(onClick = {
startActivity(
Intent(
this@MainActivity,
ActivityDetailActivity::class.java
).apply {
putExtra(
ActivityDetailActivity::class.java.name,
activityVo.activityId
)
putExtra(
AssociationActivity::class.java.name,
associationVo.associationId
)
})
})
)
}
else -> Text(text = "图片加载失败")
}
}

@ -6,7 +6,6 @@
<string name="join_association">申请入团</string>
<string name="update_exam">更新题库</string>
<string name="post_answer">提交答案</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string>
<string name="at"></string>
<string name="city">城市</string>

@ -6,7 +6,6 @@
<string name="join_association">申请入团</string>
<string name="update_exam">更新题库</string>
<string name="post_answer">提交答案</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string>
<string name="at"></string>
<string name="city">城市</string>

@ -6,7 +6,6 @@
<string name="join_association">申请入团</string>
<string name="update_exam">更新题库</string>
<string name="post_answer">提交答案</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string>
<string name="at"></string>
<string name="city">城市</string>

@ -0,0 +1,57 @@
package com.gyf.lib.model
import android.app.Application
import android.net.Uri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.ApiResponse
import com.gyf.lib.util.*
import kotlinx.coroutines.launch
import java.io.File
import java.io.InputStream
open class ChoosePhotoViewModel(application: Application) : AndroidViewModel(application) {
protected val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri?> = _picture
fun setPicture(uri: Uri) {
_picture.value = uri
}
protected fun getInputSteam(): InputStream? {
_picture.value?.let {
val resolver = getApplication<Application>().contentResolver
return resolver.openInputStream(it)
}
throw IllegalArgumentException("图片uri为空")
}
/**
* 上传头像
*
* @param callback
*/
fun upload(callback: (flag: Boolean) -> Unit) {
viewModelScope.launch {
getInputSteam()?.readBytes()?.apply {
val context = getApplication<Application>()
runCatching {
val cacheFile = File(context.cacheDir, "${System.currentTimeMillis()}")
cacheFile.writeBytes(this@apply)
HttpClient.uploadFile(
url = Api.buildUrl(AccountApi.UploadHeadimg),
HttpCallback<Boolean>(action = "上传头像", onSuccess = {
it.body?.let(callback)
_picture.postValue(null)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
fileList = arrayOf(cacheFile)
)
}
}
}
}
}

@ -0,0 +1,61 @@
package com.gyf.lib.model
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.*
import com.gyf.lib.uikit.AsyncStringForm
import com.gyf.lib.util.*
import kotlinx.coroutines.launch
class RefreshViewModel(application: Application) : AndroidViewModel(application) {
private val _refresh = MutableLiveData<Boolean>()
val refresh: LiveData<Boolean> = _refresh
val name = AsyncStringForm(formDesc = "姓名", textLength = 4)
val desc = AsyncStringForm(formDesc = "简介", textLength = 20)
fun <T : OwnInfoVo> onSuccess(res: ApiResponse<T>) {
res.body.let {
if (it != null) {
TokenManager.update(it)
}
_refresh.postValue(it != null)
}
}
fun refresh() {
viewModelScope.launch {
val jsonParam = InfoUpdateVo(
name = name.formValue.value, desc = desc.formValue.value,
token = TokenManager.getToken(),
clientType = TokenManager.getClientType()
)
when (jsonParam.clientType) {
ClientType.Foreground -> HttpClient.post(
url = Api.buildUrl(AccountApi.Refresh),
callback = HttpCallback<UserVo>(
action = "刷新个人信息",
onSuccess = { onSuccess(it) },
typeToken = object : TypeToken<ApiResponse<UserVo>>() {}.type
),
jsonParam = jsonParam
)
ClientType.Background -> HttpClient.post(
url = Api.buildUrl(AccountApi.Refresh),
callback = HttpCallback<ManagerVo>(
action = "刷新个人信息",
onSuccess = { onSuccess(it) },
typeToken = object : TypeToken<ApiResponse<ManagerVo>>() {}.type
),
jsonParam = jsonParam
)
}
}
}
}

@ -0,0 +1,83 @@
package com.gyf.lib.uikit
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.provider.MediaStore
import androidx.activity.ComponentActivity
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.lib.R
import com.gyf.lib.model.ChoosePhotoViewModel
import com.orhanobut.logger.Logger
class ChoosePhoto(val activity: ComponentActivity) {
fun loadPicture(photoLauncher: ManagedActivityResultLauncher<Intent, *>) {
photoLauncher.launch(
Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
)
}
@Composable
fun photoLauncher(model: ChoosePhotoViewModel = viewModel()): ManagedActivityResultLauncher<Intent, ActivityResult> {
return rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) {
ComponentActivity.RESULT_OK -> {
it.data?.data?.apply {
Logger.i("uri=$this")
model.setPicture(this)
}
}
}
}
}
@Composable
fun permissionLauncher(
photoLauncher: ManagedActivityResultLauncher<Intent, *>,
scaffoldModel: ScaffoldModel = viewModel()
): ManagedActivityResultLauncher<String, Boolean> {
return rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// Permission Accepted: Do something
loadPicture(photoLauncher = photoLauncher)
} else {
// Permission Denied: Do something
scaffoldModel.update(message = activity.getString(R.string.denined_photo_permission))
}
}
}
fun requestPhoto(
photoLauncher: ManagedActivityResultLauncher<Intent, *>,
permissionLauncher: ManagedActivityResultLauncher<String, Boolean>
) {
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
activity.applicationContext,
Manifest.permission.READ_EXTERNAL_STORAGE
) -> {
// Some works that require permission
loadPicture(photoLauncher = photoLauncher)
}
else -> {
// Asking for permission
permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
}
}

@ -2,43 +2,56 @@ package com.gyf.lib.uikit
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.coil.rememberCoilPainter
import com.google.accompanist.imageloading.ImageLoadState
import com.gyf.csams.module.ManagerInfoVo
import com.gyf.csams.module.ManagerVo
import com.gyf.csams.module.PersonInfoVo
import com.gyf.csams.module.UserVo
import com.gyf.lib.model.ChoosePhotoViewModel
import com.gyf.lib.model.RefreshViewModel
import com.gyf.lib.util.Api
import com.gyf.lib.util.TokenManager
import com.orhanobut.logger.Logger
@Composable
fun Profile(
modifier: Modifier,
personInfoVo: PersonInfoVo = TokenManager.getOwnInfo(),
personInfoVo: PersonInfoVo,
setHeadImg: () -> Unit = {},
choosePhotoViewModel: ChoosePhotoViewModel = viewModel(),
refreshViewModel: RefreshViewModel = viewModel(),
readOnly: Boolean = false,
duty: @Composable () -> Unit = {
Text(
text = when (personInfoVo) {
is UserVo -> personInfoVo.manager?.duty?.desc ?: "----"
is ManagerVo -> personInfoVo.duty.desc
is ManagerInfoVo -> personInfoVo.duty.desc
else -> throw IllegalArgumentException("个人信息类型错误:${personInfoVo}")
else -> "-----"
}
)
}
) {
Column(
modifier = modifier,
modifier = modifier.border(1.dp, Color.Black),
verticalArrangement = Arrangement.SpaceEvenly
) {
var isEdit by remember {
mutableStateOf(false)
}
Logger.i("info:${personInfoVo}")
Row(
modifier = Modifier
.fillMaxWidth()
@ -46,28 +59,51 @@ fun Profile(
.border(width = 1.dp, color = MaterialTheme.colors.onBackground),
horizontalArrangement = Arrangement.SpaceBetween
) {
val picture by choosePhotoViewModel.picture.observeAsState()
picture?.let {
Logger.i("picture=${it}")
choosePhotoViewModel.upload {
refreshViewModel.refresh()
}
}
personInfoVo.headImg.let {
if (it != null) {
val headImg = rememberCoilPainter(request = Api.buildUrl(it))
Image(
painter = headImg,
contentDescription = null,
modifier = Modifier
.weight(0.4F)
.fillMaxHeight()
)
} else {
Box(
when {
it != null -> {
val headImg = rememberCoilPainter(request = Api.buildUrl(it))
when (headImg.loadState) {
is ImageLoadState.Loading ->
Box(
modifier = Modifier
.weight(0.4F)
.fillMaxHeight(), contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
is ImageLoadState.Success -> Image(
painter = headImg,
contentDescription = null,
modifier = Modifier
.weight(0.4F)
.fillMaxHeight()
.clickable(onClick = setHeadImg)
)
else -> Text(text = "图片加载失败")
}
}
!readOnly -> Box(
modifier = Modifier
.weight(0.4F)
.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
Text(text = "没有头像")
OutlinedButton(onClick = setHeadImg) {
Text(text = "设置头像")
}
}
}
}
Column(
modifier = Modifier
.weight(0.4F)
@ -76,23 +112,64 @@ fun Profile(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = personInfoVo.name)
if (isEdit) {
BaseTextField(form = refreshViewModel.name)
} else {
Text(text = personInfoVo.name)
}
duty()
}
}
Spacer(modifier = Modifier.weight(0.05F))
Card(
backgroundColor = MaterialTheme.colors.background,
modifier = Modifier.weight(0.15F)
modifier = Modifier.weight(0.3F)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
modifier = Modifier.fillMaxWidth()
) {
Text(text = personInfoVo.desc)
Box(
contentAlignment = Alignment.Center, modifier = Modifier
.weight(0.8F)
.fillMaxHeight()
) {
if (isEdit) {
BaseTextField(form = refreshViewModel.desc)
} else {
Text(text = personInfoVo.desc)
}
}
if (!readOnly) {
Box(
modifier = Modifier
.weight(0.2F)
.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
OutlinedButton(onClick = {
isEdit = !isEdit
if (isEdit) {
refreshViewModel.name.setValue(personInfoVo.name)
refreshViewModel.desc.setValue(personInfoVo.desc)
} else {
refreshViewModel.refresh()
}
}) {
if (isEdit) {
Text(text = "完成")
} else {
Text(text = "编辑")
}
}
}
}
}
}
}
}

@ -49,6 +49,8 @@ enum class AccountApi(val path: String) : UrlPath {
//加载部门干事概况
Load("/load/manager"),
UploadHeadimg("/upload/img"),
//加载部门详情
LoadDetail("${Load.path}/detail");

@ -23,7 +23,7 @@ class ClientToken(
data class OnlyToken(
override val clientType: ClientType
override val clientType: ClientType = TokenManager.getClientType()
) : ClientBaseVo() {
override val token: ClientToken = TokenManager.getToken()
}
@ -46,51 +46,69 @@ interface TokenDao {
object TokenManager {
private var ownInfo: OwnInfoVo? = null
private var personInfo: PersonInfoVo? = null
private var clientToken: ClientToken? = null
private val ownInfo = MutableLiveData<OwnInfoVo?>()
private val personInfo = MutableLiveData<PersonInfoVo?>()
private val clientToken = MutableLiveData<ClientToken?>()
fun update(ownInfo: OwnInfoVo) {
this.ownInfo = ownInfo
this.ownInfo.postValue(ownInfo)
ownInfo.token.let {
clientToken = ClientToken(id = it.id, token = it.token, createTime = it.createTime)
clientToken.postValue(
ClientToken(
id = it.id,
token = it.token,
createTime = it.createTime
)
)
}
when (ownInfo) {
is ManagerVo -> this.personInfo = ManagerInfoVo(
duty = ownInfo.duty,
name = ownInfo.name,
headImg = ownInfo.headImg,
desc = ownInfo.desc
is ManagerVo -> this.personInfo.postValue(
ManagerInfoVo(
duty = ownInfo.duty,
name = ownInfo.name,
headImg = ownInfo.headImg,
desc = ownInfo.desc
)
)
is UserVo -> this.personInfo.postValue(
UserInfoVo(
name = ownInfo.name,
headImg = ownInfo.headImg,
desc = ownInfo.desc
)
)
is UserVo -> this.personInfo =
UserInfoVo(name = ownInfo.name, headImg = ownInfo.headImg, desc = ownInfo.desc)
else -> throw IllegalArgumentException("token初始化失败")
}
Logger.i("token刷新完成")
}
fun clear() {
ownInfo = null
ownInfo.postValue(null)
personInfo.postValue(null)
clientToken.postValue(null)
}
fun getOwnInfo(): OwnInfoVo {
return ownInfo ?: throw IllegalArgumentException("token没有初始化,非法调用")
fun getClientType(): ClientType {
return when (ownInfo.value) {
is UserVo -> ClientType.Foreground
is ManagerVo -> ClientType.Background
else -> throw IllegalArgumentException("个人信息还没有初始化")
}
}
fun getUserInfo(): UserVo? {
return ownInfo as? UserVo
return ownInfo.value as? UserVo
}
fun getManagerInfo(): ManagerVo? {
return ownInfo as? ManagerVo
fun getPersonInfoData(): LiveData<PersonInfoVo?> {
return personInfo
}
fun getPersonInfo(): PersonInfoVo {
return personInfo ?: throw IllegalArgumentException("个人信息没有初始化,非法调用")
fun getManagerInfo(): ManagerVo? {
return ownInfo.value as? ManagerVo
}
fun getToken(): ClientToken {
return clientToken ?: throw IllegalArgumentException("token没有初始化,非法调用")
return clientToken.value ?: throw IllegalArgumentException("token没有初始化,非法调用")
}
}

@ -33,4 +33,5 @@
<string name="activity_date">活动日期</string>
<string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
</resources>

@ -33,4 +33,5 @@
<string name="activity_date">活动日期</string>
<string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
</resources>

@ -33,4 +33,5 @@
<string name="activity_date">活动日期</string>
<string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
</resources>
Loading…
Cancel
Save