申请活动

master
pan 4 years ago
parent d6d3193837
commit f093f47c37
  1. 2
      background/src/main/AndroidManifest.xml
  2. 2
      background/src/main/java/com/gyf/csams/InitActivity.kt
  3. 5
      background/src/main/java/com/gyf/csams/account/model/LoginViewModel.kt
  4. 6
      background/src/main/java/com/gyf/csams/main/model/AssociationManagementViewModel.kt
  5. 29
      background/src/main/java/com/gyf/csams/main/model/AuditActViewModel.kt
  6. 76
      background/src/main/java/com/gyf/csams/main/model/AuditAssociationViewModel.kt
  7. 2
      background/src/main/java/com/gyf/csams/main/model/BackgroundViewModel.kt
  8. 89
      background/src/main/java/com/gyf/csams/main/model/BaseAuditViewModel.kt
  9. 46
      background/src/main/java/com/gyf/csams/main/model/CheckActViewModel.kt
  10. 3
      background/src/main/java/com/gyf/csams/main/model/CheckQualityReportViewModel.kt
  11. 5
      background/src/main/java/com/gyf/csams/main/model/ManagementOfficerModel.kt
  12. 22
      background/src/main/java/com/gyf/csams/main/model/ManagerActViewModel.kt
  13. 2
      background/src/main/java/com/gyf/csams/main/model/MenuViewModel.kt
  14. 3
      background/src/main/java/com/gyf/csams/main/model/RenameViewModel.kt
  15. 4
      background/src/main/java/com/gyf/csams/main/ui/AssociationManagementActivity.kt
  16. 96
      background/src/main/java/com/gyf/csams/main/ui/AuditActActivity.kt
  17. 241
      background/src/main/java/com/gyf/csams/main/ui/AuditAssociationActivity.kt
  18. 92
      background/src/main/java/com/gyf/csams/main/ui/CheckActActivity.kt
  19. 5
      background/src/main/java/com/gyf/csams/main/ui/CheckQualityReportActivity.kt
  20. 5
      background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  21. 2
      background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt
  22. 22
      background/src/main/java/com/gyf/csams/main/ui/ManagerActActivity.kt
  23. 7
      background/src/main/java/com/gyf/csams/main/ui/NotificationActivity.kt
  24. 2
      background/src/main/java/com/gyf/csams/main/ui/RenameActivity.kt
  25. 266
      background/src/main/java/com/gyf/csams/uikit/CheckForm.kt
  26. 15
      background/src/main/java/com/gyf/csams/uikit/Table.kt
  27. 3
      foreground/src/main/java/com/gyf/csams/InitActivity.kt
  28. 44
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  29. 2
      foreground/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
  30. 19
      foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt
  31. 171
      foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt
  32. 26
      foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt
  33. 180
      foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt
  34. 14
      foreground/src/main/java/com/gyf/csams/association/model/AssociationViewModel.kt
  35. 65
      foreground/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
  36. 49
      foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  37. 19
      foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
  38. 21
      foreground/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
  39. 81
      foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  40. 13
      foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  41. 57
      foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  42. 3
      foreground/src/main/java/com/gyf/csams/message/model/ForegroundViewModel.kt
  43. 7
      foreground/src/main/java/com/gyf/csams/message/ui/SysMessageActivity.kt
  44. 52
      foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt
  45. 39
      foreground/src/main/java/com/gyf/csams/util/GsonUtil.kt
  46. 3
      foreground/src/main/java/com/gyf/csams/util/HttpCallback.kt
  47. 9
      foreground/src/main/res/drawable/ic_clock.xml
  48. 3
      foreground/src/test/java/com/gyf/csams/ExampleUnitTest.kt
  49. 2
      lib/build.gradle.kts
  50. 2
      lib/src/androidTest/java/com/gyf/lib/ExampleInstrumentedTest.kt
  51. 23
      lib/src/main/java/com/gyf/lib/model/AbstractLoginViewModel.kt
  52. 8
      lib/src/main/java/com/gyf/lib/model/InitViewModel.kt
  53. 9
      lib/src/main/java/com/gyf/lib/model/SysMessageViewModel.kt
  54. 3
      lib/src/main/java/com/gyf/lib/service/BaseActivity.kt
  55. 13
      lib/src/main/java/com/gyf/lib/service/MessageService.kt
  56. 3
      lib/src/main/java/com/gyf/lib/uikit/AbstractInitActivity.kt
  57. 13
      lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt
  58. 8
      lib/src/main/java/com/gyf/lib/uikit/Profile.kt
  59. 33
      lib/src/main/java/com/gyf/lib/util/Api.kt
  60. 53
      lib/src/main/java/com/gyf/lib/util/DateTimeUtil.kt
  61. 53
      lib/src/main/java/com/gyf/lib/util/HttpUtil.kt
  62. 22
      lib/src/main/java/com/gyf/lib/util/RandomUtil.kt
  63. 61
      lib/src/main/java/com/gyf/lib/util/TokenUtil.kt
  64. 527
      lib/src/main/java/com/gyf/lib/util/Vo.kt
  65. 1
      lib/src/main/res/values-en/integers.xml
  66. 1
      lib/src/main/res/values-en/strings.xml
  67. 1
      lib/src/main/res/values-zh/integers.xml
  68. 1
      lib/src/main/res/values-zh/strings.xml
  69. 1
      lib/src/main/res/values/integers.xml
  70. 1
      lib/src/main/res/values/strings.xml
  71. 8
      lib/src/test/java/com/gyf/lib/ExampleUnitTest.kt
  72. 4
      settings.gradle.kts

@ -47,7 +47,7 @@
android:name=".main.ui.AuditAssociationActivity" android:name=".main.ui.AuditAssociationActivity"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<!--活动申请书--> <!--活动申请书-->
<activity android:name=".main.ui.CheckActActivity" /> <activity android:name=".main.ui.AuditActActivity" />
<!--活动质量汇报单--> <!--活动质量汇报单-->
<activity android:name=".main.ui.CheckQualityReportActivity" /> <activity android:name=".main.ui.CheckQualityReportActivity" />
<!--通知界面--> <!--通知界面-->

@ -4,10 +4,10 @@ import android.app.Activity
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.account.ui.LoginActivity import com.gyf.csams.account.ui.LoginActivity
import com.gyf.csams.main.ui.MainActivity import com.gyf.csams.main.ui.MainActivity
import com.gyf.csams.module.ManagerVo
import com.gyf.lib.uikit.AbstractInitActivity import com.gyf.lib.uikit.AbstractInitActivity
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ApiResponse import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.ManagerVo
import java.lang.reflect.Type import java.lang.reflect.Type
class InitActivity : AbstractInitActivity<ManagerVo>() { class InitActivity : AbstractInitActivity<ManagerVo>() {

@ -4,12 +4,13 @@ import android.app.Activity
import android.app.Application import android.app.Application
import android.os.Build import android.os.Build
import com.gyf.csams.account.ui.LoginActivity import com.gyf.csams.account.ui.LoginActivity
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.ManagerLoginVo
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.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ClientType
import com.gyf.lib.util.ManagerLoginVo
class LoginViewModel(application: Application) : AbstractLoginViewModel(application) { class LoginViewModel(application: Application) : AbstractLoginViewModel(application) {
override val id = ValidStringForm(formDesc = "管理帐号", textLength = 8) override val id = ValidStringForm(formDesc = "管理帐号", textLength = 8)

@ -1,11 +1,9 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import com.gyf.csams.module.AssociationLevel
import com.gyf.csams.module.AssociationVo
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.AssociationLevel
import com.gyf.lib.util.AssociationVo
/** /**

@ -0,0 +1,29 @@
package com.gyf.csams.main.model
import android.app.Application
import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.AuditActVo
import com.gyf.lib.util.ActivityApi
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.UrlPath
import java.lang.reflect.Type
/**
* 活动审核
*
*/
class AuditActViewModel(application: Application) : BaseAuditViewModel<AuditActVo>(application) {
override val auditApi: UrlPath = ActivityApi.Audit
override val acceptApi: UrlPath = ActivityApi.Accept
override val checkApi: UrlPath = ActivityApi.Check
override val typeToken: Type =
object : TypeToken<ApiResponse<MutableList<AuditActVo>>>() {}.type
init {
load { }
}
}

@ -1,78 +1,28 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.lib.model.ScrollViewModel import com.gyf.csams.module.AuditAssociationVo
import com.gyf.lib.util.* import com.gyf.lib.util.ApiResponse
import kotlinx.coroutines.launch import com.gyf.lib.util.AssociationApi
import com.gyf.lib.util.UrlPath
import java.lang.reflect.Type
class AuditAssociationViewModel(application: Application) : ScrollViewModel<DisposeRegInfoVo>(
class AuditAssociationViewModel(application: Application) : BaseAuditViewModel<AuditAssociationVo>(
application application
) { ) {
override val initSize: Int = 10
override val auditApi: UrlPath = AssociationApi.Audit
override val acceptApi: UrlPath = AssociationApi.Accept
override val checkApi: UrlPath = AssociationApi.Check
override val typeToken: Type =
object : TypeToken<ApiResponse<MutableList<AuditAssociationVo>>>() {}.type
init { init {
load { } load { }
} }
fun load(callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Audit), HttpCallback<MutableList<DisposeRegInfoVo>>(
action = "获取审核列表",
onSuccess = { it ->
it.body?.let {
_data.postValue(it)
}
},
typeToken = object :
TypeToken<ApiResponse<MutableList<DisposeRegInfoVo>>>() {}.type
),
jsonParam = OnlyToken(clientType = ClientType.Background)
)
}
}
/**
* 受理注册资料
*
* @param callback
*/
fun accept(regId: Int, isFirstAccept: Boolean, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Accept),
HttpCallback<Boolean>(action = "受理社团注册资料", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = AcceptRegAssociation(regId = regId, isFirstAccept = isFirstAccept)
)
}
}
/**
* 提交审核结果
*
* @param regId
* @param result
* @param callback
*/
fun check(regId: Int, cause: String, result: Boolean, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Check),
HttpCallback<Boolean>(action = "提交审核结果", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = CheckRegVo(
regId = regId, result = result,
cause = cause
)
)
}
}
} }

@ -1,8 +1,8 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import com.gyf.csams.module.ClientType
import com.gyf.lib.model.SysMessageViewModel import com.gyf.lib.model.SysMessageViewModel
import com.gyf.lib.util.ClientType
class BackgroundViewModel(application: Application) : SysMessageViewModel(application) { class BackgroundViewModel(application: Application) : SysMessageViewModel(application) {
override fun clientType(): ClientType { override fun clientType(): ClientType {

@ -0,0 +1,89 @@
package com.gyf.csams.main.model
import android.app.Application
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.AcceptVo
import com.gyf.csams.module.AuditVo
import com.gyf.csams.module.CheckVo
import com.gyf.csams.module.ClientType
import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.*
import kotlinx.coroutines.launch
import java.lang.reflect.Type
abstract class BaseAuditViewModel<T : AuditVo>(application: Application) :
ScrollViewModel<T>(application = application) {
abstract val auditApi: UrlPath
abstract val acceptApi: UrlPath
abstract val checkApi: UrlPath
abstract val typeToken: Type
/**
* 加载审核记录
*
* @param callback
*/
fun load(callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(auditApi), HttpCallback<MutableList<T>>(
action = "加载资料",
onSuccess = { it ->
it.body?.let {
_data.postValue(it)
}
},
typeToken = typeToken
),
jsonParam = OnlyToken(clientType = ClientType.Background)
)
}
}
/**
* 受理资料
*
* @param callback
*/
fun accept(auditId: Int, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(acceptApi),
HttpCallback<Boolean>(action = "受理资料", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = AcceptVo(auditId = auditId, token = TokenManager.getToken())
)
}
}
/**
* 审核资料
*
* @param auditId
* @param result
* @param callback
*/
fun check(auditId: Int, cause: String, result: Boolean, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(checkApi),
HttpCallback<Boolean>(action = "审核资料", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = CheckVo(
auditId = auditId,
result = result,
cause = cause,
token = TokenManager.getToken()
)
)
}
}
}

@ -1,46 +0,0 @@
package com.gyf.csams.main.model
import android.app.Application
import androidx.lifecycle.viewModelScope
import com.gyf.lib.model.ApplyViewModel
import com.gyf.lib.util.ApplyActVo
import com.gyf.lib.util.format
import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime
import kotlinx.coroutines.launch
/**
* 活动数据管理
*
*/
class CheckActViewModel(application: Application) : ApplyViewModel<ApplyActVo>(application) {
override val initSize: Int = 10
init {
load()
}
fun load() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize) {
add(
ApplyActVo(
activityName = randomChinese(5),
activityTime = randomDateTime().format(),
location = randomChinese(10),
desc = randomChinese(10),
size = 10
)
)
}
}
}
}
fun loadMore(callback: (message: String) -> Unit) {
TODO("Not yet implemented")
}
}

@ -2,8 +2,9 @@ package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.csams.module.QualityReportVo
import com.gyf.lib.model.ApplyViewModel import com.gyf.lib.model.ApplyViewModel
import com.gyf.lib.util.QualityReportVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

