申请入团

master
pan 4 years ago
parent acb519df85
commit a38dcf6587
  1. 3
      foreground/src/main/AndroidManifest.xml
  2. 3
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  3. 42
      foreground/src/main/java/com/gyf/csams/association/model/AssociationViewModel.kt
  4. 53
      foreground/src/main/java/com/gyf/csams/association/model/AuditJoinViewModel.kt
  5. 285
      foreground/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
  6. 201
      foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
  7. 133
      foreground/src/main/java/com/gyf/csams/association/ui/AuditJoinActivity.kt
  8. 354
      foreground/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
  9. 2
      foreground/src/main/java/com/gyf/csams/util/GsonUtil.kt
  10. 1
      foreground/src/main/res/values-en/strings.xml
  11. 1
      foreground/src/main/res/values-zh/strings.xml
  12. 1
      foreground/src/main/res/values/strings.xml
  13. 13
      foreground/src/test/java/com/gyf/csams/ExampleUnitTest.kt
  14. 13
      lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt
  15. 30
      lib/src/main/java/com/gyf/lib/uikit/Snackbar.kt
  16. 26
      lib/src/main/java/com/gyf/lib/util/Api.kt
  17. 2
      lib/src/main/res/values-en/strings.xml
  18. 2
      lib/src/main/res/values-zh/strings.xml
  19. 2
      lib/src/main/res/values/strings.xml

@ -86,6 +86,9 @@
<!--系统通知--> <!--系统通知-->
<activity android:name=".message.ui.SysMessageActivity" /> <activity android:name=".message.ui.SysMessageActivity" />
<!--入团申请-->
<activity android:name=".association.ui.AuditJoinActivity" />
<service android:name="com.gyf.lib.service.MessageService" /> <service android:name="com.gyf.lib.service.MessageService" />
</application> </application>

@ -176,6 +176,7 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
password = it.body ?: throw IllegalArgumentException("无法获取生成密码") password = it.body ?: throw IllegalArgumentException("无法获取生成密码")
) )
) )
resetForm()
}, },
onFail = onFail, onFail = onFail,
typeToken = object : TypeToken<ApiResponse<String>>() {}.type typeToken = object : TypeToken<ApiResponse<String>>() {}.type
@ -185,7 +186,6 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
name = name.getValue() name = name.getValue()
) )
) )
resetForm()
} else { } else {
Logger.wtf("表单校验失败,无法$regBtnDesc!!!") Logger.wtf("表单校验失败,无法$regBtnDesc!!!")
} }
@ -198,6 +198,7 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
private fun resetForm() { private fun resetForm() {
id.clean() id.clean()
name.clean() name.clean()
checkForm()
} }

@ -21,10 +21,6 @@ class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
override val _currentMenu: MutableLiveData<AssociationMenu> = MutableLiveData() override val _currentMenu: MutableLiveData<AssociationMenu> = MutableLiveData()
override val currentMenu: LiveData<AssociationMenu> = _currentMenu override val currentMenu: LiveData<AssociationMenu> = _currentMenu
private val _dropDownMenuResult = MutableLiveData<String?>()
val dropDownMenuResult: LiveData<String?> = _dropDownMenuResult
/** /**
* 下拉菜单状态 * 下拉菜单状态
*/ */
@ -34,10 +30,21 @@ class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
private val _associationVo = MutableLiveData<AssociationMainVo>() private val _associationVo = MutableLiveData<AssociationMainVo>()
val associationVo: LiveData<AssociationMainVo> = _associationVo val associationVo: LiveData<AssociationMainVo> = _associationVo
fun update(message: String?) { private val _applyAssociationResultVo = MutableLiveData<ApplyAssociationResultVo?>()
_dropDownMenuResult.postValue(message) val applyAssociationResultVo: LiveData<ApplyAssociationResultVo?> = _applyAssociationResultVo
private val _dropMenuMessage = MutableLiveData<String?>()
val dropMenuMessage: LiveData<String?> = _dropMenuMessage
fun update(message: String? = null) {
_dropMenuMessage.postValue(message)
}
fun update(applyAssociationResultVo: ApplyAssociationResultVo? = null) {
_applyAssociationResultVo.postValue(applyAssociationResultVo)
} }
fun load(id: Int) { fun load(id: Int) {
viewModelScope.launch { viewModelScope.launch {
HttpClient.post( HttpClient.post(
@ -64,6 +71,29 @@ class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
_expanded.value = false _expanded.value = false
} }
/**
* 申请入团
*
*/
fun applyAssociation(associationId: Int, callback: (result: ApplyAssociationResultVo) -> Unit) {
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(AssociationApi.CheckApply),
callback = HttpCallback<ApplyAssociationResultVo>(
action = "申请入团",
onSuccess = {
it.body?.let {
callback(it)
}
},
typeToken = object : TypeToken<ApiResponse<ApplyAssociationResultVo>>() {}.type
),
jsonParam =
SearchExamVo(associationId = associationId, token = TokenManager.getToken())
)
}
}
} }
/** /**

@ -0,0 +1,53 @@
package com.gyf.csams.association.model
import android.app.Application
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.module.ApiResponse
import com.gyf.csams.module.AuditJoinVo
import com.gyf.csams.module.JoinAssociationVo
import com.gyf.csams.module.SearchExamVo
import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.*
import kotlinx.coroutines.launch
class AuditJoinViewModel(application: Application) :
ScrollViewModel<JoinAssociationVo>(application = application) {
fun load(associationId: Int) {
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(AssociationApi.CheckJoin),
callback = HttpCallback<MutableList<JoinAssociationVo>>(
action = "查看入团申请记录",
onSuccess = {
it.body?.let {
_data.postValue(it)
}
},
typeToken = object :
TypeToken<ApiResponse<MutableList<JoinAssociationVo>>>() {}.type
), jsonParam =
SearchExamVo(associationId = associationId, token = TokenManager.getToken())
)
}
}
/**
* 审核结果
*
* @param joinId
* @param result
*/
fun audit(joinId: Int, result: Boolean, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(AssociationApi.AuditJoin),
callback = HttpCallback<Boolean>(action = "审核入团申请", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type), jsonParam =
AuditJoinVo(joinId = joinId, result = result, token = TokenManager.getToken())
)
}
}
}

