master
pan 3 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.Api
import com.gyf.csams.InitActivity
import com.gyf.csams.R
import com.gyf.csams.account.ui.AccountRoute
import com.gyf.csams.util.AppDatabase
import com.gyf.csams.util.SimpleCallback
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.ValidStringForm
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.HttpClient
import com.orhanobut.logger.Logger
@ -58,49 +60,28 @@ data class DialogMessage(val message: String, val userResDto: UserResDto?)
typealias Token = TokenResDto
/**
* 注册表单
*/
class AccountViewModel(application: Application) : AndroidViewModel(application) {
//欢迎信息
val welcomeStart = "同学您好\n"
val welcomeEnd = "欢迎使用"
lateinit var scaffoldModel: ScaffoldModel
//学号
val studentId = object : StringForm(formDesc = "学号", textLength = 8) {
override fun onChange(value: String) {
super.onChange(value)
viewModelScope.launch {
checkRepeat {
scaffoldModel.update(message = it)
val studentId = object : ValidStringForm(formDesc = "学号", textLength = 8) {
override fun check() {
_formValue.value?.let {
when {
!it.matches(Regex("\\d{8}")) -> _statusForm.value = FormStatus.FormatError
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 canRegister = "$regBtnDesc"
//提示信息
val checkRegTip = "检测学号是否已${regBtnDesc}。。。"
private var checkJob: Job? = null
//姓名
@ -159,43 +140,33 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
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) {
if (checkStudentId()) {
private fun checkRepeat(result: MutableLiveData<FormStatus>) {
viewModelScope.launch {
if (checkJob?.isActive == true) {
checkJob?.join()
} else {
_isRepeat.postValue(null)
checkJob = viewModelScope.launch {
val url = Api.buildUrl(AccountApi.CheckId)
Logger.i("检测${studentId.formDesc},请求接口$url")
HttpClient.get(
url, SimpleCallback<Boolean>(
action = "${studentId.formDesc}重复检测",
onSuccess = {
_isRepeat.postValue(it.body)
_isValidForm.postValue(_isValidName.value == true && it.body == false)
},
onFail = onFail,
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
), mapOf("studentId" to "${studentId.formValue.value}")
)
}
checkJob?.cancel()
}
checkJob = viewModelScope.launch {
val url = Api.buildUrl(AccountApi.CheckId)
Logger.i("检测${studentId.formDesc},请求接口$url")
HttpClient.get(
url, SimpleCallback<Boolean>(
action = "${studentId.formDesc}重复检测",
onSuccess = {
if (it.body == true) {
result.postValue(FormStatus.Repeat)
} else {
result.postValue(FormStatus.Valid)
}
},
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
} else {
_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
}

@ -13,6 +13,7 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.input.ImeAction
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.composable
import com.gyf.csams.BuildConfig
import com.gyf.csams.R
import com.gyf.csams.account.model.AccountViewModel
import com.gyf.csams.account.model.DialogMessage
import com.gyf.csams.uikit.AnimationText
import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ScaffoldModel
import com.orhanobut.logger.Logger
enum class AccountRoute {
@ -131,7 +132,6 @@ class AccountActivity : ComponentActivity() {
route: AccountRoute,
Action: @Composable (isValidForm: Boolean, accountViewModel: AccountViewModel, scaffoldModel: ScaffoldModel) -> Unit
) {
accountViewModel.scaffoldModel = scaffoldModel
accountViewModel.route = route
Row(
horizontalArrangement = Arrangement.Center,
@ -147,10 +147,10 @@ class AccountActivity : ComponentActivity() {
append(accountViewModel.name.formValue.value ?: "")
}
withStyle(style = MaterialTheme.typography.subtitle1.toSpanStyle()) {
append(accountViewModel.welcomeStart)
append(stringResource(id = R.string.welcome_start))
}
withStyle(style = MaterialTheme.typography.subtitle2.toSpanStyle()) {
append(accountViewModel.welcomeEnd)
append(stringResource(id = R.string.welcome_end))
}
withStyle(
style = MaterialTheme.typography.subtitle2.toSpanStyle()
@ -188,53 +188,39 @@ class AccountActivity : ComponentActivity() {
Column {
val isValidStudentId: Boolean by accountViewModel.isValidStudentId.observeAsState(false)
val isValidStudentId by accountViewModel.studentId.statusForm.observeAsState()
BaseTextField(
form = accountViewModel.studentId, keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
), isError = !isValidStudentId
), isError = isValidStudentId != FormStatus.Valid
)
if (isValidStudentId) {
if (checkRepeat) {
val isRepeat: Boolean? by accountViewModel.isRepeat.observeAsState(null)
Logger.i("isRepeat=$isRepeat")
when (isRepeat) {
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,
when (isValidStudentId) {
FormStatus.Empty,
FormStatus.FormatError -> Text(
text = stringResource(id = R.string.student_id_format),
color = MaterialTheme.colors.error,
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) {
override fun onChange(value: String) {
if (value.length > textLength) {
formError.value = "${formDesc}不能超过最大长度$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 =
application.getString(R.string.activity_size_error, 1, maxActivitySize)
// formError.value =
// application.getString(R.string.activity_size_error, 1, maxActivitySize)
} else {
_formValue.value = value
formError.value = ""
// formError.value = ""
}
}

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

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

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

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

@ -18,8 +18,22 @@ import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
enum class FormStatus {
//空
Empty,
//格式不匹配
FormatError,
//学号已存在
Repeat,
//校验通过
Valid
}
interface FormLength {
val formError: MutableLiveData<String>
val textLength: Int
}
abstract class FormName<T>(val formDesc: String) {
@ -38,7 +52,7 @@ abstract class FormName<T>(val formDesc: String) {
*
* @param formDesc
*/
open class StringForm(formDesc: String, val textLength: Int) :
open class StringForm(formDesc: String, override val textLength: Int) :
FormName<String>(formDesc = formDesc),
FormLength {
@ -49,19 +63,29 @@ open class StringForm(formDesc: String, val textLength: Int) :
_formValue.value = value
}
override val formError = MutableLiveData("")
override fun onChange(value: String) {
if (value.length > textLength) {
formError.value = "${formDesc}不能超过最大长度$textLength"
_formValue.postValue(value.slice(IntRange(0, textLength - 1)))
} else {
_formValue.postValue(value)
if (value.length <= textLength) {
_formValue.value = 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