@ -3,8 +3,9 @@ package com.gyf.csams.main.model
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.gyf.lib.util.AllOfficerVo import com.gyf.csams.module.AllOfficerVo
import com.gyf.lib.util.ManagerInfoVo import com.gyf.csams.module.ManagerInfoVo
enum class ColumnType { enum class ColumnType {
Text, Text,

@ -1,11 +1,8 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import com.gyf.csams.module.ManagerActVo
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.ActivityVo
import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime
import com.gyf.lib.util.randomNum
/** /**
@ -15,7 +12,7 @@ import com.gyf.lib.util.randomNum
* *
* @param application * @param application
*/ */
class ManagerActViewModel(application: Application) : ScrollViewModel<ActivityVo>(application) { class ManagerActViewModel(application: Application) : ScrollViewModel<ManagerActVo>(application) {
override val initSize: Int = 10 override val initSize: Int = 10
init { init {
@ -23,20 +20,7 @@ class ManagerActViewModel(application: Application) : ScrollViewModel<ActivityVo
} }
fun load() { fun load() {
_data.value?.apply { TODO("活动信息管理")
repeat(initSize) {
add(
ActivityVo(
activityId = randomNum(8).toLong(),
activityName = randomChinese(5),
association = randomChinese(5),
score = (1..5).random(),
activityTime = randomDateTime(),
location = randomChinese(10)
)
)
}
}
} }
fun loadMore(callback: (message: String) -> Unit) { fun loadMore(callback: (message: String) -> Unit) {

@ -15,7 +15,7 @@ enum class MenuType(val desc: String, val clazz: Map<String, Class<out Activity>
), ),
Act( Act(
"活动管理", mapOf( "活动管理", mapOf(
"审核社团活动" to CheckActActivity::class.java, "审核社团活动" to AuditActActivity::class.java,
"审核质量报告单" to CheckQualityReportActivity::class.java, "审核质量报告单" to CheckQualityReportActivity::class.java,
"查看社团活动" to ManagerActActivity::class.java "查看社团活动" to ManagerActActivity::class.java
) )

@ -3,9 +3,10 @@ package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.module.RenameVo
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.RenameVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomNum import com.gyf.lib.util.randomNum
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

@ -17,9 +17,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.main.model.AssociationManagementViewModel import com.gyf.csams.main.model.AssociationManagementViewModel
import com.gyf.csams.module.AssociationLevel
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainBoxFrame import com.gyf.lib.uikit.MainBoxFrame
import com.gyf.lib.util.AssociationLevel
/** /**
* 社团管理 * 社团管理
@ -52,7 +52,7 @@ class AssociationManagementActivity : ComponentActivity() {
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text( Text(
text = "$id", text = "$associationId",
style = MaterialTheme.typography.h6 style = MaterialTheme.typography.h6
) )
Text( Text(

@ -0,0 +1,96 @@
package com.gyf.csams.main.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R
import com.gyf.csams.main.model.AuditActViewModel
import com.gyf.csams.module.AuditActVo
import com.gyf.csams.uikit.BASE_HEIGHT
import com.gyf.csams.uikit.CheckForm
import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTableImeSimple
import com.gyf.lib.uikit.ImeBody
import com.gyf.lib.util.DateTimeUtil.datetimeFormat
import java.util.*
/**
* 审批社团活动
*
*/
class AuditActActivity : ComponentActivity() {
@ExperimentalMaterialApi
@ExperimentalAnimatedInsets
@ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
ImeBody {
val model: AuditActViewModel = viewModel()
val data by model.data.observeAsState()
TestTableImeSimple(
title = R.string.activity_association
) {
data?.forEach {
item {
RegisterForm(vo = it)
CheckForm<AuditActVo, AuditActViewModel>(vo = it)
Spacer(modifier = Modifier.height(10.dp))
}
}
}
}
}
}
@Composable
private fun RegisterForm(
modifier: Modifier = Modifier,
vo: AuditActVo
) {
Column(modifier = modifier) {
vo.activityVo.activityTime
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.activity_name,
value = vo.activityVo.activityName
)
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.activity_time,
value = Date(vo.activityVo.activityTime).datetimeFormat()
)
RowItem(
modifier = Modifier.height(BASE_HEIGHT * 1.5F),
key = R.string.activity_address,
value = vo.activityVo.activityAddress
)
RowItem(
modifier = Modifier.height(BASE_HEIGHT * 3),
key = R.string.activity_desc,
value = vo.activityVo.activityDesc
)
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.activity_size,
value = "${vo.activityVo.activitySize}"
)
}
}
}

@ -5,7 +5,9 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
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.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -18,13 +20,15 @@ import androidx.core.view.WindowCompat
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.google.accompanist.insets.ExperimentalAnimatedInsets import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.google.accompanist.insets.navigationBarsWithImePadding
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.AuditAssociationViewModel import com.gyf.csams.main.model.AuditAssociationViewModel
import com.gyf.csams.module.AuditAssociationVo
import com.gyf.csams.uikit.BASE_HEIGHT
import com.gyf.csams.uikit.CheckForm
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTableImeSimple import com.gyf.csams.uikit.TestTableImeSimple
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.ImeBody
import com.gyf.lib.util.* import com.gyf.lib.util.Api
class AuditAssociationActivity : ComponentActivity() { class AuditAssociationActivity : ComponentActivity() {
@ExperimentalMaterialApi @ExperimentalMaterialApi
@ -43,6 +47,7 @@ class AuditAssociationActivity : ComponentActivity() {
data?.forEach { data?.forEach {
item { item {
RegisterForm(vo = it) RegisterForm(vo = it)
CheckForm<AuditAssociationVo, AuditAssociationViewModel>(vo = it)
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
} }
} }
@ -52,208 +57,9 @@ class AuditAssociationActivity : ComponentActivity() {
} }
@Composable
private fun CheckForm(
vo: DisposeRegInfoVo,
scaffoldModel: ScaffoldModel = viewModel(),
auditAssociationViewModel: AuditAssociationViewModel = viewModel(),
first: @Composable () -> Unit,
last: @Composable () -> Unit
) {
val baseHeight = 50.dp
(TokenManager.getOwnInfo() as? ManagerVo)?.let { it ->
var confirmDesc: Int? = null
var backDesc: Int? = null
var onConfirm: (() -> Unit)? = null
var onBack: (() -> Unit)? = null
val check: (result: Boolean, cause: StringForm) -> Unit =
{ result: Boolean, cause: StringForm ->
scaffoldModel.update(
message = "确认${if (result) "上报" else "驳回"}",
actionLabel = "确认"
) {
auditAssociationViewModel.check(
regId = vo.log.id,
result = result,
cause = cause.formValue.value
?: throw IllegalArgumentException("无法获取审核理由")
) {
scaffoldModel.update(message = it, actionLabel = "刷新") {
auditAssociationViewModel.load { }
}
}
}
}
val accept: (m: String, isFirstAccept: Boolean) -> Unit =
{ m: String, isFirstAccept: Boolean ->
scaffoldModel.update("确认${m}", actionLabel = "确认") {
auditAssociationViewModel.accept(
regId = vo.log.id,
isFirstAccept = isFirstAccept
) {
scaffoldModel.update(message = it, actionLabel = "刷新") {
auditAssociationViewModel.load { }
}
}
}
}
val doCheck: @Composable () -> Unit = {
val cause = ValidStringForm(
formDesc = "审核理由",
textLength = 20
)
val statusForm by cause.statusForm.observeAsState()
onConfirm = {
if (statusForm == FormStatus.Empty) {
scaffoldModel.update("${cause.formDesc}不能为空", actionLabel = "知道了")
} else {
check(true, cause)
}
}
onBack = {
if (statusForm == FormStatus.Empty) {
scaffoldModel.update("${cause.formDesc}不能为空", actionLabel = "知道了")
} else {
check(false, cause)
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
BaseTextField(
modifier = Modifier.navigationBarsWithImePadding(),
form = cause
)
}
}
when {
//初审记录,负责人为空 等待初审
vo.log.nextAudit == null && vo.log.manager == null -> {
if (it.duty == Duty.PamphaBhusal) {
confirmDesc = R.string.accept_btn
onConfirm = { accept("受理", true) }
}
first()
RowItem(key = R.string.first_approver_origin, value = "")
RowItem(key = R.string.first_result, value = "")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "等待初审"
)
}
//初审记录,负责人不为空 初审受理
vo.log.nextAudit == null && vo.log.manager != null -> {
first()
if (it.duty == Duty.PamphaBhusal) {
confirmDesc = R.string.reported_btn
backDesc = R.string.reject_btn
doCheck()
} else {
RowItem(key = R.string.first_approver_origin, value = "")
}
RowItem(key = R.string.first_result, value = "")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "初审受理"
)
}
//初审记录,审核通过(上报) 等待复审
vo.log.nextAudit != null && vo.log.nextAudit?.manager == null -> {
if (it.duty == Duty.Teacher) {
confirmDesc = R.string.recheck_btn
onConfirm = { accept("复审", false) }
}
first()
RowItem(key = R.string.first_approver_origin, value = vo.log.cause)
RowItem(key = R.string.first_result, value = "通过")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "等待复审"
)
}
//复审记录,审核结果为空 复审受理
vo.log.nextAudit != null && vo.log.nextAudit?.result == null -> {
first()
RowItem(key = R.string.first_approver_origin, value = vo.log.cause)
RowItem(key = R.string.first_result, value = "通过")
last()
if (it.duty == Duty.Teacher) {
confirmDesc = R.string.allow_btn
backDesc = R.string.reject_btn
doCheck()
} else {
RowItem(key = R.string.last_approver_origin, value = "")
}
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "复审受理"
)
}
else -> {
first()
RowItem(key = R.string.first_approver_origin, value = vo.log.cause)
RowItem(
key = R.string.first_result, value = when (vo.log.result) {
null -> ""
true -> "通过"
false -> "不通过"
}
)
last()
RowItem(
key = R.string.last_approver_origin,
value = vo.log.nextAudit?.cause ?: ""
)
RowItem(
key = R.string.last_result, value = when (vo.log.nextAudit?.result) {
null -> ""
true -> "通过"
false -> "不通过"
}
)
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "完成审核"
)
}
}
if (confirmDesc != null) {
BottomButton(
confirmDesc = confirmDesc,
backDesc = backDesc,
modifier = Modifier.fillMaxWidth(),
onBack = onBack
) {
onConfirm?.let { it() }
}
}
}
}
@Composable @Composable
private fun RegisterForm( private fun RegisterForm(
modifier: Modifier = Modifier, vo: DisposeRegInfoVo modifier: Modifier = Modifier, vo: AuditAssociationVo
) { ) {
Column( Column(
modifier = modifier.border( modifier = modifier.border(
@ -261,28 +67,26 @@ class AuditAssociationActivity : ComponentActivity() {
color = MaterialTheme.colors.onBackground color = MaterialTheme.colors.onBackground
) )
) { ) {
val baseHeight = 50.dp
RowItem( RowItem(
modifier = Modifier.height(baseHeight), modifier = Modifier.height(BASE_HEIGHT),
key = R.string.petitioner, key = R.string.petitioner,
value = vo.log.user.name value = vo.audit.user.name
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight), modifier = Modifier.height(BASE_HEIGHT),
key = R.string.association_name, key = R.string.association_name,
value = vo.name value = vo.name
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight), modifier = Modifier.height(BASE_HEIGHT),
key = R.string.association_desc, key = R.string.association_desc,
value = vo.desc value = vo.desc
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight * 3), modifier = Modifier.height(BASE_HEIGHT * 3),
key = R.string.association_logo key = R.string.association_logo
) { ) {
//TODO 图片全屏显示 //TODO 图片全屏显示
@ -290,21 +94,6 @@ class AuditAssociationActivity : ComponentActivity() {
rememberCoilPainter(Api.buildUrl(vo.logo)) rememberCoilPainter(Api.buildUrl(vo.logo))
Image(painter = painter, contentDescription = null) Image(painter = painter, contentDescription = null)
} }
CheckForm(vo = vo, first = {
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.first_approver,
value = vo.log.manager?.name ?: ""
)
}, last = {
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.last_approver,
value = vo.log.nextAudit?.manager?.name ?: ""
)
})
} }
} }
} }

@ -1,92 +0,0 @@
package com.gyf.csams.main.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R
import com.gyf.csams.main.model.CheckActViewModel
import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable
import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.ApplyActVo
import com.gyf.lib.util.BottomButton
/**
* 审批社团活动
*
*/
class CheckActActivity : ComponentActivity() {
@ExperimentalAnimatedInsets
@ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestTable(
clazz = CheckActViewModel::class.java,
title = R.string.activity_application
) {
ApplyActForm(vo = it)
}
}
}
@Composable
private fun ApplyActForm(
modifier: Modifier = Modifier, model: CheckActViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel(), vo: ApplyActVo
) {
Column(modifier = modifier) {
val baseHeight = 50.dp
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.activity_name,
value = vo.activityName
)
RowItem(
modifier = Modifier.height(baseHeight * 1.5F),
key = R.string.activity_address,
value = vo.location
)
RowItem(
modifier = Modifier.height(baseHeight * 3),
key = R.string.activity_desc,
value = vo.desc
)
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.activity_size,
value = "${vo.size}"
)
RowItem(
modifier = Modifier.height(baseHeight), key = R.string.first_approver, value = ""
/**TODO 获取审批人**/
)
RowItem(
modifier = Modifier.height(baseHeight * 3),
key = R.string.first_approver_origin
) {
BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approveOrigin)
}
val message = stringResource(id = R.string.not_impl_error)
BottomButton(
confirmDesc = R.string.reported_btn, backDesc = R.string.reject_btn,
modifier = Modifier.fillMaxWidth()
) {
scaffoldModel.update(message = message)
}
}
}
}