@ -3,10 +3,15 @@ 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.google.gson.reflect.TypeToken
import com.gyf.csams.module.*
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.AsyncStringForm
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.util.*
import com.gyf.lib.util.NOT_IMPL_TIP import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
/** /**
* 题库界面类型 * 题库界面类型
@ -17,7 +22,10 @@ enum class ExamActivityType(val menuName: String) {
SET_EXAM("入团题库"), SET_EXAM("入团题库"),
//入团申请表 //入团申请表
JOIN_Association("入团申请表") JOIN_Association("入团申请表"),
//查看答卷
Answer("查看答卷")
} }
@ -51,48 +59,57 @@ enum class ExamType(val type: String) {
OQ("开放题") OQ("开放题")
} }
abstract class Exam { abstract class Exam() {
abstract val examType: ExamType abstract val examType: ExamType
abstract val _question: StringForm
//**TODO 题目反序列化 abstract fun check(): Boolean
abstract val question: String? abstract val questionId: Int?
abstract val _question: StringForm?
} }
/** /**
* 选择题 * 选择题
* *
* @property examType 题型描述 *
* @property answers 答案
* @property rightAnswer 正确答案
* @property question 问题
* TODO 题目反序列化
*/ */
data class ChoiceQuestionVo( data class ChoiceQuestionVo(
val id: Int? = null,
override val examType: ExamType = ExamType.CQ, override val examType: ExamType = ExamType.CQ,
val _answers: MutableList<StringForm> = mutableListOf(), val _answers: MutableList<StringForm>,
val answers: List<String> = ('A'..'D').map { "选项${it}" }.toList(),
val rightAnswer: Int, val rightAnswer: Int,
override val question: String? = null, val chooseOption: Int? = null,
override val _question: StringForm?, override val _question: StringForm,
override val questionId: Int? = null,
) : Exam() {
) : Exam() override fun check(): Boolean {
if (_question.formValue.value?.isEmpty() == true || _question.formValue.value == null) {
return false
}
_answers.forEach {
if (it.formValue.value == null || it.formValue.value?.isEmpty() == true) {
return false
}
}
return true
}
}
/** /**
* 开放题 * 开放题
* *
* @property examType 题型描述
* @property question 问题
* TODO 题目反序列化
*/ */
data class OpenQuestionsVo( data class OpenQuestionsVo(
override val examType: ExamType = ExamType.OQ, override val examType: ExamType = ExamType.OQ,
override val question: String? = null, override val _question: StringForm,
override val _question: StringForm? override val questionId: Int?,
) : Exam() ) : Exam() {
override fun check(): Boolean {
TODO("Not yet implemented")
}
}
/** /**
* 题库状态管理 * 题库状态管理
@ -100,53 +117,64 @@ data class OpenQuestionsVo(
*/ */
class ExamViewModel(application: Application) : ScrollViewModel<Exam>(application) { class ExamViewModel(application: Application) : ScrollViewModel<Exam>(application) {
val questionIsNull: String = "问题不能为空" val questionIsNull: String = "问题或答案为空"
val deleteLeastOne: String = "至少保留一道题目"
val deleteTip = "确定删除此题目?" val deleteTip = "确定删除此题目?"
val addTip = "确定添加此题目?" val addTip = "确定添加此题目?"
val actionLabel = "确定" val actionLabel = "确定"
private val _newExam: MutableLiveData<Exam> = MutableLiveData(createExam(ExamType.CQ)) private val _newExam: MutableLiveData<Exam> = MutableLiveData()
val newExam: LiveData<Exam> = _newExam val newExam: LiveData<Exam> = _newExam
init { private val _deleteIds = mutableListOf<Int>()
load()
}
/**
* 切换题型
*
*/
fun switchType(exam: Exam) {
if (exam is ChoiceQuestionVo) _newExam.value = createExam(ExamType.OQ) else _newExam.value =
createExam(ExamType.CQ)
}
private val _allowPostAnswer = MutableLiveData<Boolean>()
val allowPostAnswer: LiveData<Boolean> = _allowPostAnswer
/** /**
* 创建题目 * 创建题目
* *
* @param type
* @return * @return
*/ */
private fun createExam(type: ExamType): Exam { private fun createExam(examVo: ExamVo? = null, answer: Int? = null): Exam {
val questionForm = AsyncStringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH)
val questionForm = ValidStringForm(formDesc = "", textLength = QUESTION_TEXT_LENGTH) val answerA = AsyncStringForm(formDesc = "选项A", textLength = ANSWER_TEXT_LENGTH)
val answerA = ValidStringForm(formDesc = "选项A", textLength = ANSWER_TEXT_LENGTH) val answerB = AsyncStringForm(formDesc = "选项B", textLength = ANSWER_TEXT_LENGTH)
val answerB = ValidStringForm(formDesc = "选项B", textLength = ANSWER_TEXT_LENGTH) val answerC = AsyncStringForm(formDesc = "选项C", textLength = ANSWER_TEXT_LENGTH)
val answerC = ValidStringForm(formDesc = "选项C", textLength = ANSWER_TEXT_LENGTH) val answerD = AsyncStringForm(formDesc = "选项D", textLength = ANSWER_TEXT_LENGTH)
val answerD = ValidStringForm(formDesc = "选项D", textLength = ANSWER_TEXT_LENGTH) if (examVo != null) {
questionForm.setValue(examVo.question)
return ChoiceQuestionVo( answerA.setValue(examVo.optionsA)
_answers = mutableListOf(answerA, answerB, answerC, answerD), answerB.setValue(examVo.optionsB)
rightAnswer = 0, answerC.setValue(examVo.optionsC)
_question = questionForm, answerD.setValue(examVo.optionsD)
) return ChoiceQuestionVo(
_answers = mutableListOf(answerA, answerB, answerC, answerD),
rightAnswer = examVo.rightOption,
_question = questionForm,
questionId = examVo.questionId,
chooseOption = answer
)
} else {
return ChoiceQuestionVo(
_answers = mutableListOf(answerA, answerB, answerC, answerD),
rightAnswer = 0,
_question = questionForm,
)
}
} }
fun checkChooseAnswer() {
_data.value?.forEach {
if (it is ChoiceQuestionVo && it.chooseOption == null) {
_allowPostAnswer.postValue(false)
return
}
}
_allowPostAnswer.postValue(true)
}
/** /**
* 更新题目 * 更新题目
@ -167,32 +195,153 @@ class ExamViewModel(application: Application) : ScrollViewModel<Exam>(applicatio
} }
/** /**
* TODO 更新题库 * 更新题库
* *
* @param callback * @param callback
*/ */
fun updateExam(callback: (message: String) -> Unit) { fun updateExam(associationId: Int, callback: (message: String) -> Unit) {
viewModelScope.launch {
_data.value?.dropLast(1)?.mapNotNull { it ->
if (it is ChoiceQuestionVo) {
toExamVo(choiceQuestionVo = it)
} else {
null
}
}?.apply {
HttpClient.post(url = Api.buildUrl(AssociationApi.UpdateExam),
callback = HttpCallback<Boolean>(action = "更新题库",
onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = AddExamVo(
questions = this,
associationId = associationId,
token = TokenManager.getToken(),
deleteIds = _deleteIds
)
)
}
}
}
private fun toExamVo(choiceQuestionVo: ChoiceQuestionVo): ExamVo? {
return choiceQuestionVo.let {
val options = it._answers.map {
it.getValue()
}
if (options.size == 4) {
ExamVo(
question = it._question.getValue(), optionsA = options[0],
optionsB = options[1], optionsC = options[2], optionsD = options[3],
rightOption = it.rightAnswer,
questionId = it.questionId
)
} else {
null
}
}
} }
/** /**
* TODO 提交答案 * 提交答卷
*
* @param callback * @param callback
*/ */
fun postAnswer(callback: (message: String) -> Unit) { fun postAnswer(associationId: Int, callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP) viewModelScope.launch {
_data.value?.mapNotNull { it ->
if (it is ChoiceQuestionVo) {
toExamVo(choiceQuestionVo = it)?.let { choice ->
ApplyAnswerVo(
examVo = choice,
answer = it.chooseOption
?: throw IllegalArgumentException("题目[id=${choice.questionId}]没有填写答案???")
)
}.apply {
Logger.i("$this")
}
} else {
null
}
}?.let {
Logger.i("提交${it.size}道题目答案")
HttpClient.post(url = Api.buildUrl(AssociationApi.ApplyAnswer),
callback = HttpCallback<Boolean>(action = "提交答案",
onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = AddAnswerVo(
answers = it, token = TokenManager.getToken(),
associationId = associationId
)
)
}
}
} }
/** /**
* 加载题目 * 加载题
* *
*/ */
fun load() { fun load(associationId: Int, activityType: ExamActivityType) {
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(
when (activityType) {
ExamActivityType.SET_EXAM -> AssociationApi.LoadExam
ExamActivityType.JOIN_Association -> AssociationApi.CreatePaper
else -> throw IllegalArgumentException("非法类型:${activityType}")
}
),
callback = HttpCallback<MutableList<ExamVo>>(action = "加载题库", onSuccess = { it ->
it.body?.let { it1 ->
_data.postValue(it1.map {
createExam(examVo = it)
}.toMutableList().apply {
if (activityType == ExamActivityType.SET_EXAM) {
add(createExam())
}
})
}
}, typeToken = object : TypeToken<ApiResponse<MutableList<ExamVo>>>() {}.type),
jsonParam =
SearchExamVo(associationId = associationId, token = TokenManager.getToken())
)
}
}
/**
* 加载答卷
*
* @param joinId
* @param callback
*/
fun showAnswer(joinId: Int, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
url = Api.buildUrl(AssociationApi.ShowAnswers),
callback = HttpCallback<List<ApplyAnswerVo>>(action = "查看答案", onSuccess = { it ->
it.body?.apply {
val exam = map {
createExam(examVo = it.examVo, answer = it.answer)
}
_data.postValue(exam.toMutableList())
}
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<List<ApplyAnswerVo>>>() {}.type),
jsonParam = ShowAnswerVo(joinId = joinId, token = TokenManager.getToken())
)
}
} }
/**
* 添加题目
*
*/
fun addQuestion() { fun addQuestion() {
_data.value?.apply { _data.value?.apply {
_newExam.value?.let { _newExam.value?.let {
@ -201,20 +350,24 @@ class ExamViewModel(application: Application) : ScrollViewModel<Exam>(applicatio
list.add(it) list.add(it)
_data.postValue(list) _data.postValue(list)
} }
_newExam.value = createExam(ExamType.CQ) _newExam.value = createExam()
} }
} }
/** /**
* TODO 删除题目 *删除题目
*
*/ */
fun deleteQuestion(exam: Exam) { fun deleteQuestion(exam: Exam) {
_data.value?.apply { _data.value?.apply {
val list = mutableListOf<Exam>() val list = mutableListOf<Exam>()
remove(exam) remove(exam)
list.addAll(this) list.addAll(this)
Logger.i("size=${list.size}")
_data.value?.clear()
_data.postValue(list) _data.postValue(list)
exam.questionId?.let {
_deleteIds.add(it)
}
} }
} }
} }

