个人资料

master
pan 4 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.foundation.layout.padding
import androidx.compose.material.OutlinedButton import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text 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.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp 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.account.model.LoginViewModel
import com.gyf.csams.main.model.MenuType import com.gyf.csams.main.model.MenuType
import com.gyf.csams.module.ClientType import com.gyf.csams.module.ClientType
import com.gyf.csams.module.ManagerInfoVo
import com.gyf.lib.service.BaseActivity import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.TokenManager import com.gyf.lib.util.TokenManager
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
override val clientType: ClientType = ClientType.Background override val clientType: ClientType = ClientType.Background
private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -34,17 +36,28 @@ class MainActivity : BaseActivity() {
Body { Body {
val loginViewModel: LoginViewModel = viewModel() val loginViewModel: LoginViewModel = viewModel()
val scaffoldModel: ScaffoldModel = 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( Profile(
modifier = Modifier modifier = Modifier
.weight(0.3F) .weight(0.4F)
.padding(10.dp), .padding(10.dp),
personInfoVo = it personInfoVo = it,
setHeadImg = {
choosePhoto.requestPhoto(
photoLauncher = photoLauncher,
permissionLauncher = permissionLauncher
)
}
) )
} }
Column( Column(
modifier = Modifier.weight(0.7F), modifier = Modifier.weight(0.6F),
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {

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

@ -14,7 +14,6 @@ import com.gyf.csams.R
import com.gyf.csams.main.model.BaseAuditViewModel import com.gyf.csams.main.model.BaseAuditViewModel
import com.gyf.csams.module.AuditVo import com.gyf.csams.module.AuditVo
import com.gyf.csams.module.Duty import com.gyf.csams.module.Duty
import com.gyf.csams.module.ManagerVo
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.TokenManager 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 confirmDesc: Int? = null
var backDesc: Int? = null var backDesc: Int? = null
var onConfirm: (() -> Unit)? = null var onConfirm: (() -> Unit)? = null

@ -3,7 +3,6 @@ package com.gyf.csams.account.model
import android.app.Activity import android.app.Activity
import android.app.Application import android.app.Application
import android.os.Build import android.os.Build
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -11,11 +10,17 @@ import com.google.gson.reflect.TypeToken
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.account.ui.AccountActivity import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.account.ui.AccountRoute 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.model.AbstractLoginViewModel
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm 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 com.orhanobut.logger.Logger
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch 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 { viewModelScope.launch {
HttpClient.post( HttpClient.post(
url = Api.buildUrl(ActivityApi.Register), url = Api.buildUrl(ActivityApi.Register),

@ -379,21 +379,14 @@ class ActivityDetailActivity : ComponentActivity() {
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
val photo = rememberCoilPainter(request = Api.buildUrl(vo.url)) 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.MyLocationListener
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.activity.model.ApplyActViewModel import com.gyf.csams.activity.model.ApplyActViewModel
import com.gyf.csams.association.ui.AssociationActivity
import com.gyf.csams.module.CheckStatus import com.gyf.csams.module.CheckStatus
import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
@ -71,6 +72,15 @@ class ApplyActActivity : AppCompatActivity() {
private lateinit var mLocationClient: LocationClient 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() { override fun onStop() {
super.onStop() super.onStop()
mLocationClient.stop() mLocationClient.stop()
@ -216,7 +226,7 @@ class ApplyActActivity : AppCompatActivity() {
), ),
confirmDesc = if (checkInfo?.body != null) R.string.reg_again_btn else R.string.confirm_btn, 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 = "返回") { scaffoldModel.update(message = it, actionLabel = "返回") {
onBackPressed() onBackPressed()
} }

@ -4,7 +4,6 @@ import android.app.Application
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope 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.AssociationRegVo
import com.gyf.csams.module.ClientType import com.gyf.csams.module.ClientType
import com.gyf.lib.BuildConfig import com.gyf.lib.BuildConfig
import com.gyf.lib.model.ChoosePhotoViewModel
import com.gyf.lib.uikit.AsyncStringForm import com.gyf.lib.uikit.AsyncStringForm
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.util.* import com.gyf.lib.util.*
@ -24,19 +24,15 @@ import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream
class RegAssociationViewModel(application: Application) : AndroidViewModel(application) { class RegAssociationViewModel(application: Application) : ChoosePhotoViewModel(application) {
val frameDesc = "社团注册资料" val frameDesc = "社团注册资料"
val name = AsyncStringForm(formDesc = "社团名称", textLength = 5) val name = AsyncStringForm(formDesc = "社团名称", textLength = 5)
val desc = AsyncStringForm(formDesc = "社团简介", textLength = 30) val desc = AsyncStringForm(formDesc = "社团简介", textLength = 30)
private val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri?> = _picture
private val _fileId = MutableLiveData<Int>() private val _fileId = MutableLiveData<Int>()
val fileId: LiveData<Int> = _fileId val fileId: LiveData<Int> = _fileId
@ -51,17 +47,6 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
read() 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), Api.buildUrl(AssociationApi.Read),
HttpCallback<AssociationCheckVo>(action = "加载历史注册资料", onSuccess = { it -> HttpCallback<AssociationCheckVo>(action = "加载历史注册资料", onSuccess = { it ->
_checkInfo.postValue(it) _checkInfo.postValue(it)
it.body?.let { it -> it.body?.let { checkVo ->
name.setValue(it.associationVo.name) name.setValue(checkVo.associationVo.name)
desc.setValue(it.associationVo.desc) desc.setValue(checkVo.associationVo.desc)
val context = getApplication<Application>() val context = getApplication<Application>()
val request = ImageRequest.Builder(context) val request = ImageRequest.Builder(context)
.data("${BuildConfig.SERVER_ADDRESS}/${it.associationVo.logo}") .data("${BuildConfig.SERVER_ADDRESS}/${checkVo.associationVo.logo}")
.target( .target(
onSuccess = { result -> onSuccess = { result ->
it.associationVo.logo.split("/").apply { checkVo.associationVo.logo.split("/").apply {
File.createTempFile(last(), null, context.cacheDir).apply { File.createTempFile(last(), null, context.cacheDir).apply {
Logger.d("文件路径:${absolutePath}") Logger.d("文件路径:${absolutePath}")
FileOutputStream(this).use { FileOutputStream(this).use {
@ -97,7 +82,7 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
.apply { .apply {
Logger.i("读取缓存图片") Logger.i("读取缓存图片")
setPicture(Uri.fromFile(this)) 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 data by model.data.observeAsState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
LazyColumn(state = listState, modifier = modifier) { LazyColumn(
state = listState, modifier = modifier, verticalArrangement =
Arrangement.spacedBy(space = 10.dp, alignment = Alignment.CenterVertically)
) {
data?.forEach { data?.forEach {
item { item {
Column { Profile(modifier = Modifier.height(300.dp), personInfoVo = it, readOnly = true)
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)
}
} }
} }
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 package com.gyf.csams.association.ui
import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
@ -24,7 +19,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.coil.rememberCoilPainter import com.google.accompanist.coil.rememberCoilPainter
import com.gyf.csams.R import com.gyf.csams.R
@ -43,6 +37,9 @@ import com.orhanobut.logger.Logger
* *
*/ */
class RegAssociationActivity : ComponentActivity() { class RegAssociationActivity : ComponentActivity() {
private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
@ -111,7 +108,6 @@ class RegAssociationActivity : ComponentActivity() {
} }
/** /**
* 社团Logo * 社团Logo
* *
@ -123,42 +119,11 @@ class RegAssociationActivity : ComponentActivity() {
scaffoldModel: ScaffoldModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel(),
modifier: Modifier 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) { Box(contentAlignment = Alignment.Center, modifier = modifier) {
@ -171,19 +136,10 @@ class RegAssociationActivity : ComponentActivity() {
if (uri == null) { if (uri == null) {
OutlinedButton(onClick = { OutlinedButton(onClick = {
when (PackageManager.PERMISSION_GRANTED) { choosePhoto.requestPhoto(
ContextCompat.checkSelfPermission( photoLauncher = photoLauncher,
applicationContext, permissionLauncher = permissionLauncher
Manifest.permission.READ_EXTERNAL_STORAGE )
) -> {
// Some works that require permission
loadPicture()
}
else -> {
// Asking for permission
launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
}) { }) {
Text(text = model.picturePlaceHolder) Text(text = model.picturePlaceHolder)
} }
@ -201,7 +157,7 @@ class RegAssociationActivity : ComponentActivity() {
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
IconButton(onClick = { IconButton(onClick = {
loadPicture() choosePhoto.loadPicture(photoLauncher = photoLauncher)
}) { }) {
Image( Image(
painter = painterResource(id = R.drawable.ic_exchange_rate), 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.google.accompanist.pager.rememberPagerState
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.account.model.AccountViewModel 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.activity.ui.ActivityDetailActivity
import com.gyf.csams.association.ui.AssociationActivity import com.gyf.csams.association.ui.AssociationActivity
import com.gyf.csams.association.ui.RegAssociationActivity 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.message.ui.SysMessageActivity
import com.gyf.csams.module.* import com.gyf.csams.module.*
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.model.RefreshViewModel
import com.gyf.lib.service.BaseActivity import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.* import com.gyf.lib.util.*
@ -55,6 +55,8 @@ class MainActivity : BaseActivity() {
override val clientType: ClientType = ClientType.Foreground override val clientType: ClientType = ClientType.Foreground
private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this)
@ExperimentalPagerApi @ExperimentalPagerApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -100,23 +102,36 @@ class MainActivity : BaseActivity() {
accountViewModel: AccountViewModel = viewModel(), accountViewModel: AccountViewModel = viewModel(),
navController: NavHostController navController: NavHostController
) { ) {
MainColumnFrame( MainColumnFrame(
background = { Background(image = BackgroundImage.Center, alpha = 0.5F) }, background = { Background(image = BackgroundImage.Center, alpha = 0.5F) },
mainMenu = MainMenu.Center, mainMenu = MainMenu.Center,
nav = navController nav = navController
) { ) {
Profile( val photoLauncher = choosePhoto.photoLauncher()
modifier = Modifier val permissionLauncher = choosePhoto.permissionLauncher(photoLauncher = photoLauncher)
.weight(0.3F) val personInfoData by TokenManager.getPersonInfoData().observeAsState()
.padding(10.dp)
)
personInfoData?.let {
Profile(
modifier = Modifier
.weight(0.4F)
.padding(10.dp),
setHeadImg = {
choosePhoto.requestPhoto(
photoLauncher = photoLauncher,
permissionLauncher = permissionLauncher
)
},
personInfoVo = it
)
}
Column( Column(
modifier = Modifier.weight(0.7F), modifier = Modifier.weight(0.6F),
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
(TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId?.let { TokenManager.getUserInfo()?.associationVo?.associationId?.let {
CenterMenuItem(text = model.myAssociationDesc) { CenterMenuItem(text = model.myAssociationDesc) {
startActivity( startActivity(
Intent( Intent(
@ -215,8 +230,7 @@ class MainActivity : BaseActivity() {
mainMenu = MainMenu.List, mainMenu = MainMenu.List,
nav = navController nav = navController
) { ) {
val associationVo: AssociationVo? = val associationVo = TokenManager.getUserInfo()?.associationVo
(TokenManager.getOwnInfo() as? UserVo)?.associationVo
if (associationVo == null) { if (associationVo == null) {
RegisterAssociation(navController = navController) RegisterAssociation(navController = navController)
} }
@ -506,37 +520,31 @@ class MainActivity : BaseActivity() {
.fillMaxWidth() .fillMaxWidth()
) { ) {
when (photo.loadState) { when (photo.loadState) {
is ImageLoadState.Loading -> { is ImageLoadState.Loading -> CircularProgressIndicator()
// Display a circular progress indicator whilst loading is ImageLoadState.Success -> it[page].apply {
CircularProgressIndicator() 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 -> { else -> Text(text = "图片加载失败")
// 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
)
})
})
)
}
} }
} }

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

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

@ -6,7 +6,6 @@
<string name="join_association">申请入团</string> <string name="join_association">申请入团</string>
<string name="update_exam">更新题库</string> <string name="update_exam">更新题库</string>
<string name="post_answer">提交答案</string> <string name="post_answer">提交答案</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
<string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string> <string name="denined_location_permission">部分权限请求失败,无法获取地理位置</string>
<string name="at"></string> <string name="at"></string>
<string name="city">城市</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.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.Card import androidx.compose.material.*
import androidx.compose.material.MaterialTheme import androidx.compose.runtime.*
import androidx.compose.material.Text import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.coil.rememberCoilPainter import com.google.accompanist.coil.rememberCoilPainter
import com.google.accompanist.imageloading.ImageLoadState
import com.gyf.csams.module.ManagerInfoVo import com.gyf.csams.module.ManagerInfoVo
import com.gyf.csams.module.ManagerVo import com.gyf.csams.module.ManagerVo
import com.gyf.csams.module.PersonInfoVo import com.gyf.csams.module.PersonInfoVo
import com.gyf.csams.module.UserVo 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.Api
import com.orhanobut.logger.Logger
import com.gyf.lib.util.TokenManager
@Composable @Composable
fun Profile( fun Profile(
modifier: Modifier, modifier: Modifier,
personInfoVo: PersonInfoVo = TokenManager.getOwnInfo(), personInfoVo: PersonInfoVo,
setHeadImg: () -> Unit = {},
choosePhotoViewModel: ChoosePhotoViewModel = viewModel(),
refreshViewModel: RefreshViewModel = viewModel(),
readOnly: Boolean = false,
duty: @Composable () -> Unit = { duty: @Composable () -> Unit = {
Text( Text(
text = when (personInfoVo) { text = when (personInfoVo) {
is UserVo -> personInfoVo.manager?.duty?.desc ?: "----" is UserVo -> personInfoVo.manager?.duty?.desc ?: "----"
is ManagerVo -> personInfoVo.duty.desc is ManagerVo -> personInfoVo.duty.desc
is ManagerInfoVo -> personInfoVo.duty.desc is ManagerInfoVo -> personInfoVo.duty.desc
else -> throw IllegalArgumentException("个人信息类型错误:${personInfoVo}") else -> "-----"
} }
) )
} }
) { ) {
Column( Column(
modifier = modifier, modifier = modifier.border(1.dp, Color.Black),
verticalArrangement = Arrangement.SpaceEvenly
) { ) {
var isEdit by remember {
mutableStateOf(false)
}
Logger.i("info:${personInfoVo}")
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -46,28 +59,51 @@ fun Profile(
.border(width = 1.dp, color = MaterialTheme.colors.onBackground), .border(width = 1.dp, color = MaterialTheme.colors.onBackground),
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
val picture by choosePhotoViewModel.picture.observeAsState()
picture?.let {
Logger.i("picture=${it}")
choosePhotoViewModel.upload {
refreshViewModel.refresh()
}
}
personInfoVo.headImg.let { personInfoVo.headImg.let {
if (it != null) { when {
val headImg = rememberCoilPainter(request = Api.buildUrl(it)) it != null -> {
Image( val headImg = rememberCoilPainter(request = Api.buildUrl(it))
painter = headImg,
contentDescription = null, when (headImg.loadState) {
modifier = Modifier is ImageLoadState.Loading ->
.weight(0.4F) Box(
.fillMaxHeight() modifier = Modifier
) .weight(0.4F)
} else { .fillMaxHeight(), contentAlignment = Alignment.Center
Box( ) {
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 modifier = Modifier
.weight(0.4F) .weight(0.4F)
.fillMaxHeight(), .fillMaxHeight(),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text(text = "没有头像") OutlinedButton(onClick = setHeadImg) {
Text(text = "设置头像")
}
} }
} }
} }
Column( Column(
modifier = Modifier modifier = Modifier
.weight(0.4F) .weight(0.4F)
@ -76,23 +112,64 @@ fun Profile(
verticalArrangement = Arrangement.SpaceEvenly, verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text(text = personInfoVo.name) if (isEdit) {
BaseTextField(form = refreshViewModel.name)
} else {
Text(text = personInfoVo.name)
}
duty() duty()
} }
} }
Spacer(modifier = Modifier.weight(0.05F))
Card( Card(
backgroundColor = MaterialTheme.colors.background, backgroundColor = MaterialTheme.colors.background,
modifier = Modifier.weight(0.15F) modifier = Modifier.weight(0.3F)
) { ) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth()
horizontalArrangement = Arrangement.Center
) { ) {
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"), Load("/load/manager"),
UploadHeadimg("/upload/img"),
//加载部门详情 //加载部门详情
LoadDetail("${Load.path}/detail"); LoadDetail("${Load.path}/detail");

@ -23,7 +23,7 @@ class ClientToken(
data class OnlyToken( data class OnlyToken(
override val clientType: ClientType override val clientType: ClientType = TokenManager.getClientType()
) : ClientBaseVo() { ) : ClientBaseVo() {
override val token: ClientToken = TokenManager.getToken() override val token: ClientToken = TokenManager.getToken()
} }
@ -46,51 +46,69 @@ interface TokenDao {
object TokenManager { object TokenManager {
private var ownInfo: OwnInfoVo? = null private val ownInfo = MutableLiveData<OwnInfoVo?>()
private var personInfo: PersonInfoVo? = null private val personInfo = MutableLiveData<PersonInfoVo?>()
private var clientToken: ClientToken? = null private val clientToken = MutableLiveData<ClientToken?>()
fun update(ownInfo: OwnInfoVo) { fun update(ownInfo: OwnInfoVo) {
this.ownInfo = ownInfo this.ownInfo.postValue(ownInfo)
ownInfo.token.let { 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) { when (ownInfo) {
is ManagerVo -> this.personInfo = ManagerInfoVo( is ManagerVo -> this.personInfo.postValue(
duty = ownInfo.duty, ManagerInfoVo(
name = ownInfo.name, duty = ownInfo.duty,
headImg = ownInfo.headImg, name = ownInfo.name,
desc = ownInfo.desc 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初始化失败") else -> throw IllegalArgumentException("token初始化失败")
} }
Logger.i("token刷新完成") Logger.i("token刷新完成")
} }
fun clear() { fun clear() {
ownInfo = null ownInfo.postValue(null)
personInfo.postValue(null)
clientToken.postValue(null)
} }
fun getOwnInfo(): OwnInfoVo { fun getClientType(): ClientType {
return ownInfo ?: throw IllegalArgumentException("token没有初始化,非法调用") return when (ownInfo.value) {
is UserVo -> ClientType.Foreground
is ManagerVo -> ClientType.Background
else -> throw IllegalArgumentException("个人信息还没有初始化")
}
} }
fun getUserInfo(): UserVo? { fun getUserInfo(): UserVo? {
return ownInfo as? UserVo return ownInfo.value as? UserVo
} }
fun getManagerInfo(): ManagerVo? { fun getPersonInfoData(): LiveData<PersonInfoVo?> {
return ownInfo as? ManagerVo return personInfo
} }
fun getPersonInfo(): PersonInfoVo { fun getManagerInfo(): ManagerVo? {
return personInfo ?: throw IllegalArgumentException("个人信息没有初始化,非法调用") return ownInfo.value as? ManagerVo
} }
fun getToken(): ClientToken { 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="activity_date">活动日期</string>
<string name="allow_btn">通过</string> <string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string> <string name="refuse_btn">拒绝</string>
<string name="denined_photo_permission">相册权限请求失败,无法读取相册</string>
</resources> </resources>

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

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