@ -15,13 +15,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.CheckQualityReportViewModel import com.gyf.csams.main.model.CheckQualityReportViewModel
import com.gyf.csams.module.MAX_SCORE
import com.gyf.csams.module.QualityReportVo
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.MAX_SCORE
import com.gyf.lib.util.QualityReportVo
/** /**
* 审批质量报告单 * 审批质量报告单

@ -16,10 +16,11 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R 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.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.ClientType
import com.gyf.lib.util.ManagerInfoVo
import com.gyf.lib.util.TokenManager import com.gyf.lib.util.TokenManager
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {

@ -21,11 +21,11 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.ManagementOfficerModel import com.gyf.csams.main.model.ManagementOfficerModel
import com.gyf.csams.module.ManagerInfoVo
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.Profile import com.gyf.lib.uikit.Profile
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.ManagerInfoVo
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
/** /**

@ -16,11 +16,13 @@ import androidx.compose.ui.unit.dp
import com.google.accompanist.insets.ExperimentalAnimatedInsets import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.ManagerActViewModel import com.gyf.csams.main.model.ManagerActViewModel
import com.gyf.csams.module.MAX_SCORE
import com.gyf.csams.module.ManagerActVo
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.util.ActivityVo import com.gyf.lib.util.DateTimeUtil.datetimeFormat
import com.gyf.lib.util.MAX_SCORE import java.util.*
import com.gyf.lib.util.format
/** /**
* 查看活动信息 * 查看活动信息
@ -40,19 +42,15 @@ class ManagerActActivity : ComponentActivity() {
} }
@Composable @Composable
private fun ActivityTable(vo: ActivityVo) { private fun ActivityTable(vo: ManagerActVo) {
val baseHeight = 50.dp val baseHeight = 50.dp
RowItem( RowItem(
modifier = Modifier.height(baseHeight), modifier = Modifier.height(baseHeight),
key = R.string.activity_id, value = "${vo.activityId}" key = R.string.activity_name, value = vo.activityVo.activityName
)
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.activity_name, value = vo.activityName
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight), modifier = Modifier.height(baseHeight),
key = R.string.activity_association, value = vo.association key = R.string.activity_association, value = vo.association.name
) )
RowItem(modifier = Modifier.height(baseHeight), key = R.string.activity_evaluate) { RowItem(modifier = Modifier.height(baseHeight), key = R.string.activity_evaluate) {
Row( Row(
@ -70,11 +68,11 @@ class ManagerActActivity : ComponentActivity() {
} }
RowItem( RowItem(
modifier = Modifier.height(baseHeight), modifier = Modifier.height(baseHeight),
key = R.string.activity_time, value = vo.activityTime.format() key = R.string.activity_time, value = Date(vo.activityVo.activityTime).datetimeFormat()
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight * 2), modifier = Modifier.height(baseHeight * 2),
key = R.string.activity_location, value = vo.location key = R.string.activity_location, value = vo.activityVo.activityAddress
) )
} }
} }

@ -15,10 +15,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.main.model.BackgroundViewModel import com.gyf.csams.main.model.BackgroundViewModel
import com.gyf.csams.module.NotificationVo
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.util.NotificationVo import com.gyf.lib.util.DateTimeUtil.datetimeFormat
import com.gyf.lib.util.format
import java.util.* import java.util.*
/** /**
@ -72,7 +73,7 @@ class NotificationActivity : ComponentActivity() {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Text(text = Date(content.createTime).format()) Text(text = Date(content.createTime).datetimeFormat())
} }
} }
} }

@ -16,12 +16,12 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.RenameViewModel import com.gyf.csams.main.model.RenameViewModel
import com.gyf.csams.module.RenameVo
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.RenameVo
class RenameActivity : ComponentActivity() { class RenameActivity : ComponentActivity() {

@ -0,0 +1,266 @@
package com.gyf.csams.uikit
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets
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
val BASE_HEIGHT = 50.dp
@ExperimentalMaterialApi
@ExperimentalAnimatedInsets
@Composable
inline fun <reified T : AuditVo, reified E : BaseAuditViewModel<T>> ContentCheckForm(
@StringRes title: Int,
crossinline RegisterForm: @Composable (vo: T) -> Unit
) {
ImeBody {
val model: E = viewModel()
val data by model.data.observeAsState()
TestTableImeSimple(
title = title
) {
data?.forEach {
item {
RegisterForm(vo = it)
CheckForm<T, E>(vo = it)
Spacer(modifier = Modifier.height(10.dp))
}
}
}
}
}
/**
* 表格审核组件
*
* @param VO
* @param M
* @param vo
* @param scaffoldModel
* @param model
* @param first
* @param last
*/
@Composable
inline fun <reified VO : AuditVo, reified M : BaseAuditViewModel<VO>> CheckForm(
vo: VO,
scaffoldModel: ScaffoldModel = viewModel(),
model: M = viewModel(),
first: @Composable () -> Unit = {
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.first_approver,
value = vo.audit.manager?.name ?: ""
)
},
last: @Composable () -> Unit = {
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.last_approver,
value = vo.audit.nextAudit?.manager?.name ?: ""
)
}
) {
(TokenManager.getOwnInfo() as? ManagerVo)?.let { it ->
var confirmDesc: Int? = null
var backDesc: Int? = null
var onConfirm: (() -> Unit)? = null
var onBack: (() -> Unit)? = null
val check: (result: Boolean, cause: StringForm) -> Unit =
{ result: Boolean, cause: StringForm ->
scaffoldModel.update(
message = "确认${if (result) "上报" else "驳回"}",
actionLabel = "确认"
) {
model.check(
auditId = vo.audit.id,
result = result,
cause = cause.formValue.value
?: throw IllegalArgumentException("无法获取审核理由")
) {
scaffoldModel.update(message = it, actionLabel = "刷新") {
model.load { }
}
}
}
}
val accept: (m: String) -> Unit =
{ m: String ->
scaffoldModel.update("确认${m}", actionLabel = "确认") {
model.accept(
auditId = vo.audit.id
) {
scaffoldModel.update(message = it, actionLabel = "刷新") {
model.load { }
}
}
}
}
val doCheck: @Composable () -> Unit = {
val cause = ValidStringForm(
formDesc = "审核理由",
textLength = 20
)
val statusForm by cause.statusForm.observeAsState()
onConfirm = {
if (statusForm == FormStatus.Empty) {
scaffoldModel.update("${cause.formDesc}不能为空", actionLabel = "知道了")
} else {
check(true, cause)
}
}
onBack = {
if (statusForm == FormStatus.Empty) {
scaffoldModel.update("${cause.formDesc}不能为空", actionLabel = "知道了")
} else {
check(false, cause)
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
BaseTextField(
form = cause
)
}
}
when {
//初审记录,负责人为空 等待初审
vo.audit.nextAudit == null && vo.audit.manager == null -> {
if (it.duty == Duty.PamphaBhusal) {
confirmDesc = R.string.accept_btn
onConfirm = { accept("受理") }
}
first()
RowItem(key = R.string.first_approver_origin, value = "")
RowItem(key = R.string.first_result, value = "")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.audit_phases, value = "等待初审"
)
}
//初审记录,负责人不为空 初审受理
vo.audit.nextAudit == null && vo.audit.manager != null -> {
first()
if (it.duty == Duty.PamphaBhusal) {
confirmDesc = R.string.reported_btn
backDesc = R.string.reject_btn
doCheck()
} else {
RowItem(key = R.string.first_approver_origin, value = "")
}
RowItem(key = R.string.first_result, value = "")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.audit_phases, value = "初审受理"
)
}
//初审记录,审核通过(上报) 等待复审
vo.audit.nextAudit != null && vo.audit.nextAudit?.manager == null -> {
if (it.duty == Duty.Teacher) {
confirmDesc = R.string.recheck_btn
onConfirm = { accept("复审") }
}
first()
RowItem(key = R.string.first_approver_origin, value = vo.audit.cause)
RowItem(key = R.string.first_result, value = "通过")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.audit_phases, value = "等待复审"
)
}
//复审记录,审核结果为空 复审受理
vo.audit.nextAudit != null && vo.audit.nextAudit?.result == null -> {
first()
RowItem(key = R.string.first_approver_origin, value = vo.audit.cause)
RowItem(key = R.string.first_result, value = "通过")
last()
if (it.duty == Duty.Teacher) {
confirmDesc = R.string.allow_btn
backDesc = R.string.reject_btn
doCheck()
} else {
RowItem(key = R.string.last_approver_origin, value = "")
}
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.audit_phases, value = "复审受理"
)
}
else -> {
first()
RowItem(key = R.string.first_approver_origin, value = vo.audit.cause)
RowItem(
key = R.string.first_result, value = when (vo.audit.result) {
null -> ""
true -> "通过"
false -> "不通过"
}
)
last()
RowItem(
key = R.string.last_approver_origin,
value = vo.audit.nextAudit?.cause ?: ""
)
RowItem(
key = R.string.last_result, value = when (vo.audit.nextAudit?.result) {
null -> ""
true -> "通过"
false -> "不通过"
}
)
RowItem(
modifier = Modifier.height(BASE_HEIGHT),
key = R.string.audit_phases, value = "完成审核"
)
}
}
if (confirmDesc != null) {
BottomButton(
confirmDesc = confirmDesc,
backDesc = backDesc,
modifier = Modifier.fillMaxWidth(),
onBack = onBack
) {
onConfirm?.let { it() }
}
}
}
}

@ -2,7 +2,6 @@ package com.gyf.csams.uikit
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
@ -13,6 +12,7 @@ import androidx.compose.runtime.livedata.observeAsState
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.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -93,16 +93,17 @@ fun TestTableImeSimple(
val listState = rememberLazyListState() val listState = rememberLazyListState()
val insets = LocalWindowInsets.current val insets = LocalWindowInsets.current
val imeBottom = with(LocalDensity.current) { insets.ime.bottom.toDp() } //TODO 解决键盘遮挡问题
val isVisible = with(LocalDensity.current) { insets.ime.isVisible }
val bottom = with(LocalDensity.current) { insets.ime.bottom.toDp() }
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier
.fillMaxSize()
.nestedScroll(connection = rememberImeNestedScrollConnection()),
contentPadding = PaddingValues(vertical = 10.dp),
state = listState, state = listState,
content = content content = content
) )
LaunchedEffect(imeBottom) {
listState.scrollBy(imeBottom.value)
}
} }
} }

@ -4,10 +4,11 @@ import android.app.Activity
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.account.ui.AccountActivity import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.main.ui.MainActivity import com.gyf.csams.main.ui.MainActivity
import com.gyf.csams.module.UserVo
import com.gyf.lib.uikit.AbstractInitActivity import com.gyf.lib.uikit.AbstractInitActivity
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ApiResponse import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.UserVo
import java.lang.reflect.Type import java.lang.reflect.Type
class InitActivity : AbstractInitActivity<UserVo>() { class InitActivity : AbstractInitActivity<UserVo>() {

@ -3,6 +3,7 @@ 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
@ -10,6 +11,10 @@ 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.ClientType
import com.gyf.csams.module.UserLoginVo
import com.gyf.csams.module.UserRegVo
import com.gyf.csams.module.UserVo
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
@ -23,9 +28,9 @@ import kotlinx.coroutines.launch
* 密码弹窗信息 * 密码弹窗信息
* *
* @property message * @property message
* @property userPasswordVo * @property password
*/ */
data class DialogMessage(val message: String, val userPasswordVo: UserPasswordVo?) data class DialogMessage(val message: String, val password: String)
/** /**
* 注册表单 * 注册表单
@ -60,7 +65,10 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
private var checkJob: Job? = null private var checkJob: Job? = null
//姓名 //姓名
val name = object : ValidStringForm(formDesc = "姓名", textLength = 4) { val name = object : ValidStringForm(
formDesc = "姓名",
textLength = application.resources.getInteger(R.integer.name_length)
) {
override fun check() { override fun check() {
_statusForm.value = FormStatus.Valid _statusForm.value = FormStatus.Valid
checkForm() checkForm()
@ -164,18 +172,18 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
val url = Api.buildUrl(AccountApi.Register) val url = Api.buildUrl(AccountApi.Register)
Logger.i("开始$regBtnDesc,请求接口:$url") Logger.i("开始$regBtnDesc,请求接口:$url")
HttpClient.post( HttpClient.post(
url, HttpCallback<UserPasswordVo>( url, HttpCallback<String>(
action = regBtnDesc, action = regBtnDesc,
onSuccess = { onSuccess = {
_dialogMsg.postValue( _dialogMsg.postValue(
DialogMessage( DialogMessage(
message = it.message, message = it.message,
userPasswordVo = it.body password = it.body ?: throw IllegalArgumentException("无法获取生成密码")
) )
) )
}, },
onFail = onFail, onFail = onFail,
typeToken = object : TypeToken<ApiResponse<UserPasswordVo>>() {}.type typeToken = object : TypeToken<ApiResponse<String>>() {}.type
), ),
jsonParam = UserRegVo( jsonParam = UserRegVo(
studentId = id.formValue.value ?: throw IllegalArgumentException("学号为空"), studentId = id.formValue.value ?: throw IllegalArgumentException("学号为空"),
@ -199,3 +207,27 @@ 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)
)
}
}
}

@ -240,7 +240,7 @@ class AccountActivity : ComponentActivity() {
private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()) { private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()) {
val dialogMsg: DialogMessage? by accountViewModel.dialogMsg.observeAsState(null) val dialogMsg: DialogMessage? by accountViewModel.dialogMsg.observeAsState(null)
val message = dialogMsg?.userPasswordVo?.password val message = dialogMsg?.password
if (message?.isNotEmpty() == true) { if (message?.isNotEmpty() == true) {
PasswordDialog(message = message) PasswordDialog(message = message)
} }

@ -4,14 +4,16 @@ import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import com.gyf.csams.module.ActivityDetailVo
import com.gyf.csams.module.ActivityPhotoVo
import com.gyf.csams.module.BBSVo
import com.gyf.csams.module.UserInfoVo
import com.gyf.csams.uikit.AbstractComment import com.gyf.csams.uikit.AbstractComment
import com.gyf.csams.uikit.ActivityDetailMenu import com.gyf.csams.uikit.ActivityDetailMenu
import com.gyf.csams.uikit.TopMenuInterface import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.* import com.gyf.lib.util.NOT_IMPL_TIP
import kotlinx.coroutines.launch
/** /**
* 活动详情菜单通用状态 * 活动详情菜单通用状态
@ -57,16 +59,7 @@ class ActivityInfoViewModel : ViewModel() {
} }
private fun loadInfo() { private fun loadInfo() {
viewModelScope.launch { TODO()
_activityDetailVo.value = ActivityDetailVo(
activityName = randomChinese(4),
associationName = randomChinese(4),
activityTime = randomDateTime(),
activityLocation = randomChinese(3),
activityDesc = randomChinese(500)
)
}
} }
} }

@ -7,6 +7,7 @@ import android.graphics.BitmapFactory
import androidx.lifecycle.AndroidViewModel 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 com.baidu.mapapi.map.* import com.baidu.mapapi.map.*
import com.baidu.mapapi.model.LatLng import com.baidu.mapapi.model.LatLng
import com.baidu.mapapi.model.LatLngBounds import com.baidu.mapapi.model.LatLngBounds
@ -17,44 +18,25 @@ import com.baidu.mapapi.search.poi.*
import com.baidu.mapapi.search.sug.SuggestionResult import com.baidu.mapapi.search.sug.SuggestionResult
import com.baidu.mapapi.search.sug.SuggestionSearch import com.baidu.mapapi.search.sug.SuggestionSearch
import com.baidu.mapapi.search.sug.SuggestionSearchOption import com.baidu.mapapi.search.sug.SuggestionSearchOption
import com.google.gson.reflect.TypeToken
import com.gyf.csams.BuildConfig import com.gyf.csams.BuildConfig
import com.gyf.csams.MyLocationListener import com.gyf.csams.MyLocationListener
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.csams.module.*
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.*
import com.gyf.lib.util.ContextUtil import com.gyf.lib.util.*
import com.gyf.lib.util.DATETIME_FORMAT import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.NOT_IMPL_TIP import com.gyf.lib.util.DateTimeUtil.DATE_FORMAT
import com.gyf.lib.util.DateTimeUtil.TIME_FORMAT
import com.gyf.lib.util.DateTimeUtil.dateFormat
import com.gyf.lib.util.DateTimeUtil.timeFormat
import com.gyf.lib.util.DateTimeUtil.toDate
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.* import java.util.*
class SizeForm(val application: Application) :
StringForm(formDesc = application.getString(R.string.activity_size), textLength = 2) {
val _formError = MutableLiveData<String>()
val formError: LiveData<String> = _formError
val maxActivitySize = application.resources.getInteger(R.integer.activity_size)
val rangeError = application.getString(R.string.activity_size_error, 1, maxActivitySize)
override fun onChange(value: String) {
if (value.length > textLength) {
_formError.value = "${formDesc}不能超过最大长度$textLength"
_formValue.value = value.slice(IntRange(0, textLength - 1))
} else if (value.matches(Regex("\\d+")) && value.toInt() !in 1..maxActivitySize) {
Logger.i("活动人数:${value}不合法")
_formError.value = rangeError
} else {
_formValue.value = value
_formError.value = ""
}
}
}
/** /**
* 申请活动数据状态管理 * 申请活动数据状态管理
* *
@ -65,20 +47,48 @@ class SizeForm(val application: Application) :
*/ */
class ApplyActViewModel(application: Application) : AndroidViewModel(application) { class ApplyActViewModel(application: Application) : AndroidViewModel(application) {
val activityName = val activityName =
StringForm(formDesc = application.getString(R.string.activity_name), textLength = 10) AsyncStringForm(formDesc = application.getString(R.string.activity_name), textLength = 10)
val activityTime = StringForm( val activityDate = AsyncStringForm(
formDesc = application.getString(R.string.activity_date),
textLength = DATE_FORMAT.length
)
val activityTime = AsyncStringForm(
formDesc = application.getString(R.string.activity_time), formDesc = application.getString(R.string.activity_time),
textLength = DATETIME_FORMAT.length textLength = TIME_FORMAT.length
) )
val activityAddress = val activityAddress =
StringForm(formDesc = application.getString(R.string.activity_address), textLength = 30) AsyncStringForm(
formDesc = application.getString(R.string.activity_address),
textLength = 30
)
val activityDesc = val activityDesc =
StringForm(formDesc = application.getString(R.string.activity_desc), textLength = 50) AsyncStringForm(formDesc = application.getString(R.string.activity_desc), textLength = 50)
val activitySize = SizeForm(application = application) val activitySize = object :
AsyncStringForm(formDesc = application.getString(R.string.activity_size), textLength = 2) {
val maxActivitySize = application.resources.getInteger(R.integer.activity_size)
override fun check() {
_formValue.value?.let {
when {
it.matches(Regex("\\d+")) && it.toInt() !in 1..maxActivitySize -> _statusForm.value =
FormStatus.FormatError
else -> _statusForm.value = FormStatus.Valid
}
}
}
}
private val _checkInfo = MutableLiveData<ApiResponse<ActivityCheckVo>>()
val checkInfo: LiveData<ApiResponse<ActivityCheckVo>> = _checkInfo
init {
read { }
}
val city = val city =
object : StringForm(formDesc = application.getString(R.string.city), textLength = 4) { object : ValidStringForm(formDesc = application.getString(R.string.city), textLength = 4) {
override val formPlaceholder = "" override val formPlaceholder = ""
} }
@ -102,6 +112,7 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
}) })
} }
//TODO 抽离到单独的activity model?
lateinit var scaffoldModel: ScaffoldModel lateinit var scaffoldModel: ScaffoldModel
private val _mapView = MutableLiveData<MapView>() private val _mapView = MutableLiveData<MapView>()
@ -128,7 +139,6 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
_selectPoi.postValue(null) _selectPoi.postValue(null)
} }
fun searchPoiInCity() { fun searchPoiInCity() {
if (city.formValue.value?.isEmpty() == true || if (city.formValue.value?.isEmpty() == true ||
address.formValue.value?.isEmpty() == true address.formValue.value?.isEmpty() == true
@ -164,13 +174,6 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
val mapStatusUpdate = MapStatusUpdateFactory.newLatLng(latLng) val mapStatusUpdate = MapStatusUpdateFactory.newLatLng(latLng)
map.setMapStatus(mapStatusUpdate) map.setMapStatus(mapStatusUpdate)
// 清除之前的
// clearData()
// 显示当前的
// if (!showSuggestMarker(latLng)) {
// searchPoiInCity()
// }
} }
} }
@ -377,7 +380,7 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
mBaiduMap.addOverlay(markerOptions) mBaiduMap.addOverlay(markerOptions)
} }
fun isLatlngEqual(latLng0: LatLng, latLng1: LatLng): Boolean { private fun isLatlngEqual(latLng0: LatLng, latLng1: LatLng): Boolean {
return (latLng0.latitude == latLng1.latitude return (latLng0.latitude == latLng1.latitude
&& latLng0.longitude == latLng1.longitude) && latLng0.longitude == latLng1.longitude)
} }
@ -559,7 +562,8 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
clearData() clearData()
} }
private fun destroy() { override fun onCleared() {
super.onCleared()
mPoiSearch.destroy() mPoiSearch.destroy()
mSuggestionSearch.destroy() mSuggestionSearch.destroy()
@ -569,11 +573,82 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
mBitmapDescWaterDrop?.recycle() mBitmapDescWaterDrop?.recycle()
} }
private fun clean() {
activityName.clean()
activityDate.clean()
activityTime.clean()
activityDesc.clean()
activitySize.clean()
activityAddress.clean()
}
/** /**
* TODO 提交申请 * 提交申请
* *
*/ */
fun apply(callback: (message: String) -> Unit) { fun apply(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP) val activityName = activityName.formValue.value ?: throw IllegalArgumentException("活动名称为空")
val activityDate = activityDate.formValue.value ?: throw IllegalArgumentException("活动日期为空")
val activityTime = activityTime.formValue.value ?: throw IllegalArgumentException("活动时间为空")
val activityDesc = activityDesc.formValue.value ?: throw IllegalArgumentException("活动介绍为空")
val activitySize = activitySize.formValue.value ?: throw IllegalArgumentException("活动规模为空")
val activityAddress =
activityAddress.formValue.value ?: throw IllegalArgumentException("活动地点为空")
val associationId = (TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId
?: throw IllegalArgumentException("社团id为空")
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(ActivityApi.Register),
callback = HttpCallback<Boolean>(action = "提交活动申请书", onSuccess = {
if (it.body == true) {
clean()
}
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = ActivityApplyVo(
associationId = associationId,
activityVo = ActivityVo(
activityName = activityName,
activityTime = "$activityDate $activityTime".toDate().time,
activityAddress = activityAddress,
activityDesc = activityDesc,
activitySize = activitySize.toInt()
),
activityId = _checkInfo.value?.body?.activityId,
token = TokenManager.getToken()
)
)
}
} }
/**
* 查看审核进度
*
* @param callback
*/
private fun read(callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(url = Api.buildUrl(ActivityApi.Read),
HttpCallback<ActivityCheckVo>(action = "查看活动申请书审核进度",
onSuccess = { it ->
_checkInfo.postValue(it)
it.body?.activityVo?.let {
activityName.setValue(it.activityName)
val d = Date(it.activityTime)
activityDate.setValue(d.dateFormat())
activityTime.setValue(d.timeFormat())
activityAddress.setValue(it.activityAddress)
activityDesc.setValue(it.activityDesc)
activitySize.setValue(it.activitySize.toString())
}
}, typeToken = object : TypeToken<ApiResponse<ActivityCheckVo>>() {}.type
),
jsonParam = OnlyToken(clientType = ClientType.Foreground)
)
}
}
} }