@ -3,6 +3,7 @@ package com.gyf.csams.association.ui
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 androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -62,21 +63,27 @@ class AssociationActivity : ComponentActivity() {
val currentMenuName: AssociationMenu by model.currentMenu.observeAsState( val currentMenuName: AssociationMenu by model.currentMenu.observeAsState(
AssociationMenu.startMenu AssociationMenu.startMenu
) )
val intent = Intent(this, ExamActivity::class.java)
val expanded by model.expanded.observeAsState(false) val expanded by model.expanded.observeAsState(false)
var message: String? by remember {
mutableStateOf(null)
}
//TODO重命名操作反馈 //TODO重命名操作反馈
val rename = val rename =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) { if (it.resultCode == RESULT_OK) {
val m = it.data?.getStringExtra(RenameActivity::class.java.name) val m = it.data?.getStringExtra(RenameActivity::class.java.name)
Logger.i("社团重命名返回:${m}") Logger.i("社团重命名返回:${m}")
message = m model.update(message = m)
} }
} }
val examResult =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val m = it.data?.getStringExtra(ExamActivity::class.java.name)
Logger.i("试卷提交结果:${m}")
model.update(message = m)
}
}
Column { Column {
TextTopAppBar(nav = nav, TextTopAppBar(nav = nav,
currentMenuName = currentMenuName.menuName, currentMenuName = currentMenuName.menuName,
@ -90,7 +97,6 @@ class AssociationActivity : ComponentActivity() {
onDismissRequest = { }, onDismissRequest = { },
properties = PopupProperties() properties = PopupProperties()
) { ) {
when { when {
TokenManager.getUserInfo()?.associationVo?.associationId == associationId && TokenManager.getUserInfo()?.isHead == true -> { TokenManager.getUserInfo()?.associationVo?.associationId == associationId && TokenManager.getUserInfo()?.isHead == true -> {
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
@ -118,13 +124,20 @@ class AssociationActivity : ComponentActivity() {
} }
} }
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
intent.apply { startActivity(
putExtra( Intent(
ExamActivityType::name.name, this@AssociationActivity,
ExamActivityType.SET_EXAM ExamActivity::class.java
) ).apply {
} putExtra(
startActivity(intent) AssociationActivity::class.java.name,
associationId
)
putExtra(
ExamActivityType::name.name,
ExamActivityType.SET_EXAM
)
})
model.close() model.close()
}) { }) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
@ -135,6 +148,27 @@ class AssociationActivity : ComponentActivity() {
) )
} }
} }
DropdownMenuItem(onClick = {
startActivity(
Intent(
this@AssociationActivity,
AuditJoinActivity::class.java
).apply {
putExtra(
AssociationActivity::class.java.name,
associationId
)
})
model.close()
}) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = ("入团审核"))
Icon(
painter = painterResource(id = R.drawable.ic_exchange_rate),
contentDescription = null
)
}
}
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
rename.launch(Intent( rename.launch(Intent(
this@AssociationActivity, this@AssociationActivity,
@ -159,13 +193,9 @@ class AssociationActivity : ComponentActivity() {
} }
TokenManager.getUserInfo()?.associationVo == null -> DropdownMenuItem( TokenManager.getUserInfo()?.associationVo == null -> DropdownMenuItem(
onClick = { onClick = {
intent.apply { model.applyAssociation(associationId = associationId) {
putExtra( model.update(applyAssociationResultVo = it)
ExamActivityType::name.name,
ExamActivityType.JOIN_Association
)
} }
startActivity(intent)
model.close() model.close()
}) { }) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
@ -177,7 +207,6 @@ class AssociationActivity : ComponentActivity() {
} }
} }
} }
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
model.close() model.close()
}) { }) {
@ -202,32 +231,19 @@ class AssociationActivity : ComponentActivity() {
composable(AssociationMenu.Member.name) { composable(AssociationMenu.Member.name) {
model.clickMenu(AssociationMenu.Member) model.clickMenu(AssociationMenu.Member)
Member() Member()
ShowTip(model = model, examResult = examResult)
ShowSnackbar(scaffoldState = scaffoldState) ShowSnackbar(scaffoldState = scaffoldState)
} }
composable(AssociationMenu.Main.name) { composable(AssociationMenu.Main.name) {
model.clickMenu(AssociationMenu.Main) model.clickMenu(AssociationMenu.Main)
Main() Main()
//TODO 提示 ShowTip(model = model, examResult = examResult)
val scaffoldModel: ScaffoldModel = viewModel()
message?.let {
scaffoldModel.update(message = message, actionLabel = "刷新")
val s by scaffoldModel.data.observeAsState()
if (s == null) {
message = null
// nav.navigate(AssociationMenu.Main.name)
}
LaunchedEffect(message) {
delay(it.length * 300L)
message = null
// nav.navigate(AssociationMenu.Main.name)
}
}
ShowSnackbar(scaffoldState = scaffoldState) ShowSnackbar(scaffoldState = scaffoldState)
} }
composable(AssociationMenu.ActivityList.name) { composable(AssociationMenu.ActivityList.name) {
model.clickMenu(AssociationMenu.ActivityList) model.clickMenu(AssociationMenu.ActivityList)
AssociationList() AssociationList()
ShowTip(model = model, examResult = examResult)
ShowSnackbar(scaffoldState = scaffoldState) ShowSnackbar(scaffoldState = scaffoldState)
} }
} }
@ -238,16 +254,71 @@ class AssociationActivity : ComponentActivity() {
} }
} }
//TODO下拉菜单操作反馈 /**
* 显示提示
*
* @param model
* @param examResult
*/
@Composable @Composable
private fun ShowTip( private fun ShowTip(
model: AssociationViewModel = viewModel(), model: AssociationViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel() examResult: ManagedActivityResultLauncher<Intent, *>
) { ) {
val m by model.dropDownMenuResult.observeAsState() val scaffoldModel: ScaffoldModel = viewModel()
m?.let { val message by model.dropMenuMessage.observeAsState()
Logger.i("收到${it}") message?.let {
scaffoldModel.update(message = it, actionLabel = "关闭提示") scaffoldModel.update(message = message, actionLabel = "刷新")
val s by scaffoldModel.data.observeAsState()
if (s == null) {
model.update(message = null)
}
LaunchedEffect(message) {
delay(it.length * 300L)
model.update(message = null)
}
}
val applyAssociationResultVo by model.applyAssociationResultVo.observeAsState()
applyAssociationResultVo?.let {
when {
it.result == true && it.hasPaper == true ->
scaffoldModel.update(
message = "申请此社团需要完成一份笔试题,点击确认获取试卷",
actionLabel = "确认"
) {
examResult.launch(
Intent(
this@AssociationActivity,
ExamActivity::class.java
).apply {
putExtra(AssociationActivity::class.java.name, associationId)
putExtra(
ExamActivityType::name.name,
ExamActivityType.JOIN_Association
)
})
}
it.result == true ->
scaffoldModel.update(
message = "入团申请发送成功,请耐心等待审核结果",
actionLabel = "收到"
)
it.associationVo != null ->
scaffoldModel.update(
message = "您已申请进入${it.associationVo?.name},请耐心等待审核结果",
actionLabel = "收到"
)
else ->
scaffoldModel.update(
message = "入团申请发送失败!",
actionLabel = "确认"
)
}
model.update(applyAssociationResultVo = null)
} }
} }
@ -491,16 +562,15 @@ class AssociationActivity : ComponentActivity() {
alpha = 07F alpha = 07F
) )
}) { }) {
val onGoWeight = 0.3F // val onGoWeight = 0.3F
OngoingActivity( // OngoingActivity(
modifier = Modifier // modifier = Modifier
.weight(onGoWeight) // .weight(onGoWeight)
.fillMaxWidth() // .fillMaxWidth()
) // )
HistoryActivityList( HistoryActivityList(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1 - onGoWeight)
.border(width = 1.dp, color = MaterialTheme.colors.onBackground) .border(width = 1.dp, color = MaterialTheme.colors.onBackground)
) )
} }
@ -540,23 +610,30 @@ class AssociationActivity : ComponentActivity() {
val listState = rememberLazyListState() val listState = rememberLazyListState()
val list by model.data.observeAsState() val list by model.data.observeAsState()
LazyColumn(state = listState, modifier = modifier) { if (list?.size == 0) {
list?.chunked(2)?.forEach { Row(modifier = Modifier.fillMaxWidth()) {
item { Text(text = "目前社团没有任何活动")
Row(modifier = Modifier.fillMaxWidth()) { }
HistoryActivity(modifier = Modifier.weight(0.4F), it[0]) } else {
Spacer(modifier = Modifier.weight(0.2F)) LazyColumn(state = listState, modifier = modifier) {
if (it.size == 2) HistoryActivity(modifier = Modifier.weight(0.4F), it[1]) list?.chunked(2)?.forEach {
else Box(modifier = Modifier.weight(0.4F)) item {
Row(modifier = Modifier.fillMaxWidth()) {
HistoryActivity(modifier = Modifier.weight(0.4F), it[0])
Spacer(modifier = Modifier.weight(0.2F))
if (it.size == 2) HistoryActivity(
modifier = Modifier.weight(0.4F),
it[1]
)
else Box(modifier = Modifier.weight(0.4F))
}
Spacer(modifier = Modifier.height(10.dp))
Divider(color = MaterialTheme.colors.background)
} }
Spacer(modifier = Modifier.height(10.dp))
Divider(color = MaterialTheme.colors.background)
} }
} }
} }
if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.initSize / 2 - 1) {
TODO("加载更多")
}
} }
/** /**

@ -0,0 +1,133 @@
package com.gyf.csams.association.ui
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Card
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R
import com.gyf.csams.association.model.AuditJoinViewModel
import com.gyf.csams.association.model.ExamActivityType
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.DateTimeUtil.datetimeFormat
import java.util.*
/**
* 审核入团申请
*
*/
class AuditJoinActivity : ComponentActivity() {
private val associationId: Int
get() {
val id = intent.getIntExtra(
AssociationActivity::class.java.name,
0
)
return if (id == 0) throw IllegalArgumentException("社团id:${id}不合法,初始化失败") else id
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Body {
MainColumnFrame(background = { }) {
val model: AuditJoinViewModel = viewModel()
val data by model.data.observeAsState()
val scaffoldModel: ScaffoldModel = viewModel()
LaunchedEffect(associationId) {
model.load(associationId = associationId)
}
LazyColumn(
verticalArrangement = Arrangement.spacedBy(
10.dp,
Alignment.CenterVertically
)
) {
data?.forEach {
item {
Card(elevation = 5.dp) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "申请人:${it.user.name}")
Text(text = "申请时间:${Date(it.applyTime).datetimeFormat()}")
if (it.hasPaper) {
Spacer(modifier = Modifier.height(5.dp))
OutlinedButton(onClick = {
startActivity(
Intent(
this@AuditJoinActivity,
ExamActivity::class.java
).apply {
putExtra(
ExamActivityType::name.name,
ExamActivityType.Answer
)
putExtra(
AuditJoinActivity::class.java.name,
it.id
)
putExtra(
AssociationActivity::class.java.name,
it.associationVo.associationId
)
})
}) {
Text(text = "查看申请答卷")
}
Spacer(modifier = Modifier.height(5.dp))
}
when (val result = it.result) {
null -> BottomButton(confirmDesc = R.string.allow_btn,
backDesc = R.string.refuse_btn,
modifier = Modifier.fillMaxWidth(),
onBack = {
model.audit(joinId = it.id, result = false) {
scaffoldModel.update(
message = it,
actionLabel = "关闭提示"
)
model.load(associationId = associationId)
}
},
onConfirm = {
model.audit(joinId = it.id, result = true) {
scaffoldModel.update(
message = it,
actionLabel = "关闭提示"
)
model.load(associationId = associationId)
}
})
true, false -> {
Text("审核结果:${if (result) "通过" else "不通过"}")
Text("审核时间:${it.auditTime?.let { Date(it).datetimeFormat() }}")
}
}
}
}
}
}
}
}
}
}
}
}

