diff --git a/foreground/src/main/java/com/gyf/csams/Api.kt b/foreground/src/main/java/com/gyf/csams/Api.kt index 6eba528..a19c7ba 100644 --- a/foreground/src/main/java/com/gyf/csams/Api.kt +++ b/foreground/src/main/java/com/gyf/csams/Api.kt @@ -30,7 +30,9 @@ enum class AccountApi(val path: String) : UrlPath { } enum class MainApi(val path: String) : UrlPath { - HotActivity("/hotActivity"); + HotActivity("/hotActivity"), + LeaveMessage("/leaveMessage"), + GetMessage("getMessage"); override fun build(): String { return "/api/main/${this.path}" @@ -51,4 +53,6 @@ class Api { const val NOT_IMPL_TIP = "功能尚未实现!" +const val UNKNOW_ERROR = "出现未知异常,请联系管理员" + diff --git a/foreground/src/main/java/com/gyf/csams/InitViewModel.kt b/foreground/src/main/java/com/gyf/csams/InitViewModel.kt index 3ce1737..04942b7 100644 --- a/foreground/src/main/java/com/gyf/csams/InitViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/InitViewModel.kt @@ -5,18 +5,13 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import com.gyf.csams.util.AppDatabase -import com.gyf.csams.util.SimpleCallback -import com.gyf.csams.util.Token -import com.gyf.csams.util.TokenManager +import com.gyf.csams.util.* import com.gyf.lib.util.ApiResponse import com.gyf.lib.util.HttpClient import com.orhanobut.logger.Logger import kotlinx.coroutines.launch -data class TokenVo(val token: String, val studentId: String) class InitViewModel : ViewModel() { /** @@ -80,11 +75,9 @@ class InitViewModel : ViewModel() { onFail = { TODO("token校验失败") }, type = object : TypeToken>() {}.type ), - jsonBody = Gson().toJson( - TokenVo( - token = currentToken.token, - studentId = currentToken.studentId - ) + jsonParam = TokenVo( + token = currentToken.token, + studentId = currentToken.studentId ) ) } else if (tokenList != null && tokenList.size > 1) { diff --git a/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt b/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt index 70ea6e5..d1685e5 100644 --- a/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.gyf.csams.AccountApi import com.gyf.csams.Api @@ -16,8 +15,8 @@ import com.gyf.csams.account.ui.AccountRoute import com.gyf.csams.main.ui.MainActivity import com.gyf.csams.util.AppDatabase import com.gyf.csams.util.SimpleCallback +import com.gyf.csams.util.Token import com.gyf.csams.util.TokenManager -import com.gyf.csams.util.TokenResDto import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.ValidStringForm @@ -59,9 +58,6 @@ data class UserLoginVo(val studentId: String, val password: String, val device: */ data class DialogMessage(val message: String, val userResDto: UserResDto?) -typealias Token = TokenResDto - - /** * 注册表单 */ @@ -227,11 +223,9 @@ class AccountViewModel(application: Application) : AndroidViewModel(application) onFail = onFail, type = object : TypeToken>() {}.type ), - jsonBody = Gson().toJson( - UserVo( - studentId = "${studentId.formValue.value}", - name = "${name.formValue.value}" - ) + jsonParam = UserVo( + studentId = "${studentId.formValue.value}", + name = "${name.formValue.value}" ) ) resetForm() @@ -269,12 +263,11 @@ class AccountViewModel(application: Application) : AndroidViewModel(application) Logger.i(it.message) callback(it.message) val context = getApplication().applicationContext - val token = it.body?.token - if (token != null) { + it.body?.let { val db = AppDatabase.getInstance(context) viewModelScope.launch { - TokenManager.token = token - db?.tokenDao()?.save(token = token) + TokenManager.token = it + db?.tokenDao()?.save(token = it) }.invokeOnCompletion { context.startActivity(Intent(context, MainActivity::class.java)) _finishLogin.postValue(true) @@ -284,7 +277,7 @@ class AccountViewModel(application: Application) : AndroidViewModel(application) onFail = { callback(it) }, type = object : TypeToken>() {}.type ), - param = UserLoginVo( + jsonParam = UserLoginVo( studentId = studentId, password = password, device = "${Build.MANUFACTURER} ${Build.MODEL}" diff --git a/foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt b/foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt index c584410..7f5dc3d 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt @@ -5,11 +5,11 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.gyf.csams.NOT_IMPL_TIP +import com.gyf.csams.uikit.AbstractComment import com.gyf.csams.uikit.ActivityDetailMenu -import com.gyf.csams.uikit.SendInterface import com.gyf.csams.uikit.TopMenuInterface import com.gyf.lib.ScrollList -import com.gyf.lib.uikit.StringForm +import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomDateTime import com.gyf.lib.util.randomNum @@ -244,42 +244,36 @@ class ActivityMemberViewModel : ScrollList() { data class BBSVo(val studentId: String, val name: String, val createTime: Date, val content: String) +class T + + +class BBSCommentModel : AbstractComment() { + override val newContent = ValidStringForm(formDesc = "评论内容", textLength = 80) + + /** + * TODO 发送评论 + * + * @param callback + */ + override fun send(callback: (message: String) -> Unit) { + callback(NOT_IMPL_TIP) + } +} + /** * 交流区数据状态管理 * */ -class BBSViewModel : ScrollList(), SendInterface { +class BBSViewModel : ScrollList() { override val initSize: Int = 10 val title = "发送评论" - override val newContent = StringForm(formDesc = "评论内容", textLength = 80) - - override val _openDialog = MutableLiveData() - override val openDialog: LiveData = _openDialog init { load() } - - override fun openDialog() { - _openDialog.value = true - } - - override fun closeDialog() { - _openDialog.value = false - } - - /** - * TODO 发送评论 - * - * @param callback - */ - override fun send(callback: (message: String) -> Unit) { - callback(NOT_IMPL_TIP) - } - /** * TODO 加载评论 * diff --git a/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt b/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt index 186f0da..623ed72 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.app.Application import android.content.Context import android.graphics.BitmapFactory -import android.view.inputmethod.InputMethodManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -21,6 +20,7 @@ import com.baidu.mapapi.search.sug.SuggestionSearchOption import com.gyf.csams.MyLocationListener import com.gyf.csams.NOT_IMPL_TIP import com.gyf.csams.R +import com.gyf.csams.util.ContextUtil import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.StringForm import com.gyf.lib.util.DATETIME_FORMAT @@ -430,15 +430,6 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application private lateinit var marker: MarkerOptions - fun hideKeyBoard(activity: Activity?) { - Logger.i("隐藏软键盘") - activity?.apply { - val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager - window.peekDecorView()?.let { - imm.hideSoftInputFromWindow(it.windowToken, 0) - } - } - } fun createMapView(context: Context): MapView { val mapView = MapView(context).apply { @@ -451,7 +442,7 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application map.setMapStatus(mapStatus(mCenter = mCenter)) map.setOnMapTouchListener { - hideKeyBoard(context as Activity) + ContextUtil.hideKeyBoard(context as Activity) } map.setOnMarkerClickListener { diff --git a/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt b/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt index 8320622..a6ea995 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt @@ -365,7 +365,11 @@ class ActivityDetailActivity : ComponentActivity() { * */ @Composable - private fun BBS(model: BBSViewModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel()) { + private fun BBS( + model: BBSViewModel = viewModel(), + scaffoldModel: ScaffoldModel = viewModel(), + bbsCommentModel: BBSCommentModel = viewModel() + ) { MainColumnFrame(background = { Background( image = BackgroundImage.ActivityBBS, @@ -380,15 +384,7 @@ class ActivityDetailActivity : ComponentActivity() { horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically ) { - // IconToggleButton(checked = false, onCheckedChange = { /*TODO*/ }) { - // Row(horizontalArrangement = Arrangement.SpaceEvenly) { - // Icon(painter = painterResource(id = R.drawable.ic_configuration), contentDescription = null) - // } - // } - // Text(text = "") - - - IconButton(onClick = { model.openDialog() }) { + IconButton(onClick = { bbsCommentModel.openDialog() }) { Icon( painter = painterResource(id = R.drawable.ic_editor), contentDescription = null @@ -396,7 +392,7 @@ class ActivityDetailActivity : ComponentActivity() { } } - SendComment(model = model) + SendComment(model = bbsCommentModel) Box( modifier = Modifier diff --git a/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt b/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt index ac47543..cc929b4 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType @@ -40,7 +41,10 @@ import com.gyf.csams.activity.model.ApplyActViewModel import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.DescCard -import com.gyf.lib.uikit.* +import com.gyf.lib.uikit.BaseTextField +import com.gyf.lib.uikit.BodyS +import com.gyf.lib.uikit.MainColumnFrame +import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.util.BottomButton import com.gyf.lib.util.format import com.orhanobut.logger.Logger @@ -252,7 +256,7 @@ class ApplyActActivity : AppCompatActivity() { sugResult: List ) { val state = rememberLazyListState() - + val focusManager = LocalFocusManager.current LazyColumn(modifier = modifier, state = state) { sugResult.forEach { item { @@ -261,7 +265,7 @@ class ApplyActActivity : AppCompatActivity() { modifier = Modifier .fillMaxWidth() .clickable(onClick = { - model.hideKeyBoard(this@ApplyActActivity) + focusManager.clearFocus() model.locateSuggestPoi(suggestInfo = it) }) ) { diff --git a/foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt b/foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt index 735f2bd..58ab4ac 100644 --- a/foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt @@ -7,17 +7,19 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.gson.reflect.TypeToken -import com.gyf.csams.AccountApi -import com.gyf.csams.Api -import com.gyf.csams.NOT_IMPL_TIP +import com.gyf.csams.* import com.gyf.csams.account.ui.AccountActivity -import com.gyf.csams.uikit.SendInterface +import com.gyf.csams.uikit.AbstractComment +import com.gyf.csams.uikit.OnlyToken import com.gyf.csams.util.AppDatabase import com.gyf.csams.util.SimpleCallback +import com.gyf.csams.util.Token import com.gyf.csams.util.TokenManager import com.gyf.lib.ScrollList +import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.PersonInfoVo 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.gyf.lib.util.randomChinese @@ -27,31 +29,112 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +data class LeaveMessageFormatVo(val message: String, val studentId: String) + /** * 跑马灯 * */ -class MarqueeViewModel : ViewModel() { +class MarqueeViewModel : AbstractComment() { + + private val _marqueeTexts = MutableLiveData>() - val marqueeTexts = listOf("床前明月光", "疑是地上霜", "举头望明月", "低头思故乡") + val marqueeTexts: LiveData> = _marqueeTexts private val _marqueeIndex = MutableLiveData(0) var marqueeIndex: LiveData = _marqueeIndex private var marqueeJob: Job? = null + override val newContent: ValidStringForm = + object : ValidStringForm(formDesc = "留言", textLength = 20) { + override fun check() { + _formValue.value?.let { + if (it.contains("\n")) _statusForm.value = + FormStatus.FormatError else _statusForm.value = FormStatus.Valid + } + } + } + + /** + * + * @param callback + */ + override fun send(callback: (message: String) -> Unit) { + TokenManager.token.let { + if (it != null) { + viewModelScope.launch { + HttpClient.post(url = Api.buildUrl(MainApi.LeaveMessage), + SimpleCallback( + action = "发送留言", onSuccess = { + if (it.body == true) { + callback("留言发送成功") + newContent.clean() + _marqueeTexts.value?.apply { + val c = mutableListOf() + c.add( + LeaveMessageFormatVo( + message = "${TokenManager.token?.name}说:${newContent.formValue.value}", + studentId = "${TokenManager.token?.studentId}" + ) + ) + c.addAll(this) + _marqueeTexts.postValue(c) + } + } else { + callback("留言发送失败") + } + }, onFail = { + callback("留言发送失败") + }, type = object : TypeToken>() {}.type + ), + jsonParam = LeaveMessageVo( + message = newContent.formValue.value ?: "", + token = it + ) + ) + } + } else { + callback(UNKNOW_ERROR) + } + } + } + + + fun loadMessage(callback: (message: String) -> Unit) { + viewModelScope.launch { + HttpClient.post( + Api.buildUrl(MainApi.GetMessage), + SimpleCallback>(action = "获取留言", onSuccess = { +// callback(it.message) + Logger.i(it.message) + it.body?.let { + _marqueeTexts.postValue(it) + } + }, onFail = { + Logger.e(it) + callback("留言获取失败") + }, type = object : TypeToken>>() {}.type), + jsonParam = OnlyToken() + ) + } + } + fun addAsync(delayMillis: Int) { if (marqueeJob == null || marqueeJob?.isCompleted == true) { marqueeJob = viewModelScope.launch { - _marqueeIndex.postValue( - if (_marqueeIndex.value == marqueeTexts.size - 1) 0 else _marqueeIndex.value?.plus( - 1 + _marqueeTexts.value?.apply { + _marqueeIndex.postValue( + if (_marqueeIndex.value == size - 1) 0 else _marqueeIndex.value?.plus( + 1 + ) ) - ) - delay(timeMillis = delayMillis.toLong()) + delay(timeMillis = delayMillis.toLong()) + } } } } + } /** @@ -61,32 +144,51 @@ class MarqueeViewModel : ViewModel() { */ data class AssociationListVo(val name: String) +data class LeaveMessageVo(val message: String, val token: Token) + /** * 主页 * */ -class MainViewModel : ViewModel(), SendInterface { +class MainViewModel : AbstractComment() { - override val _openDialog: MutableLiveData = MutableLiveData() - override val openDialog: LiveData = _openDialog - override val newContent: StringForm = StringForm(formDesc = "留言", textLength = 30) - - override fun openDialog() { - _openDialog.value = true - } - - override fun closeDialog() { - _openDialog.value = false - } + override val newContent: ValidStringForm = + object : ValidStringForm(formDesc = "留言", textLength = 20) { + override fun check() { + _formValue.value?.let { + if (it.contains("\n")) _statusForm.value = + FormStatus.FormatError else _statusForm.value = FormStatus.Valid + } + } + } /** - * TODO 发送留言 * * @param callback */ override fun send(callback: (message: String) -> Unit) { - callback(NOT_IMPL_TIP) + TokenManager.token.let { + if (it != null) { + viewModelScope.launch { + HttpClient.post(url = Api.buildUrl(MainApi.LeaveMessage), + SimpleCallback( + action = "发送留言", onSuccess = { + callback("留言发送${if (it.body == true) "成功" else "失败"}") + }, onFail = { + callback("留言发送失败") + }, type = object : TypeToken>() {}.type + ), + jsonParam = LeaveMessageVo( + message = newContent.formValue.value ?: "", + token = it + ) + ) + } + } else { + callback(UNKNOW_ERROR) + } + } } } @@ -245,7 +347,7 @@ class CenterViewModel : ViewModel() { }, onFail = { callback("退出登陆失败") }, type = object : TypeToken>() {}.type ), - param = UserLogoutVo(studentId = studentId) + jsonParam = UserLogoutVo(studentId = studentId) ) } } else { diff --git a/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt b/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt index 2a9554d..1acb91e 100644 --- a/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt @@ -30,7 +30,10 @@ import com.gyf.csams.R import com.gyf.csams.activity.ui.ActivityDetailActivity import com.gyf.csams.association.ui.AssociationActivity import com.gyf.csams.association.ui.RegAssociationActivity -import com.gyf.csams.main.model.* +import com.gyf.csams.main.model.AssociationListVo +import com.gyf.csams.main.model.CenterViewModel +import com.gyf.csams.main.model.ListViewModel +import com.gyf.csams.main.model.MarqueeViewModel import com.gyf.csams.message.ui.MessageActivity import com.gyf.csams.uikit.* import com.gyf.lib.uikit.* @@ -52,17 +55,19 @@ class MainActivity : ComponentActivity() { setContent { - TestBody { nav, s -> + TestBody { nav, scaffoldState -> NavHost(navController = nav, startDestination = MainMenu.Main.name) { composable(MainMenu.Main.name) { Main(navController = nav) + ShowSnackbar(scaffoldState = scaffoldState) } composable(MainMenu.List.name) { AssociationList(navController = nav) + ShowSnackbar(scaffoldState = scaffoldState) } composable(MainMenu.Center.name) { Center(navController = nav) - ShowSnackbar(scaffoldState = s) + ShowSnackbar(scaffoldState = scaffoldState) } } @@ -384,33 +389,33 @@ class MainActivity : ComponentActivity() { private fun MessageBoard( modifier: Modifier = Modifier, model: MarqueeViewModel = viewModel(), - mainViewModel: MainViewModel = viewModel() + scaffoldModel: ScaffoldModel = viewModel() ) { + + model.loadMessage { + scaffoldModel.update(message = it) + } + Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) { Row( verticalAlignment = Alignment.CenterVertically ) { - IconButton(onClick = { mainViewModel.openDialog() }) { + IconButton(onClick = { model.openDialog() }) { Icon( painter = painterResource(id = R.drawable.ic_comments), contentDescription = null, ) } - SendComment(model = mainViewModel) + SendComment(model = model) Row( horizontalArrangement = Arrangement.Center, modifier = Modifier .fillMaxWidth() ) { - Marquee(model = model) { model, value -> - MarqueeText( - model = model, - offset = value - ) - } + Marquee(model = model) } } diff --git a/foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt b/foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt index f2895fe..298ec99 100644 --- a/foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt +++ b/foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt @@ -6,6 +6,7 @@ import androidx.compose.animation.Crossfade import androidx.compose.animation.animateColor import androidx.compose.animation.core.* import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -14,24 +15,29 @@ import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import com.gyf.csams.MainApplication import com.gyf.csams.R import com.gyf.csams.main.model.MarqueeViewModel import com.gyf.lib.uikit.* -import com.gyf.lib.uikit.theme.CSAMSTheme +import com.orhanobut.logger.Logger /** * 淡入淡出并且颜色变化文本 @@ -137,37 +143,45 @@ enum class ActivityDetailMenu(override val menuName: String) : TopBarMenu { } } - -interface SendInterface { +/** + * 评论数据状态 + * + */ +abstract class AbstractComment : ViewModel() { /** * 弹窗状态 */ - val _openDialog: MutableLiveData - val openDialog: LiveData + protected val _openDialog: MutableLiveData = MutableLiveData() + val openDialog: LiveData = _openDialog /** * 编辑内容 */ - val newContent: StringForm + abstract val newContent: ValidStringForm + /** * 打开弹窗 * */ - fun openDialog() + fun openDialog() { + _openDialog.postValue(true) + } /** * 关闭弹窗 * */ - fun closeDialog() + fun closeDialog() { + _openDialog.postValue(false) + } /** * 发送评论 * * @param callback */ - fun send(callback: (message: String) -> Unit) + abstract fun send(callback: (message: String) -> Unit) } /** @@ -177,22 +191,27 @@ interface SendInterface { * @param model * @param scaffoldModel */ + @Composable -fun SendComment(model: T, scaffoldModel: ScaffoldModel = viewModel()) { +fun SendComment(model: T, scaffoldModel: ScaffoldModel = viewModel()) { val openDialog by model.openDialog.observeAsState() + + val status by model.newContent.statusForm.observeAsState() if (openDialog == true) { - AlertDialog(onDismissRequest = { /*TODO*/ }, + + AlertDialog( + onDismissRequest = { /*TODO*/ }, buttons = { Row( - modifier = Modifier - .padding(10.dp) - .fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { OutlinedButton(onClick = { - model.send { scaffoldModel.update(message = it) } - }) { - + model.send { + model.closeDialog() + scaffoldModel.update(message = it) + } + }, enabled = status == FormStatus.Valid) { Text(text = "发送") } OutlinedButton(onClick = { @@ -204,13 +223,12 @@ fun SendComment(model: T, scaffoldModel: ScaffoldModel = vie }, text = { Column(modifier = Modifier.padding(10.dp)) { Card( - backgroundColor = MaterialTheme.colors.background, - modifier = Modifier.padding(10.dp) + backgroundColor = MaterialTheme.colors.background ) { BaseTextField( - form = model.newContent, modifier = Modifier - .fillMaxWidth() - .height(200.dp) + modifier = Modifier.fillMaxWidth(), + form = model.newContent, + isError = status !== FormStatus.Valid ) } } @@ -309,17 +327,18 @@ fun TextTopAppBar( * 跑马灯 * * @param model - * @param content */ @Composable fun Marquee( - model: MarqueeViewModel = viewModel(), content: @Composable BoxScope.( - model: MarqueeViewModel, - value: State - ) -> Unit + model: MarqueeViewModel = viewModel() ) { - + val poetryIndex: Int by model.marqueeIndex.observeAsState(0) + val marqueeTexts by model.marqueeTexts.observeAsState(mutableListOf()) + val m = + if (marqueeTexts.isEmpty()) "欢迎大家踊跃留言" else marqueeTexts[poetryIndex % marqueeTexts.size].message val delayMillis = 2000 + val durationMillis = m.length * 300 + Logger.d("durationMillis=${durationMillis}") Column { BoxWithConstraints { val transition = rememberInfiniteTransition() @@ -328,12 +347,15 @@ fun Marquee( initialValue = 0F, targetValue = maxWidth.value, animationSpec = infiniteRepeatable( - animation = tween(durationMillis = 3000, delayMillis = delayMillis), + animation = tween(durationMillis = durationMillis, delayMillis = delayMillis), repeatMode = RepeatMode.Restart ) ) Box(modifier = Modifier.fillMaxWidth()) { - content(model = model, value = offset) + MarqueeText( + model = model, + offset = offset + ) } if (offset.value.toInt() == maxWidth.value.toInt()) { @@ -352,11 +374,9 @@ fun Marquee( @Composable fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State) { val poetryIndex: Int by model.marqueeIndex.observeAsState(0) - -// Text(text = "$poetryIndex") -// Text(text = "offset.value=${offset.value}") + val marqueeTexts by model.marqueeTexts.observeAsState(mutableListOf()) Text( - text = model.marqueeTexts[poetryIndex % model.marqueeTexts.size], + text = if (marqueeTexts.isEmpty()) "欢迎大家踊跃留言" else marqueeTexts[poetryIndex % marqueeTexts.size].message, modifier = Modifier.offset(x = offset.value.dp), maxLines = 1, overflow = TextOverflow.Ellipsis @@ -587,8 +607,29 @@ fun MyBottomAppBarPreview() { @Preview @Composable -fun TestPreview() { - CSAMSTheme { - Text(text = "fuckyou") +fun MainContent() { + Column( + Modifier + .background(Color(0xFFEDEAE0)) + .fillMaxSize() + .padding(32.dp), + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + var textState by remember { mutableStateOf(TextFieldValue()) } + val localFocusManager = LocalFocusManager.current + val focusRequester = FocusRequester() + + BaseTextField( + modifier = Modifier + .focusRequester(focusRequester) + .fillMaxWidth(), form = StringForm(formDesc = "", textLength = 5) + ) + + + Button(onClick = { + localFocusManager.clearFocus() + }) { + Text(text = "Clear Focus") + } } } \ No newline at end of file diff --git a/foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt b/foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt index ac95403..09f8b23 100644 --- a/foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt @@ -9,6 +9,8 @@ import androidx.lifecycle.viewModelScope import com.google.gson.reflect.TypeToken import com.gyf.csams.R import com.gyf.csams.util.SimpleCallback +import com.gyf.csams.util.Token +import com.gyf.csams.util.TokenManager import com.gyf.lib.util.HttpClient import com.gyf.lib.util.ImageUtil import com.orhanobut.logger.Logger @@ -17,6 +19,14 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch +sealed class BaseVo { + abstract val token: Token +} + +data class OnlyToken( + override val token: Token = TokenManager.token ?: throw IllegalArgumentException("无法获取token") +) : BaseVo() + class ImageModel(application: Application, private val urlPath: String) : AndroidViewModel(application) { private val _image = MutableLiveData() diff --git a/foreground/src/main/java/com/gyf/csams/util/ContextUtil.kt b/foreground/src/main/java/com/gyf/csams/util/ContextUtil.kt new file mode 100644 index 0000000..5a2c59e --- /dev/null +++ b/foreground/src/main/java/com/gyf/csams/util/ContextUtil.kt @@ -0,0 +1,30 @@ +package com.gyf.csams.util + +import android.app.Activity +import android.view.View +import android.view.inputmethod.InputMethodManager +import com.orhanobut.logger.Logger + +object ContextUtil { + fun hideKeyBoard(activity: Activity?) { + Logger.i("隐藏软键盘") + activity?.apply { + val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + window.peekDecorView()?.let { + imm.hideSoftInputFromWindow(it.windowToken, 0) + } + } + } + + fun testHideKeyBoard(activity: Activity) { + Logger.i("隐藏软键盘") + val imm = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + //Find the currently focused view, so we can grab the correct window token from it. + var view: View? = activity.currentFocus + //If no view currently has focus, create a new one, just so we can grab a window token from it + if (view == null) { + view = View(activity) + } + imm.hideSoftInputFromWindow(view.windowToken, 0) + } +} \ No newline at end of file diff --git a/foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt b/foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt index 42432a9..775dee9 100644 --- a/foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt +++ b/foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt @@ -13,16 +13,19 @@ import androidx.room.* data class Token( @PrimaryKey val studentId: String, @ColumnInfo val token: String, - @ColumnInfo val createTime: Long + @ColumnInfo val createTime: Long, + @ColumnInfo val name: String ) + +//data class TokenVo(val token:String,val studentId:String) + /** * 令牌传输 * - * @property isValid * @property token */ -data class TokenResDto(val isValid: Boolean, val token: Token?) +data class TokenVo(val token: String, val studentId: String) @Dao interface TokenDao { diff --git a/lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt b/lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt index 0f8af8b..2c93c38 100644 --- a/lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt +++ b/lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt @@ -65,6 +65,10 @@ open class StringForm(formDesc: String, val textLength: Int) : } Logger.i("${formDesc}更新值:${_formValue.value}") } + + fun clean() { + _formValue.postValue("") + } } open class ValidStringForm(formDesc: String, textLength: Int) : StringForm(formDesc, textLength) { @@ -79,7 +83,9 @@ open class ValidStringForm(formDesc: String, textLength: Int) : StringForm(formD } } - protected open fun check() {} + protected open fun check() { + _statusForm.value = FormStatus.Valid + } } /** diff --git a/lib/src/main/java/com/gyf/lib/util/HttpUtil.kt b/lib/src/main/java/com/gyf/lib/util/HttpUtil.kt index e9980d9..3b7c848 100644 --- a/lib/src/main/java/com/gyf/lib/util/HttpUtil.kt +++ b/lib/src/main/java/com/gyf/lib/util/HttpUtil.kt @@ -105,8 +105,8 @@ object HttpClient { call.enqueue(callback) } - fun post(url: String, callback: Callback, param: Any) { - val jsonBody = json.toJson(param) + fun post(url: String, callback: Callback, jsonParam: Any) { + val jsonBody = json.toJson(jsonParam) Logger.json(jsonBody) val request = Request.Builder() .url(url)