@ -22,15 +22,18 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.activity.model.* import com.gyf.csams.activity.model.*
import com.gyf.csams.module.ActivityPhotoVo
import com.gyf.csams.module.BBSVo
import com.gyf.csams.module.UserInfoVo
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.NavBody import com.gyf.lib.uikit.NavBody
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.uikit.ShowSnackbar import com.gyf.lib.uikit.ShowSnackbar
import com.gyf.lib.util.ActivityPhotoVo
import com.gyf.lib.util.BBSVo import com.gyf.lib.util.DateTimeUtil.datetimeFormat
import com.gyf.lib.util.UserInfoVo import java.util.*
import com.gyf.lib.util.format
/** /**
* 活动详情 * 活动详情
@ -140,10 +143,10 @@ class ActivityDetailActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceAround verticalArrangement = Arrangement.SpaceAround
) { ) {
RectListItem(text = it.associationName) RectListItem(text = it.associationVo.name)
RectListItem(text = it.activityName) RectListItem(text = it.activityVo.activityName)
RectListItem(text = it.activityTime.format()) RectListItem(text = Date(it.activityVo.activityTime).datetimeFormat())
RectListItem(text = it.activityLocation) RectListItem(text = it.activityVo.activityAddress)
} }
} }
@ -184,7 +187,10 @@ class ActivityDetailActivity : ComponentActivity() {
Column(modifier = modifier) { Column(modifier = modifier) {
val activityDetailVo by model.activityDetailVo.observeAsState() val activityDetailVo by model.activityDetailVo.observeAsState()
activityDetailVo?.let { activityDetailVo?.let {
DescCard(modifier = Modifier.weight(0.5F), content = it.activityDesc) DescCard(
modifier = Modifier.weight(0.5F),
content = it.activityVo.activityDesc
)
} }
} }
} }
@ -440,7 +446,7 @@ class ActivityDetailActivity : ComponentActivity() {
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
Text(text = vo.name) Text(text = vo.name)
Text(text = vo.createTime.format()) Text(text = vo.createTime.datetimeFormat())
} }
} }
Spacer(modifier = Modifier.weight(0.5F)) Spacer(modifier = Modifier.weight(0.5F))

@ -13,16 +13,14 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
@ -33,19 +31,21 @@ import com.baidu.location.LocationClientOption
import com.baidu.mapapi.map.MapStatusUpdateFactory import com.baidu.mapapi.map.MapStatusUpdateFactory
import com.baidu.mapapi.search.sug.SuggestionResult import com.baidu.mapapi.search.sug.SuggestionResult
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.MaterialTimePicker.INPUT_MODE_KEYBOARD
import com.google.android.material.timepicker.TimeFormat
import com.gyf.csams.MainApplication 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.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
import com.gyf.csams.uikit.CheckTip
import com.gyf.csams.uikit.DescCard import com.gyf.csams.uikit.DescCard
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.*
import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.format import com.gyf.lib.util.DateTimeUtil.dateFormat
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.* import java.util.*
@ -87,7 +87,7 @@ class ApplyActActivity : AppCompatActivity() {
model.scaffoldModel = scaffoldModel model.scaffoldModel = scaffoldModel
val showMap by model.showMap.observeAsState(false) val showMap by model.showMap.observeAsState(false)
val location by MyLocationListener.location.observeAsState() val location by MyLocationListener.location.observeAsState()
val checkInfo by model.checkInfo.observeAsState()
when { when {
showMap && location != null -> { showMap && location != null -> {
Box { Box {
@ -133,19 +133,35 @@ class ApplyActActivity : AppCompatActivity() {
) )
} }
} }
} }
} }
} }
else -> { else -> {
checkInfo?.body?.auditCheckVo.let {
if (it == null) {
Title(modifier = Modifier.weight(0.2F)) Title(modifier = Modifier.weight(0.2F))
} else {
Title()
CheckTip(it, modifier = Modifier.weight(0.15F))
}
}
Row( Row(
Modifier Modifier
.weight(0.1F) .weight(0.1F)
.fillMaxWidth(), horizontalArrangement = Arrangement.Center .fillMaxWidth(), horizontalArrangement = Arrangement.Center
) { ) {
BaseTextField(form = model.activityName) BaseTextField(
form = model.activityName,
readOnly = isReadOnly(model = model)
)
}
Row(
Modifier
.weight(0.1F)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
ActivityDate()
} }
Row( Row(
Modifier Modifier
@ -162,13 +178,9 @@ class ApplyActActivity : AppCompatActivity() {
) )
DescCard( DescCard(
modifier = Modifier.weight(0.3F), modifier = Modifier.weight(0.3F),
stringForm = model.activityDesc stringForm = model.activityDesc,
readonly = isReadOnly(model = model)
) )
//TODO 活动人数不合法警告
// val error by model.activitySize.formError.observeAsState()
// if (error?.isNotEmpty() == true) {
// scaffoldModel.update(message = error)
// }
Row( Row(
Modifier Modifier
.weight(0.1F) .weight(0.1F)
@ -177,18 +189,58 @@ class ApplyActActivity : AppCompatActivity() {
BaseTextField( BaseTextField(
form = model.activitySize, form = model.activitySize,
keyboardOptions = keyboardOptions =
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number) KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
readOnly = isReadOnly(model = model)
) )
} }
Spacer(modifier = Modifier.weight(0.05F)) Spacer(modifier = Modifier.weight(0.05F))
BottomButton(modifier = Modifier.fillMaxWidth()) { val activityName = model.activityName.statusForm.observeAsState()
model.apply { scaffoldModel.update(message = it) } val activityDate = model.activityDate.statusForm.observeAsState()
val activityTime = model.activityTime.statusForm.observeAsState()
val activityDesc = model.activityDesc.statusForm.observeAsState()
val activitySize = model.activitySize.statusForm.observeAsState()
BottomButton(
modifier = Modifier.fillMaxWidth(),
enabled = check(
activityName,
activityDate,
activityTime,
activityDesc,
activitySize
),
confirmDesc = if (checkInfo?.body != null) R.string.reg_again_btn else R.string.confirm_btn,
) {
model.apply {
scaffoldModel.update(message = it, actionLabel = "返回") {
onBackPressed()
}
}
}
}
}
}
} }
} }
} }
@Composable
private fun isReadOnly(model: ApplyActViewModel): Boolean {
val checkInfo by model.checkInfo.observeAsState()
val flag = (checkInfo?.body?.let { it.auditCheckVo.checkStatus != CheckStatus.Finish })
Logger.d("flag=${flag}")
return flag == true
} }
private fun check(vararg arrayOfStates: State<FormStatus?>): Boolean {
arrayOfStates.forEach {
if (it.value != FormStatus.Valid) {
return false
} }
} }
return true
} }
@Composable @Composable
@ -233,16 +285,6 @@ class ApplyActActivity : AppCompatActivity() {
BaseTextField(modifier = Modifier.weight(0.5F), form = model.address) BaseTextField(modifier = Modifier.weight(0.5F), form = model.address)
} }
} }
// Row(
// modifier = Modifier.fillMaxWidth(),
// horizontalArrangement = Arrangement.Center
// ) {
// OutlinedButton(onClick = {
// model.searchPoiInCity()
// }) {
// Text(text = stringResource(id = R.string.search_btn))
// }
// }
} }
@ -363,11 +405,40 @@ class ApplyActActivity : AppCompatActivity() {
Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text( Text(
text = stringResource(id = R.string.activity_application), text = stringResource(id = R.string.activity_application),
style = MaterialTheme.typography.h4 style = MaterialTheme.typography.h6
) )
} }
} }
@Composable
private fun ActivityDate(
modifier: Modifier = Modifier,
model: ApplyActViewModel = viewModel()
) {
val onClick: () -> Unit = {
MaterialDatePicker.Builder.datePicker().build().apply {
show(supportFragmentManager, tag)
addOnPositiveButtonClickListener {
model.activityDate.onChange(Date(it).dateFormat())
}
}
}
BaseTextField(
modifier = modifier.clickable(onClick = onClick),
form = model.activityDate,
readOnly = true
) {
if (!isReadOnly(model = model)) {
IconButton(onClick = onClick) {
Icon(
painter = painterResource(id = R.drawable.ic_date),
contentDescription = null
)
}
}
}
}
/** /**
* 活动时间 * 活动时间
* *
@ -380,31 +451,42 @@ class ApplyActActivity : AppCompatActivity() {
model: ApplyActViewModel = viewModel() model: ApplyActViewModel = viewModel()
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
BaseTextField( val onClick: () -> Unit = {
modifier = modifier,
form = model.activityTime, readOnly = true
) {
IconButton(onClick = {
scope.launch { scope.launch {
val picker = MaterialDatePicker MaterialTimePicker.Builder()
.Builder .setInputMode(INPUT_MODE_KEYBOARD)
.datePicker() .setTimeFormat(TimeFormat.CLOCK_24H)
.setSelection(Date().time)
.build() .build()
picker.show(supportFragmentManager, picker.toString()) .apply {
picker.addOnPositiveButtonClickListener { show(supportFragmentManager, tag)
model.activityTime.onChange(Date(it).format()) addOnPositiveButtonClickListener {
model.activityTime.onChange(
"${
String.format(
"%02d",
hour
)
}:${String.format("%02d", minute)}"
)
} }
} }
}
}) { }
BaseTextField(
modifier = modifier.clickable(onClick = onClick),
form = model.activityTime,
readOnly = true
) {
if (!isReadOnly(model = model)) {
IconButton(onClick = onClick) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_date), painter = painterResource(id = R.drawable.ic_clock),
contentDescription = null contentDescription = null
) )
} }
} }
} }
}
private fun requestLocation() { private fun requestLocation() {
//如果想获取地址信息,需在配置LocationClientOption类时做相应的设置 //如果想获取地址信息,需在配置LocationClientOption类时做相应的设置
@ -471,6 +553,7 @@ class ApplyActActivity : AppCompatActivity() {
} }
BaseTextField(modifier = modifier, form = model.activityAddress, readOnly = true) { BaseTextField(modifier = modifier, form = model.activityAddress, readOnly = true) {
if (!isReadOnly(model = model)) {
IconButton(onClick = { IconButton(onClick = {
when (PackageManager.PERMISSION_GRANTED) { when (PackageManager.PERMISSION_GRANTED) {
checkSelfPermissions() -> { checkSelfPermissions() -> {
@ -493,5 +576,6 @@ class ApplyActActivity : AppCompatActivity() {
} }
} }
} }
}
} }

@ -6,11 +6,14 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.R
import com.gyf.csams.module.*
import com.gyf.csams.uikit.AssociationMenu import com.gyf.csams.uikit.AssociationMenu
import com.gyf.csams.uikit.TopMenuInterface import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.gyf.lib.util.ApiResponse
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -35,7 +38,7 @@ class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
HttpCallback<AssociationMainVo>(action = "获取社团信息", onSuccess = { HttpCallback<AssociationMainVo>(action = "获取社团信息", onSuccess = {
it.body?.let { _associationVo.postValue(it) } it.body?.let { _associationVo.postValue(it) }
}, typeToken = object : TypeToken<ApiResponse<AssociationMainVo>>() {}.type), }, typeToken = object : TypeToken<ApiResponse<AssociationMainVo>>() {}.type),
jsonParam = ShowAssociationVo(id = id) jsonParam = ShowAssociationVo(id = id, token = TokenManager.getToken())
) )
} }
} }
@ -61,7 +64,8 @@ class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
* *
*/ */
class MemberViewModel(application: Application) : ScrollViewModel<UserInfoVo>(application) { class MemberViewModel(application: Application) : ScrollViewModel<UserInfoVo>(application) {
val name = StringForm(formDesc = "姓名关键字", 5) val name =
StringForm(formDesc = "姓名关键字", application.resources.getInteger(R.integer.name_length))
override val initSize: Int = 10 override val initSize: Int = 10
@ -81,7 +85,11 @@ class MemberViewModel(application: Application) : ScrollViewModel<UserInfoVo>(ap
}, },
typeToken = object : TypeToken<ApiResponse<MutableList<UserInfoVo>>>() {}.type typeToken = object : TypeToken<ApiResponse<MutableList<UserInfoVo>>>() {}.type
), ),
jsonParam = QueryAssociationMembers(id = id, name = name.formValue.value) jsonParam = QueryAssociationMembers(
id = id,
name = name.formValue.value,
token = TokenManager.getToken()
)
) )
} }
} }

