master
pan 4 years ago
parent 42dbaef67a
commit 52c6088fd8
  1. 95
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  2. 70
      foreground/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
  3. 8
      foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt
  4. 9
      foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt
  5. 3
      foreground/src/main/res/values-en/strings.xml
  6. 3
      foreground/src/main/res/values-zh/strings.xml
  7. 3
      foreground/src/main/res/values/strings.xml
  8. 42
      lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt

@ -12,12 +12,14 @@ import com.google.gson.reflect.TypeToken
import com.gyf.csams.AccountApi import com.gyf.csams.AccountApi
import com.gyf.csams.Api import com.gyf.csams.Api
import com.gyf.csams.InitActivity import com.gyf.csams.InitActivity
import com.gyf.csams.R
import com.gyf.csams.account.ui.AccountRoute import com.gyf.csams.account.ui.AccountRoute
import com.gyf.csams.util.AppDatabase import com.gyf.csams.util.AppDatabase
import com.gyf.csams.util.SimpleCallback import com.gyf.csams.util.SimpleCallback
import com.gyf.csams.util.TokenResDto import com.gyf.csams.util.TokenResDto
import com.gyf.lib.uikit.ScaffoldModel 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.util.ApiResponse import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.HttpClient import com.gyf.lib.util.HttpClient
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -58,49 +60,28 @@ data class DialogMessage(val message: String, val userResDto: UserResDto?)
typealias Token = TokenResDto typealias Token = TokenResDto
/** /**
* 注册表单 * 注册表单
*/ */
class AccountViewModel(application: Application) : AndroidViewModel(application) { class AccountViewModel(application: Application) : AndroidViewModel(application) {
//欢迎信息
val welcomeStart = "同学您好\n"
val welcomeEnd = "欢迎使用"
lateinit var scaffoldModel: ScaffoldModel
//学号 //学号
val studentId = object : StringForm(formDesc = "学号", textLength = 8) { val studentId = object : ValidStringForm(formDesc = "学号", textLength = 8) {
override fun onChange(value: String) { override fun check() {
super.onChange(value) _formValue.value?.let {
viewModelScope.launch { when {
checkRepeat { !it.matches(Regex("\\d{8}")) -> _statusForm.value = FormStatus.FormatError
scaffoldModel.update(message = it) it.matches(Regex("\\d{8}")) -> checkRepeat(_statusForm)
} }
} }
} }
} }
private val _isValidStudentId = MutableLiveData<Boolean>()
val isValidStudentId: LiveData<Boolean> = _isValidStudentId
val studentIdFormat = "入学年份(四位)+班级代码(两位)+学生代码(两位)"
//学号已存在
private val _isRepeat = MutableLiveData<Boolean?>()
val isRepeat: LiveData<Boolean?> = _isRepeat
val regBtnDesc = "注册" val regBtnDesc = application.getString(R.string.reg_btn)
//已注册 //已注册
val registered = "$regBtnDesc" val registered = "$regBtnDesc"
//可注册
val canRegister = "$regBtnDesc"
//提示信息
val checkRegTip = "检测学号是否已${regBtnDesc}。。。"
private var checkJob: Job? = null private var checkJob: Job? = null
//姓名 //姓名
@ -159,43 +140,33 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
lateinit var route: AccountRoute lateinit var route: AccountRoute
/**
* 检查学号格式
*
*/
private fun checkStudentId(): Boolean {
_isValidStudentId.value = studentId.formValue.value?.matches(Regex("\\d{8}"))
return _isValidStudentId.value == true
}
/** /**
* 检查学号是否已注册 * 检查学号是否已注册
* *
*/ */
private suspend fun checkRepeat(onFail: (error: String) -> Unit) { private fun checkRepeat(result: MutableLiveData<FormStatus>) {
if (checkStudentId()) { viewModelScope.launch {
if (checkJob?.isActive == true) { if (checkJob?.isActive == true) {
checkJob?.join() checkJob?.cancel()
} else { }
_isRepeat.postValue(null) checkJob = viewModelScope.launch {
checkJob = viewModelScope.launch { val url = Api.buildUrl(AccountApi.CheckId)
val url = Api.buildUrl(AccountApi.CheckId) Logger.i("检测${studentId.formDesc},请求接口$url")
Logger.i("检测${studentId.formDesc},请求接口$url") HttpClient.get(
HttpClient.get( url, SimpleCallback<Boolean>(
url, SimpleCallback<Boolean>( action = "${studentId.formDesc}重复检测",
action = "${studentId.formDesc}重复检测", onSuccess = {
onSuccess = { if (it.body == true) {
_isRepeat.postValue(it.body) result.postValue(FormStatus.Repeat)
_isValidForm.postValue(_isValidName.value == true && it.body == false) } else {
}, result.postValue(FormStatus.Valid)
onFail = onFail, }
type = object : TypeToken<ApiResponse<Boolean>>() {}.type },
), mapOf("studentId" to "${studentId.formValue.value}") onFail = { Logger.e(it) },
) type = object : TypeToken<ApiResponse<Boolean>>() {}.type
} ), mapOf("studentId" to "${studentId.formValue.value}")
)
} }
} else {
_isValidForm.postValue(false)
} }
} }
@ -224,7 +195,7 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
_isValidForm.value = false _isValidForm.value = false
} else { } else {
_isValidForm.value = _isValidForm.value =
checkStudentId() && (if (route == AccountRoute.Register) checkName() && isRepeat.value == false else checkPassword()) studentId.statusForm.value == FormStatus.Valid && (if (route == AccountRoute.Register) checkName() else checkPassword())
} }
return _isValidForm.value == true return _isValidForm.value == true
} }