@ -1,5 +1,6 @@
package com.gyf.csams.association.ui package com.gyf.csams.association.ui
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
@ -9,6 +10,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue 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
@ -19,6 +21,7 @@ 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.association.model.* import com.gyf.csams.association.model.*
import com.gyf.csams.module.QUESTION_MIN_SIZE
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
@ -26,6 +29,7 @@ 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.orhanobut.logger.Logger
/** /**
@ -34,13 +38,32 @@ import com.gyf.lib.util.BottomButton
*/ */
class ExamActivity : ComponentActivity() { class ExamActivity : ComponentActivity() {
lateinit var activityType: ExamActivityType private val activityType: ExamActivityType
get() {
return intent?.getSerializableExtra(ExamActivityType::name.name) as ExamActivityType
}
private val associationId: Int
get() {
val id = intent.getIntExtra(
AssociationActivity::class.java.name,
0
)
return if (id == 0) throw IllegalArgumentException("不存在社团id,获取失败") else id
}
private val joinId: Int
get() {
val id = intent.getIntExtra(
AuditJoinActivity::class.java.name,
0
)
return if (id == 0) throw IllegalArgumentException("不存在申请记录id,获取失败") else id
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
activityType = intent?.getSerializableExtra(ExamActivityType::name.name) as ExamActivityType
setContent { setContent {
Body { Body {
MainColumnFrame(background = { MainColumnFrame(background = {
@ -49,14 +72,35 @@ class ExamActivity : ComponentActivity() {
alpha = 0.6F alpha = 0.6F
) )
}) { }) {
Spacer(modifier = Modifier.weight(0.1F)) Title()
Title(modifier = Modifier.weight(0.1F)) Spacer(modifier = Modifier.height(10.dp))
Exam(modifier = Modifier.weight(0.8F)) Exam(modifier = Modifier.weight(0.8F))
} }
} }
} }
} }
@Composable
private fun ErrorRadioButton(onClick: () -> Unit = {}) {
RadioButton(
selected = true,
colors =
RadioButtonDefaults.colors(disabledColor = MaterialTheme.colors.error),
onClick = onClick,
enabled = false
)
}
@Composable
private fun RightRadioButton(selected: Boolean, onClick: () -> Unit = {}) {
RadioButton(
selected = selected,
colors =
RadioButtonDefaults.colors(disabledColor = MaterialTheme.colors.primary),
onClick = onClick,
enabled = false
)
}
/** /**
* 标题 * 标题
@ -64,12 +108,44 @@ class ExamActivity : ComponentActivity() {
* @param modifier * @param modifier
*/ */
@Composable @Composable
private fun Title(modifier: Modifier = Modifier) { private fun Title(modifier: Modifier = Modifier, model: ExamViewModel = viewModel()) {
Row(horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth()) { val data by model.data.observeAsState()
Text(text = activityType.menuName, style = MaterialTheme.typography.h4) Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = activityType.menuName, style = MaterialTheme.typography.h5)
data?.size?.let {
Text(buildString {
append("共有${if (activityType == ExamActivityType.SET_EXAM) it - 1 else it}道题目")
if (it < QUESTION_MIN_SIZE) {
append(",还差${QUESTION_MIN_SIZE - it}道题目才可以生成入团笔试题")
}
}, style = MaterialTheme.typography.h6.copy(color = MaterialTheme.colors.primary))
}
when (activityType) {
ExamActivityType.Answer -> Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Row {
RightRadioButton(selected = true)
Text(text = "表示正确选项")
}
Row {
ErrorRadioButton()
Text(text = "表示错误选项")
}
}
ExamActivityType.SET_EXAM -> Row(modifier = Modifier.fillMaxWidth()) {
Text(text = "通过单选按钮设置正确答案")
}
ExamActivityType.JOIN_Association -> Row(modifier = Modifier.fillMaxWidth()) {
Text(text = "通过单选按钮选择正确答案")
}
}
} }
} }
@Composable @Composable
private fun ExamChild(it: Exam, examHeight: Dp, isAdd: Boolean = false) { private fun ExamChild(it: Exam, examHeight: Dp, isAdd: Boolean = false) {
val questionWeight = 0.3F val questionWeight = 0.3F
@ -77,7 +153,7 @@ class ExamActivity : ComponentActivity() {
is OpenQuestionsVo -> ExamOQ( is OpenQuestionsVo -> ExamOQ(
openQuestionsVo = it, openQuestionsVo = it,
modifier = Modifier.height(examHeight * questionWeight), modifier = Modifier.height(examHeight * questionWeight),
isAdd = isAdd isAdd = isAdd,
) )
is ChoiceQuestionVo -> ExamCQ( is ChoiceQuestionVo -> ExamCQ(
choiceQuestionVo = it, choiceQuestionVo = it,
@ -101,64 +177,88 @@ class ExamActivity : ComponentActivity() {
scaffoldModel: ScaffoldModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel(),
examHeight: Dp = 350.dp examHeight: Dp = 350.dp
) { ) {
LaunchedEffect(associationId) {
when (activityType) {
ExamActivityType.JOIN_Association, ExamActivityType.SET_EXAM -> model.load(
associationId = associationId,
activityType = activityType
)
ExamActivityType.Answer -> model.showAnswer(joinId = joinId) {
scaffoldModel.update(message = "答卷加载成功", actionLabel = "关闭提示")
}
}
}
val listState = rememberLazyListState() val listState = rememberLazyListState()
val data by model.data.observeAsState() val data by model.data.observeAsState()
val newExam by model.newExam.observeAsState() val newExam by model.newExam.observeAsState()
LazyColumn(state = listState, modifier = modifier) { Column(modifier = modifier) {
data?.forEach { LazyColumn(state = listState, modifier = Modifier.weight(0.8F)) {
item { data?.forEach {
ExamChild(it = it, examHeight = examHeight) item {
Spacer(modifier = Modifier.height(20.dp)) ExamChild(it = it, examHeight = examHeight)
Spacer(modifier = Modifier.height(20.dp))
}
} }
} newExam?.let {
newExam?.let { item {
item { Column {
Column {
// OutlinedButton(onClick = { model.switchType(it) }) { // OutlinedButton(onClick = { model.switchType(it) }) {
// Text(text = "切换到${if (newExam is ChoiceQuestionVo) "开放题" else "选择题"}") // Text(text = "切换到${if (newExam is ChoiceQuestionVo) "开放题" else "选择题"}")
// } // }
ExamChild(it = it, examHeight = examHeight, isAdd = true) ExamChild(it = it, examHeight = examHeight, isAdd = true)
}
} }
} }
} }
item { Divider(color = MaterialTheme.colors.background)
Column { Spacer(modifier = Modifier.height(30.dp))
Divider(color = MaterialTheme.colors.background) when (activityType) {
Spacer(modifier = Modifier.height(30.dp)) ExamActivityType.SET_EXAM -> {
BottomButton(
when (activityType) { modifier = Modifier.fillMaxWidth(),
ExamActivityType.SET_EXAM -> { confirmDesc = R.string.update_exam,
BottomButton( enabled = data?.size ?: 0 > 0
modifier = Modifier.fillMaxWidth(), ) {
confirmDesc = R.string.update_exam model.updateExam(associationId = associationId) {
) { scaffoldModel.update(
model.updateExam { scaffoldModel.update(message = it) } message = it, actionLabel = "关闭提示"
} )
} }
ExamActivityType.JOIN_Association -> { }
BottomButton( }
modifier = Modifier.fillMaxWidth(), ExamActivityType.JOIN_Association -> {
confirmDesc = R.string.post_answer val allowPostAnswer by model.allowPostAnswer.observeAsState(false)
) { BottomButton(
model.postAnswer { scaffoldModel.update(message = it) } modifier = Modifier.fillMaxWidth(),
} confirmDesc = R.string.post_answer,
backDesc = R.string.cancel_apply,
enabled = allowPostAnswer
) {
model.postAnswer(associationId = associationId) {
scaffoldModel.update(message = it)
setResult(RESULT_OK, Intent().apply {
putExtra(ExamActivity::class.java.name, it)
})
finish()
}
}
}
ExamActivityType.Answer -> {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
OutlinedButton(onClick = { onBackPressed() }) {
Text(text = "返回")
} }
} }
} }
} }
} }
// if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.initSize / 2 - 1) {
// model.loadMore { scaffoldModel.update(message = it) }
// }
} }
/** /**
@ -172,12 +272,14 @@ class ExamActivity : ComponentActivity() {
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
exam: Exam exam: Exam
) { ) {
exam._question?.let { exam._question.let {
BaseTextField( BaseTextField(
form = it, form = it,
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.background(color = MaterialTheme.colors.background) .background(color = MaterialTheme.colors.background),
readOnly = activityType != ExamActivityType.SET_EXAM,
showTrailingIcon = activityType == ExamActivityType.SET_EXAM
) )
} }
@ -194,44 +296,36 @@ class ExamActivity : ComponentActivity() {
scaffoldModel: ScaffoldModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel(),
exam: Exam exam: Exam
) { ) {
val list by model.data.observeAsState() Box(
model.newExam.value?._question?.let { contentAlignment = Alignment.Center,
val value by it.formValue.observeAsState() modifier = modifier
Box( ) {
contentAlignment = Alignment.Center, IconButton(onClick = {
modifier = modifier if (isAdd) {
) { if (exam.check()) {
IconButton(onClick = { scaffoldModel.update(
if (isAdd) { message = model.addTip,
if ((value?.isNotEmpty() == true)) { actionLabel = model.actionLabel
scaffoldModel.update( ) {
message = model.addTip, model.addQuestion()
actionLabel = model.actionLabel
) {
model.addQuestion()
}
} else {
scaffoldModel.update(message = model.questionIsNull)
} }
} else { } else {
if (list?.size == 1) { scaffoldModel.update(message = model.questionIsNull, actionLabel = "知道了")
scaffoldModel.update(model.deleteLeastOne) }
} else {
scaffoldModel.update( } else {
message = model.deleteTip, scaffoldModel.update(
actionLabel = model.actionLabel message = model.deleteTip,
) { actionLabel = model.actionLabel
model.deleteQuestion(exam = exam) ) {
} model.deleteQuestion(exam = exam)
}
} }
}) {
Icon(
painter = painterResource(id = if (isAdd) R.drawable.ic_add_select else R.drawable.ic_sami_select),
contentDescription = null
)
} }
}) {
Icon(
painter = painterResource(id = if (isAdd) R.drawable.ic_add_select else R.drawable.ic_sami_select),
contentDescription = null
)
} }
} }
@ -247,9 +341,15 @@ class ExamActivity : ComponentActivity() {
private fun ExamOQ( private fun ExamOQ(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
openQuestionsVo: OpenQuestionsVo, openQuestionsVo: OpenQuestionsVo,
isAdd: Boolean = false isAdd: Boolean = false,
model: ExamViewModel = viewModel()
) { ) {
val data by model.data.observeAsState()
Row(modifier = modifier) { Row(modifier = modifier) {
Logger.i("index=${data?.indexOf(openQuestionsVo)}")
data?.indexOf(openQuestionsVo)?.let {
Text(text = "${it}.", modifier = Modifier.weight(0.05F))
}
Question( Question(
exam = openQuestionsVo, modifier = exam = openQuestionsVo, modifier =
if (activityType == ExamActivityType.SET_EXAM) if (activityType == ExamActivityType.SET_EXAM)
@ -262,7 +362,7 @@ class ExamActivity : ComponentActivity() {
if (activityType == ExamActivityType.SET_EXAM) { if (activityType == ExamActivityType.SET_EXAM) {
ActionButton( ActionButton(
modifier = Modifier modifier = Modifier
.weight(0.2F) .weight(0.15F)
.fillMaxHeight(), .fillMaxHeight(),
isAdd = isAdd, isAdd = isAdd,
exam = openQuestionsVo exam = openQuestionsVo
@ -285,6 +385,16 @@ class ExamActivity : ComponentActivity() {
questionWeight: Float questionWeight: Float
) { ) {
Row(modifier = modifier) { Row(modifier = modifier) {
model.data.value?.let {
Text(
text = "${
if (it.indexOf(choiceQuestionVo) == -1) it.size else it.indexOf(
choiceQuestionVo
) + 1
}."
)
}
Column( Column(
modifier = if (activityType == ExamActivityType.SET_EXAM) modifier = if (activityType == ExamActivityType.SET_EXAM)
Modifier Modifier
@ -315,16 +425,52 @@ class ExamActivity : ComponentActivity() {
.weight(1F / ANSWER_SIZE) .weight(1F / ANSWER_SIZE)
) { ) {
val answerIndex: Int = indexOf(it) val answerIndex: Int = indexOf(it)
val click = {
model.update(
oldExam = choiceQuestionVo, when (activityType) {
newExam = choiceQuestionVo.copy(rightAnswer = answerIndex) ExamActivityType.SET_EXAM -> {
) RadioButton(
selected = choiceQuestionVo.rightAnswer == answerIndex,
onClick = {
model.update(
oldExam = choiceQuestionVo,
newExam = choiceQuestionVo.copy(rightAnswer = answerIndex)
)
})
}
ExamActivityType.JOIN_Association -> {
RadioButton(
selected = choiceQuestionVo.chooseOption == answerIndex,
onClick = {
model.update(
oldExam = choiceQuestionVo,
newExam = choiceQuestionVo.copy(chooseOption = answerIndex)
)
model.checkChooseAnswer()
})
}
ExamActivityType.Answer -> {
when {
choiceQuestionVo.rightAnswer == answerIndex -> RightRadioButton(
selected = true
)
choiceQuestionVo.chooseOption != choiceQuestionVo.rightAnswer && choiceQuestionVo.chooseOption == answerIndex ->
ErrorRadioButton()
else -> RadioButton(
selected = false,
onClick = {},
enabled = false
)
}
}
} }
val isRightAnswer =
choiceQuestionVo.rightAnswer == answerIndex BaseTextField(
RadioButton(selected = isRightAnswer, onClick = click) form = it,
BaseTextField(form = it) enabled = activityType != ExamActivityType.Answer,
showTrailingIcon = activityType == ExamActivityType.SET_EXAM
)
} }
} }
@ -333,15 +479,13 @@ class ExamActivity : ComponentActivity() {
} }
} }
if (activityType == ExamActivityType.SET_EXAM) { ActionButton(
ActionButton( modifier = Modifier
modifier = Modifier .weight(0.2F)
.weight(0.2F) .fillMaxHeight(),
.fillMaxHeight(), isAdd = isAdd,
isAdd = isAdd, exam = choiceQuestionVo
exam = choiceQuestionVo )
)
}
} }
} }

@ -50,7 +50,7 @@ class ChoiceQuestionVoSerializer : JsonSerializer<ChoiceQuestionVo> {
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
} }

@ -20,4 +20,5 @@
<string name="collect_btn">收藏</string> <string name="collect_btn">收藏</string>
<string name="upload_btn">上传</string> <string name="upload_btn">上传</string>
<string name="close_btn">关闭</string> <string name="close_btn">关闭</string>
<string name="cancel_apply">放弃申请</string>
</resources> </resources>

@ -20,4 +20,5 @@
<string name="collect_btn">收藏</string> <string name="collect_btn">收藏</string>
<string name="upload_btn">上传</string> <string name="upload_btn">上传</string>
<string name="close_btn">关闭</string> <string name="close_btn">关闭</string>
<string name="cancel_apply">放弃申请</string>
</resources> </resources>

@ -20,4 +20,5 @@
<string name="collect_btn">收藏</string> <string name="collect_btn">收藏</string>
<string name="upload_btn">上传</string> <string name="upload_btn">上传</string>
<string name="close_btn">关闭</string> <string name="close_btn">关闭</string>
<string name="cancel_apply">放弃申请</string>
</resources> </resources>

@ -14,13 +14,24 @@ import org.junit.Test
* *
* See [testing documentation](http://d.android.com/tools/testing). * See [testing documentation](http://d.android.com/tools/testing).
*/ */
abstract class TestA {
val i: Int = 3
}
data class TestB(val b: Int = 3) : TestA() {
fun test() {
println("${i}")
}
}
class ExampleUnitTest { class ExampleUnitTest {
@Test @Test
fun addition_isCorrect() { fun addition_isCorrect() {
assertEquals(4, 2 + 2) assertEquals(4, 2 + 2)
} }
data class Fuck(val name:String) data class Fuck(val name: String)

@ -131,6 +131,7 @@ fun <T : StringForm> BaseTextField(
isError: Boolean = false, isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None, visualTransformation: VisualTransformation = VisualTransformation.None,
readOnly: Boolean = false, readOnly: Boolean = false,
enabled: Boolean = true,
textStyle: TextStyle = LocalTextStyle.current, textStyle: TextStyle = LocalTextStyle.current,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null,
@ -151,7 +152,8 @@ fun <T : StringForm> BaseTextField(
isError = isError, isError = isError,
visualTransformation = visualTransformation, visualTransformation = visualTransformation,
readOnly = readOnly, readOnly = readOnly,
textStyle = textStyle textStyle = textStyle,
enabled = enabled
) )
} }
@ -174,7 +176,9 @@ fun <T : StringForm> BaseTextField(
keyboardOptions: KeyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), keyboardOptions: KeyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
isError: Boolean = false, isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None, visualTransformation: VisualTransformation = VisualTransformation.None,
readOnly: Boolean = false readOnly: Boolean = false,
showTrailingIcon: Boolean = true,
enabled: Boolean = true
) { ) {
val name: String by form.formValue.observeAsState("") val name: String by form.formValue.observeAsState("")
BaseTextField( BaseTextField(
@ -184,8 +188,9 @@ fun <T : StringForm> BaseTextField(
keyboardOptions, keyboardOptions,
isError, isError,
visualTransformation, visualTransformation,
trailingIcon = { Text(text = "${name.length}/${form.textLength}") }, trailingIcon = { if (showTrailingIcon) Text(text = "${name.length}/${form.textLength}") },
readOnly = readOnly readOnly = readOnly,
enabled = enabled
) )
} }

@ -17,7 +17,7 @@ import kotlinx.coroutines.launch
data class SnackBar( data class SnackBar(
val message: String?, val message: String?,
val actionLabel: String? = null, val actionLabel: String,
val duration: SnackbarDuration = SnackbarDuration.Short, val duration: SnackbarDuration = SnackbarDuration.Short,
val callback: () -> Unit? val callback: () -> Unit?
) )
@ -30,7 +30,7 @@ class ScaffoldModel : ViewModel() {
private val _data = MutableLiveData<SnackBar?>() private val _data = MutableLiveData<SnackBar?>()
val data: LiveData<SnackBar?> = _data val data: LiveData<SnackBar?> = _data
fun update(message: String? = null, actionLabel: String? = null, callback: () -> Unit? = {}) { fun update(message: String? = null, actionLabel: String = "关闭提示", callback: () -> Unit? = {}) {
if (message == null) { if (message == null) {
_data.postValue(null) _data.postValue(null)
} else { } else {
@ -55,23 +55,19 @@ fun ShowSnackbar(model: ScaffoldModel = viewModel(), scaffoldState: ScaffoldStat
if (message != null) { if (message != null) {
LaunchedEffect(scaffoldState) { LaunchedEffect(scaffoldState) {
launch { launch {
if (actionLabel != null) { val result = scaffoldState.snackbarHostState.showSnackbar(
val result = scaffoldState.snackbarHostState.showSnackbar( message = message, actionLabel = actionLabel,
message = message, actionLabel = actionLabel, duration = duration
duration = duration
) )
when (result) { when (result) {
SnackbarResult.ActionPerformed -> { SnackbarResult.ActionPerformed -> {
Logger.i("点击操作按钮") Logger.d("点击操作按钮")
callback() callback()
} }
SnackbarResult.Dismissed -> { SnackbarResult.Dismissed -> {
Logger.d("窗口消失") Logger.d("窗口消失")
}
} }
} else {
scaffoldState.snackbarHostState.showSnackbar(message = message)
} }
model.update() model.update()
} }

@ -131,7 +131,31 @@ enum class AssociationApi(val path: String) : UrlPath {
RenameAudit("${Rename.path}${Audit.path}"), RenameAudit("${Rename.path}${Audit.path}"),
//换名申请表审核进度 //换名申请表审核进度
RenameRead("${Rename.path}${Read.path}"); RenameRead("${Rename.path}${Read.path}"),
//更新题库
UpdateExam("/update/exam"),
//加载题库
LoadExam("/load/exam"),
//申请入团
CheckApply("/check/apply"),
//创建试卷
CreatePaper("/create/paper"),
//提交答卷
ApplyAnswer("/apply/paper"),
//检查入团申请
CheckJoin("/check/join"),
//审核入团申请
AuditJoin("/audit/join"),
//显示答案
ShowAnswers("/show/answer");
override fun build(): String { override fun build(): String {
return "/api/association${this.path}" return "/api/association${this.path}"

@ -31,4 +31,6 @@
<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> <string name="activity_date">活动日期</string>
<string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string>
</resources> </resources>

@ -31,4 +31,6 @@
<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> <string name="activity_date">活动日期</string>
<string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string>
</resources> </resources>

@ -31,4 +31,6 @@
<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> <string name="activity_date">活动日期</string>
<string name="allow_btn">通过</string>
<string name="refuse_btn">拒绝</string>
</resources> </resources>
Loading…
Cancel
Save