@ -3,12 +3,12 @@ package com.gyf.csams.association.model
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import com.gyf.csams.module.ChoiceQuestionVo
import com.gyf.csams.module.Exam
import com.gyf.csams.module.ExamType
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.* import com.gyf.lib.util.NOT_IMPL_TIP
import kotlinx.coroutines.launch
import kotlin.random.Random
/** /**
* 题库界面类型 * 题库界面类型
@ -64,6 +64,10 @@ class ExamViewModel(application: Application) : ScrollViewModel<Exam>(applicatio
load() load()
} }
fun createQuestion(): StringForm {
return StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH)
}
/** /**
* 切换题型 * 切换题型
* *
@ -81,21 +85,7 @@ class ExamViewModel(application: Application) : ScrollViewModel<Exam>(applicatio
* @return * @return
*/ */
private fun createExam(type: ExamType): Exam { private fun createExam(type: ExamType): Exam {
val question = StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH) TODO("创建题目")
return when (type) {
ExamType.CQ -> ChoiceQuestionVo(
answers = ('A'..'D').map {
StringForm(
formDesc = "选项",
textLength = ANSWER_TEXT_LENGTH,
value = "选项$it"
)
},
rightAnswer = 0,
question = question
)
ExamType.OQ -> OpenQuestionsVo(question = question)
}
} }
@ -140,42 +130,7 @@ class ExamViewModel(application: Application) : ScrollViewModel<Exam>(applicatio
* *
*/ */
fun load() { fun load() {
viewModelScope.launch { TODO("加载题目")
_data.value?.apply {
repeat(initSize) {
if (Random.nextBoolean()) {
add(
OpenQuestionsVo(
question = StringForm(
formDesc = "问题",
textLength = QUESTION_TEXT_LENGTH,
value = "这是一道开放题:$size"
)
)
)
} else {
add(
ChoiceQuestionVo(
question = StringForm(
formDesc = "问题",
textLength = QUESTION_TEXT_LENGTH,
value = "这是一道选择题:$size"
),
answers = ('A'..'D').map {
StringForm(
formDesc = "选项",
textLength = ANSWER_TEXT_LENGTH,
value = "选项$it"
)
},
rightAnswer = Random.nextInt(ANSWER_SIZE)
)
)
}
}
}
}
} }
/** /**

@ -12,9 +12,12 @@ import coil.imageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.MainApplication import com.gyf.csams.MainApplication
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.BuildConfig
import com.gyf.lib.uikit.AsyncStringForm
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -22,19 +25,13 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
class TestStringForm(formDesc: String, textLength: Int) : ValidStringForm(formDesc, textLength) {
fun setValue(value: String) {
_formValue.postValue(value)
}
}
class RegAssociationViewModel(application: Application) : AndroidViewModel(application) { class RegAssociationViewModel(application: Application) : AndroidViewModel(application) {
val frameDesc = "社团注册资料" val frameDesc = "社团注册资料"
val name = TestStringForm(formDesc = "社团名称", textLength = 5) val name = AsyncStringForm(formDesc = "社团名称", textLength = 5)
val desc = TestStringForm(formDesc = "社团简介", textLength = 30) val desc = AsyncStringForm(formDesc = "社团简介", textLength = 30)
private val _picture = MutableLiveData<Uri?>() private val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri?> = _picture val picture: LiveData<Uri?> = _picture
@ -42,8 +39,8 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
private val _fileId = MutableLiveData<Int>() private val _fileId = MutableLiveData<Int>()
val fileId: LiveData<Int> = _fileId val fileId: LiveData<Int> = _fileId
private val _checkInfo = MutableLiveData<AssociationCheckVo?>() private val _checkInfo = MutableLiveData<ApiResponse<AssociationCheckVo>>()
val checkInfo: LiveData<AssociationCheckVo?> = _checkInfo val checkInfo: LiveData<ApiResponse<AssociationCheckVo>> = _checkInfo
val picturePlaceHolder = "请上传图片" val picturePlaceHolder = "请上传图片"
@ -74,17 +71,18 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
HttpClient.post( HttpClient.post(
Api.buildUrl(AssociationApi.Read), Api.buildUrl(AssociationApi.Read),
HttpCallback<AssociationCheckVo>(action = "加载历史注册资料", onSuccess = { it -> HttpCallback<AssociationCheckVo>(action = "加载历史注册资料", onSuccess = { it ->
it.body?.let { it ->
name.setValue(it.name)
desc.setValue(it.desc)
_checkInfo.postValue(it) _checkInfo.postValue(it)
it.body?.let { it ->
name.setValue(it.associationVo.name)
desc.setValue(it.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.logo}") .data("${BuildConfig.SERVER_ADDRESS}/${it.associationVo.logo}")
.target( .target(
onSuccess = { result -> onSuccess = { result ->
it.logo.split("/").apply { it.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 {
@ -146,6 +144,13 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
} }
} }
private fun clean() {
name.clean()
desc.clean()
_picture.postValue(null)
}
/** /**
* *
* *
@ -164,18 +169,18 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
HttpCallback<Boolean>("注册社团", onSuccess = { HttpCallback<Boolean>("注册社团", onSuccess = {
Logger.i(it.message) Logger.i(it.message)
callback(it.message) callback(it.message)
name.clean() if (it.body == true) {
desc.clean() clean()
_picture.postValue(null) }
}, onFail = { }, onFail = {
Logger.e(it) Logger.e(it)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type), }, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = jsonParam = AssociationRegVo(
AssociationRegVo(
name = nameValue, name = nameValue,
desc = descValue, desc = descValue,
fileId = fileId, fileId = fileId,
id = checkInfo.value?.id associationId = _checkInfo.value?.body?.associationVo?.associationId,
token = TokenManager.getToken()
) )
) )
} }

@ -30,9 +30,10 @@ import com.gyf.csams.R
import com.gyf.csams.activity.ui.ActivityDetailActivity import com.gyf.csams.activity.ui.ActivityDetailActivity
import com.gyf.csams.activity.ui.ApplyActActivity import com.gyf.csams.activity.ui.ApplyActActivity
import com.gyf.csams.association.model.* import com.gyf.csams.association.model.*
import com.gyf.csams.module.HistoryActVo
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.HistoryActVo import com.gyf.lib.util.TokenManager
/** /**
@ -73,13 +74,23 @@ class AssociationActivity : ComponentActivity() {
onDismissRequest = { }, onDismissRequest = { },
properties = PopupProperties() properties = PopupProperties()
) { ) {
when {
TokenManager.getUserInfo()?.associationVo?.associationId == associationId && TokenManager.getUserInfo()?.isHead == true -> {
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
startActivity( startActivity(
Intent( Intent(
this@AssociationActivity, this@AssociationActivity,
ApplyActActivity::class.java ApplyActActivity::class.java
).apply {
putExtra(
AssociationActivity::javaClass.name,
associationId
) )
}
) )
model.close() model.close()
}) { }) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
@ -125,7 +136,9 @@ class AssociationActivity : ComponentActivity() {
) )
} }
} }
DropdownMenuItem(onClick = { }
TokenManager.getUserInfo()?.associationVo == null -> DropdownMenuItem(
onClick = {
intent.apply { intent.apply {
putExtra( putExtra(
ExamActivityType::name.name, ExamActivityType::name.name,
@ -143,6 +156,8 @@ class AssociationActivity : ComponentActivity() {
) )
} }
} }
}
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
model.close() model.close()
}) { }) {

@ -21,6 +21,9 @@ import com.gyf.csams.R
import com.gyf.csams.association.model.ANSWER_SIZE import com.gyf.csams.association.model.ANSWER_SIZE
import com.gyf.csams.association.model.ExamActivityType import com.gyf.csams.association.model.ExamActivityType
import com.gyf.csams.association.model.ExamViewModel import com.gyf.csams.association.model.ExamViewModel
import com.gyf.csams.module.ChoiceQuestionVo
import com.gyf.csams.module.Exam
import com.gyf.csams.module.OpenQuestionsVo
import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
@ -28,9 +31,6 @@ import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.ChoiceQuestionVo
import com.gyf.lib.util.Exam
import com.gyf.lib.util.OpenQuestionsVo
/** /**
@ -173,9 +173,15 @@ class ExamActivity : ComponentActivity() {
* @param exam * @param exam
*/ */
@Composable @Composable
private fun Question(modifier: Modifier = Modifier, exam: Exam) { private fun Question(
modifier: Modifier = Modifier,
exam: Exam,
model: ExamViewModel = viewModel()
) {
/*问题**/
val s = model.createQuestion()
BaseTextField( BaseTextField(
form = exam.question, form = s,
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.background(color = MaterialTheme.colors.background) .background(color = MaterialTheme.colors.background)
@ -202,7 +208,7 @@ class ExamActivity : ComponentActivity() {
) { ) {
IconButton(onClick = { IconButton(onClick = {
if (isAdd) { if (isAdd) {
if ((newExam?.question?.formValue?.value ?: "").isNotEmpty()) { if ((newExam?.question ?: "").isNotEmpty()) {
scaffoldModel.update( scaffoldModel.update(
message = model.addTip, message = model.addTip,
actionLabel = model.actionLabel actionLabel = model.actionLabel
@ -321,7 +327,8 @@ class ExamActivity : ComponentActivity() {
val isRightAnswer = val isRightAnswer =
choiceQuestionVo.rightAnswer == answerIndex choiceQuestionVo.rightAnswer == answerIndex
RadioButton(selected = isRightAnswer, onClick = click) RadioButton(selected = isRightAnswer, onClick = click)
BaseTextField(form = it) val c = model.createQuestion()
BaseTextField(form = c)
} }
} }
} }

@ -1,7 +1,6 @@
package com.gyf.csams.association.ui package com.gyf.csams.association.ui
import android.Manifest import android.Manifest
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
@ -24,23 +23,19 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment 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.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat 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
import com.gyf.csams.association.model.RegAssociationViewModel import com.gyf.csams.association.model.RegAssociationViewModel
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
import com.gyf.csams.uikit.CheckTip
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.CheckStatus
import com.gyf.lib.util.format
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import java.util.*
/** /**
@ -51,6 +46,15 @@ class RegAssociationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
val model: RegAssociationViewModel = viewModel()
val checkInfo by model.checkInfo.observeAsState()
checkInfo?.body.let { it ->
if (it?.auditCheckVo?.checkStatus == CheckStatus.Finish && it.auditCheckVo.result) {
setResult(RESULT_OK, Intent().apply {
putExtra(RegAssociationActivity::class.java.name, CheckStatus.Finish.name)
})
onBackPressed()
} else {
Body { Body {
MainColumnFrame(background = { MainColumnFrame(background = {
Background( Background(
@ -63,7 +67,7 @@ class RegAssociationActivity : ComponentActivity() {
.weight(0.1F) .weight(0.1F)
) )
Title() Title()
Tip() it?.auditCheckVo?.let { CheckTip(it) }
Name() Name()
Desc( Desc(
modifier = Modifier modifier = Modifier
@ -78,16 +82,16 @@ class RegAssociationActivity : ComponentActivity() {
) )
Spacer(modifier = Modifier.weight(0.05F)) Spacer(modifier = Modifier.weight(0.05F))
val model: RegAssociationViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel() val scaffoldModel: ScaffoldModel = viewModel()
val name by model.name.statusForm.observeAsState() val name by model.name.statusForm.observeAsState()
val desc by model.name.statusForm.observeAsState() val desc by model.name.statusForm.observeAsState()
val fileId by model.fileId.observeAsState() val fileId by model.fileId.observeAsState()
val checkInfo by model.checkInfo.observeAsState()
BottomButton( BottomButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
enabled = name == FormStatus.Valid && desc == FormStatus.Valid && fileId != null, enabled = name == FormStatus.Valid && desc == FormStatus.Valid && fileId != null,
confirmDesc = if (checkInfo != null) R.string.reg_again_btn else R.string.reg_btn, confirmDesc = if (it != null) R.string.reg_again_btn else R.string.reg_btn,
) { ) {
model.register { model.register {
@ -99,49 +103,13 @@ class RegAssociationActivity : ComponentActivity() {
Spacer(modifier = Modifier.weight(0.05F)) Spacer(modifier = Modifier.weight(0.05F))
} }
} }
}
}
@Composable
private fun Tip(model: RegAssociationViewModel = viewModel()) {
val checkInfo by model.checkInfo.observeAsState()
checkInfo?.let {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
when (val status = it.checkStatus) {
CheckStatus.Finish -> Text(buildAnnotatedString {
withStyle(style = MaterialTheme.typography.h5.toParagraphStyle()) {
append("您于${Date(it.applyTime).format()}提交的${model.frameDesc}审核不通过原因如下:\n")
}
withStyle(
style = MaterialTheme.typography.h6.copy(color = MaterialTheme.colors.error)
.toParagraphStyle()
) {
append("初审意见:${it.firstCause}\n")
it.lastCause?.let {
append("复审意见:${it}")
}
}
})
else -> {
Text(
text = status.desc, style =
MaterialTheme.typography.h5.copy(color = MaterialTheme.colors.primary)
)
}
} }
} }
} }
} }
@Preview
@Composable
private fun TestPreview9() {
Text(text = "123", color = MaterialTheme.colors.error)
}
/** /**
@ -164,7 +132,7 @@ class RegAssociationActivity : ComponentActivity() {
val resultLauncher = val resultLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) { when (it.resultCode) {
Activity.RESULT_OK -> { RESULT_OK -> {
it.data?.data?.apply { it.data?.data?.apply {
Logger.i("uri=$this") Logger.i("uri=$this")
@ -227,8 +195,7 @@ class RegAssociationActivity : ComponentActivity() {
Image( Image(
painter = logo, contentDescription = null painter = logo, contentDescription = null
) )
val checkInfo by model.checkInfo.observeAsState() if (!isReadOnly(model = model)) {
if (checkInfo == null || checkInfo?.checkStatus == CheckStatus.Finish) {
Column( Column(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
@ -285,16 +252,23 @@ class RegAssociationActivity : ComponentActivity() {
} }
} }
@Composable
private fun isReadOnly(model: RegAssociationViewModel): Boolean {
val checkInfo by model.checkInfo.observeAsState()
val flag = (checkInfo?.body?.let { it.auditCheckVo.checkStatus != CheckStatus.Finish })
Logger.d("flag=${flag}")
return flag == true
}
/** /**
* 社团名称 * 社团名称
* @param model * @param model
*/ */
@Composable @Composable
private fun Name(model: RegAssociationViewModel = viewModel()) { private fun Name(model: RegAssociationViewModel = viewModel()) {
val checkInfo by model.checkInfo.observeAsState()
BaseTextField( BaseTextField(
form = model.name, singeLine = true, modifier = Modifier.fillMaxWidth(), form = model.name, singeLine = true, modifier = Modifier.fillMaxWidth(),
readOnly = checkInfo?.checkStatus != CheckStatus.Finish readOnly = isReadOnly(model = model)
) )
} }
@ -304,10 +278,9 @@ class RegAssociationActivity : ComponentActivity() {
*/ */
@Composable @Composable
private fun Desc(model: RegAssociationViewModel = viewModel(), modifier: Modifier) { private fun Desc(model: RegAssociationViewModel = viewModel(), modifier: Modifier) {
val checkInfo by model.checkInfo.observeAsState()
BaseTextField( BaseTextField(
form = model.desc, modifier = modifier, form = model.desc, modifier = modifier,
readOnly = checkInfo?.checkStatus != CheckStatus.Finish readOnly = isReadOnly(model = model)
) )
} }

@ -3,12 +3,14 @@ package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.* import androidx.lifecycle.*
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.*
import com.gyf.csams.uikit.AbstractComment import com.gyf.csams.uikit.AbstractComment
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.gyf.lib.util.ApiResponse
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -42,6 +44,7 @@ class NotificationViewModel(application: Application) : AndroidViewModel(applica
jsonParam = NotificationDto( jsonParam = NotificationDto(
receiverId = TokenManager.getToken().id, receiverId = TokenManager.getToken().id,
receiverClient = ClientType.Foreground, receiverClient = ClientType.Foreground,
token = TokenManager.getToken()
) )
) )
} }
@ -94,7 +97,8 @@ class MarqueeViewModel : AbstractComment() {
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type }, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type
), ),
jsonParam = LeaveMessageVo( jsonParam = LeaveMessageVo(
message = newContent.formValue.value ?: "" message = newContent.formValue.value ?: "",
token = TokenManager.getToken()
) )
) )
} }
@ -180,7 +184,8 @@ class AssociationListViewModel(application: Application) :
), ),
jsonParam = SearchAssociationVo( jsonParam = SearchAssociationVo(
name = name.formValue.value ?: "", name = name.formValue.value ?: "",
desc = desc.formValue.value ?: "" desc = desc.formValue.value ?: "",
token = TokenManager.getToken()
) )
) )
} }
@ -199,14 +204,10 @@ class CenterViewModel : ViewModel() {
val myCollectActivity = "我收藏的社团活动" val myCollectActivity = "我收藏的社团活动"
init {
load()
}
private fun load() { private fun load() {
viewModelScope.launch { viewModelScope.launch {
TODO() TODO()
} }
} }
} }

