留言区对接服务端接口

登录token更新结构
Dialog组件优化
master
pan 3 years ago
parent bfdd997ae8
commit 306d953e76
  1. 6
      foreground/src/main/java/com/gyf/csams/Api.kt
  2. 15
      foreground/src/main/java/com/gyf/csams/InitViewModel.kt
  3. 23
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  4. 44
      foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt
  5. 13
      foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt
  6. 18
      foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt
  7. 10
      foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt
  8. 154
      foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  9. 29
      foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  10. 115
      foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt
  11. 10
      foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt
  12. 30
      foreground/src/main/java/com/gyf/csams/util/ContextUtil.kt
  13. 9
      foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt
  14. 8
      lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt
  15. 4
      lib/src/main/java/com/gyf/lib/util/HttpUtil.kt

@ -30,7 +30,9 @@ enum class AccountApi(val path: String) : UrlPath {
} }
enum class MainApi(val path: String) : UrlPath { enum class MainApi(val path: String) : UrlPath {
HotActivity("/hotActivity"); HotActivity("/hotActivity"),
LeaveMessage("/leaveMessage"),
GetMessage("getMessage");
override fun build(): String { override fun build(): String {
return "/api/main/${this.path}" return "/api/main/${this.path}"
@ -51,4 +53,6 @@ class Api {
const val NOT_IMPL_TIP = "功能尚未实现!" const val NOT_IMPL_TIP = "功能尚未实现!"
const val UNKNOW_ERROR = "出现未知异常,请联系管理员"

@ -5,18 +5,13 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.util.AppDatabase import com.gyf.csams.util.*
import com.gyf.csams.util.SimpleCallback
import com.gyf.csams.util.Token
import com.gyf.csams.util.TokenManager
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
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
data class TokenVo(val token: String, val studentId: String)
class InitViewModel : ViewModel() { class InitViewModel : ViewModel() {
/** /**
@ -80,11 +75,9 @@ class InitViewModel : ViewModel() {
onFail = { TODO("token校验失败") }, onFail = { TODO("token校验失败") },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type type = object : TypeToken<ApiResponse<Boolean>>() {}.type
), ),
jsonBody = Gson().toJson( jsonParam = TokenVo(
TokenVo( token = currentToken.token,
token = currentToken.token, studentId = currentToken.studentId
studentId = currentToken.studentId
)
) )
) )
} else if (tokenList != null && tokenList.size > 1) { } else if (tokenList != null && tokenList.size > 1) {

@ -7,7 +7,6 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken 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
@ -16,8 +15,8 @@ import com.gyf.csams.account.ui.AccountRoute
import com.gyf.csams.main.ui.MainActivity import com.gyf.csams.main.ui.MainActivity
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.Token
import com.gyf.csams.util.TokenManager import com.gyf.csams.util.TokenManager
import com.gyf.csams.util.TokenResDto
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
@ -59,9 +58,6 @@ data class UserLoginVo(val studentId: String, val password: String, val device:
*/ */
data class DialogMessage(val message: String, val userResDto: UserResDto?) data class DialogMessage(val message: String, val userResDto: UserResDto?)
typealias Token = TokenResDto
/** /**
* 注册表单 * 注册表单
*/ */
@ -227,11 +223,9 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
onFail = onFail, onFail = onFail,
type = object : TypeToken<ApiResponse<UserResDto>>() {}.type type = object : TypeToken<ApiResponse<UserResDto>>() {}.type
), ),
jsonBody = Gson().toJson( jsonParam = UserVo(
UserVo( studentId = "${studentId.formValue.value}",
studentId = "${studentId.formValue.value}", name = "${name.formValue.value}"
name = "${name.formValue.value}"
)
) )
) )
resetForm() resetForm()
@ -269,12 +263,11 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
Logger.i(it.message) Logger.i(it.message)
callback(it.message) callback(it.message)
val context = getApplication<Application>().applicationContext val context = getApplication<Application>().applicationContext
val token = it.body?.token it.body?.let {
if (token != null) {
val db = AppDatabase.getInstance(context) val db = AppDatabase.getInstance(context)
viewModelScope.launch { viewModelScope.launch {
TokenManager.token = token TokenManager.token = it
db?.tokenDao()?.save(token = token) db?.tokenDao()?.save(token = it)
}.invokeOnCompletion { }.invokeOnCompletion {
context.startActivity(Intent(context, MainActivity::class.java)) context.startActivity(Intent(context, MainActivity::class.java))
_finishLogin.postValue(true) _finishLogin.postValue(true)
@ -284,7 +277,7 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
onFail = { callback(it) }, onFail = { callback(it) },
type = object : TypeToken<ApiResponse<Token>>() {}.type type = object : TypeToken<ApiResponse<Token>>() {}.type
), ),
param = UserLoginVo( jsonParam = UserLoginVo(
studentId = studentId, studentId = studentId,
password = password, password = password,
device = "${Build.MANUFACTURER} ${Build.MODEL}" device = "${Build.MANUFACTURER} ${Build.MODEL}"

@ -5,11 +5,11 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.AbstractComment
import com.gyf.csams.uikit.ActivityDetailMenu import com.gyf.csams.uikit.ActivityDetailMenu
import com.gyf.csams.uikit.SendInterface
import com.gyf.csams.uikit.TopMenuInterface import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.ScrollList 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.randomChinese
import com.gyf.lib.util.randomDateTime import com.gyf.lib.util.randomDateTime
import com.gyf.lib.util.randomNum import com.gyf.lib.util.randomNum
@ -244,42 +244,36 @@ class ActivityMemberViewModel : ScrollList<ActivityMemberVo>() {
data class BBSVo(val studentId: String, val name: String, val createTime: Date, val content: String) 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<BBSVo>(), SendInterface { class BBSViewModel : ScrollList<BBSVo>() {
override val initSize: Int = 10 override val initSize: Int = 10
val title = "发送评论" val title = "发送评论"
override val newContent = StringForm(formDesc = "评论内容", textLength = 80)
override val _openDialog = MutableLiveData<Boolean>()
override val openDialog: LiveData<Boolean> = _openDialog
init { init {
load() 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 加载评论 * TODO 加载评论
* *

@ -4,7 +4,6 @@ import android.app.Activity
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.view.inputmethod.InputMethodManager
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -21,6 +20,7 @@ import com.baidu.mapapi.search.sug.SuggestionSearchOption
import com.gyf.csams.MyLocationListener import com.gyf.csams.MyLocationListener
import com.gyf.csams.NOT_IMPL_TIP import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.util.ContextUtil
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.DATETIME_FORMAT import com.gyf.lib.util.DATETIME_FORMAT
@ -430,15 +430,6 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
private lateinit var marker: MarkerOptions 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 { fun createMapView(context: Context): MapView {
val mapView = MapView(context).apply { val mapView = MapView(context).apply {
@ -451,7 +442,7 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
map.setMapStatus(mapStatus(mCenter = mCenter)) map.setMapStatus(mapStatus(mCenter = mCenter))
map.setOnMapTouchListener { map.setOnMapTouchListener {
hideKeyBoard(context as Activity) ContextUtil.hideKeyBoard(context as Activity)
} }
map.setOnMarkerClickListener { map.setOnMarkerClickListener {

@ -365,7 +365,11 @@ class ActivityDetailActivity : ComponentActivity() {
* *
*/ */
@Composable @Composable
private fun BBS(model: BBSViewModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel()) { private fun BBS(
model: BBSViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel(),
bbsCommentModel: BBSCommentModel = viewModel()
) {
MainColumnFrame(background = { MainColumnFrame(background = {
Background( Background(
image = BackgroundImage.ActivityBBS, image = BackgroundImage.ActivityBBS,
@ -380,15 +384,7 @@ class ActivityDetailActivity : ComponentActivity() {
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// IconToggleButton(checked = false, onCheckedChange = { /*TODO*/ }) { IconButton(onClick = { bbsCommentModel.openDialog() }) {
// Row(horizontalArrangement = Arrangement.SpaceEvenly) {
// Icon(painter = painterResource(id = R.drawable.ic_configuration), contentDescription = null)
// }
// }
// Text(text = "")
IconButton(onClick = { model.openDialog() }) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_editor), painter = painterResource(id = R.drawable.ic_editor),
contentDescription = null contentDescription = null
@ -396,7 +392,7 @@ class ActivityDetailActivity : ComponentActivity() {
} }
} }
SendComment(model = model) SendComment(model = bbsCommentModel)
Box( Box(
modifier = Modifier modifier = Modifier

@ -21,6 +21,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType 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.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.csams.uikit.DescCard 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.BottomButton
import com.gyf.lib.util.format import com.gyf.lib.util.format
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -252,7 +256,7 @@ class ApplyActActivity : AppCompatActivity() {
sugResult: List<SuggestionResult.SuggestionInfo> sugResult: List<SuggestionResult.SuggestionInfo>
) { ) {
val state = rememberLazyListState() val state = rememberLazyListState()
val focusManager = LocalFocusManager.current
LazyColumn(modifier = modifier, state = state) { LazyColumn(modifier = modifier, state = state) {
sugResult.forEach { sugResult.forEach {
item { item {
@ -261,7 +265,7 @@ class ApplyActActivity : AppCompatActivity() {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = { .clickable(onClick = {
model.hideKeyBoard(this@ApplyActActivity) focusManager.clearFocus()
model.locateSuggestPoi(suggestInfo = it) model.locateSuggestPoi(suggestInfo = it)
}) })
) { ) {

@ -7,17 +7,19 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.AccountApi import com.gyf.csams.*
import com.gyf.csams.Api
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.account.ui.AccountActivity 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.AppDatabase
import com.gyf.csams.util.SimpleCallback import com.gyf.csams.util.SimpleCallback
import com.gyf.csams.util.Token
import com.gyf.csams.util.TokenManager import com.gyf.csams.util.TokenManager
import com.gyf.lib.ScrollList import com.gyf.lib.ScrollList
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.PersonInfoVo import com.gyf.lib.uikit.PersonInfoVo
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.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
@ -27,31 +29,112 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
data class LeaveMessageFormatVo(val message: String, val studentId: String)
/** /**
* 跑马灯 * 跑马灯
* *
*/ */
class MarqueeViewModel : ViewModel() { class MarqueeViewModel : AbstractComment() {
private val _marqueeTexts = MutableLiveData<List<LeaveMessageFormatVo>>()
val marqueeTexts = listOf("床前明月光", "疑是地上霜", "举头望明月", "低头思故乡") val marqueeTexts: LiveData<List<LeaveMessageFormatVo>> = _marqueeTexts
private val _marqueeIndex = MutableLiveData(0) private val _marqueeIndex = MutableLiveData(0)
var marqueeIndex: LiveData<Int> = _marqueeIndex var marqueeIndex: LiveData<Int> = _marqueeIndex
private var marqueeJob: Job? = null 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<Boolean>(
action = "发送留言", onSuccess = {
if (it.body == true) {
callback("留言发送成功")
newContent.clean()
_marqueeTexts.value?.apply {
val c = mutableListOf<LeaveMessageFormatVo>()
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<ApiResponse<Boolean>>() {}.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<List<LeaveMessageFormatVo>>(action = "获取留言", onSuccess = {
// callback(it.message)
Logger.i(it.message)
it.body?.let {
_marqueeTexts.postValue(it)
}
}, onFail = {
Logger.e(it)
callback("留言获取失败")
}, type = object : TypeToken<ApiResponse<List<LeaveMessageFormatVo>>>() {}.type),
jsonParam = OnlyToken()
)
}
}
fun addAsync(delayMillis: Int) { fun addAsync(delayMillis: Int) {
if (marqueeJob == null || marqueeJob?.isCompleted == true) { if (marqueeJob == null || marqueeJob?.isCompleted == true) {
marqueeJob = viewModelScope.launch { marqueeJob = viewModelScope.launch {
_marqueeIndex.postValue( _marqueeTexts.value?.apply {
if (_marqueeIndex.value == marqueeTexts.size - 1) 0 else _marqueeIndex.value?.plus( _marqueeIndex.postValue(
1 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 AssociationListVo(val name: String)
data class LeaveMessageVo(val message: String, val token: Token)
/** /**
* 主页 * 主页
* *
*/ */
class MainViewModel : ViewModel(), SendInterface { class MainViewModel : AbstractComment() {
override val _openDialog: MutableLiveData<Boolean> = MutableLiveData()
override val openDialog: LiveData<Boolean> = _openDialog
override val newContent: StringForm = StringForm(formDesc = "留言", textLength = 30) override val newContent: ValidStringForm =
object : ValidStringForm(formDesc = "留言", textLength = 20) {
override fun openDialog() { override fun check() {
_openDialog.value = true _formValue.value?.let {
} if (it.contains("\n")) _statusForm.value =
FormStatus.FormatError else _statusForm.value = FormStatus.Valid
override fun closeDialog() { }
_openDialog.value = false }
} }
/** /**
* TODO 发送留言
* *
* @param callback * @param callback
*/ */
override fun send(callback: (message: String) -> Unit) { 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<Boolean>(
action = "发送留言", onSuccess = {
callback("留言发送${if (it.body == true) "成功" else "失败"}")
}, onFail = {
callback("留言发送失败")
}, type = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = LeaveMessageVo(
message = newContent.formValue.value ?: "",
token = it
)
)
}
} else {
callback(UNKNOW_ERROR)
}
}
} }
} }
@ -245,7 +347,7 @@ class CenterViewModel : ViewModel() {
}, onFail = { callback("退出登陆失败") }, }, onFail = { callback("退出登陆失败") },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type type = object : TypeToken<ApiResponse<Boolean>>() {}.type
), ),
param = UserLogoutVo(studentId = studentId) jsonParam = UserLogoutVo(studentId = studentId)
) )
} }
} else { } else {

@ -30,7 +30,10 @@ import com.gyf.csams.R
import com.gyf.csams.activity.ui.ActivityDetailActivity import com.gyf.csams.activity.ui.ActivityDetailActivity
import com.gyf.csams.association.ui.AssociationActivity import com.gyf.csams.association.ui.AssociationActivity
import com.gyf.csams.association.ui.RegAssociationActivity import com.gyf.csams.association.ui.RegAssociationActivity
import com.gyf.csams.main.model.* import com.gyf.csams.main.model.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.message.ui.MessageActivity
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
@ -52,17 +55,19 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
TestBody { nav, s -> TestBody { nav, scaffoldState ->
NavHost(navController = nav, startDestination = MainMenu.Main.name) { NavHost(navController = nav, startDestination = MainMenu.Main.name) {
composable(MainMenu.Main.name) { composable(MainMenu.Main.name) {
Main(navController = nav) Main(navController = nav)
ShowSnackbar(scaffoldState = scaffoldState)
} }
composable(MainMenu.List.name) { composable(MainMenu.List.name) {
AssociationList(navController = nav) AssociationList(navController = nav)
ShowSnackbar(scaffoldState = scaffoldState)
} }
composable(MainMenu.Center.name) { composable(MainMenu.Center.name) {
Center(navController = nav) Center(navController = nav)
ShowSnackbar(scaffoldState = s) ShowSnackbar(scaffoldState = scaffoldState)
} }
} }
@ -384,33 +389,33 @@ class MainActivity : ComponentActivity() {
private fun MessageBoard( private fun MessageBoard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
model: MarqueeViewModel = viewModel(), model: MarqueeViewModel = viewModel(),
mainViewModel: MainViewModel = viewModel() scaffoldModel: ScaffoldModel = viewModel()
) { ) {
model.loadMessage {
scaffoldModel.update(message = it)
}
Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) { Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) {
Row( Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
IconButton(onClick = { mainViewModel.openDialog() }) { IconButton(onClick = { model.openDialog() }) {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_comments), painter = painterResource(id = R.drawable.ic_comments),
contentDescription = null, contentDescription = null,
) )
} }
SendComment(model = mainViewModel) SendComment(model = model)
Row( Row(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
) { ) {
Marquee(model = model) { model, value -> Marquee(model = model)
MarqueeText(
model = model,
offset = value
)
}
} }
} }

@ -6,6 +6,7 @@ import androidx.compose.animation.Crossfade
import androidx.compose.animation.animateColor import androidx.compose.animation.animateColor
import androidx.compose.animation.core.* import androidx.compose.animation.core.*
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
@ -14,24 +15,29 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState 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.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.gyf.csams.MainApplication import com.gyf.csams.MainApplication
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.MarqueeViewModel import com.gyf.csams.main.model.MarqueeViewModel
import com.gyf.lib.uikit.* 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<Boolean> protected val _openDialog: MutableLiveData<Boolean> = MutableLiveData()
val openDialog: LiveData<Boolean> val openDialog: LiveData<Boolean> = _openDialog
/** /**
* 编辑内容 * 编辑内容
*/ */
val newContent: StringForm abstract val newContent: ValidStringForm
/** /**
* 打开弹窗 * 打开弹窗
* *
*/ */
fun openDialog() fun openDialog() {
_openDialog.postValue(true)
}
/** /**
* 关闭弹窗 * 关闭弹窗
* *
*/ */
fun closeDialog() fun closeDialog() {
_openDialog.postValue(false)
}
/** /**
* 发送评论 * 发送评论
* *
* @param callback * @param callback
*/ */
fun send(callback: (message: String) -> Unit) abstract fun send(callback: (message: String) -> Unit)
} }
/** /**
@ -177,22 +191,27 @@ interface SendInterface {
* @param model * @param model
* @param scaffoldModel * @param scaffoldModel
*/ */
@Composable @Composable
fun <T : SendInterface> SendComment(model: T, scaffoldModel: ScaffoldModel = viewModel()) { fun <T : AbstractComment> SendComment(model: T, scaffoldModel: ScaffoldModel = viewModel()) {
val openDialog by model.openDialog.observeAsState() val openDialog by model.openDialog.observeAsState()
val status by model.newContent.statusForm.observeAsState()
if (openDialog == true) { if (openDialog == true) {
AlertDialog(onDismissRequest = { /*TODO*/ },
AlertDialog(
onDismissRequest = { /*TODO*/ },
buttons = { buttons = {
Row( Row(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.padding(10.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly horizontalArrangement = Arrangement.SpaceEvenly
) { ) {
OutlinedButton(onClick = { OutlinedButton(onClick = {
model.send { scaffoldModel.update(message = it) } model.send {
}) { model.closeDialog()
scaffoldModel.update(message = it)
}
}, enabled = status == FormStatus.Valid) {
Text(text = "发送") Text(text = "发送")
} }
OutlinedButton(onClick = { OutlinedButton(onClick = {
@ -204,13 +223,12 @@ fun <T : SendInterface> SendComment(model: T, scaffoldModel: ScaffoldModel = vie
}, text = { }, text = {
Column(modifier = Modifier.padding(10.dp)) { Column(modifier = Modifier.padding(10.dp)) {
Card( Card(
backgroundColor = MaterialTheme.colors.background, backgroundColor = MaterialTheme.colors.background
modifier = Modifier.padding(10.dp)
) { ) {
BaseTextField( BaseTextField(
form = model.newContent, modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth() form = model.newContent,
.height(200.dp) isError = status !== FormStatus.Valid
) )
} }
} }
@ -309,17 +327,18 @@ fun TextTopAppBar(
* 跑马灯 * 跑马灯
* *
* @param model * @param model
* @param content
*/ */
@Composable @Composable
fun Marquee( fun Marquee(
model: MarqueeViewModel = viewModel(), content: @Composable BoxScope.( model: MarqueeViewModel = viewModel()
model: MarqueeViewModel,
value: State<Float>
) -> Unit
) { ) {
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 delayMillis = 2000
val durationMillis = m.length * 300
Logger.d("durationMillis=${durationMillis}")
Column { Column {
BoxWithConstraints { BoxWithConstraints {
val transition = rememberInfiniteTransition() val transition = rememberInfiniteTransition()
@ -328,12 +347,15 @@ fun Marquee(
initialValue = 0F, initialValue = 0F,
targetValue = maxWidth.value, targetValue = maxWidth.value,
animationSpec = infiniteRepeatable( animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 3000, delayMillis = delayMillis), animation = tween(durationMillis = durationMillis, delayMillis = delayMillis),
repeatMode = RepeatMode.Restart repeatMode = RepeatMode.Restart
) )
) )
Box(modifier = Modifier.fillMaxWidth()) { Box(modifier = Modifier.fillMaxWidth()) {
content(model = model, value = offset) MarqueeText(
model = model,
offset = offset
)
} }
if (offset.value.toInt() == maxWidth.value.toInt()) { if (offset.value.toInt() == maxWidth.value.toInt()) {
@ -352,11 +374,9 @@ fun Marquee(
@Composable @Composable
fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State<Float>) { fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State<Float>) {
val poetryIndex: Int by model.marqueeIndex.observeAsState(0) val poetryIndex: Int by model.marqueeIndex.observeAsState(0)
val marqueeTexts by model.marqueeTexts.observeAsState(mutableListOf())
// Text(text = "$poetryIndex")
// Text(text = "offset.value=${offset.value}")
Text( 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), modifier = Modifier.offset(x = offset.value.dp),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@ -587,8 +607,29 @@ fun MyBottomAppBarPreview() {
@Preview @Preview
@Composable @Composable
fun TestPreview() { fun MainContent() {
CSAMSTheme { Column(
Text(text = "fuckyou") 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")
}
} }
} }

@ -9,6 +9,8 @@ import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.util.SimpleCallback 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.HttpClient
import com.gyf.lib.util.ImageUtil import com.gyf.lib.util.ImageUtil
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -17,6 +19,14 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch 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) : class ImageModel(application: Application, private val urlPath: String) :
AndroidViewModel(application) { AndroidViewModel(application) {
private val _image = MutableLiveData<ImageBitmap>() private val _image = MutableLiveData<ImageBitmap>()

@ -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)
}
}

@ -13,16 +13,19 @@ import androidx.room.*
data class Token( data class Token(
@PrimaryKey val studentId: String, @PrimaryKey val studentId: String,
@ColumnInfo val token: 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 * @property token
*/ */
data class TokenResDto(val isValid: Boolean, val token: Token?) data class TokenVo(val token: String, val studentId: String)
@Dao @Dao
interface TokenDao { interface TokenDao {

@ -65,6 +65,10 @@ open class StringForm(formDesc: String, val textLength: Int) :
} }
Logger.i("${formDesc}更新值:${_formValue.value}") Logger.i("${formDesc}更新值:${_formValue.value}")
} }
fun clean() {
_formValue.postValue("")
}
} }
open class ValidStringForm(formDesc: String, textLength: Int) : StringForm(formDesc, textLength) { 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
}
} }
/** /**

@ -105,8 +105,8 @@ object HttpClient {
call.enqueue(callback) call.enqueue(callback)
} }
fun post(url: String, callback: Callback, param: Any) { fun post(url: String, callback: Callback, jsonParam: Any) {
val jsonBody = json.toJson(param) val jsonBody = json.toJson(jsonParam)
Logger.json(jsonBody) Logger.json(jsonBody)
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)

Loading…
Cancel
Save