@ -13,6 +13,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.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
@ -23,13 +24,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import com.gyf.csams.BuildConfig import com.gyf.csams.BuildConfig
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.DialogMessage import com.gyf.csams.account.model.DialogMessage
import com.gyf.csams.uikit.AnimationText
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.orhanobut.logger.Logger
enum class AccountRoute { enum class AccountRoute {
@ -131,7 +132,6 @@ class AccountActivity : ComponentActivity() {
route: AccountRoute, route: AccountRoute,
Action: @Composable (isValidForm: Boolean, accountViewModel: AccountViewModel, scaffoldModel: ScaffoldModel) -> Unit Action: @Composable (isValidForm: Boolean, accountViewModel: AccountViewModel, scaffoldModel: ScaffoldModel) -> Unit
) { ) {
accountViewModel.scaffoldModel = scaffoldModel
accountViewModel.route = route accountViewModel.route = route
Row( Row(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
@ -147,10 +147,10 @@ class AccountActivity : ComponentActivity() {
append(accountViewModel.name.formValue.value ?: "") append(accountViewModel.name.formValue.value ?: "")
} }
withStyle(style = MaterialTheme.typography.subtitle1.toSpanStyle()) { withStyle(style = MaterialTheme.typography.subtitle1.toSpanStyle()) {
append(accountViewModel.welcomeStart) append(stringResource(id = R.string.welcome_start))
} }
withStyle(style = MaterialTheme.typography.subtitle2.toSpanStyle()) { withStyle(style = MaterialTheme.typography.subtitle2.toSpanStyle()) {
append(accountViewModel.welcomeEnd) append(stringResource(id = R.string.welcome_end))
} }
withStyle( withStyle(
style = MaterialTheme.typography.subtitle2.toSpanStyle() style = MaterialTheme.typography.subtitle2.toSpanStyle()
@ -188,53 +188,39 @@ class AccountActivity : ComponentActivity() {
Column { Column {
val isValidStudentId: Boolean by accountViewModel.isValidStudentId.observeAsState(false) val isValidStudentId by accountViewModel.studentId.statusForm.observeAsState()
BaseTextField( BaseTextField(
form = accountViewModel.studentId, keyboardOptions = KeyboardOptions.Default.copy( form = accountViewModel.studentId, keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number, keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done imeAction = ImeAction.Done
), isError = !isValidStudentId ), isError = isValidStudentId != FormStatus.Valid
) )
if (isValidStudentId) {
if (checkRepeat) { when (isValidStudentId) {
val isRepeat: Boolean? by accountViewModel.isRepeat.observeAsState(null) FormStatus.Empty,
Logger.i("isRepeat=$isRepeat") FormStatus.FormatError -> Text(
when (isRepeat) { text = stringResource(id = R.string.student_id_format),
null -> AnimationText(text = accountViewModel.checkRegTip)
true ->
Text(buildAnnotatedString {
append(accountViewModel.studentId.formDesc)
withStyle(
style = MaterialTheme.typography.body1.toSpanStyle().copy(
color = MaterialTheme.colors.error
)
) {
append(accountViewModel.studentId.formValue.value ?: "")
}
append(accountViewModel.registered)
})
false ->
Text(buildAnnotatedString {
append(accountViewModel.studentId.formDesc)
withStyle(
style = MaterialTheme.typography.body1.toSpanStyle().copy(
color = MaterialTheme.colors.primary
)
) {
append(accountViewModel.studentId.formValue.value ?: "")
}
append(accountViewModel.canRegister)
})
}
}
} else {
Text(
text = accountViewModel.studentIdFormat,
color = MaterialTheme.colors.error, color = MaterialTheme.colors.error,
style = MaterialTheme.typography.body1 style = MaterialTheme.typography.body1
) )
FormStatus.Repeat ->
Text(buildAnnotatedString {
append(accountViewModel.studentId.formDesc)
withStyle(
style = MaterialTheme.typography.body1.toSpanStyle().copy(
color = MaterialTheme.colors.error
)
) {
append(accountViewModel.studentId.formValue.value ?: "")
}
append(accountViewModel.registered)
})
else -> {
}
} }
} }
} }

@ -54,15 +54,15 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
StringForm(formDesc = application.getString(R.string.activity_size), textLength = 2) { StringForm(formDesc = application.getString(R.string.activity_size), textLength = 2) {
override fun onChange(value: String) { override fun onChange(value: String) {
if (value.length > textLength) { if (value.length > textLength) {
formError.value = "${formDesc}不能超过最大长度$textLength" // formError.value = "${formDesc}不能超过最大长度$textLength"
_formValue.value = value.slice(IntRange(0, textLength - 1)) _formValue.value = value.slice(IntRange(0, textLength - 1))
} else if (value.matches(Regex("\\d+")) && value.toInt() !in 1..maxActivitySize) { } else if (value.matches(Regex("\\d+")) && value.toInt() !in 1..maxActivitySize) {
Logger.i("活动人数:${value}不合法") Logger.i("活动人数:${value}不合法")
formError.value = // formError.value =
application.getString(R.string.activity_size_error, 1, maxActivitySize) // application.getString(R.string.activity_size_error, 1, maxActivitySize)
} else { } else {
_formValue.value = value _formValue.value = value
formError.value = "" // formError.value = ""
} }
} }

@ -161,10 +161,11 @@ class ApplyActActivity : AppCompatActivity() {
modifier = Modifier.weight(0.3F), modifier = Modifier.weight(0.3F),
stringForm = model.activityDesc stringForm = model.activityDesc
) )
val error by model.activitySize.formError.observeAsState() //TODO 活动人数不合法警告
if (error?.isNotEmpty() == true) { // val error by model.activitySize.formError.observeAsState()
scaffoldModel.update(message = error) // if (error?.isNotEmpty() == true) {
} // scaffoldModel.update(message = error)
// }
Row( Row(
Modifier Modifier
.weight(0.1F) .weight(0.1F)

@ -12,4 +12,7 @@
<string name="city">城市</string> <string name="city">城市</string>
<string name="address">地点</string> <string name="address">地点</string>
<string name="incity">市内找</string> <string name="incity">市内找</string>
<string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string>
<string name="welcome_start">同学您好\n</string>
<string name="welcome_end">欢迎使用</string>
</resources> </resources>

@ -12,4 +12,7 @@
<string name="city">城市</string> <string name="city">城市</string>
<string name="address">地点</string> <string name="address">地点</string>
<string name="incity">市内找</string> <string name="incity">市内找</string>
<string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string>
<string name="welcome_start">同学您好\n</string>
<string name="welcome_end">欢迎使用</string>
</resources> </resources>

@ -12,4 +12,7 @@
<string name="city">城市</string> <string name="city">城市</string>
<string name="address">地点</string> <string name="address">地点</string>
<string name="incity">市内找</string> <string name="incity">市内找</string>
<string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string>
<string name="welcome_start">同学您好\n</string>
<string name="welcome_end">欢迎使用</string>
</resources> </resources>

@ -18,8 +18,22 @@ import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
enum class FormStatus {
//空
Empty,
//格式不匹配
FormatError,
//学号已存在
Repeat,
//校验通过
Valid
}
interface FormLength { interface FormLength {
val formError: MutableLiveData<String> val textLength: Int
} }
abstract class FormName<T>(val formDesc: String) { abstract class FormName<T>(val formDesc: String) {
@ -38,7 +52,7 @@ abstract class FormName<T>(val formDesc: String) {
* *
* @param formDesc * @param formDesc
*/ */
open class StringForm(formDesc: String, val textLength: Int) : open class StringForm(formDesc: String, override val textLength: Int) :
FormName<String>(formDesc = formDesc), FormName<String>(formDesc = formDesc),
FormLength { FormLength {
@ -49,19 +63,29 @@ open class StringForm(formDesc: String, val textLength: Int) :
_formValue.value = value _formValue.value = value
} }
override val formError = MutableLiveData("")
override fun onChange(value: String) { override fun onChange(value: String) {
if (value.length > textLength) { if (value.length <= textLength) {
formError.value = "${formDesc}不能超过最大长度$textLength" _formValue.value = value
_formValue.postValue(value.slice(IntRange(0, textLength - 1)))
} else {
_formValue.postValue(value)
} }
Logger.i("${formDesc}更新值:${_formValue.value}") Logger.i("${formDesc}更新值:${_formValue.value}")
} }
} }
open class ValidStringForm(formDesc: String, textLength: Int) : StringForm(formDesc, textLength) {
protected val _statusForm = MutableLiveData(FormStatus.Empty)
val statusForm: LiveData<FormStatus> = _statusForm
override fun onChange(value: String) {
super.onChange(value)
when (_formValue.value?.length) {
null, 0 -> _statusForm.value = FormStatus.Empty
else -> check()
}
}
protected open fun check() {}
}
/** /**
* 通用文本输入框 * 通用文本输入框
* *

Loading…
Cancel
Save