@ -2,7 +2,9 @@ package com.gyf.csams.main.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
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.clickable import androidx.compose.foundation.clickable
@ -26,11 +28,16 @@ import androidx.navigation.compose.composable
import com.google.accompanist.coil.rememberCoilPainter import com.google.accompanist.coil.rememberCoilPainter
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
import com.gyf.csams.main.model.* import com.gyf.csams.main.model.*
import com.gyf.csams.message.ui.MessageActivity import com.gyf.csams.message.ui.SysMessageActivity
import com.gyf.csams.module.AssociationVo
import com.gyf.csams.module.CheckStatus
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.UserVo
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.service.BaseActivity import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
@ -64,8 +71,13 @@ class MainActivity : BaseActivity() {
imageViewModel.cancel() imageViewModel.cancel()
} }
composable(MainMenu.Center.name) { composable(MainMenu.Center.name) {
val refresh: RefreshViewModel = viewModel()
refresh.refresh()
val load by refresh.refresh.observeAsState()
load?.let {
Center(navController = nav) Center(navController = nav)
ShowSnackbar(scaffoldState = scaffoldState) ShowSnackbar(scaffoldState = scaffoldState)
}
imageViewModel.cancel() imageViewModel.cancel()
} }
} }
@ -105,12 +117,21 @@ class MainActivity : BaseActivity() {
modifier = Modifier.weight(0.7F), modifier = Modifier.weight(0.7F),
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
(TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId?.let {
CenterMenuItem(text = model.myAssociationDesc) { CenterMenuItem(text = model.myAssociationDesc) {
startActivity(Intent(this@MainActivity, AssociationActivity::class.java)) startActivity(
Intent(
this@MainActivity,
AssociationActivity::class.java
).apply {
putExtra(AssociationActivity::javaClass.name, it)
})
} }
CenterMenuItem(text = model.myJoinActivity) CenterMenuItem(text = model.myJoinActivity)
CenterMenuItem(text = model.myLikeActivity) CenterMenuItem(text = model.myLikeActivity)
CenterMenuItem(text = model.myCollectActivity) CenterMenuItem(text = model.myCollectActivity)
}
CenterMenuItem(text = "退出登录") { CenterMenuItem(text = "退出登录") {
accountViewModel.logout(this@MainActivity) { accountViewModel.logout(this@MainActivity) {
scaffoldModel.update(message = it) scaffoldModel.update(message = it)
@ -191,7 +212,7 @@ class MainActivity : BaseActivity() {
val associationVo: AssociationVo? = val associationVo: AssociationVo? =
(TokenManager.getOwnInfo() as? UserVo)?.associationVo (TokenManager.getOwnInfo() as? UserVo)?.associationVo
if (associationVo == null) { if (associationVo == null) {
RegisterAssociation() RegisterAssociation(navController = navController)
} }
AssociationSearch() AssociationSearch()
AssociationListBody() AssociationListBody()
@ -203,7 +224,26 @@ class MainActivity : BaseActivity() {
* *
*/ */
@Composable @Composable
private fun RegisterAssociation() { private fun RegisterAssociation(
model: AssociationListViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel(),
navController: NavHostController
) {
val launch =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { it ->
when (it.resultCode) {
RESULT_OK -> {
it.data?.getStringExtra(RegAssociationActivity::class.java.name)?.let {
if (it == CheckStatus.Finish.name) {
// model.load {
// scaffoldModel.update(message = "社团审核通过",actionLabel = "关闭提示")
// }
navController.navigate(MainMenu.List.name)
}
}
}
}
}
Row( Row(
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
modifier = Modifier modifier = Modifier
@ -211,7 +251,7 @@ class MainActivity : BaseActivity() {
.padding(10.dp) .padding(10.dp)
) { ) {
IconButton(onClick = { IconButton(onClick = {
startActivity(Intent(this@MainActivity, RegAssociationActivity::class.java)) launch.launch(Intent(this@MainActivity, RegAssociationActivity::class.java))
}) { }) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_add_fill), painter = painterResource(id = R.drawable.ic_add_fill),
@ -228,8 +268,7 @@ class MainActivity : BaseActivity() {
*/ */
@Composable @Composable
private fun AssociationListBody( private fun AssociationListBody(
model: AssociationListViewModel = viewModel(), model: AssociationListViewModel = viewModel()
scaffoldModel: ScaffoldModel = viewModel()
) { ) {
val associationListList: MutableList<AssociationVo>? by model.data.observeAsState() val associationListList: MutableList<AssociationVo>? by model.data.observeAsState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
@ -279,7 +318,7 @@ class MainActivity : BaseActivity() {
private fun Association(associationVo: AssociationVo) { private fun Association(associationVo: AssociationVo) {
Card(modifier = Modifier.clickable(onClick = { Card(modifier = Modifier.clickable(onClick = {
val intent = Intent(this, AssociationActivity::class.java) val intent = Intent(this, AssociationActivity::class.java)
intent.putExtra(AssociationActivity::javaClass.name, associationVo.id) intent.putExtra(AssociationActivity::javaClass.name, associationVo.associationId)
startActivity(intent) startActivity(intent)
})) { })) {
val backgroundImage = rememberCoilPainter(request = R.drawable.association_list_border) val backgroundImage = rememberCoilPainter(request = R.drawable.association_list_border)
@ -362,7 +401,7 @@ class MainActivity : BaseActivity() {
.padding(10.dp) .padding(10.dp)
) { ) {
IconButton(onClick = { IconButton(onClick = {
startActivity(Intent(this@MainActivity, MessageActivity::class.java)) startActivity(Intent(this@MainActivity, SysMessageActivity::class.java))
}) { }) {
Icon( Icon(
painter = painterResource(id = if (it > 0) R.drawable.ic_notice else R.drawable.ic_notification), painter = painterResource(id = if (it > 0) R.drawable.ic_notice else R.drawable.ic_notification),

@ -1,8 +1,9 @@
package com.gyf.csams.message.model package com.gyf.csams.message.model
import android.app.Application import android.app.Application
import com.gyf.csams.module.ClientType
import com.gyf.lib.model.SysMessageViewModel import com.gyf.lib.model.SysMessageViewModel
import com.gyf.lib.util.ClientType
class ForegroundViewModel(application: Application) : SysMessageViewModel(application) { class ForegroundViewModel(application: Application) : SysMessageViewModel(application) {
override fun clientType(): ClientType { override fun clientType(): ClientType {

@ -20,6 +20,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.message.model.ForegroundViewModel import com.gyf.csams.message.model.ForegroundViewModel
import com.gyf.csams.message.model.MessageType import com.gyf.csams.message.model.MessageType
import com.gyf.csams.module.NotificationVo
import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.csams.uikit.TextTopAppBar import com.gyf.csams.uikit.TextTopAppBar
@ -28,8 +29,8 @@ import com.gyf.lib.model.JoinContent
import com.gyf.lib.model.RenameContent import com.gyf.lib.model.RenameContent
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.util.NotificationVo import com.gyf.lib.util.DateTimeUtil.datetimeFormat
import com.gyf.lib.util.format
import java.util.* import java.util.*
/** /**
@ -98,7 +99,7 @@ class SysMessageActivity : ComponentActivity() {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Text(text = Date(content.createTime).format()) Text(text = Date(content.createTime).datetimeFormat())
} }
} }
} }

@ -23,7 +23,9 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
@ -35,8 +37,12 @@ import com.google.accompanist.coil.rememberCoilPainter
import com.gyf.csams.MainApplication import com.gyf.csams.MainApplication
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.MarqueeViewModel import com.gyf.csams.main.model.MarqueeViewModel
import com.gyf.csams.module.AuditCheckVo
import com.gyf.csams.module.CheckStatus
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.DateTimeUtil.datetimeFormat
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import java.util.*
/** /**
@ -484,7 +490,12 @@ fun Poster(modifier: Modifier = Modifier, imageBitmap: ImageBitmap? = null) {
* *
*/ */
@Composable @Composable
fun DescCard(modifier: Modifier, content: String? = null, stringForm: StringForm? = null) { fun DescCard(
modifier: Modifier,
content: String? = null,
stringForm: StringForm? = null,
readonly: Boolean = false
) {
Card( Card(
modifier = modifier, modifier = modifier,
backgroundColor = Color.Transparent backgroundColor = Color.Transparent
@ -504,7 +515,11 @@ fun DescCard(modifier: Modifier, content: String? = null, stringForm: StringForm
) { ) {
Spacer(modifier = Modifier.weight(0.15F)) Spacer(modifier = Modifier.weight(0.15F))
if (stringForm != null) { if (stringForm != null) {
BaseTextField(modifier = Modifier.weight(0.65F), form = stringForm) BaseTextField(
modifier = Modifier.weight(0.65F),
form = stringForm,
readOnly = readonly
)
} else { } else {
Text( Text(
modifier = Modifier.weight(0.65F), modifier = Modifier.weight(0.65F),
@ -520,10 +535,37 @@ fun DescCard(modifier: Modifier, content: String? = null, stringForm: StringForm
} }
} }
//@Preview /**
* 资料审核提示
*
* @param it
*/
@Composable @Composable
fun AnimationTextPreview() { fun CheckTip(it: AuditCheckVo, modifier: Modifier = Modifier) {
AnimationText(text = "6666") Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
when (val status = it.checkStatus) {
CheckStatus.Finish -> Text(buildAnnotatedString {
withStyle(style = MaterialTheme.typography.h5.toParagraphStyle()) {
append("您于${Date(it.applyTime).datetimeFormat()}提交的资料审核不通过原因如下:")
}
withStyle(
style = MaterialTheme.typography.h6.copy(color = MaterialTheme.colors.error)
.toParagraphStyle()
) {
append("初审意见:${it.firstCause}\n")
it.lastCause?.let {
append("复审意见:${it}")
}
}
})
else -> {
Text(
text = status.desc, style =
MaterialTheme.typography.h5.copy(color = MaterialTheme.colors.primary)
)
}
}
}
} }
//@Preview //@Preview

@ -3,13 +3,12 @@ package com.gyf.csams.util
import com.google.gson.* import com.google.gson.*
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.association.model.ANSWER_SIZE import com.gyf.csams.association.model.ANSWER_SIZE
import com.gyf.csams.association.model.ANSWER_TEXT_LENGTH
import com.gyf.csams.association.model.QUESTION_TEXT_LENGTH import com.gyf.csams.association.model.QUESTION_TEXT_LENGTH
import com.gyf.csams.module.ChoiceQuestionVo
import com.gyf.csams.module.Exam
import com.gyf.csams.module.ExamType
import com.gyf.csams.module.OpenQuestionsVo
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.ChoiceQuestionVo
import com.gyf.lib.util.Exam
import com.gyf.lib.util.ExamType
import com.gyf.lib.util.OpenQuestionsVo
import java.lang.reflect.Type import java.lang.reflect.Type
class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> { class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> {
@ -21,7 +20,7 @@ class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> {
val gson = Gson() val gson = Gson()
val c = JsonObject() val c = JsonObject()
c.add("examType", gson.toJsonTree(vo?.examType?.name)) c.add("examType", gson.toJsonTree(vo?.examType?.name))
c.add("question", gson.toJsonTree(vo?.question?.formValue?.value)) // c.add("question", gson.toJsonTree(vo?.question?.formValue?.value))
return c return c
} }
} }
@ -37,7 +36,8 @@ class OpenQuestionsVoDeserializer : JsonDeserializer<OpenQuestionsVo> {
val question = StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH) val question = StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH)
if (value != null) { if (value != null) {
question.onChange(value) question.onChange(value)
return OpenQuestionsVo(question = question) TODO()
// return OpenQuestionsVo(question = question)
} else { } else {
throw NullPointerException("问题无法解析!!!!") throw NullPointerException("问题无法解析!!!!")
} }
@ -53,7 +53,7 @@ class ChoiceQuestionVoSerializer : JsonSerializer<ChoiceQuestionVo> {
val gson = Gson() val gson = Gson()
val c = JsonObject() val c = JsonObject()
c.add("examType", gson.toJsonTree(vo?.examType?.name)) c.add("examType", gson.toJsonTree(vo?.examType?.name))
c.add("question", gson.toJsonTree(vo?.question?.formValue?.value)) // c.add("question", gson.toJsonTree(vo?.question?.formValue?.value))
c.add("answers", gson.toJsonTree(vo?.answers)) c.add("answers", gson.toJsonTree(vo?.answers))
c.add("rightAnswer", gson.toJsonTree(vo?.rightAnswer)) c.add("rightAnswer", gson.toJsonTree(vo?.rightAnswer))
return c return c
@ -78,17 +78,18 @@ class ChoiceQuestionVoDeserializer : JsonDeserializer<ChoiceQuestionVo> {
throw IllegalArgumentException("选项数量!=$QUESTION_TEXT_LENGTH") throw IllegalArgumentException("选项数量!=$QUESTION_TEXT_LENGTH")
} }
val rightAnswer = root.get("rightAnswer").asInt val rightAnswer = root.get("rightAnswer").asInt
return ChoiceQuestionVo( TODO()
question = question, // return ChoiceQuestionVo(
answers = answers.map { // question = question,
StringForm( // answers = answers.map {
formDesc = "选项", // StringForm(
textLength = ANSWER_TEXT_LENGTH, // formDesc = "选项",
value = it // textLength = ANSWER_TEXT_LENGTH,
) // value = it
}, // )
rightAnswer = rightAnswer // },
) // rightAnswer = rightAnswer
// )
} else { } else {
throw NullPointerException("问题无法解析!!!!") throw NullPointerException("问题无法解析!!!!")
} }

@ -2,8 +2,9 @@ package com.gyf.csams.util
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.gyf.csams.module.Exam
import com.gyf.lib.util.ApiResponse import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.Exam
import com.gyf.lib.util.HttpCallback import com.gyf.lib.util.HttpCallback
import java.lang.reflect.Type import java.lang.reflect.Type

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M512,149.33c200.3,0 362.67,162.37 362.67,362.67s-162.37,362.67 -362.67,362.67S149.33,712.3 149.33,512 311.7,149.33 512,149.33zM512,213.33c-164.95,0 -298.67,133.72 -298.67,298.67s133.72,298.67 298.67,298.67 298.67,-133.72 298.67,-298.67 -133.72,-298.67 -298.67,-298.67zM544,291.82v201.13h140.76v64L512,556.95a32,32 0,0 1,-32 -32L480,291.84h64z" />
</vector>

@ -50,8 +50,7 @@ class ExampleUnitTest {
@Test @Test
fun testLib() { fun testLib() {
println(0 in 1..2)
println(1 in 1..2)
} }
} }

@ -51,6 +51,8 @@ android {
} }
dependencies { dependencies {
api(project(":module"))
implementation("androidx.test.ext:junit-ktx:1.1.2")
//生命周期组件版本 //生命周期组件版本
val lifecycle_version = "2.3.1" val lifecycle_version = "2.3.1"
//https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=zh-cn //https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=zh-cn

@ -2,7 +2,7 @@ package com.gyf.lib
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.* import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith

@ -8,6 +8,9 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.ManagerVo
import com.gyf.csams.module.UserVo
import com.gyf.lib.R import com.gyf.lib.R
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.* import com.gyf.lib.util.*
@ -66,8 +69,14 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
it.body?.let { it.body?.let {
val db = AppDatabase.getInstance(context) val db = AppDatabase.getInstance(context)
viewModelScope.launch { viewModelScope.launch {
TokenManager.init(it) TokenManager.update(it)
db?.tokenDao()?.save(token = it.token) db?.tokenDao()?.save(
clientToken = ClientToken(
id = it.token.id,
token = it.token.token,
createTime = it.token.createTime
)
)
}.invokeOnCompletion { }.invokeOnCompletion {
_finishLogin.postValue(true) _finishLogin.postValue(true)
} }
@ -85,8 +94,14 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
it.body?.let { it.body?.let {
val db = AppDatabase.getInstance(context) val db = AppDatabase.getInstance(context)
viewModelScope.launch { viewModelScope.launch {
TokenManager.init(it) TokenManager.update(it)
db?.tokenDao()?.save(token = it.token) db?.tokenDao()?.save(
clientToken = ClientToken(
id = it.token.id,
token = it.token.token,
createTime = it.token.createTime
)
)
}.invokeOnCompletion { }.invokeOnCompletion {
_finishLogin.postValue(true) _finishLogin.postValue(true)
} }

@ -6,6 +6,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.OwnInfoVo
import com.gyf.csams.module.Token
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -50,7 +52,7 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
val db = AppDatabase.getInstance(context) val db = AppDatabase.getInstance(context)
val tokenList = db?.tokenDao()?.queryAll() val tokenList = db?.tokenDao()?.queryAll()
if (tokenList != null && tokenList.size == 1) { if (tokenList != null && tokenList.size == 1) {
val currentToken: Token = tokenList[0] val currentClientToken: Token = tokenList[0]
val url = Api.buildUrl(api) val url = Api.buildUrl(api)
val action = "校验token" val action = "校验token"
Logger.i("${action}api=$url") Logger.i("${action}api=$url")
@ -61,7 +63,7 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
onSuccess = { it -> onSuccess = { it ->
it.body?.let { it.body?.let {
Logger.i("token校验结果:${it}") Logger.i("token校验结果:${it}")
TokenManager.init(it) TokenManager.update(it)
onSuccess() onSuccess()
} }
@ -70,7 +72,7 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
Logger.e(it) Logger.e(it)
onFail() onFail()
}, typeToken = typeToken), }, typeToken = typeToken),
jsonParam = currentToken jsonParam = currentClientToken
) )
} else if (tokenList != null && tokenList.size > 1) { } else if (tokenList != null && tokenList.size > 1) {

@ -5,6 +5,9 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.NotificationDto
import com.gyf.csams.module.NotificationVo
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -118,10 +121,8 @@ abstract class SysMessageViewModel(application: Application) :
), ),
jsonParam = NotificationDto( jsonParam = NotificationDto(
receiverId = TokenManager.getToken().id, receiverId = TokenManager.getToken().id,
receiverClient = clientType(), page = PageDto( receiverClient = clientType(),
currentPage = _currentPage.value ?: 1, token = TokenManager.getToken()
pageSize = initSize
)
) )
) )
} }

@ -3,7 +3,8 @@ package com.gyf.lib.service
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.gyf.lib.util.ClientType import com.gyf.csams.module.ClientType
abstract class BaseActivity : ComponentActivity() { abstract class BaseActivity : ComponentActivity() {

@ -6,6 +6,9 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.ClientType
import com.gyf.csams.module.NotificationDto
import com.gyf.csams.module.NotificationVo
import com.gyf.lib.R import com.gyf.lib.R
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -32,11 +35,17 @@ class MessageService : JobIntentService() {
} }
override fun onHandleWork(intent: Intent) { override fun onHandleWork(intent: Intent) {
val token = try {
TokenManager.getToken()
} catch (e: Exception) {
throw IllegalArgumentException("通知服务非法调用,请先初始化token")
}
HttpClient.postAsync<List<NotificationVo>>( HttpClient.postAsync<List<NotificationVo>>(
url = Api.buildUrl(NotificationApi.Pull), url = Api.buildUrl(NotificationApi.Pull),
jsonParam = NotificationDto( jsonParam = NotificationDto(
receiverId = TokenManager.getToken().id, receiverId = token.id,
receiverClient = clientType receiverClient = clientType,
token = token
), ),
type = object : TypeToken<ApiResponse<List<NotificationVo>>>() {}.type type = object : TypeToken<ApiResponse<List<NotificationVo>>>() {}.type
)?.let { it1 -> )?.let { it1 ->

@ -9,10 +9,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.module.OwnInfoVo
import com.gyf.lib.BuildConfig import com.gyf.lib.BuildConfig
import com.gyf.lib.model.InitViewModel import com.gyf.lib.model.InitViewModel
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.OwnInfoVo
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay

@ -88,6 +88,19 @@ open class ValidStringForm(formDesc: String, textLength: Int) : StringForm(formD
} }
} }
/**
*
* 异步更新值
* @param formDesc
* @param textLength
*/
open class AsyncStringForm(formDesc: String, textLength: Int) :
ValidStringForm(formDesc, textLength) {
fun setValue(value: String) {
_formValue.postValue(value)
}
}
/** /**
* 通用文本输入框 * 通用文本输入框
* *

@ -11,7 +11,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.accompanist.coil.rememberCoilPainter import com.google.accompanist.coil.rememberCoilPainter
import com.gyf.lib.util.* 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.util.Api
import com.gyf.lib.util.TokenManager
@Composable @Composable

@ -1,5 +1,6 @@
package com.gyf.lib.util package com.gyf.lib.util
import com.gyf.csams.module.ClientType
import com.gyf.lib.BuildConfig import com.gyf.lib.BuildConfig
import java.util.* import java.util.*
@ -40,7 +41,10 @@ enum class AccountApi(val path: String) : UrlPath {
BackgroundToken("${BackgroundLogin.path}/token"), BackgroundToken("${BackgroundLogin.path}/token"),
//登出 //登出
Logout("/logout"); Logout("/logout"),
//刷新用户信息
Refresh("/refresh");
override fun build(): String { override fun build(): String {
@ -107,6 +111,33 @@ enum class AssociationApi(val path: String) : UrlPath {
} }
} }
/**
* 活动接口
*
* @property path
*/
enum class ActivityApi(val path: String) : UrlPath {
//前台提交活动申请书
Register("/register"),
//后台查看活动申请书
Audit("/audit"),
//后台受理活动申请书
Accept("/accept"),
//后台审核活动申请书
Check("/check"),
//审核进度
Read("/read");
override fun build(): String {
return "/api/activity${this.path}"
}
}
enum class NotificationApi(val path: String) : UrlPath { enum class NotificationApi(val path: String) : UrlPath {
//通知计数 //通知计数
Count("/count"), Count("/count"),

@ -0,0 +1,53 @@
package com.gyf.lib.util
import java.sql.Timestamp
import java.text.SimpleDateFormat
import java.util.*
object DateTimeUtil {
const val DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm"
const val DATE_FORMAT = "yyyy-MM-dd"
const val TIME_FORMAT = "HH:mm"
const val DATETIME_FORMAT = "yyyy-MM-dd HH:mm"
const val START_TIME = "2021-01-01 00:00"
fun String.toTimeStamp(pattern: String = DATE_TIME_FORMAT): Timestamp {
return Timestamp(this.toDate(pattern).time)
}
fun String.toDate(pattern: String = DATE_TIME_FORMAT): Date {
return SimpleDateFormat(pattern, Locale.CHINA).parse(this)
?: throw IllegalArgumentException("日期字符串按[$pattern]转换失败")
}
val startUnix = SimpleDateFormat(DATETIME_FORMAT, Locale.CHINA).parse(START_TIME)?.time
fun randomDateTime(): Date {
if (startUnix != null) {
return Date("${(startUnix..Date().time).random()}".toLong())
} else {
throw IllegalArgumentException("生成随机失败,无法获取起始时间")
}
}
fun Date.datetimeFormat(): String {
return SimpleDateFormat(DATETIME_FORMAT, Locale.CHINA).format(this)
}
fun Date.dateFormat(): String {
return SimpleDateFormat(DATE_FORMAT, Locale.CHINA).format(this)
}
fun Date.timeFormat(): String {
return SimpleDateFormat(TIME_FORMAT, Locale.CHINA).format(this)
}
}

@ -1,7 +1,7 @@
package com.gyf.lib.util package com.gyf.lib.util
import com.google.gson.Gson import com.google.gson.*
import com.google.gson.JsonSyntaxException import com.gyf.csams.module.Token
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import okhttp3.* import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
@ -9,20 +9,55 @@ import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.reflect.Field
import java.lang.reflect.Type import java.lang.reflect.Type
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
interface TokenInterface { object SuperclassExclusionStrategy : ExclusionStrategy {
fun token(): String //序列化反序列化白名单
val whiteClass = listOf<Class<*>>(Token::class.java)
override fun shouldSkipClass(arg0: Class<*>?): Boolean {
return false
}
override fun shouldSkipField(fieldAttributes: FieldAttributes): Boolean {
val fieldName = fieldAttributes.name
val theClass = fieldAttributes.declaringClass
return isFieldInSuperclass(theClass, fieldName)
}
private fun isFieldInSuperclass(subclass: Class<*>, fieldName: String): Boolean {
var superclass = subclass.superclass
var field: Field?
while (superclass != null && !whiteClass.contains(subclass)) {
field = getField(superclass, fieldName)
if (field != null) return true
superclass = superclass.superclass
}
return false
}
private fun getField(theClass: Class<*>, fieldName: String): Field? {
return try {
theClass.getDeclaredField(fieldName)
} catch (e: Exception) {
null
}
}
} }
val HTTP_CALLBACK_GSON_CONFIG: Gson = GsonBuilder()
.addSerializationExclusionStrategy(SuperclassExclusionStrategy)
.addDeserializationExclusionStrategy(SuperclassExclusionStrategy)
.create()
object HttpClient { object HttpClient {
private val httpClient: OkHttpClient = OkHttpClient() private val httpClient: OkHttpClient = OkHttpClient()
private val JSON_CONTENT_TYPE = "application/json; charset=UTF-8".toMediaType() private val JSON_CONTENT_TYPE = "application/json; charset=UTF-8".toMediaType()
private val json = Gson()
/** /**
* 构建url查询参数 * 构建url查询参数
@ -80,11 +115,11 @@ object HttpClient {
* *
* @param url * @param url
* @param callback * @param callback
* @param jsonParam * @param jsonParam TODO 改写成reified
*/ */
fun post(url: String, callback: Callback, jsonParam: Any) { fun post(url: String, callback: Callback, jsonParam: Any) {
Logger.i("request url=$url") Logger.i("request url=$url")
val jsonBody = json.toJson(jsonParam) val jsonBody = HTTP_CALLBACK_GSON_CONFIG.toJson(jsonParam)
Logger.json(jsonBody) Logger.json(jsonBody)
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)
@ -96,7 +131,7 @@ object HttpClient {
fun <T> postAsync(url: String, jsonParam: Any, type: Type): ApiResponse<T>? { fun <T> postAsync(url: String, jsonParam: Any, type: Type): ApiResponse<T>? {
Logger.i("request url=$url") Logger.i("request url=$url")
val jsonBody = json.toJson(jsonParam) val jsonBody = HTTP_CALLBACK_GSON_CONFIG.toJson(jsonParam)
Logger.json(jsonBody) Logger.json(jsonBody)
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)
@ -161,7 +196,7 @@ open class HttpCallback<T>(
private val onFail: (error: String) -> Unit = { Logger.e(it) }, private val onFail: (error: String) -> Unit = { Logger.e(it) },
private val typeToken: Type private val typeToken: Type
) : Callback, GsonBuilderInterface { ) : Callback, GsonBuilderInterface {
override val gson: Gson = Gson() override val gson: Gson = HTTP_CALLBACK_GSON_CONFIG
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
when (e) { when (e) {

@ -1,14 +1,11 @@
package com.gyf.lib.util package com.gyf.lib.util
import okhttp3.internal.toHexString import okhttp3.internal.toHexString
import java.text.SimpleDateFormat
import java.util.*
fun randomNum(length: Int = 8): String { fun randomNum(length: Int = 8): String {
return List(length) { ('0'..'9').random() }.joinToString("") return List(length) { ('0'..'9').random() }.joinToString("")
} }
fun encode(char: Char) = "\\u${char.toInt().toHexString()}" fun encode(char: Char) = "\\u${char.toInt().toHexString()}"
//unicode ->String //unicode ->String
@ -34,23 +31,4 @@ fun randomChinese(length: Int = 8): String {
}.joinToString("") }.joinToString("")
} }
const val DATETIME_FORMAT = "yyyy-MM-dd HH:mm"
const val START_TIME = "2021-01-01 00:00"
val FORMAT = SimpleDateFormat(DATETIME_FORMAT, Locale.US)
val startUnix = FORMAT.parse(START_TIME)?.time
fun randomDateTime(): Date {
if (startUnix != null) {
return Date("${(startUnix..Date().time).random()}".toLong())
} else {
throw IllegalArgumentException("生成随机失败,无法获取起始时间")
}
}
fun Date.format(): String {
return FORMAT.format(this)
}

@ -4,43 +4,56 @@ import android.content.Context
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.room.* import androidx.room.*
import com.gyf.csams.module.*
import com.orhanobut.logger.Logger
/** /**
* 登陆令牌 * 登陆令牌
*/ */
@Entity @Entity
data class Token( class ClientToken(
@PrimaryKey val id: Int, @PrimaryKey
@ColumnInfo val token: String, override val id: Int,
@ColumnInfo val createTime: Long @ColumnInfo
) override val token: String,
@ColumnInfo
override val createTime: Long,
) : Token(token, createTime, id)
data class OnlyToken( data class OnlyToken(
override val clientType: ClientType override val clientType: ClientType
) : ClientBaseVo() ) : ClientBaseVo() {
override val token: ClientToken = TokenManager.getToken()
}
@Dao @Dao
interface TokenDao { interface TokenDao {
@Query("select * from token") @Query("select * from ClientToken")
suspend fun queryAll(): List<Token> suspend fun queryAll(): List<ClientToken>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun save(token: Token) suspend fun save(clientToken: ClientToken)
@Delete @Delete
suspend fun delete(user: Token) suspend fun delete(user: ClientToken)
@Query("delete from token") @Query("delete from ClientToken")
suspend fun deleteAll() suspend fun deleteAll()
} }
object TokenManager { object TokenManager {
private var ownInfo: OwnInfoVo? = null private var ownInfo: OwnInfoVo? = null
private lateinit var personInfo: PersonInfoVo private var personInfo: PersonInfoVo? = null
private var clientToken: ClientToken? = null
fun init(ownInfo: OwnInfoVo) { fun update(ownInfo: OwnInfoVo) {
this.ownInfo = ownInfo this.ownInfo = ownInfo
ownInfo.token.let {
clientToken = ClientToken(id = it.id, token = it.token, createTime = it.createTime)
}
when (ownInfo) { when (ownInfo) {
is ManagerVo -> this.personInfo = ManagerInfoVo( is ManagerVo -> this.personInfo = ManagerInfoVo(
duty = ownInfo.duty, duty = ownInfo.duty,
@ -50,8 +63,9 @@ object TokenManager {
) )
is UserVo -> this.personInfo = is UserVo -> this.personInfo =
UserInfoVo(name = ownInfo.name, headImg = ownInfo.headImg, desc = ownInfo.desc) UserInfoVo(name = ownInfo.name, headImg = ownInfo.headImg, desc = ownInfo.desc)
else -> throw IllegalArgumentException("token失败") else -> throw IllegalArgumentException("token初始化失败")
} }
Logger.i("token刷新完成")
} }
fun clear() { fun clear() {
@ -62,17 +76,26 @@ object TokenManager {
return ownInfo ?: throw IllegalArgumentException("token没有初始化,非法调用") return ownInfo ?: throw IllegalArgumentException("token没有初始化,非法调用")
} }
fun getUserInfo(): UserVo? {
return ownInfo as? UserVo
}
fun getManagerInfo(): ManagerVo? {
return ownInfo as? ManagerVo
}
fun getPersonInfo(): PersonInfoVo { fun getPersonInfo(): PersonInfoVo {
return personInfo return personInfo ?: throw IllegalArgumentException("个人信息没有初始化,非法调用")
} }
fun getToken(): Token {
return ownInfo?.token ?: throw IllegalArgumentException("token没有初始化,非法调用") fun getToken(): ClientToken {
return clientToken ?: throw IllegalArgumentException("token没有初始化,非法调用")
} }
} }
@Database(entities = [Token::class], version = 1, exportSchema = false) @Database(entities = [ClientToken::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun tokenDao(): TokenDao abstract fun tokenDao(): TokenDao
private val mIsDatabaseCreated = MutableLiveData<Boolean>() private val mIsDatabaseCreated = MutableLiveData<Boolean>()

@ -1,527 +0,0 @@
package com.gyf.lib.util
import androidx.annotation.IntRange
import com.gyf.lib.uikit.StringForm
import java.util.*
/**
* 一般信息
*
*/
abstract class PersonInfoVo {
abstract val name: String
abstract val headImg: String?
abstract val desc: String
}
enum class Duty(val desc: String, val level: Int) {
Teacher("老师", 1),
PamphaBhusal("总部长", 2),
SecretaryOfTheMinister("秘书部部长", 3),
PropagandaDepartment("宣传部部长", 3),
LiaisonMinister("外联部部长", 3),
SecretaryDepartmentOfficer("秘书部干事", 4),
PublicityDepartmentOfficer("宣传部干事", 4),
LiaisonOfficer("外联部干事", 4);
/**
* 是否是部门部长
*
*/
fun isMinister(): Boolean {
return minister.contains(this)
}
fun isOfficer(): Boolean {
return officer.contains(this)
}
}
private val minister =
arrayOf(Duty.SecretaryOfTheMinister, Duty.LiaisonMinister, Duty.PropagandaDepartment)
private val officer =
arrayOf(Duty.SecretaryDepartmentOfficer, Duty.PublicityDepartmentOfficer, Duty.LiaisonOfficer)
/**
* 个人信息
*
*/
abstract class OwnInfoVo : PersonInfoVo() {
abstract val token: Token
}
data class ManagerInfoVo(
val duty: Duty,
override val name: String,
override val headImg: String?,
override val desc: String
) : PersonInfoVo()
data class UserInfoVo(
override val name: String,
override val headImg: String?,
override val desc: String
) : PersonInfoVo()
/**
* 管理员个人信息
*
* @property account 管理员账号
* @property name 姓名
* @property duty 职务
* @property headImg 头像
* @property desc 个人简介
*/
data class ManagerVo(
val account: String,
val duty: Duty,
override val token: Token,
override val name: String,
override val headImg: String?,
override val desc: String
) : OwnInfoVo()
/**
* 用户个人信息
*
* @property studentId 学号
* @property name 姓名
* @property headImg 头像
* @property desc 个人简介
*/
data class UserVo(
val studentId: String,
val manager: ManagerVo? = null,
override val token: Token,
override val name: String,
override val headImg: String?,
override val desc: String,
val associationVo: AssociationVo?,
val isHead: Boolean?
) : OwnInfoVo()
data class PageDto(val currentPage: Long, val pageSize: Int = 10)
data class NotificationDto(
val receiverId: Int,
val receiverClient: ClientType,
val page: PageDto? = null,
override val clientType: ClientType = receiverClient
) : ClientBaseVo()
data class UserRegVo(val studentId: String, val name: String)
/**
* 客户端类型
*
*/
enum class ClientType {
//前台
Foreground,
//后台
Background
}
data class NotificationVo(val title: String, val content: String, val id: Int, val createTime: Long)
/**
* 响应自动生成密码
*
* @property password
*/
data class UserPasswordVo(val password: String)
/**
* 用户登陆表单
*
* @property studentId 学号
* @property password 密码
* @property device 设备型号
*/
data class UserLoginVo(val studentId: String, val password: String, val device: String)
/**
*
* @property associationName 社团名字
* @property activityName 活动名
* @property activityTime 活动时间
* @property activityLocation 活动地点
* @property activityDesc 活动介绍
*/
data class ActivityDetailVo(
val associationName: String, val activityName: String,
val activityTime: Date, val activityLocation: String,
val activityDesc: String
)
/**
* 图片
* @property name 文件名
* @property size 文件大小
* @property url 文件路径
* @property md5 文件hash
* @property createTime 文件创建时间
* @property studentId 文件上传人
*/
data class ActivityPhotoVo(
val name: String,
val size: Long,
val url: String,
val md5: String,
val createTime: Date,
val studentId: String
)
const val MAX_SCORE = 5L
data class ActivityVo(
val activityId: Long, val activityName: String, val association: String,
@IntRange(from = 1, to = MAX_SCORE) val score: Int, val activityTime: Date, val location: String
)
data class AllOfficerVo(
val secretariat: MutableList<ManagerInfoVo>,
val propaganda: MutableList<ManagerInfoVo>,
val publicRelationsDepartment: MutableList<ManagerInfoVo>
)
data class ApplyActVo(
val activityName: String, val activityTime: String,
val location: String, val desc: String,
val size: Int
)
data class LeaveMessageVo(
val message: String,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
/**
* 社团级别
*
*/
enum class AssociationLevel {
A,
B,
C,
D
}
/**
* 所属院系
*
*/
enum class AssociationFaculty(val desc: String, val range: kotlin.ranges.IntRange) {
ForeignLanguageDept("外语系", 0..0),
CivilEngineeringDept("土木工程", 1..10),
SEM("经理管理学院", 11..20),
MechanicalEngineeringDept("机械工程", 21..30),
TransportationDept("交通运输", 31..40),
ArchitectureAndArts("建筑与艺术", 41..50),
ElectricalDept("电气", 51..60),
MaterialsDept("材料", 61..70),
MessageDept("信息", 71..80),
MathematicsDept("数理", 81..90),
GraduateStudent("研究生", 91..99)
}
abstract class BaseAssociationVo {
abstract val associationId: Int
abstract val name: String
abstract val desc: String
abstract val logo: String
abstract val faculty: AssociationFaculty
abstract val level: AssociationLevel?
}
/**
* 社团列表
*
*/
class AssociationVo(
override val associationId: Int,
override val name: String,
override val desc: String,
override val logo: String,
override val faculty: AssociationFaculty,
override val level: AssociationLevel?
) : BaseAssociationVo()
//审核状态
enum class CheckStatus(val desc: String) {
WaitFirst("等待初审"),
AcceptFirst("初审受理"),
WaitLast("等待复审"),
AcceptLast("复审受理"),
Finish("审核完成")
}
/**
* 活动照片
*
* @property path
*/
data class AssociationActPhotoVo(val path: String)
data class AssociationMainVo(
val associationVo: AssociationVo, val head: UserInfoVo,
val photos: List<AssociationActPhotoVo>? = null
)
data class AuditCheckVo(
val checkStatus: CheckStatus, val applyTime: Long,
val firstCause: String, val lastCause: String?
)
//前台社团注册资料
data class AssociationCheckVo(
override val associationId: Int,
override val name: String,
override val desc: String,
override val logo: String,
override val faculty: AssociationFaculty,
override val level: AssociationLevel?,
val fileId: Int,
val auditCheckVo: AuditCheckVo
) : BaseAssociationVo()
/**
* 搜索社团
*
* @property name
* @property desc
* @property clientType
*/
data class SearchAssociationVo(
val name: String, val desc: String,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
data class ShowAssociationVo(
val id: Int,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
data class QueryAssociationMembers(
val id: Int,
val name: String? = null,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
data class BBSVo(val studentId: String, val name: String, val createTime: Date, val content: String)
/**
* 题型
*
*/
enum class ExamType(val type: String) {
//选择题
CQ("选择题"),
//开放题
OQ("开放题")
}
abstract class Exam {
abstract val examType: ExamType
abstract val question: StringForm
}
/**
* 选择题
*
* @property examType 题型描述
* @property answers 答案
* @property rightAnswer 正确答案
* @property question 问题
*/
data class ChoiceQuestionVo(
override val examType: ExamType = ExamType.CQ,
val answers: List<StringForm>,
val rightAnswer: Int,
override val question: StringForm
) : Exam()
data class HistoryActVo(val name: String)
/**
* 开放题
*
* @property examType 题型描述
* @property question 问题
*/
data class OpenQuestionsVo(
override val examType: ExamType = ExamType.OQ, override val question: StringForm
) : Exam()
data class LeaveMessageFormatVo(val message: String, val user: UserInfoVo)
data class ManagerLoginVo(val account: String, val password: String, val device: String)
data class OngoingActVo(val name: String)
/**
* 活动质量汇报单
*
* @property applyName 申请人
* @property activityName 活动名称
* @property merit 优点
* @property defect 缺点
* @property score 星级评价
*/
data class QualityReportVo(
val applyName: String,
val activityName: String,
val merit: String,
val defect: String,
@IntRange(from = 1L, to = MAX_SCORE) val score: Int
)
/**
* 社团注册资料表单
*
* @property name
* @property desc
* @property fileId
*/
data class AssociationRegVo(
val associationId: Int?, val name: String, val desc: String, val fileId: Int,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
abstract class AuditVo {
abstract val audit: AuditLoggingVo
}
/**
* 社团注册审核记录
*
*/
data class AuditAssociationVo(
val name: String,
val desc: String,
val logo: String,
override val audit: AuditLoggingVo
) : AuditVo()
/**
* 通用审核记录
*
* @property id
* @property user
* @property applyTime
* @property manager
* @property acceptTime
* @property cause
* @property result
* @property auditTime
* @property nextAudit
*/
data class AuditLoggingVo(
val id: Int, val user: UserInfoVo, val applyTime: Long, val manager: ManagerInfoVo?,
val acceptTime: Long?, val cause: String?, val result: Boolean?,
val auditTime: Long?, val nextAudit: AuditLoggingVo?
)
/**
* 社团注册资料受理
*
* @property auditId
* @property clientType
*/
data class AcceptVo(
val auditId: Int,
override val clientType: ClientType = ClientType.Background
) : ClientBaseVo()
/**
* 社团注册资料审核
*
* @property auditId
* @property result
* @property cause
* @property clientType
*/
data class CheckVo(
val auditId: Int, val result: Boolean, val cause: String,
override val clientType: ClientType = ClientType.Background
) : ClientBaseVo()
/**
* 前台活动申请书
*
* @property activityId
* @property activityName
* @property activityTime
* @property activityAddress
* @property activityDesc
* @property activitySize
* @property clientType
*/
data class ActivityApplyVo(
val activityId: Int?, val associationId: Int,
val activityName: String, val activityTime: Long,
val activityAddress: String,
val activityDesc: String, val activitySize: Int,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
/**
* 后台活动申请书
*
* @property auditId
* @property activityName
* @property activityTime
* @property activityAddress
* @property activityDesc
* @property activitySize
*/
data class AuditActVo(
val auditId: Int, val activityName: String, val activityTime: Long,
val activityAddress: String,
val activityDesc: String, val activitySize: Int,
override val audit: AuditLoggingVo
) : AuditVo()
/**
* 换名申请表
*
* @property studentId 学号
* @property oldName 社团原名
* @property newName 社团新名
* @property reason 申请理由
*/
data class RenameVo(
val studentId: String,
val oldName: String,
val newName: String,
val reason: String
)
abstract class ClientBaseVo {
val token: Token = TokenManager.getToken()
abstract val clientType: ClientType
}

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<integer name="activity_size">20</integer> <integer name="activity_size">20</integer>
<integer name="name_length">4</integer>
</resources> </resources>

@ -30,4 +30,5 @@
<string name="recheck_btn">复审</string> <string name="recheck_btn">复审</string>
<string name="audit_phases">审核阶段</string> <string name="audit_phases">审核阶段</string>
<string name="last_approver_origin">复审意见</string> <string name="last_approver_origin">复审意见</string>
<string name="activity_date">活动日期</string>
</resources> </resources>

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<integer name="activity_size">20</integer> <integer name="activity_size">20</integer>
<integer name="name_length">4</integer>
</resources> </resources>

@ -30,4 +30,5 @@
<string name="recheck_btn">复审</string> <string name="recheck_btn">复审</string>
<string name="audit_phases">审核阶段</string> <string name="audit_phases">审核阶段</string>
<string name="last_approver_origin">复审意见</string> <string name="last_approver_origin">复审意见</string>
<string name="activity_date">活动日期</string>
</resources> </resources>

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<integer name="activity_size">20</integer> <integer name="activity_size">20</integer>
<integer name="name_length">4</integer>
</resources> </resources>

@ -30,4 +30,5 @@
<string name="recheck_btn">复审</string> <string name="recheck_btn">复审</string>
<string name="audit_phases">审核阶段</string> <string name="audit_phases">审核阶段</string>
<string name="last_approver_origin">复审意见</string> <string name="last_approver_origin">复审意见</string>
<string name="activity_date">活动日期</string>
</resources> </resources>

@ -8,9 +8,17 @@ import org.junit.Test
* *
* See [testing documentation](http://d.android.com/tools/testing). * See [testing documentation](http://d.android.com/tools/testing).
*/ */
class ExampleUnitTest { class ExampleUnitTest {
@Test @Test
fun addition_isCorrect() { fun addition_isCorrect() {
assertEquals(4, 2 + 2) assertEquals(4, 2 + 2)
} }
@Test
fun testClientToken() {
}
} }

@ -15,3 +15,7 @@ include(":foreground")
include(":background") include(":background")
//公共库 //公共库
include(":lib") include(":lib")
include(":module")
project(":module").projectDir = File(settingsDir, "..\\..\\IdeaProjects\\CsamsServer\\module")

Loading…
Cancel
Save