格式化代码

增加应用通知
master
pan 4 years ago
parent 589f19b69d
commit 6f85668740
  1. 34
      app/src/main/AndroidManifest.xml
  2. 22
      app/src/main/java/com/gyf/csams/APP.kt
  3. 12
      app/src/main/java/com/gyf/csams/Api.kt
  4. 16
      app/src/main/java/com/gyf/csams/InitActivity.kt
  5. 41
      app/src/main/java/com/gyf/csams/InitViewModel.kt
  6. 238
      app/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  7. 186
      app/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
  8. 155
      app/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt
  9. 58
      app/src/main/java/com/gyf/csams/association/model/AssociationViewModel.kt
  10. 120
      app/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
  11. 28
      app/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  12. 16
      app/src/main/java/com/gyf/csams/association/model/RenameViewModel.kt
  13. 295
      app/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
  14. 91
      app/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
  15. 39
      app/src/main/java/com/gyf/csams/association/ui/ReNameActivity.kt
  16. 89
      app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  17. 113
      app/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  18. 67
      app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  19. 11
      app/src/main/java/com/gyf/csams/message/model/MessageViewModel.kt
  20. 136
      app/src/main/java/com/gyf/csams/message/model/SysMessageViewModel.kt
  21. 81
      app/src/main/java/com/gyf/csams/message/ui/MessageActivity.kt
  22. 135
      app/src/main/java/com/gyf/csams/message/ui/SysMessageActivity.kt
  23. 150
      app/src/main/java/com/gyf/csams/uikit/BaseView.kt
  24. 69
      app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
  25. 81
      app/src/main/java/com/gyf/csams/util/GsonUtil.kt
  26. 66
      app/src/main/java/com/gyf/csams/util/HttpUtil.kt
  27. 28
      app/src/main/java/com/gyf/csams/util/RandomUtil.kt
  28. 10
      app/src/main/java/com/gyf/csams/util/TokenUtil.kt
  29. 9
      app/src/main/res/drawable/ic_notice.xml

@ -49,24 +49,34 @@
</activity>
<!--社团主界面-->
<activity android:name=".association.ui.AssociationActivity"
android:exported="true">
</activity>
<activity
android:name=".association.ui.AssociationActivity"
android:exported="true"></activity>
<!--社团重命名主界面-->
<activity android:name=".association.ui.ReNameActivity"
android:exported="true">
</activity>
<activity
android:name=".association.ui.ReNameActivity"
android:exported="true"></activity>
<!--题库界面-->
<activity android:name=".association.ui.ExamActivity"
android:exported="true">
</activity>
<activity
android:name=".association.ui.ExamActivity"
android:exported="true" />
<!--活动详情-->
<activity android:name=".activity.ui.ActivityDetailActivity"
android:exported="true">
</activity>
<activity
android:name=".activity.ui.ActivityDetailActivity"
android:exported="true" />
<!--通知-->
<activity
android:name=".message.ui.MessageActivity"
android:exported="true" />
<!--系统通知-->
<activity
android:name=".message.ui.SysMessageActivity"
android:exported="true" />
</application>
</manifest>

@ -15,24 +15,30 @@ import com.orhanobut.logger.Logger
class APP : Application() {
private lateinit var memoryCache: LruCache<BackgroundImage, Bitmap>
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
// Use 1/8th of the available memory for this memory cache.
val cacheSize = maxMemory / 8
fun getImage(image: BackgroundImage, reqWidth: Int, reqHeight: Int): ImageBitmap {
val bitmap=memoryCache.get(image)
return if(bitmap==null){
val bitmap = memoryCache.get(image)
return if (bitmap == null) {
Logger.i("reqWidth=$reqWidth,reqHeight=$reqHeight")
val cacheValue= decodeSampledBitmapFromResource(res = resources,image.id,reqWidth=reqWidth,reqHeight=reqHeight)
val cacheValue = decodeSampledBitmapFromResource(
res = resources,
image.id,
reqWidth = reqWidth,
reqHeight = reqHeight
)
memoryCache.put(image, cacheValue)
Logger.i("添加缓存:${image}")
cacheValue.asImageBitmap()
}else{
} else {
Logger.i("从缓存读取:${image}")
bitmap.asImageBitmap()
}
@ -59,7 +65,11 @@ class APP : Application() {
}
}
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
private fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int
): Int {
// Raw height and width of image
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1

@ -1,8 +1,8 @@
package com.gyf.csams
interface UrlPath{
fun build():String
interface UrlPath {
fun build(): String
}
/**
@ -10,7 +10,7 @@ interface UrlPath{
*
* @property path
*/
enum class AccountApi(val path: String):UrlPath{
enum class AccountApi(val path: String) : UrlPath {
register("/register"),
checkId("/register/checkId"),
login("/login"),
@ -27,13 +27,13 @@ enum class AccountApi(val path: String):UrlPath{
*
*/
class Api {
companion object{
fun buildUrl(urlPath: UrlPath):String{
companion object {
fun buildUrl(urlPath: UrlPath): String {
return "${BuildConfig.SERVER_ADDRESS}${urlPath.build()}"
}
}
}
const val NOT_IMPL_TIP="功能尚未实现!"
const val NOT_IMPL_TIP = "功能尚未实现!"

@ -22,16 +22,16 @@ class InitActivity : ComponentActivity() {
// 检查网络
setContent {
val initViewModel:InitViewModel= viewModel()
val initViewModel: InitViewModel = viewModel()
initViewModel.checkServer()
val isNetWorkWorking:Boolean? by initViewModel.isNetWorkWorking.observeAsState(null)
when(isNetWorkWorking){
null-> AnimationText(text = "检查服务器网络状态中!!!")
true-> {
val isNetWorkWorking: Boolean? by initViewModel.isNetWorkWorking.observeAsState(null)
when (isNetWorkWorking) {
null -> AnimationText(text = "检查服务器网络状态中!!!")
true -> {
Init()
finish()
}
false->{
false -> {
TODO("无法连接到服务器,请检查本地网络或联系管理员")
}
}
@ -40,9 +40,9 @@ class InitActivity : ComponentActivity() {
@Composable
private fun Init(initViewModel:InitViewModel= viewModel()){
private fun Init(initViewModel: InitViewModel = viewModel()) {
Logger.i("初始化。。。。")
val context= LocalContext.current
val context = LocalContext.current
//后台检查token
initViewModel.hasOnlyUserToken(context)
//监听token校验状态

@ -11,9 +11,9 @@ import com.gyf.csams.util.*
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
data class TokenVo(val token:String,val studentId:String)
data class TokenVo(val token: String, val studentId: String)
class InitViewModel: ViewModel() {
class InitViewModel : ViewModel() {
/**
* 服务器网络状态是否正常true=正常false=不正常
*/
@ -27,7 +27,7 @@ class InitViewModel: ViewModel() {
val token: LiveData<Boolean> = _token
fun checkServer(){
fun checkServer() {
Logger.i("测试连接到服务端")
_isNetWorkWorking.postValue(true)
}
@ -35,29 +35,38 @@ class InitViewModel: ViewModel() {
/**
* 查询本地是否有且只有一个用户token如果有则自动登录
*/
fun hasOnlyUserToken(context: Context){
viewModelScope.launch{
val db=AppDatabase.getInstance(context)
val tokenList=db?.tokenDao()?.queryAll()
fun hasOnlyUserToken(context: Context) {
viewModelScope.launch {
val db = AppDatabase.getInstance(context)
val tokenList = db?.tokenDao()?.queryAll()
if (tokenList != null && tokenList.size == 1) {
val currentToken: Token = tokenList[0]
val url=Api.buildUrl(AccountApi.loginToken)
val action="校验token"
val url = Api.buildUrl(AccountApi.loginToken)
val action = "校验token"
Logger.i("${action}api=$url")
HttpClient.post(url,SimpleCallback<Boolean>(
action=action,
HttpClient.post(
url,
SimpleCallback<Boolean>(
action = action,
onSuccess = {
_token.postValue(it.body)
Logger.i("token校验结果:${it.body}")
},
onFail = { TODO("token校验失败")},
type = object : TypeToken<ApiResponse<Boolean>>(){}.type
),jsonBody = Gson().toJson(TokenVo(token=currentToken.token,studentId = currentToken.studentId)))
}else if(tokenList != null && tokenList.size > 1){
onFail = { TODO("token校验失败") },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonBody = Gson().toJson(
TokenVo(
token = currentToken.token,
studentId = currentToken.studentId
)
)
)
} else if (tokenList != null && tokenList.size > 1) {
//TODO 实现切换历史登录帐号
Logger.i("token数量大于一,需要手动登录")
_token.postValue(false)
}else{
} else {
Logger.i("本地没有任何token,跳转到登录界面")
_token.postValue(false)
}

@ -25,7 +25,7 @@ import kotlinx.serialization.Serializable
* @property password
*/
@Serializable
data class UserResDto(val password:String)
data class UserResDto(val password: String)
/**
* 构造登录注册信息实体表单
@ -33,7 +33,7 @@ data class UserResDto(val password:String)
* @property studentId 学号
* @property name 姓名
*/
data class UserVo(val studentId:String,val name:String)
data class UserVo(val studentId: String, val name: String)
/**
* 用户登陆表单
@ -42,7 +42,7 @@ data class UserVo(val studentId:String,val name:String)
* @property password 密码
* @property device 设备型号
*/
data class UserLoginVo(val studentId: String,val password: String,val device: String)
data class UserLoginVo(val studentId: String, val password: String, val device: String)
/**
* 密码弹窗信息
@ -50,9 +50,9 @@ data class UserLoginVo(val studentId: String,val password: String,val device: St
* @property message
* @property userResDto
*/
data class DialogMessage(val message:String,val userResDto: UserResDto?)
data class DialogMessage(val message: String, val userResDto: UserResDto?)
typealias Token= TokenResDto
typealias Token = TokenResDto
/**
* 注册表单
@ -61,90 +61,97 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
//欢迎信息
val welcomeStart="同学您好\n"
val welcomeEnd="欢迎使用"
val welcomeStart = "同学您好\n"
val welcomeEnd = "欢迎使用"
//学号
private val _studentId=MutableLiveData<String>()
val studentId:LiveData<String> = _studentId
private val _isValidStudentId=MutableLiveData<Boolean>()
val isValidStudentId:LiveData<Boolean> = _isValidStudentId
val studentIdDesc="学号"
val studentIdPlaceholder="请输入$studentIdDesc"
val studentIdFormat="入学年份(四位)+班级代码(两位)+学生代码(两位)"
private val _studentId = MutableLiveData<String>()
val studentId: LiveData<String> = _studentId
private val _isValidStudentId = MutableLiveData<Boolean>()
val isValidStudentId: LiveData<Boolean> = _isValidStudentId
val studentIdDesc = "学号"
val studentIdPlaceholder = "请输入$studentIdDesc"
val studentIdFormat = "入学年份(四位)+班级代码(两位)+学生代码(两位)"
//学号已存在
private val _isRepeat=MutableLiveData<Boolean?>()
private val _isRepeat = MutableLiveData<Boolean?>()
val regBtnDesc="注册"
val regBtnDesc = "注册"
//已注册
val registered="$regBtnDesc"
val registered = "$regBtnDesc"
//可注册
val canRegister="$regBtnDesc"
val canRegister = "$regBtnDesc"
//提示信息
val checkRegTip="检测学号是否已${regBtnDesc}。。。"
val isRepeat:LiveData<Boolean?> = _isRepeat
val checkRegTip = "检测学号是否已${regBtnDesc}。。。"
val isRepeat: LiveData<Boolean?> = _isRepeat
private var checkJob: Job? = null
//姓名
private val _name=MutableLiveData<String>()
val name:LiveData<String> = _name
val nameDesc="姓名"
val namePlaceholder="请输入$nameDesc"
private val _isValidName=MutableLiveData<Boolean>()
val isValidName:LiveData<Boolean> = _isValidName
val nameFormat="姓名不能为空"
private val _name = MutableLiveData<String>()
val name: LiveData<String> = _name
val nameDesc = "姓名"
val namePlaceholder = "请输入$nameDesc"
private val _isValidName = MutableLiveData<Boolean>()
val isValidName: LiveData<Boolean> = _isValidName
val nameFormat = "姓名不能为空"
//密码
private val _password=MutableLiveData<String>()
val password:LiveData<String> = _password
val passwordDesc="密码"
val passwordPlaceholder="请输入$passwordDesc"
private val _isValidPwd=MutableLiveData<Boolean>()
val isValidPwd:LiveData<Boolean> = _isValidPwd
val passwordFormat="八位纯数字"
private val _password = MutableLiveData<String>()
val password: LiveData<String> = _password
val passwordDesc = "密码"
val passwordPlaceholder = "请输入$passwordDesc"
private val _isValidPwd = MutableLiveData<Boolean>()
val isValidPwd: LiveData<Boolean> = _isValidPwd
val passwordFormat = "八位纯数字"
//注册按钮
private val _isValidForm=MutableLiveData<Boolean>()
private val _isValidForm = MutableLiveData<Boolean>()
val isValidForm: LiveData<Boolean> = _isValidForm
val isValidForm:LiveData<Boolean> = _isValidForm
//注册请求响应信息
private val _snackBarMsg=MutableLiveData<String>()
val snackBarMsg:LiveData<String> = _snackBarMsg
private val _snackBarMsg = MutableLiveData<String>()
val snackBarMsg: LiveData<String> = _snackBarMsg
private val _dialogMsg=MutableLiveData<DialogMessage>()
val dialogMsg:LiveData<DialogMessage> = _dialogMsg
private val _dialogMsg = MutableLiveData<DialogMessage>()
val dialogMsg: LiveData<DialogMessage> = _dialogMsg
val loginDesc="登陆"
val loginDesc = "登陆"
//返回登陆
val backLogin="返回$loginDesc"
val backLogin = "返回$loginDesc"
//确定按钮
val confirmDesc="确定"
val confirmDesc = "确定"
//显示密码提示
val title="提示信息"
val passwordTip="密码会在点击${regBtnDesc}以后,在后台自动生成,请留意系统提示。"
val passwordDialogStart="${regBtnDesc}成功,后台为您自动生成的密码是"
val passwordDialogEnd="\n密码有且只有这里显示一次,请在记住密码后点击确定或${backLogin}"
val title = "提示信息"
val passwordTip = "密码会在点击${regBtnDesc}以后,在后台自动生成,请留意系统提示。"
val passwordDialogStart = "${regBtnDesc}成功,后台为您自动生成的密码是"
val passwordDialogEnd = "\n密码有且只有这里显示一次,请在记住密码后点击确定或${backLogin}"
//转到注册
var goRegister="转到$regBtnDesc"
var goRegister = "转到$regBtnDesc"
/**
* 完成登录状态
*/
private val _finishLogin=MutableLiveData<Boolean>()
val finishLogin:LiveData<Boolean> = _finishLogin
private val _finishLogin = MutableLiveData<Boolean>()
val finishLogin: LiveData<Boolean> = _finishLogin
lateinit var route:AccountRoute
lateinit var route: AccountRoute
/**
* 更新学号
*
* @param studentId 学号
*/
fun onStudentIdChange(studentId:String){
_studentId.value=studentId
fun onStudentIdChange(studentId: String) {
_studentId.value = studentId
viewModelScope.launch {
checkRepeat()
@ -158,19 +165,19 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
*/
private fun checkStudentId(): Boolean {
_isValidStudentId.value= _studentId.value?.matches(Regex("\\d{8}"))
return _isValidStudentId.value==true
_isValidStudentId.value = _studentId.value?.matches(Regex("\\d{8}"))
return _isValidStudentId.value == true
}
/**
* 检查学号是否已注册
*
*/
private suspend fun checkRepeat(){
private suspend fun checkRepeat() {
if (checkStudentId()) {
if (checkJob?.isActive == true) {
checkJob?.join()
}else {
} else {
_isRepeat.postValue(null)
checkJob = viewModelScope.launch {
val url = Api.buildUrl(AccountApi.checkId)
@ -178,15 +185,17 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
HttpClient.get(
url, SimpleCallback<Boolean>(
action = "${studentIdDesc}重复检测",
onSuccess = { _isRepeat.postValue(it.body)
_isValidForm.postValue( _isValidName.value==true && it.body==false)
onSuccess = {
_isRepeat.postValue(it.body)
_isValidForm.postValue(_isValidName.value == true && it.body == false)
},
onFail = { _snackBarMsg.postValue(it) },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type)
, mapOf("studentId" to "${_studentId.value}"))
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
), mapOf("studentId" to "${_studentId.value}")
)
}
}
}else{
} else {
_isValidForm.postValue(false)
}
}
@ -197,8 +206,8 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
*
* @param name 姓名
*/
fun onNameChange(name:String){
_name.value=name
fun onNameChange(name: String) {
_name.value = name
checkForm()
}
@ -207,9 +216,9 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
*
* @return
*/
private fun checkName():Boolean{
_isValidName.value= _name.value?.isNotEmpty()
return _isValidName.value==true
private fun checkName(): Boolean {
_isValidName.value = _name.value?.isNotEmpty()
return _isValidName.value == true
}
/**
@ -217,8 +226,8 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
*
* @param password 密码
*/
fun onPasswordChange(password: String){
_password.value=password
fun onPasswordChange(password: String) {
_password.value = password
checkForm()
}
@ -227,16 +236,17 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
*
* @return
*/
private fun checkPassword():Boolean{
_isValidPwd.value= _password.value?.matches(Regex("\\d{8}"))
return _isValidPwd.value==true
private fun checkPassword(): Boolean {
_isValidPwd.value = _password.value?.matches(Regex("\\d{8}"))
return _isValidPwd.value == true
}
private fun checkForm(): Boolean {
if(checkJob?.isActive==true){
if (checkJob?.isActive == true) {
_isValidForm.value = false
}else{
_isValidForm.value = checkStudentId() && (if (route==AccountRoute.register) checkName()&&isRepeat.value==false else checkPassword())
} else {
_isValidForm.value =
checkStudentId() && (if (route == AccountRoute.register) checkName() && isRepeat.value == false else checkPassword())
}
return _isValidForm.value == true
}
@ -245,18 +255,33 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
* 注册
*
*/
fun register(){
if(checkForm()){
val url= Api.buildUrl(AccountApi.register)
fun register() {
if (checkForm()) {
val url = Api.buildUrl(AccountApi.register)
Logger.i("开始$regBtnDesc,请求接口:$url")
HttpClient.post(url,SimpleCallback<UserResDto>(
HttpClient.post(
url, SimpleCallback<UserResDto>(
action = regBtnDesc,
onSuccess = { _dialogMsg.postValue(DialogMessage(message = it.message,userResDto = it.body)) },
onFail = { _snackBarMsg.postValue(it)},
type = object : TypeToken<ApiResponse<UserResDto>>() {}.type),
jsonBody = Gson().toJson(UserVo(studentId = "${studentId.value}",name = "${name.value}")))
onSuccess = {
_dialogMsg.postValue(
DialogMessage(
message = it.message,
userResDto = it.body
)
)
},
onFail = { _snackBarMsg.postValue(it) },
type = object : TypeToken<ApiResponse<UserResDto>>() {}.type
),
jsonBody = Gson().toJson(
UserVo(
studentId = "${studentId.value}",
name = "${name.value}"
)
)
)
resetForm()
}else{
} else {
Logger.wtf("表单校验失败,无法$regBtnDesc!!!")
}
}
@ -269,36 +294,38 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
* 重置信息
*
*/
fun resetRegisterResMsg(){
_snackBarMsg.value=""
fun resetRegisterResMsg() {
_snackBarMsg.value = ""
}
fun resetDialogMsg(){
_dialogMsg.value=null
fun resetDialogMsg() {
_dialogMsg.value = null
}
private fun resetForm(){
_studentId.value=""
_name.value=""
private fun resetForm() {
_studentId.value = ""
_name.value = ""
}
/**
* 登录
*
*/
fun login(){
if(checkForm()){
fun login() {
if (checkForm()) {
val url = Api.buildUrl(AccountApi.login)
Logger.i("开始$loginDesc,请求接口:$url")
HttpClient.post(url,SimpleCallback<Token>(
HttpClient.post(
url,
SimpleCallback<Token>(
action = loginDesc,
onSuccess = {
_snackBarMsg.postValue(it.message)
val context= getApplication<Application>().applicationContext
val context = getApplication<Application>().applicationContext
val token = it.body?.token
if(token!=null){
val db= AppDatabase.getInstance(context)
if (token != null) {
val db = AppDatabase.getInstance(context)
viewModelScope.launch {
db?.tokenDao()?.save(token = token)
}.invokeOnCompletion {
@ -307,14 +334,21 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
}
}
},
onFail = {_snackBarMsg.postValue(it)},
type = object : TypeToken<ApiResponse<Token>>(){}.type
),jsonBody = Gson().toJson(UserLoginVo(studentId = "${studentId.value}",password = "${password.value}",device = "${Build.MANUFACTURER} ${Build.MODEL}")))
}else{
onFail = { _snackBarMsg.postValue(it) },
type = object : TypeToken<ApiResponse<Token>>() {}.type
),
jsonBody = Gson().toJson(
UserLoginVo(
studentId = "${studentId.value}",
password = "${password.value}",
device = "${Build.MANUFACTURER} ${Build.MODEL}"
)
)
)
} else {
Logger.wtf("表单校验失败,无法$loginDesc!!!")
}
}
}

@ -35,42 +35,49 @@ import com.gyf.csams.uikit.Body
import com.gyf.csams.uikit.theme.CSAMSTheme
enum class AccountRoute{
enum class AccountRoute {
login,
register
}
class AccountActivity: ComponentActivity() {
class AccountActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CSAMSTheme {
Body {
nav, scaffoldState ->
Body { nav, scaffoldState ->
NavHost(navController = nav, startDestination = AccountRoute.login.name) {
composable(AccountRoute.login.name) {
Account(scaffoldState=scaffoldState,route = AccountRoute.login) { isValidForm: Boolean, accountViewModel: AccountViewModel ->
Account(
scaffoldState = scaffoldState,
route = AccountRoute.login
) { isValidForm: Boolean, accountViewModel: AccountViewModel ->
Spacer(modifier = Modifier.height(10.dp))
OutlinedButton(onClick = {accountViewModel.login()},
OutlinedButton(
onClick = { accountViewModel.login() },
enabled = isValidForm,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp)) {
.padding(bottom = 10.dp)
) {
Text(text = accountViewModel.loginDesc)
}
val finishLogin:Boolean? by accountViewModel.finishLogin.observeAsState()
if(finishLogin==true){
val finishLogin: Boolean? by accountViewModel.finishLogin.observeAsState()
if (finishLogin == true) {
finish()
}
OutlinedButton(onClick = { nav.navigate(AccountRoute.register.name)},
OutlinedButton(
onClick = { nav.navigate(AccountRoute.register.name) },
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.onBackground)) {
contentColor = MaterialTheme.colors.onBackground
)
) {
Text(text = accountViewModel.goRegister)
}
}
@ -78,21 +85,29 @@ class AccountActivity: ComponentActivity() {
}
composable(AccountRoute.register.name) {
Account(scaffoldState=scaffoldState,route = AccountRoute.register) { isValidForm: Boolean, accountViewModel: AccountViewModel ->
Account(
scaffoldState = scaffoldState,
route = AccountRoute.register
) { isValidForm: Boolean, accountViewModel: AccountViewModel ->
Spacer(modifier = Modifier.height(10.dp))
OutlinedButton(onClick = { accountViewModel.register()},
OutlinedButton(
onClick = { accountViewModel.register() },
enabled = isValidForm,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp)) {
.padding(bottom = 10.dp)
) {
Text(text = accountViewModel.regBtnDesc)
}
OutlinedButton(onClick = { nav.navigate(AccountRoute.login.name)},
OutlinedButton(
onClick = { nav.navigate(AccountRoute.login.name) },
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.onBackground)) {
contentColor = MaterialTheme.colors.onBackground
)
) {
Text(text = accountViewModel.backLogin)
}
}
@ -111,11 +126,13 @@ class AccountActivity: ComponentActivity() {
* @param Action 表单操作区域
*/
@Composable
private fun Account(accountViewModel: AccountViewModel = viewModel(),
private fun Account(
accountViewModel: AccountViewModel = viewModel(),
scaffoldState: ScaffoldState,
route: AccountRoute,
Action: @Composable (isValidForm:Boolean,accountViewModel: AccountViewModel) -> Unit){
accountViewModel.route=route
Action: @Composable (isValidForm: Boolean, accountViewModel: AccountViewModel) -> Unit
) {
accountViewModel.route = route
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
@ -144,20 +161,20 @@ private fun Account(accountViewModel: AccountViewModel = viewModel(),
}
})
StudentId(checkRepeat=route==AccountRoute.register)
StudentId(checkRepeat = route == AccountRoute.register)
Spacer(modifier = Modifier.height(10.dp))
if (route==AccountRoute.register) Name(name=name) else Password()
if (route == AccountRoute.register) Name(name = name) else Password()
Spacer(modifier = Modifier.height(10.dp))
PasswordTip()
val isValidForm: Boolean by accountViewModel.isValidForm.observeAsState(false)
Action(isValidForm=isValidForm,accountViewModel=accountViewModel)
Action(isValidForm = isValidForm, accountViewModel = accountViewModel)
val snackBarMsg:String by accountViewModel.snackBarMsg.observeAsState("")
val snackBarMsg: String by accountViewModel.snackBarMsg.observeAsState("")
if(snackBarMsg!=""){
if (snackBarMsg != "") {
LaunchedEffect(scaffoldState) {
scaffoldState.snackbarHostState.showSnackbar(
message = snackBarMsg
@ -177,11 +194,11 @@ private fun Account(accountViewModel: AccountViewModel = viewModel(),
* @param accountViewModel
*/
@Composable
private fun StudentId(accountViewModel: AccountViewModel = viewModel(),checkRepeat:Boolean){
private fun StudentId(accountViewModel: AccountViewModel = viewModel(), checkRepeat: Boolean) {
Column {
val studentId: String by accountViewModel.studentId.observeAsState("")
val isValidStudentId : Boolean by accountViewModel.isValidStudentId.observeAsState(false)
val isValidStudentId: Boolean by accountViewModel.isValidStudentId.observeAsState(false)
val focusManager = LocalFocusManager.current
OutlinedTextField(
value = studentId,
@ -189,12 +206,15 @@ private fun StudentId(accountViewModel: AccountViewModel = viewModel(),checkRepe
label = { Text(text = accountViewModel.studentIdDesc) },
placeholder = { Text(text = accountViewModel.studentIdPlaceholder) },
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
keyboardOptions = KeyboardOptions.Default.copy( keyboardType = KeyboardType.Number,imeAction = ImeAction.Done),
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
singleLine = true,
isError = !isValidStudentId
)
if (isValidStudentId) {
if(checkRepeat) {
if (checkRepeat) {
val isRepeat: Boolean? by accountViewModel.isRepeat.observeAsState(null)
when (isRepeat) {
null -> AnimationText(text = accountViewModel.checkRegTip)
@ -224,7 +244,7 @@ private fun StudentId(accountViewModel: AccountViewModel = viewModel(),checkRepe
})
}
}
}else{
} else {
Text(
text = accountViewModel.studentIdFormat,
color = MaterialTheme.colors.error,
@ -241,11 +261,11 @@ private fun StudentId(accountViewModel: AccountViewModel = viewModel(),checkRepe
* @param accountViewModel
*/
@Composable
private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()){
private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()) {
val dialogMsg: DialogMessage? by accountViewModel.dialogMsg.observeAsState(null)
val message=dialogMsg?.userResDto?.password
if(message?.isNotEmpty() == true){
val message = dialogMsg?.userResDto?.password
if (message?.isNotEmpty() == true) {
PasswordDialog(message = message)
}
}
@ -257,21 +277,28 @@ private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()){
* @param message
*/
@Composable
private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), message:String){
val context= LocalContext.current
val button:@Composable () -> Unit = {
Row(horizontalArrangement= Arrangement.Center,modifier = Modifier
private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), message: String) {
val context = LocalContext.current
val button: @Composable () -> Unit = {
Row(
horizontalArrangement = Arrangement.Center, modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp)) {
OutlinedButton(onClick = { accountViewModel.resetDialogMsg() },
modifier = Modifier.padding(end = 10.dp)) {
.padding(bottom = 10.dp)
) {
OutlinedButton(
onClick = { accountViewModel.resetDialogMsg() },
modifier = Modifier.padding(end = 10.dp)
) {
Text(text = accountViewModel.confirmDesc)
}
OutlinedButton(onClick = {
context.startActivity(Intent(context,AccountActivity::class.java))
OutlinedButton(
onClick = {
context.startActivity(Intent(context, AccountActivity::class.java))
},
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.onBackground)) {
contentColor = MaterialTheme.colors.onBackground
)
) {
Text(text = accountViewModel.backLogin)
}
}
@ -282,8 +309,10 @@ private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), mes
text = {
Text(buildAnnotatedString {
append(accountViewModel.passwordDialogStart)
withStyle(style = MaterialTheme.typography.body1.toSpanStyle()
.copy(color = MaterialTheme.colors.secondary)){
withStyle(
style = MaterialTheme.typography.body1.toSpanStyle()
.copy(color = MaterialTheme.colors.secondary)
) {
append(message)
}
append(accountViewModel.passwordDialogEnd)
@ -292,7 +321,6 @@ private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), mes
}
/**
* 姓名文本框
* TODO 需要把逻辑封装到[com.gyf.csams.uikit.BaseTextField]
@ -300,23 +328,27 @@ private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), mes
* @param accountViewModel
*/
@Composable
private fun Name(name:String, accountViewModel: AccountViewModel = viewModel()){
private fun Name(name: String, accountViewModel: AccountViewModel = viewModel()) {
Column {
val isValidName:Boolean by accountViewModel.isValidName.observeAsState(false)
val isValidName: Boolean by accountViewModel.isValidName.observeAsState(false)
val focusManager = LocalFocusManager.current
OutlinedTextField(value = name,
onValueChange = {accountViewModel.onNameChange(it)},
label={ Text(text = accountViewModel.nameDesc)},
placeholder = { Text(text = accountViewModel.namePlaceholder)},
OutlinedTextField(
value = name,
onValueChange = { accountViewModel.onNameChange(it) },
label = { Text(text = accountViewModel.nameDesc) },
placeholder = { Text(text = accountViewModel.namePlaceholder) },
singleLine = true,
isError = !isValidName,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done))
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done)
)
if (!isValidName){
Text(text = accountViewModel.nameFormat,
color=MaterialTheme.colors.error)
if (!isValidName) {
Text(
text = accountViewModel.nameFormat,
color = MaterialTheme.colors.error
)
}
}
@ -328,24 +360,31 @@ private fun Name(name:String, accountViewModel: AccountViewModel = viewModel()){
* @param accountViewModel
*/
@Composable
private fun Password(accountViewModel: AccountViewModel= viewModel()){
private fun Password(accountViewModel: AccountViewModel = viewModel()) {
Column {
val isValidPwd:Boolean by accountViewModel.isValidPwd.observeAsState(false)
val isValidPwd: Boolean by accountViewModel.isValidPwd.observeAsState(false)
val focusManager = LocalFocusManager.current
val password:String by accountViewModel.password.observeAsState("")
OutlinedTextField(value = password,
visualTransformation=PasswordVisualTransformation(),
onValueChange = {accountViewModel.onPasswordChange(it)},
label={ Text(text = accountViewModel.passwordDesc)},
placeholder = { Text(text = accountViewModel.passwordPlaceholder)},
val password: String by accountViewModel.password.observeAsState("")
OutlinedTextField(
value = password,
visualTransformation = PasswordVisualTransformation(),
onValueChange = { accountViewModel.onPasswordChange(it) },
label = { Text(text = accountViewModel.passwordDesc) },
placeholder = { Text(text = accountViewModel.passwordPlaceholder) },
singleLine = true,
isError = !isValidPwd,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done,keyboardType = KeyboardType.Number))
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Number
)
)
if(!isValidPwd){
Text(text = accountViewModel.passwordFormat,
color=MaterialTheme.colors.error)
if (!isValidPwd) {
Text(
text = accountViewModel.passwordFormat,
color = MaterialTheme.colors.error
)
}
}
}
@ -357,7 +396,7 @@ private fun Password(accountViewModel: AccountViewModel= viewModel()){
*/
@Composable
private fun PasswordTip(accountViewModel: AccountViewModel = viewModel()) {
if(accountViewModel.isValidForm.value==true) {
if (accountViewModel.isValidForm.value == true) {
Text(
text = accountViewModel.passwordTip, color = MaterialTheme.colors.primary,
modifier = Modifier.fillMaxWidth()
@ -371,14 +410,15 @@ private fun PasswordTip(accountViewModel: AccountViewModel = viewModel()) {
fun DefaultPreview() {
CSAMSTheme {
Row (
horizontalArrangement=Arrangement.Center,
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxSize()) {
modifier = Modifier.fillMaxSize()
) {
Column(modifier = Modifier.width(IntrinsicSize.Min)) {
val model:AccountViewModel= viewModel()
val model: AccountViewModel = viewModel()
StudentId(model,false)
StudentId(model, false)
Spacer(modifier = Modifier.height(10.dp))
}
}

@ -17,7 +17,7 @@ import java.util.*
* 活动详情菜单通用状态
*
*/
class ActivityDetailViewModel:ViewModel(), TopMenuInterface<ActivityDetailMenu> {
class ActivityDetailViewModel : ViewModel(), TopMenuInterface<ActivityDetailMenu> {
override val _currentMenu: MutableLiveData<ActivityDetailMenu> = MutableLiveData()
override val currentMenu: LiveData<ActivityDetailMenu> = _currentMenu
}
@ -31,22 +31,23 @@ class ActivityDetailViewModel:ViewModel(), TopMenuInterface<ActivityDetailMenu>
* @property activityLocation 活动地点
* @property activityDesc 活动介绍
*/
data class ActivityDetailVo(val associationName:String,val activityName:String,
val activityTime:Date,val activityLocation:String,
val activityDesc:String)
data class ActivityDetailVo(
val associationName: String, val activityName: String,
val activityTime: Date, val activityLocation: String,
val activityDesc: String
)
/**
* 活动信息
*
*/
class ActivityInfoViewModel:ViewModel(){
private val _activityDetailVo=MutableLiveData<ActivityDetailVo>()
val activityDetailVo:LiveData<ActivityDetailVo> = _activityDetailVo
class ActivityInfoViewModel : ViewModel() {
private val _activityDetailVo = MutableLiveData<ActivityDetailVo>()
val activityDetailVo: LiveData<ActivityDetailVo> = _activityDetailVo
val like = "点赞"
val collect="收藏"
val collect = "收藏"
init {
loadInfo()
@ -57,7 +58,7 @@ class ActivityInfoViewModel:ViewModel(){
*
* @param callback
*/
fun like(callback:(message:String)->Unit){
fun like(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
@ -66,11 +67,11 @@ class ActivityInfoViewModel:ViewModel(){
*
* @param callback
*/
fun collect(callback:(message:String)->Unit){
fun collect(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
private fun loadInfo(){
private fun loadInfo() {
viewModelScope.launch {
_activityDetailVo.value = ActivityDetailVo(
activityName = randomChinese(4),
@ -94,20 +95,27 @@ class ActivityInfoViewModel:ViewModel(){
* @property createTime 文件创建时间
* @property studentId 文件上传人
*/
data class ActivityPhotoVo(val name:String, val size:Long, val url:String, val md5:String, val createTime: Date, val studentId:String)
data class ActivityPhotoVo(
val name: String,
val size: Long,
val url: String,
val md5: String,
val createTime: Date,
val studentId: String
)
/**
* 活动相册数据状态管理
*
*/
class ActivityPhotoViewModel:ScrollList<ActivityPhotoVo>(){
class ActivityPhotoViewModel : ScrollList<ActivityPhotoVo>() {
override val initSize: Int = 10
init {
load()
}
fun upload(callback: (message: String) -> Unit){
fun upload(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
@ -118,13 +126,17 @@ class ActivityPhotoViewModel:ScrollList<ActivityPhotoVo>(){
override fun load() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize){
add(ActivityPhotoVo(name=randomChinese(3),
size= randomNum(3).toLong(),
url="",
repeat(initSize) {
add(
ActivityPhotoVo(
name = randomChinese(3),
size = randomNum(3).toLong(),
url = "",
md5 = "",
createTime = randomDateTime(),
studentId = ""))
studentId = ""
)
)
}
}
}
@ -140,15 +152,19 @@ class ActivityPhotoViewModel:ScrollList<ActivityPhotoVo>(){
Logger.i("加载更多")
viewModelScope.launch {
_data.value?.apply {
val list= mutableListOf<ActivityPhotoVo>()
val list = mutableListOf<ActivityPhotoVo>()
list.addAll(this)
repeat(initSize){
add(ActivityPhotoVo(name=randomChinese(3),
size= randomNum(3).toLong(),
url="",
repeat(initSize) {
add(
ActivityPhotoVo(
name = randomChinese(3),
size = randomNum(3).toLong(),
url = "",
md5 = "",
createTime = randomDateTime(),
studentId = ""))
studentId = ""
)
)
}
_data.postValue(list)
callback("成功加载更多相册")
@ -157,15 +173,18 @@ class ActivityPhotoViewModel:ScrollList<ActivityPhotoVo>(){
}
}
data class ActivityMemberVo(val studentId: String,val name:String)
data class ActivityMemberVo(val studentId: String, val name: String)
data class ActivityMembersVo(val organizer:ActivityMemberVo, val participant:MutableList<ActivityMemberVo>?)
data class ActivityMembersVo(
val organizer: ActivityMemberVo,
val participant: MutableList<ActivityMemberVo>?
)
class ActivityMemberViewModel:ScrollList<ActivityMemberVo>(){
class ActivityMemberViewModel : ScrollList<ActivityMemberVo>() {
override val initSize: Int = 10
private val _allMember=MutableLiveData<ActivityMembersVo>()
val allMember:LiveData<ActivityMembersVo> = _allMember
private val _allMember = MutableLiveData<ActivityMembersVo>()
val allMember: LiveData<ActivityMembersVo> = _allMember
init {
load()
@ -178,10 +197,15 @@ class ActivityMemberViewModel:ScrollList<ActivityMemberVo>(){
override fun load() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize){
add(ActivityMemberVo(studentId = randomNum(),name = randomChinese(3)))
}
_allMember.value= ActivityMembersVo(organizer = ActivityMemberVo(studentId = randomNum(),name = randomChinese(3)),participant = _data.value)
repeat(initSize) {
add(ActivityMemberVo(studentId = randomNum(), name = randomChinese(3)))
}
_allMember.value = ActivityMembersVo(
organizer = ActivityMemberVo(
studentId = randomNum(),
name = randomChinese(3)
), participant = _data.value
)
}
}
}
@ -194,14 +218,19 @@ class ActivityMemberViewModel:ScrollList<ActivityMemberVo>(){
override fun loadMore(callback: (message: String) -> Unit) {
viewModelScope.launch {
_data.value?.apply {
val list= mutableListOf<ActivityMemberVo>()
val list = mutableListOf<ActivityMemberVo>()
list.addAll(this)
repeat(initSize){
add(ActivityMemberVo(studentId = randomNum(),name = randomChinese(3)))
repeat(initSize) {
add(ActivityMemberVo(studentId = randomNum(), name = randomChinese(3)))
}
_allMember.value?.apply {
_data.postValue(list)
_allMember.postValue(ActivityMembersVo(organizer=organizer,participant=list))
_allMember.postValue(
ActivityMembersVo(
organizer = organizer,
participant = list
)
)
callback("成功加载更多活动参与者")
}
}
@ -209,33 +238,33 @@ 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 BBSViewModel:ScrollList<BBSVo>(), SendInterface {
class BBSViewModel : ScrollList<BBSVo>(), SendInterface {
override val initSize: Int = 10
val title="发送评论"
val title = "发送评论"
override val newContent = StringForm(formDesc = "评论内容",textLength = 80)
override val newContent = StringForm(formDesc = "评论内容", textLength = 80)
override val _openDialog=MutableLiveData<Boolean>()
override val openDialog:LiveData<Boolean> = _openDialog
override val _openDialog = MutableLiveData<Boolean>()
override val openDialog: LiveData<Boolean> = _openDialog
init {
load()
}
override fun openDialog(){
_openDialog.value=true
override fun openDialog() {
_openDialog.value = true
}
override fun closeDialog(){
_openDialog.value=false
override fun closeDialog() {
_openDialog.value = false
}
/**
@ -243,7 +272,7 @@ class BBSViewModel:ScrollList<BBSVo>(), SendInterface {
*
* @param callback
*/
override fun send(callback: (message: String) -> Unit){
override fun send(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
@ -254,8 +283,15 @@ class BBSViewModel:ScrollList<BBSVo>(), SendInterface {
override fun load() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize){
add(BBSVo(studentId = randomNum(),createTime = randomDateTime(),content = randomChinese(50),name = randomChinese(3)))
repeat(initSize) {
add(
BBSVo(
studentId = randomNum(),
createTime = randomDateTime(),
content = randomChinese(50),
name = randomChinese(3)
)
)
}
}
}
@ -269,11 +305,18 @@ class BBSViewModel:ScrollList<BBSVo>(), SendInterface {
override fun loadMore(callback: (message: String) -> Unit) {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize){
val list= mutableListOf<BBSVo>()
repeat(initSize) {
val list = mutableListOf<BBSVo>()
list.addAll(this)
repeat(initSize){
add(BBSVo(studentId = randomNum(),name = randomChinese(3),createTime = randomDateTime(),content = randomChinese(50)))
repeat(initSize) {
add(
BBSVo(
studentId = randomNum(),
name = randomChinese(3),
createTime = randomDateTime(),
content = randomChinese(50)
)
)
}
_data.postValue(list)
callback("成功加载更多评论")

@ -13,7 +13,7 @@ import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
class AssociationViewModel:ViewModel(),TopMenuInterface<AssociationMenu>{
class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
override val _currentMenu: MutableLiveData<AssociationMenu> = MutableLiveData()
override val currentMenu: LiveData<AssociationMenu> = _currentMenu
@ -21,41 +21,40 @@ class AssociationViewModel:ViewModel(),TopMenuInterface<AssociationMenu>{
/**
* 下拉菜单状态
*/
private val _expanded=MutableLiveData(false)
val expanded:LiveData<Boolean> = _expanded
private val _expanded = MutableLiveData(false)
val expanded: LiveData<Boolean> = _expanded
/**
* 切换下拉菜单状态
*
*/
fun switchType(){
fun switchType() {
_expanded.value?.let {
_expanded.value = !it
}
}
fun close(){
_expanded.value=false
fun close() {
_expanded.value = false
}
}
data class MemberVo(val name:String)
data class MemberVo(val name: String)
/**
* 社团会员
*
*/
class MemberViewModel:ScrollList<MemberVo>(){
val name=StringForm(formDesc = "姓名关键字",5)
class MemberViewModel : ScrollList<MemberVo>() {
val name = StringForm(formDesc = "姓名关键字", 5)
val search="搜索"
val search = "搜索"
private val _memberList=MutableLiveData<MutableList<MemberVo>>(mutableListOf())
private val _memberList = MutableLiveData<MutableList<MemberVo>>(mutableListOf())
val memberList=_memberList
val memberList = _memberList
override val initSize: Int = 10
@ -71,7 +70,7 @@ class MemberViewModel:ScrollList<MemberVo>(){
viewModelScope.launch {
_memberList.value?.apply {
repeat(initSize) {
add(MemberVo(name = "成员${size+1}"))
add(MemberVo(name = "成员${size + 1}"))
}
Logger.i("初始化社团成员size=$size")
}
@ -87,11 +86,11 @@ class MemberViewModel:ScrollList<MemberVo>(){
override fun loadMore(callback: (message: String) -> Unit) {
viewModelScope.launch {
_memberList.value?.let {
val t= mutableListOf<MemberVo>()
val t = mutableListOf<MemberVo>()
t.addAll(it)
t.apply {
repeat(10){
add(MemberVo(name = "成员${t.size+1}"))
repeat(10) {
add(MemberVo(name = "成员${t.size + 1}"))
}
}
_memberList.postValue(t)
@ -105,19 +104,18 @@ class MemberViewModel:ScrollList<MemberVo>(){
*
* @param callback
*/
fun search(callback: (value: String) -> Unit){
fun search(callback: (value: String) -> Unit) {
Logger.i("搜索条件[成员姓名:${name.formValue.value}]")
callback(NOT_IMPL_TIP)
}
}
data class OngoingActVo(val name: String)
data class OngoingActVo(val name:String)
class OngoingActViewModel:ViewModel(){
private val _act=MutableLiveData<OngoingActVo>()
val act:LiveData<OngoingActVo> = _act
class OngoingActViewModel : ViewModel() {
private val _act = MutableLiveData<OngoingActVo>()
val act: LiveData<OngoingActVo> = _act
}
data class HistoryActVo(val name: String)
@ -127,7 +125,7 @@ data class HistoryActVo(val name: String)
* 历史活动
*
*/
class HistoryActViewModel: ScrollList<HistoryActVo>() {
class HistoryActViewModel : ScrollList<HistoryActVo>() {
override val initSize = 10
init {
@ -137,21 +135,21 @@ class HistoryActViewModel: ScrollList<HistoryActVo>() {
override fun load() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize){
add(HistoryActVo(name = "活动${size+1}"))
repeat(initSize) {
add(HistoryActVo(name = "活动${size + 1}"))
}
Logger.i("初始化活动数量:${size}")
}
}
}
override fun loadMore(callback:(message:String) -> Unit) {
override fun loadMore(callback: (message: String) -> Unit) {
_data.value?.apply {
val t= mutableListOf<HistoryActVo>()
val t = mutableListOf<HistoryActVo>()
t.addAll(this)
t.apply {
repeat(10){
add(HistoryActVo(name = "活动${t.size+1}"))
repeat(10) {
add(HistoryActVo(name = "活动${t.size + 1}"))
}
}
_data.postValue(t)

@ -13,9 +13,10 @@ import kotlin.random.Random
* 题库界面类型
*
*/
enum class ExamActivityType(val menuName:String){
enum class ExamActivityType(val menuName: String) {
//入团题库
SET_EXAM("入团题库"),
//入团申请表
JOIN_Association("入团申请表")
}
@ -24,16 +25,17 @@ enum class ExamActivityType(val menuName:String){
* 题型
*
*/
enum class ExamType(val type:String){
enum class ExamType(val type: String) {
//选择题
CQ("选择题"),
//开放题
OQ("开放题")
}
sealed class Exam{
abstract val examType:ExamType
abstract val question:StringForm
sealed class Exam {
abstract val examType: ExamType
abstract val question: StringForm
}
@ -56,30 +58,29 @@ data class OpenQuestionsVo(
* @property rightAnswer 正确答案
* @property question 问题
*/
data class ChoiceQuestionVo(override val examType: ExamType = ExamType.CQ,
val answers:List<StringForm>,
val rightAnswer:Int,
data class ChoiceQuestionVo(
override val examType: ExamType = ExamType.CQ,
val answers: List<StringForm>,
val rightAnswer: Int,
override val question: StringForm
):Exam()
) : Exam()
/**
* 问题长度
*/
const val QUESTION_TEXT_LENGTH=30
const val QUESTION_TEXT_LENGTH = 30
/**
* 选择题选项数
*/
const val ANSWER_SIZE=4
const val ANSWER_SIZE = 4
/**
* 答案长度
*
*/
const val ANSWER_TEXT_LENGTH=15
const val ANSWER_TEXT_LENGTH = 15
/**
@ -87,22 +88,22 @@ const val ANSWER_TEXT_LENGTH=15
*
*/
class ExamViewModel:ScrollList<Exam>() {
class ExamViewModel : ScrollList<Exam>() {
val questionIsNull: String = "问题不能为空"
val deleteLeastOne: String="至少保留一道题目"
val updateExam="更新题库"
val postAnswer="提交答案"
val back="返回"
val deleteTip="确定删除此题目?"
val addTip="确定添加此题目?"
val actionLabel="确定"
val deleteLeastOne: String = "至少保留一道题目"
val updateExam = "更新题库"
val postAnswer = "提交答案"
val back = "返回"
val deleteTip = "确定删除此题目?"
val addTip = "确定添加此题目?"
val actionLabel = "确定"
override val initSize = 10
private val _newExam:MutableLiveData<Exam> = MutableLiveData(createExam(ExamType.CQ))
val newExam:LiveData<Exam> = _newExam
private val _newExam: MutableLiveData<Exam> = MutableLiveData(createExam(ExamType.CQ))
val newExam: LiveData<Exam> = _newExam
init {
load()
@ -112,8 +113,9 @@ class ExamViewModel:ScrollList<Exam>() {
* 切换题型
*
*/
fun switchType(exam: Exam){
if(exam is ChoiceQuestionVo) _newExam.value=createExam(ExamType.OQ) else _newExam.value=createExam(ExamType.CQ)
fun switchType(exam: Exam) {
if (exam is ChoiceQuestionVo) _newExam.value = createExam(ExamType.OQ) else _newExam.value =
createExam(ExamType.CQ)
}
@ -124,14 +126,20 @@ class ExamViewModel:ScrollList<Exam>() {
* @return
*/
private fun createExam(type: ExamType): Exam {
val question=StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH)
return when(type){
ExamType.CQ-> ChoiceQuestionVo(
answers = ('A'..'D').map { StringForm(formDesc = "选项",textLength = ANSWER_TEXT_LENGTH,value = "选项$it") },
val question = StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH)
return when (type) {
ExamType.CQ -> ChoiceQuestionVo(
answers = ('A'..'D').map {
StringForm(
formDesc = "选项",
textLength = ANSWER_TEXT_LENGTH,
value = "选项$it"
)
},
rightAnswer = 0,
question = question
)
ExamType.OQ-> OpenQuestionsVo(question = question)
ExamType.OQ -> OpenQuestionsVo(question = question)
}
}
@ -140,16 +148,16 @@ class ExamViewModel:ScrollList<Exam>() {
* 更新题目
*
*/
fun update(oldExam: Exam,newExam:Exam){
if(oldExam==_newExam.value){
_newExam.value=newExam
}else{
fun update(oldExam: Exam, newExam: Exam) {
if (oldExam == _newExam.value) {
_newExam.value = newExam
} else {
_data.value?.apply {
this[indexOf(oldExam)]=newExam
this[indexOf(oldExam)] = newExam
val list = mutableListOf<Exam>()
list.addAll(this)
_data.value?.clear()
_data.value=list
_data.value = list
}
}
}
@ -159,7 +167,7 @@ class ExamViewModel:ScrollList<Exam>() {
*
* @param callback
*/
fun updateExam(callback: (message: String) -> Unit){
fun updateExam(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
@ -168,7 +176,7 @@ class ExamViewModel:ScrollList<Exam>() {
*
* @param callback
*/
fun postAnswer(callback: (message: String) -> Unit){
fun postAnswer(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
@ -180,13 +188,31 @@ class ExamViewModel:ScrollList<Exam>() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize) {
if (Random.nextBoolean()){
add(OpenQuestionsVo(question = StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH,value = "这是一道开放题:$size")))
} else{
if (Random.nextBoolean()) {
add(
OpenQuestionsVo(
question = StringForm(
formDesc = "问题",
textLength = QUESTION_TEXT_LENGTH,
value = "这是一道开放题:$size"
)
)
)
} else {
add(
ChoiceQuestionVo(
question = StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH,value = "这是一道选择题:$size"),
answers = ('A'..'D').map { StringForm(formDesc = "选项",textLength = ANSWER_TEXT_LENGTH,value = "选项$it") },
question = StringForm(
formDesc = "问题",
textLength = QUESTION_TEXT_LENGTH,
value = "这是一道选择题:$size"
),
answers = ('A'..'D').map {
StringForm(
formDesc = "选项",
textLength = ANSWER_TEXT_LENGTH,
value = "选项$it"
)
},
rightAnswer = Random.nextInt(ANSWER_SIZE)
)
)
@ -226,13 +252,13 @@ class ExamViewModel:ScrollList<Exam>() {
fun addQuestion() {
_data.value?.apply {
_newExam.value?.let{
val list= mutableListOf<Exam>()
_newExam.value?.let {
val list = mutableListOf<Exam>()
list.addAll(this)
list.add(it)
_data.postValue(list)
}
_newExam.value=createExam(ExamType.CQ)
_newExam.value = createExam(ExamType.CQ)
}
}

@ -8,30 +8,30 @@ import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.StringForm
data class Image(val uri:Uri,val createTime:Long,val size:Long)
data class Image(val uri: Uri, val createTime: Long, val size: Long)
class RegAssociationViewModel : ViewModel() {
val frameDesc="社团注册资料"
val frameDesc = "社团注册资料"
val name= StringForm(formDesc = "社团名称",textLength = 5)
val desc = StringForm(formDesc = "社团简介",textLength = 30)
val name = StringForm(formDesc = "社团名称", textLength = 5)
val desc = StringForm(formDesc = "社团简介", textLength = 30)
val _picture=MutableLiveData<Uri>()
val picture:LiveData<Uri> =_picture
val _picture = MutableLiveData<Uri>()
val picture: LiveData<Uri> = _picture
val piciurePlaceHolder="请上传图片"
val piciurePlaceHolder = "请上传图片"
val errorPicture="图片加载失败,请联系管理员"
val errorPicture = "图片加载失败,请联系管理员"
val deninedPermission="拒绝授权"
val deninedPermission = "拒绝授权"
val register="注册"
val back="返回"
val register = "注册"
val back = "返回"
fun setPicture(uri: Uri){
_picture.value=uri
fun setPicture(uri: Uri) {
_picture.value = uri
}
/**
@ -39,7 +39,7 @@ class RegAssociationViewModel : ViewModel() {
*
* @param callback
*/
fun register(callback: (value: String) -> Unit){
fun register(callback: (value: String) -> Unit) {
callback(NOT_IMPL_TIP)
}

@ -8,21 +8,21 @@ import com.gyf.csams.uikit.StringForm
* 社团重命名状态管理
*
*/
class RenameViewModel:ViewModel() {
val menuName="换名申请表"
class RenameViewModel : ViewModel() {
val menuName = "换名申请表"
val oldName=StringForm(formDesc = "社团原名",textLength = 10)
val newName=StringForm(formDesc = "社团新名",textLength = 10)
val cause=StringForm(formDesc = "换名原因",textLength = 30)
val oldName = StringForm(formDesc = "社团原名", textLength = 10)
val newName = StringForm(formDesc = "社团新名", textLength = 10)
val cause = StringForm(formDesc = "换名原因", textLength = 30)
val postDesc="提交申请"
val back="返回"
val postDesc = "提交申请"
val back = "返回"
/**
* TODO 提交表单
*
*/
fun post(callback:(message:String) -> Unit){
fun post(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
}

@ -34,32 +34,33 @@ import com.gyf.csams.util.randomChinese
import com.orhanobut.logger.Logger
/**
* 社团界面
*
*/
class AssociationActivity: ComponentActivity() {
class AssociationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CSAMSTheme {
Body {
nav, scaffoldState ->
val context= LocalContext.current as AssociationActivity
val model:AssociationViewModel= viewModel()
val currentMenuName:AssociationMenu by model.currentMenu.observeAsState(AssociationMenu.startMenu)
val intent=Intent(context,ExamActivity::class.java)
Body { nav, scaffoldState ->
val context = LocalContext.current as AssociationActivity
val model: AssociationViewModel = viewModel()
val currentMenuName: AssociationMenu by model.currentMenu.observeAsState(
AssociationMenu.startMenu
)
val intent = Intent(context, ExamActivity::class.java)
val expanded by model.expanded.observeAsState(false)
Column {
Logger.i("expanded=$expanded")
TextTopAppBar(nav = nav,
currentMenuName= currentMenuName.menuName,
currentMenuName = currentMenuName.menuName,
menuNames = AssociationMenu.values(),
iconMenu = {model.switchType()}){
Row{
DropdownMenu(expanded = expanded,
iconMenu = { model.switchType() }) {
Row {
DropdownMenu(
expanded = expanded,
onDismissRequest = { /*TODO*/ },
// offset = DpOffset.Zero.copy(x=50.dp),
properties = PopupProperties()
@ -67,70 +68,99 @@ class AssociationActivity: ComponentActivity() {
DropdownMenuItem(onClick = {
model.close()
}) {
Row(verticalAlignment = Alignment.CenterVertically){
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "申请活动")
Icon(painter = painterResource(id = R.drawable.ic_add_fill), contentDescription = null)
Icon(
painter = painterResource(id = R.drawable.ic_add_fill),
contentDescription = null
)
}
}
DropdownMenuItem(onClick = {
intent.apply {
putExtra(ExamActivityType::name.name, ExamActivityType.SET_EXAM)
putExtra(
ExamActivityType::name.name,
ExamActivityType.SET_EXAM
)
}
context.startActivity(intent)
model.close()
}) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "设置题库")
Icon(painter = painterResource(id = R.drawable.ic_editor), contentDescription = null)
Icon(
painter = painterResource(id = R.drawable.ic_editor),
contentDescription = null
)
}
}
DropdownMenuItem(onClick = {
context.startActivity(Intent(context,ReNameActivity::class.java))
context.startActivity(
Intent(
context,
ReNameActivity::class.java
)
)
model.close()
}) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "社团命名")
Icon(painter = painterResource(id = R.drawable.ic_exchange_rate), contentDescription = null)
Icon(
painter = painterResource(id = R.drawable.ic_exchange_rate),
contentDescription = null
)
}
}
DropdownMenuItem(onClick = {
intent.apply {
putExtra(ExamActivityType::name.name, ExamActivityType.JOIN_Association)
putExtra(
ExamActivityType::name.name,
ExamActivityType.JOIN_Association
)
}
context.startActivity(intent)
model.close()
}) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "申请入团")
Icon(painter = painterResource(id = R.drawable.ic_add_account), contentDescription = null)
Icon(
painter = painterResource(id = R.drawable.ic_add_account),
contentDescription = null
)
}
}
DropdownMenuItem(onClick = {
model.close()
}) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(painter = painterResource(id = R.drawable.ic_arrow_up), contentDescription = null,
Icon(
painter = painterResource(id = R.drawable.ic_arrow_up),
contentDescription = null,
modifier = Modifier
.height(30.dp)
.fillMaxWidth())
.fillMaxWidth()
)
}
}
}
}
}
NavHost(navController = nav, startDestination = AssociationMenu.startMenu.name) {
composable(AssociationMenu.Member.name){
NavHost(
navController = nav,
startDestination = AssociationMenu.startMenu.name
) {
composable(AssociationMenu.Member.name) {
model.clickMenu(AssociationMenu.Member)
Member()
ShowSnackbar(scaffoldState = scaffoldState)
}
composable(AssociationMenu.Main.name){
composable(AssociationMenu.Main.name) {
model.clickMenu(AssociationMenu.Main)
Main()
ShowSnackbar(scaffoldState = scaffoldState)
}
composable(AssociationMenu.ActivityList.name){
composable(AssociationMenu.ActivityList.name) {
model.clickMenu(AssociationMenu.ActivityList)
AssociationList()
ShowSnackbar(scaffoldState = scaffoldState)
@ -149,15 +179,19 @@ class AssociationActivity: ComponentActivity() {
*
*/
@Composable
private fun Member(){
private fun Member() {
MainFrame(background = { Background(image = BackgroundImage.AssociationMain) }) {
val searchWeight=0.2F
Search(modifier = Modifier
val searchWeight = 0.2F
Search(
modifier = Modifier
.fillMaxWidth()
.weight(searchWeight))
MemberList(modifier = Modifier
.weight(searchWeight)
)
MemberList(
modifier = Modifier
.fillMaxWidth()
.weight(1 - searchWeight))
.weight(1 - searchWeight)
)
}
}
@ -167,20 +201,33 @@ class AssociationActivity: ComponentActivity() {
*
*/
@Composable
private fun Search(modifier:Modifier=Modifier, model: MemberViewModel= viewModel(), scaffoldModel: ScaffoldModel= viewModel()){
private fun Search(
modifier: Modifier = Modifier,
model: MemberViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
Column(modifier = modifier.fillMaxSize()) {
Spacer(modifier = Modifier.weight(0.5F))
Row(modifier = Modifier
Row(
modifier = Modifier
.fillMaxWidth()
.weight(0.5F),verticalAlignment = Alignment.CenterVertically) {
.weight(0.5F), verticalAlignment = Alignment.CenterVertically
) {
val textFieldWeight=0.4F
val buttonWeight=0.2F
val spaceWeight=(1-textFieldWeight-buttonWeight)/3
val textFieldWeight = 0.4F
val buttonWeight = 0.2F
val spaceWeight = (1 - textFieldWeight - buttonWeight) / 3
Spacer(modifier = Modifier.weight((spaceWeight)))
BaseTextField(modifier = Modifier.weight(textFieldWeight),form = model.name,singeLine = true)
BaseTextField(
modifier = Modifier.weight(textFieldWeight),
form = model.name,
singeLine = true
)
Spacer(modifier = Modifier.weight(spaceWeight))
OutlinedButton(onClick = { model.search { scaffoldModel.update(message=it) } },modifier = Modifier.weight(buttonWeight)) {
OutlinedButton(
onClick = { model.search { scaffoldModel.update(message = it) } },
modifier = Modifier.weight(buttonWeight)
) {
Text(text = model.search)
}
Spacer(modifier = Modifier.weight(spaceWeight))
@ -193,15 +240,19 @@ class AssociationActivity: ComponentActivity() {
*
*/
@Composable
private fun MemberList(modifier: Modifier=Modifier, model: MemberViewModel=viewModel(), scaffoldModel: ScaffoldModel= viewModel()){
val list:MutableList<MemberVo>? by model.memberList.observeAsState()
val listState= rememberLazyListState()
LazyColumn(state = listState,modifier = modifier) {
private fun MemberList(
modifier: Modifier = Modifier,
model: MemberViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
val list: MutableList<MemberVo>? by model.memberList.observeAsState()
val listState = rememberLazyListState()
LazyColumn(state = listState, modifier = modifier) {
list?.forEach {
item {
Column {
Spacer(modifier = Modifier.height(10.dp))
Row{
Row {
val weight = 1F / 3
Spacer(modifier = Modifier.weight(weight))
Card(
@ -218,11 +269,19 @@ class AssociationActivity: ComponentActivity() {
}
}
item {
Row(horizontalArrangement = Arrangement.Center,modifier = Modifier.fillMaxWidth()) {
IconButton(onClick = { model.loadMore{
scaffoldModel.update(message=it)
} }) {
Icon(painter = painterResource(id = R.drawable.ic_arrow_down), contentDescription = null)
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
IconButton(onClick = {
model.loadMore {
scaffoldModel.update(message = it)
}
}) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_down),
contentDescription = null
)
}
}
@ -235,15 +294,17 @@ class AssociationActivity: ComponentActivity() {
*
*/
@Composable
private fun Main(){
private fun Main() {
MainFrame(background = {
Background(image = BackgroundImage.AssociationMain,alpha = 0.7F)
Background(image = BackgroundImage.AssociationMain, alpha = 0.7F)
}) {
val nameW=0.1F
val cardW=0.66F*0.4F
Name(modifier = Modifier
val nameW = 0.1F
val cardW = 0.66F * 0.4F
Name(
modifier = Modifier
.fillMaxWidth()
.weight(nameW))
.weight(nameW)
)
DescCard(
modifier = Modifier
.weight(cardW)
@ -253,7 +314,7 @@ class AssociationActivity: ComponentActivity() {
Commander(
modifier = Modifier.weight(cardW)
)
Showcase(modifier = Modifier.weight(1F-nameW-cardW-cardW))
Showcase(modifier = Modifier.weight(1F - nameW - cardW - cardW))
}
}
@ -263,20 +324,22 @@ class AssociationActivity: ComponentActivity() {
* @param modifier
*/
@Composable
private fun Name(modifier: Modifier){
private fun Name(modifier: Modifier) {
Box(modifier = modifier) {
Image(
painter = painterResource(id = R.drawable.association_name_border),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier=Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize()
)
Column{
Column {
Spacer(modifier = Modifier.weight(1.7F / 3))
Row(modifier = Modifier
Row(
modifier = Modifier
.fillMaxWidth()
.weight(1F / 3),
horizontalArrangement = Arrangement.Center) {
horizontalArrangement = Arrangement.Center
) {
Text(text = "社团名字")
}
Spacer(modifier = Modifier.weight(0.3F / 3))
@ -290,11 +353,12 @@ class AssociationActivity: ComponentActivity() {
* @param modifier
*/
@Composable
private fun Commander(modifier: Modifier){
Box(modifier=modifier,contentAlignment = Alignment.Center){
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
private fun Commander(modifier: Modifier) {
Box(modifier = modifier, contentAlignment = Alignment.Center) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Image(painter = painterResource(id = R.drawable.persion_name_border),
Image(
painter = painterResource(id = R.drawable.persion_name_border),
contentDescription = null
)
}
@ -308,12 +372,17 @@ class AssociationActivity: ComponentActivity() {
* @param modifier
*/
@Composable
private fun Showcase(modifier: Modifier){
Box(modifier = modifier,contentAlignment = Alignment.Center) {
Image(painter = painterResource(id = R.drawable.showcase_border),
private fun Showcase(modifier: Modifier) {
Box(modifier = modifier, contentAlignment = Alignment.Center) {
Image(
painter = painterResource(id = R.drawable.showcase_border),
contentDescription = null,
modifier=Modifier.fillMaxSize())
Image(painter = painterResource(id = R.drawable.ic_launcher_foreground), contentDescription = null)
modifier = Modifier.fillMaxSize()
)
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = null
)
}
}
@ -322,16 +391,25 @@ class AssociationActivity: ComponentActivity() {
*
*/
@Composable
private fun AssociationList(){
MainFrame(background = { Background(image = BackgroundImage.AssociationMain,alpha = 07F) }) {
val onGoWeight=0.3F
OngoingActivity(modifier = Modifier
private fun AssociationList() {
MainFrame(background = {
Background(
image = BackgroundImage.AssociationMain,
alpha = 07F
)
}) {
val onGoWeight = 0.3F
OngoingActivity(
modifier = Modifier
.weight(onGoWeight)
.fillMaxWidth())
HistoryActivityList(modifier = Modifier
.fillMaxWidth()
)
HistoryActivityList(
modifier = Modifier
.fillMaxWidth()
.weight(1 - onGoWeight)
.border(width = 1.dp, color = MaterialTheme.colors.onBackground))
.border(width = 1.dp, color = MaterialTheme.colors.onBackground)
)
}
}
@ -340,16 +418,19 @@ class AssociationActivity: ComponentActivity() {
*
*/
@Composable
private fun OngoingActivity(modifier: Modifier=Modifier,model:OngoingActViewModel= viewModel()){
val context= LocalContext.current
private fun OngoingActivity(
modifier: Modifier = Modifier,
model: OngoingActViewModel = viewModel()
) {
val context = LocalContext.current
val act by model.act.observeAsState()
Row(modifier = modifier.clickable(onClick = {
context.startActivity(Intent(context,ActivityDetailActivity::class.java))
}),horizontalArrangement = Arrangement.Center) {
val weight=0.5F
val spaceWeight=(1-0.5F)/2
context.startActivity(Intent(context, ActivityDetailActivity::class.java))
}), horizontalArrangement = Arrangement.Center) {
val weight = 0.5F
val spaceWeight = (1 - 0.5F) / 2
Spacer(modifier = Modifier.weight(spaceWeight))
Poster(id = R.drawable.ic_launcher_foreground,modifier = Modifier.weight(weight))
Poster(id = R.drawable.ic_launcher_foreground, modifier = Modifier.weight(weight))
Spacer(modifier = Modifier.weight(spaceWeight))
}
@ -361,16 +442,20 @@ class AssociationActivity: ComponentActivity() {
* @param modifier
*/
@Composable
private fun HistoryActivityList(modifier: Modifier,model:HistoryActViewModel= viewModel(),scaffoldModel: ScaffoldModel= viewModel()){
val listState= rememberLazyListState()
private fun HistoryActivityList(
modifier: Modifier,
model: HistoryActViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
val listState = rememberLazyListState()
val list by model.data.observeAsState()
LazyColumn(state = listState,modifier = modifier) {
LazyColumn(state = listState, modifier = modifier) {
list?.chunked(2)?.forEach {
item {
Row(modifier=Modifier.fillMaxWidth()) {
HistoryActivity(modifier = Modifier.weight(0.4F),it[0])
Row(modifier = Modifier.fillMaxWidth()) {
HistoryActivity(modifier = Modifier.weight(0.4F), it[0])
Spacer(modifier = Modifier.weight(0.2F))
if (it.size==2) HistoryActivity(modifier = Modifier.weight(0.4F),it[1])
if (it.size == 2) HistoryActivity(modifier = Modifier.weight(0.4F), it[1])
else Box(modifier = Modifier.weight(0.4F))
}
Spacer(modifier = Modifier.height(10.dp))
@ -378,8 +463,8 @@ class AssociationActivity: ComponentActivity() {
}
}
}
if(listState.layoutInfo.totalItemsCount-listState.firstVisibleItemIndex==model.initSize/2-1){
model.loadMore { scaffoldModel.update(message=it) }
if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.initSize / 2 - 1) {
model.loadMore { scaffoldModel.update(message = it) }
}
}
@ -389,17 +474,21 @@ class AssociationActivity: ComponentActivity() {
* @param modifier
*/
@Composable
private fun HistoryActivity(modifier: Modifier,historyActVo: HistoryActVo){
val context= LocalContext.current
Box(modifier=modifier.clickable(onClick = {
context.startActivity(Intent(context,ActivityDetailActivity::class.java))
}),contentAlignment = Alignment.Center){
Image(painter = painterResource(id = R.drawable.history_activity_border),
private fun HistoryActivity(modifier: Modifier, historyActVo: HistoryActVo) {
val context = LocalContext.current
Box(modifier = modifier.clickable(onClick = {
context.startActivity(Intent(context, ActivityDetailActivity::class.java))
}), contentAlignment = Alignment.Center) {
Image(
painter = painterResource(id = R.drawable.history_activity_border),
contentDescription = null,
modifier = Modifier.fillMaxSize())
Image(painter = painterResource(id = R.drawable.ic_launcher_foreground),
modifier = Modifier.fillMaxSize()
)
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = null,
modifier = Modifier.fillMaxSize())
modifier = Modifier.fillMaxSize()
)
Text(text = historyActVo.name)
}
@ -407,7 +496,7 @@ class AssociationActivity: ComponentActivity() {
@Preview
@Composable
fun NamePreview(){
fun NamePreview() {
Divider(color = MaterialTheme.colors.background)
}
}

@ -23,23 +23,22 @@ import com.gyf.csams.association.model.*
import com.gyf.csams.uikit.*
/**
* 题库管理
*
*/
class ExamActivity : ComponentActivity() {
lateinit var activityType:ExamActivityType
lateinit var activityType: ExamActivityType
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityType=intent?.getSerializableExtra(ExamActivityType::name.name) as ExamActivityType
activityType = intent?.getSerializableExtra(ExamActivityType::name.name) as ExamActivityType
setContent {
Body { scaffoldState ->
MainFrame(background = { Background(image = BackgroundImage.Exam,alpha = 0.6F) }) {
MainFrame(background = { Background(image = BackgroundImage.Exam, alpha = 0.6F) }) {
Spacer(modifier = Modifier.weight(0.1F))
Title(modifier = Modifier.weight(0.1F))
Exam(modifier = Modifier.weight(0.8F))
@ -50,8 +49,6 @@ class ExamActivity : ComponentActivity() {
}
/**
* 底部按钮
*
@ -65,14 +62,14 @@ class ExamActivity : ComponentActivity() {
val context = LocalContext.current as ExamActivity
Row(modifier = modifier, horizontalArrangement = Arrangement.Center) {
when(context.activityType){
ExamActivityType.SET_EXAM->OutlinedButton(onClick = {
model.updateExam { scaffoldModel.update(message=it) }
when (context.activityType) {
ExamActivityType.SET_EXAM -> OutlinedButton(onClick = {
model.updateExam { scaffoldModel.update(message = it) }
}, modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
Text(text = model.updateExam)
}
ExamActivityType.JOIN_Association->OutlinedButton(onClick = {
model.postAnswer { scaffoldModel.update(message=it) }
ExamActivityType.JOIN_Association -> OutlinedButton(onClick = {
model.postAnswer { scaffoldModel.update(message = it) }
}, modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
Text(text = model.postAnswer)
}
@ -94,19 +91,19 @@ class ExamActivity : ComponentActivity() {
*/
@Composable
private fun Title(modifier: Modifier = Modifier) {
val context= LocalContext.current as ExamActivity
val context = LocalContext.current as ExamActivity
Row(horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth()) {
Text(text = context.activityType.menuName, style = MaterialTheme.typography.h4)
}
}
@Composable
private fun ExamChild(it:Exam,examHeight: Dp,isAdd: Boolean=false){
val questionWeight=0.3F
private fun ExamChild(it: Exam, examHeight: Dp, isAdd: Boolean = false) {
val questionWeight = 0.3F
when (it) {
is OpenQuestionsVo -> ExamOQ(
openQuestionsVo = it,
modifier = Modifier.height(examHeight*questionWeight),
modifier = Modifier.height(examHeight * questionWeight),
isAdd = isAdd
)
is ChoiceQuestionVo -> ExamCQ(
@ -147,7 +144,7 @@ class ExamActivity : ComponentActivity() {
OutlinedButton(onClick = { model.switchType(it) }) {
Text(text = "切换到${if (newExam is ChoiceQuestionVo) "开放题" else "选择题"}")
}
ExamChild(it = it, examHeight = examHeight,isAdd = true)
ExamChild(it = it, examHeight = examHeight, isAdd = true)
}
}
}
@ -163,7 +160,7 @@ class ExamActivity : ComponentActivity() {
}
if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.initSize / 2 - 1) {
model.loadMore { scaffoldModel.update(message=it) }
model.loadMore { scaffoldModel.update(message = it) }
}
@ -191,10 +188,12 @@ class ExamActivity : ComponentActivity() {
*
*/
@Composable
private fun ActionButton(modifier: Modifier = Modifier, isAdd: Boolean,
model: ExamViewModel= viewModel(),
scaffoldModel: ScaffoldModel= viewModel(),
exam: Exam) {
private fun ActionButton(
modifier: Modifier = Modifier, isAdd: Boolean,
model: ExamViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel(),
exam: Exam
) {
val list by model.data.observeAsState()
val newExam by model.newExam.observeAsState()
Box(
@ -202,20 +201,26 @@ class ExamActivity : ComponentActivity() {
modifier = modifier
) {
IconButton(onClick = {
if(isAdd){
if((newExam?.question?.formValue?.value ?: "").isNotEmpty()){
scaffoldModel.update(message=model.addTip,actionLabel = model.actionLabel){
if (isAdd) {
if ((newExam?.question?.formValue?.value ?: "").isNotEmpty()) {
scaffoldModel.update(
message = model.addTip,
actionLabel = model.actionLabel
) {
model.addQuestion()
}
}else{
} else {
scaffoldModel.update(message = model.questionIsNull)
}
}else{
if(list?.size==1){
} else {
if (list?.size == 1) {
scaffoldModel.update(model.deleteLeastOne)
}else{
scaffoldModel.update(message=model.deleteTip,actionLabel = model.actionLabel){
} else {
scaffoldModel.update(
message = model.deleteTip,
actionLabel = model.actionLabel
) {
model.deleteQuestion(exam = exam)
}
}
@ -242,15 +247,17 @@ class ExamActivity : ComponentActivity() {
isAdd: Boolean = false
) {
Row(modifier = modifier) {
val context= LocalContext.current as ExamActivity
val context = LocalContext.current as ExamActivity
Question(
exam = openQuestionsVo, modifier =
if(context.activityType==ExamActivityType.SET_EXAM)
Modifier.weight(0.8F).fillMaxHeight()
if (context.activityType == ExamActivityType.SET_EXAM)
Modifier
.weight(0.8F)
.fillMaxHeight()
else
Modifier
)
if(context.activityType==ExamActivityType.SET_EXAM){
if (context.activityType == ExamActivityType.SET_EXAM) {
ActionButton(
modifier = Modifier
.weight(0.2F)
@ -273,13 +280,15 @@ class ExamActivity : ComponentActivity() {
choiceQuestionVo: ChoiceQuestionVo,
isAdd: Boolean = false,
model: ExamViewModel = viewModel(),
questionWeight:Float
questionWeight: Float
) {
Row(modifier = modifier) {
val context= LocalContext.current as ExamActivity
val context = LocalContext.current as ExamActivity
Column(
modifier = if(context.activityType==ExamActivityType.SET_EXAM)
Modifier.weight(0.8F).fillMaxHeight()
modifier = if (context.activityType == ExamActivityType.SET_EXAM)
Modifier
.weight(0.8F)
.fillMaxHeight()
else
Modifier
) {
@ -299,9 +308,11 @@ class ExamActivity : ComponentActivity() {
choiceQuestionVo.answers.apply {
Column {
forEach {
Row(modifier = Modifier
Row(
modifier = Modifier
.fillMaxWidth()
.weight(1F / ANSWER_SIZE)) {
.weight(1F / ANSWER_SIZE)
) {
val answerIndex: Int = indexOf(it)
val click = {
model.update(
@ -320,7 +331,7 @@ class ExamActivity : ComponentActivity() {
}
}
if(context.activityType==ExamActivityType.SET_EXAM) {
if (context.activityType == ExamActivityType.SET_EXAM) {
ActionButton(
modifier = Modifier
.weight(0.2F)

@ -19,13 +19,12 @@ import com.gyf.csams.uikit.*
* 社团重命名
*
*/
class ReNameActivity: ComponentActivity() {
class ReNameActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Body {
scaffoldState ->
Body { scaffoldState ->
MainFrame(background = { Background(image = BackgroundImage.Rename) }) {
Spacer(
modifier = Modifier
@ -42,7 +41,7 @@ class ReNameActivity: ComponentActivity() {
Spacer(modifier = Modifier.height(10.dp))
BottomButton(modifier = Modifier.weight(0.1F))
Spacer(modifier = Modifier.weight(1-0.2F*2-0.1F*4))
Spacer(modifier = Modifier.weight(1 - 0.2F * 2 - 0.1F * 4))
ShowSnackbar(scaffoldState = scaffoldState)
}
}
@ -54,9 +53,9 @@ class ReNameActivity: ComponentActivity() {
*
*/
@Composable
private fun Title(modifier: Modifier=Modifier,model:RenameViewModel= viewModel()){
Row(modifier = modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
Text(text = model.menuName,style = MaterialTheme.typography.h4)
private fun Title(modifier: Modifier = Modifier, model: RenameViewModel = viewModel()) {
Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Text(text = model.menuName, style = MaterialTheme.typography.h4)
}
}
@ -65,8 +64,8 @@ class ReNameActivity: ComponentActivity() {
*
*/
@Composable
private fun OldName(modifier: Modifier=Modifier,model:RenameViewModel= viewModel()){
BaseTextField(form = model.oldName,modifier = modifier.fillMaxWidth(),singeLine = true)
private fun OldName(modifier: Modifier = Modifier, model: RenameViewModel = viewModel()) {
BaseTextField(form = model.oldName, modifier = modifier.fillMaxWidth(), singeLine = true)
}
/**
@ -74,8 +73,8 @@ class ReNameActivity: ComponentActivity() {
*
*/
@Composable
private fun NewName(modifier: Modifier=Modifier,model:RenameViewModel= viewModel()){
BaseTextField(form = model.newName,modifier = modifier.fillMaxWidth(),singeLine = true)
private fun NewName(modifier: Modifier = Modifier, model: RenameViewModel = viewModel()) {
BaseTextField(form = model.newName, modifier = modifier.fillMaxWidth(), singeLine = true)
}
/**
@ -83,8 +82,8 @@ class ReNameActivity: ComponentActivity() {
*
*/
@Composable
private fun Cause(modifier: Modifier=Modifier,model:RenameViewModel= viewModel()){
BaseTextField(form = model.cause,modifier = modifier.fillMaxWidth())
private fun Cause(modifier: Modifier = Modifier, model: RenameViewModel = viewModel()) {
BaseTextField(form = model.cause, modifier = modifier.fillMaxWidth())
}
/**
@ -94,13 +93,17 @@ class ReNameActivity: ComponentActivity() {
* @param model
*/
@Composable
private fun BottomButton(modifier: Modifier=Modifier,model:RenameViewModel= viewModel(),scaffoldModel: ScaffoldModel= viewModel()){
private fun BottomButton(
modifier: Modifier = Modifier,
model: RenameViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
Row(modifier = modifier.fillMaxWidth()) {
val weight=(1-0.5F)/2
val context= LocalContext.current as ReNameActivity
val weight = (1 - 0.5F) / 2
val context = LocalContext.current as ReNameActivity
Spacer(modifier = Modifier.weight(weight))
Row(modifier=Modifier.weight(0.5F)) {
OutlinedButton(onClick = { model.post{scaffoldModel.update(message=it)} }) {
Row(modifier = Modifier.weight(0.5F)) {
OutlinedButton(onClick = { model.post { scaffoldModel.update(message = it) } }) {
Text(text = model.postDesc)
}
Spacer(modifier = Modifier.width(10.dp))

@ -43,14 +43,18 @@ import com.orhanobut.logger.Logger
* 注册社团
*
*/
class RegAssociationActivity: ComponentActivity(){
class RegAssociationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CSAMSTheme {
Body {
scaffoldState ->
MainFrame(background = { Background(BackgroundImage.RegAssociation,alpha = 0.5F) }) {
Body { scaffoldState ->
MainFrame(background = {
Background(
BackgroundImage.RegAssociation,
alpha = 0.5F
)
}) {
Spacer(
modifier = Modifier
.weight(0.1F)
@ -86,21 +90,22 @@ class RegAssociationActivity: ComponentActivity(){
* @param modifier
*/
@Composable
private fun Logo(model:RegAssociationViewModel= viewModel(),modifier: Modifier) {
val photoIntent=Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
private fun Logo(model: RegAssociationViewModel = viewModel(), modifier: Modifier) {
val photoIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
photoIntent.type = "image/*"
val uri:Uri? by model.picture.observeAsState()
val uri: Uri? by model.picture.observeAsState()
val resultLauncher=rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when(it.resultCode){
Activity.RESULT_OK->{
val resultLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) {
Activity.RESULT_OK -> {
Logger.i("uri=${it.data?.data}")
it.data?.data?.let { it1 -> model.setPicture(it1) }
}
}
}
val loadPicture={
val loadPicture = {
//model.loadPicture(context)
resultLauncher.launch(photoIntent)
}
@ -118,14 +123,15 @@ class RegAssociationActivity: ComponentActivity(){
}
val context = LocalContext.current
val context= LocalContext.current
Box(contentAlignment = Alignment.Center,modifier = modifier) {
Row(verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,modifier = Modifier
Box(contentAlignment = Alignment.Center, modifier = modifier) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center, modifier = Modifier
.fillMaxSize()
.border(width = 1.dp, color = Color.Black)) {
.border(width = 1.dp, color = Color.Black)
) {
if (uri == null) {
OutlinedButton(onClick = {
@ -147,19 +153,28 @@ class RegAssociationActivity: ComponentActivity(){
}
} else {
uri.let {
if(it!=null){
if (it != null) {
Row {
Image(bitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(it))
.asImageBitmap(), contentDescription = null)
Image(
bitmap = BitmapFactory.decodeStream(
context.contentResolver.openInputStream(
it
)
)
.asImageBitmap(), contentDescription = null
)
IconButton(onClick = {
loadPicture()
}) {
Image(painter = painterResource(id = R.drawable.ic_exchange_rate), contentDescription = null)
Image(
painter = painterResource(id = R.drawable.ic_exchange_rate),
contentDescription = null
)
}
}
}else{
} else {
Text(text = model.errorPicture)
}
}
@ -174,18 +189,22 @@ class RegAssociationActivity: ComponentActivity(){
}
@Composable
private fun BottomButton(modifier: Modifier=Modifier,scaffoldModel: ScaffoldModel= viewModel(),model:RegAssociationViewModel= viewModel()){
val context= LocalContext.current as RegAssociationActivity
Row(modifier = modifier,horizontalArrangement = Arrangement.Center) {
private fun BottomButton(
modifier: Modifier = Modifier,
scaffoldModel: ScaffoldModel = viewModel(),
model: RegAssociationViewModel = viewModel()
) {
val context = LocalContext.current as RegAssociationActivity
Row(modifier = modifier, horizontalArrangement = Arrangement.Center) {
OutlinedButton(onClick = {
model.register { scaffoldModel.update(message=it) }
},modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
model.register { scaffoldModel.update(message = it) }
}, modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
Text(text = model.register)
}
Spacer(modifier = Modifier.width(10.dp))
OutlinedButton(onClick = {
context.onBackPressed()
},modifier = Modifier.background(color = MaterialTheme.colors.secondary)) {
}, modifier = Modifier.background(color = MaterialTheme.colors.secondary)) {
Text(text = model.back)
}
}
@ -197,9 +216,9 @@ class RegAssociationActivity: ComponentActivity(){
*
*/
@Composable
private fun Title(model:RegAssociationViewModel= viewModel()){
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
Text(text = model.frameDesc,style = MaterialTheme.typography.h4)
private fun Title(model: RegAssociationViewModel = viewModel()) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Text(text = model.frameDesc, style = MaterialTheme.typography.h4)
}
}
@ -208,8 +227,8 @@ class RegAssociationActivity: ComponentActivity(){
* @param model
*/
@Composable
private fun Name(model:RegAssociationViewModel= viewModel()){
BaseTextField(form = model.name,singeLine = true,modifier = Modifier.fillMaxWidth())
private fun Name(model: RegAssociationViewModel = viewModel()) {
BaseTextField(form = model.name, singeLine = true, modifier = Modifier.fillMaxWidth())
}
/**
@ -217,8 +236,8 @@ class RegAssociationActivity: ComponentActivity(){
* @param model
*/
@Composable
private fun Desc(model:RegAssociationViewModel= viewModel(),modifier:Modifier){
BaseTextField(form = model.desc,modifier = modifier)
private fun Desc(model: RegAssociationViewModel = viewModel(), modifier: Modifier) {
BaseTextField(form = model.desc, modifier = modifier)
}
}

@ -20,21 +20,20 @@ import kotlinx.coroutines.launch
* 跑马灯
*
*/
class MarqueeViewModel:ViewModel() {
class MarqueeViewModel : ViewModel() {
val marqueeTexts= listOf("床前明月光","疑是地上霜","举头望明月","低头思故乡")
val marqueeTexts = listOf("床前明月光", "疑是地上霜", "举头望明月", "低头思故乡")
private val _marqueeIndex=MutableLiveData(0)
var marqueeIndex:LiveData<Int> = _marqueeIndex
private var marqueeJob:Job? = null
private val _marqueeIndex = MutableLiveData(0)
var marqueeIndex: LiveData<Int> = _marqueeIndex
private var marqueeJob: Job? = null
fun addAsync(delayMillis:Int){
if(marqueeJob == null || marqueeJob?.isCompleted==true) {
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(
if (_marqueeIndex.value == marqueeTexts.size - 1) 0 else _marqueeIndex.value?.plus(
1
)
)
@ -48,25 +47,30 @@ class MarqueeViewModel:ViewModel() {
* 海报轮播
*
*/
class CarouselViewModel:ViewModel(){
val imageList= listOf(R.drawable.ic_launcher_foreground,R.drawable.ic_account_fill,R.drawable.ic_all_fill,R.drawable.ic_home_fill)
class CarouselViewModel : ViewModel() {
val imageList = listOf(
R.drawable.ic_launcher_foreground,
R.drawable.ic_account_fill,
R.drawable.ic_all_fill,
R.drawable.ic_home_fill
)
private val _index=MutableLiveData(0)
private val _index = MutableLiveData(0)
val index:LiveData<Int> = _index
val index: LiveData<Int> = _index
private var job:Job? = null
private var job: Job? = null
init {
start()
}
private fun start(){
private fun start() {
job = viewModelScope.launch {
do{
_index.postValue(if (_index.value==imageList.size-1) 0 else _index.value?.plus(1))
do {
_index.postValue(if (_index.value == imageList.size - 1) 0 else _index.value?.plus(1))
delay(5000)
}while (job?.isActive==true)
} while (job?.isActive == true)
}
}
@ -77,25 +81,25 @@ class CarouselViewModel:ViewModel(){
*
* @property name 社团名称
*/
data class AssociationDto(val name:String)
data class AssociationDto(val name: String)
/**
* 主页
*
*/
class MainViewModel:ViewModel(),SendInterface{
class MainViewModel : ViewModel(), SendInterface {
override val _openDialog: MutableLiveData<Boolean> = MutableLiveData()
override val openDialog: LiveData<Boolean> = _openDialog
override val newContent: StringForm = StringForm(formDesc = "留言",textLength = 30)
override val newContent: StringForm = StringForm(formDesc = "留言", textLength = 30)
override fun openDialog() {
_openDialog.value=true
_openDialog.value = true
}
override fun closeDialog() {
_openDialog.value=false
_openDialog.value = false
}
/**
@ -106,35 +110,25 @@ class MainViewModel:ViewModel(),SendInterface{
override fun send(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
/**
* TODO 打开通知
*
* @param callback
*/
fun openNotification(callback: (value: String) -> Unit){
callback(NOT_IMPL_TIP)
}
}
/**
* 社团列表
*
*/
class ListViewModel: ScrollList<AssociationDto>() {
val name = StringForm(formDesc = "社团名称",textLength = 5)
class ListViewModel : ScrollList<AssociationDto>() {
val name = StringForm(formDesc = "社团名称", textLength = 5)
val desc = StringForm(formDesc = "社团简介",textLength = 10)
val desc = StringForm(formDesc = "社团简介", textLength = 10)
//社团列表加载数量
val associationListSize=10
val associationListSize = 10
//社团列表
private val _associationList=MutableLiveData<MutableList<AssociationDto>>(mutableListOf())
val associationDto:LiveData<MutableList<AssociationDto>> = _associationList
val searchDesc="搜索"
private val _associationList = MutableLiveData<MutableList<AssociationDto>>(mutableListOf())
val associationDto: LiveData<MutableList<AssociationDto>> = _associationList
val searchDesc = "搜索"
/**
@ -142,7 +136,7 @@ class ListViewModel: ScrollList<AssociationDto>() {
*
* @param callback
*/
fun search(callback: (value: String) -> Unit){
fun search(callback: (value: String) -> Unit) {
Logger.i("搜索条件[社团名称:${name.formValue.value},社团简介:${desc.formValue.value}]")
callback(NOT_IMPL_TIP)
}
@ -161,7 +155,8 @@ class ListViewModel: ScrollList<AssociationDto>() {
override fun load() {
viewModelScope.launch {
_associationList.value?.apply {
repeat(10
repeat(
10
) {
add(AssociationDto(name = "社团${_associationList.value?.size}"))
}
@ -178,10 +173,10 @@ class ListViewModel: ScrollList<AssociationDto>() {
override fun loadMore(callback: (message: String) -> Unit) {
viewModelScope.launch {
_associationList.value?.apply {
val list= mutableListOf<AssociationDto>()
val list = mutableListOf<AssociationDto>()
list.addAll(this)
list.apply {
repeat(10){
repeat(10) {
add(AssociationDto(name = "社团${size}"))
}
}
@ -203,32 +198,40 @@ class ListViewModel: ScrollList<AssociationDto>() {
* @property headImg 头像
* @property desc 个人简介
*/
data class InfoVo(val studentId:String,val name:String,val duty:String,val headImg:String,val desc:String)
data class InfoVo(
val studentId: String,
val name: String,
val duty: String,
val headImg: String,
val desc: String
)
/**
* 个人中心
*
*/
class CenterViewModel:ViewModel(){
val myAssociationDesc="我的社团"
val myJoinActivity="我参加的社团活动"
val myLikeActivity="我点赞的社团活动"
val myCollectActivity="我收藏的社团活动"
class CenterViewModel : ViewModel() {
val myAssociationDesc = "我的社团"
val myJoinActivity = "我参加的社团活动"
val myLikeActivity = "我点赞的社团活动"
val myCollectActivity = "我收藏的社团活动"
private val _info=MutableLiveData<InfoVo>()
val info:LiveData<InfoVo> = _info
private val _info = MutableLiveData<InfoVo>()
val info: LiveData<InfoVo> = _info
init {
load()
}
private fun load(){
private fun load() {
viewModelScope.launch {
_info.value= InfoVo(studentId = randomNum(),
_info.value = InfoVo(
studentId = randomNum(),
name = randomChinese(3),
duty = randomChinese(3),
headImg = "",
desc = randomChinese(10))
desc = randomChinese(10)
)
}
}

@ -10,7 +10,6 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -31,6 +30,7 @@ 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.message.ui.MessageActivity
import com.gyf.csams.uikit.*
import com.gyf.csams.uikit.theme.CSAMSTheme
import com.gyf.csams.util.randomChinese
@ -90,14 +90,18 @@ class MainActivity : ComponentActivity() {
Column(
modifier = Modifier.weight(0.3F).padding(10.dp),
modifier = Modifier
.weight(0.3F)
.padding(10.dp),
) {
info?.let {
Row(modifier = Modifier
Row(
modifier = Modifier
.fillMaxWidth()
.weight(0.7F)
.border(width = 1.dp, color = MaterialTheme.colors.background),
horizontalArrangement = Arrangement.SpaceBetween) {
horizontalArrangement = Arrangement.SpaceBetween
) {
Image(
painter = painterResource(
id = R.drawable.ic_launcher_foreground
@ -122,7 +126,10 @@ class MainActivity : ComponentActivity() {
Spacer(modifier = Modifier.weight(0.05F))
Card(backgroundColor = MaterialTheme.colors.background,modifier = Modifier.weight(0.15F)) {
Card(
backgroundColor = MaterialTheme.colors.background,
modifier = Modifier.weight(0.15F)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
@ -186,10 +193,15 @@ class MainActivity : ComponentActivity() {
) {
Column(modifier = Modifier.weight(0.33F)) {
Notification()
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 15.dp),
verticalArrangement = Arrangement.SpaceEvenly
) {
MessageBoard()
Spacer(modifier = Modifier.height(10.dp))
ClubActivitiesTitle()
Spacer(modifier = Modifier.height(10.dp))
}
}
Column(modifier = Modifier.weight(0.66F)) {
PosterWithDesc()
@ -366,10 +378,8 @@ class MainActivity : ComponentActivity() {
*
*/
@Composable
private fun Notification(
mainViewModel: MainViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
private fun Notification() {
val context = LocalContext.current
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
@ -377,7 +387,7 @@ class MainActivity : ComponentActivity() {
.padding(10.dp)
) {
IconButton(onClick = {
mainViewModel.openNotification { scaffoldModel.update(message = it) }
context.startActivity(Intent(context, MessageActivity::class.java))
}) {
Icon(
painter = painterResource(id = R.drawable.ic_notification),
@ -388,37 +398,17 @@ class MainActivity : ComponentActivity() {
}
}
/**
* 圆角矩形边框
*
*/
@Composable
private fun MyBorder(content: @Composable BoxScope.() -> Unit) {
Box(modifier = Modifier.padding(horizontal = 15.dp)) {
Box(
modifier = Modifier
.border(
width = 1.dp,
color = MaterialTheme.colors.onBackground,
shape = RoundedCornerShape(size = 20.dp)
),
) {
content()
}
}
}
/**
* 留言板
*
*/
@Composable
private fun MessageBoard(
modifier: Modifier = Modifier,
model: MarqueeViewModel = viewModel(),
mainViewModel: MainViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
mainViewModel: MainViewModel = viewModel()
) {
MyBorder {
Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
@ -454,12 +444,11 @@ class MainActivity : ComponentActivity() {
*
*/
@Composable
private fun ClubActivitiesTitle() {
MyBorder {
private fun ClubActivitiesTitle(modifier: Modifier = Modifier) {
Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {

@ -0,0 +1,11 @@
package com.gyf.csams.message.model
import androidx.lifecycle.ViewModel
enum class MessageType(val desc: String) {
System("系统通知")
}
class MessageViewModel : ViewModel() {
val message = "消息"
}

@ -0,0 +1,136 @@
package com.gyf.csams.message.model
import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.ScrollList
import com.gyf.csams.util.randomChinese
import com.gyf.csams.util.randomDateTime
import com.gyf.csams.util.randomNum
import kotlinx.coroutines.launch
import java.util.*
import kotlin.random.Random
enum class SystemType(val desc: String) {
Join("入团通知"),
ActCheck("活动审核通知"),
Rename("社团重命名审核通知")
}
/**
* 通知内容
*
*/
sealed class MessageContent {
//通知时间
abstract val createTime: Date
//通知状态
abstract val readState: Boolean
//通知类型
abstract val type: SystemType
}
/**
* 入团通知
* @param studentId 学号
* @param studentName 姓名
*
*/
data class JoinContent(
val studentId: Long,
val studentName: String,
override val createTime: Date,
override val readState: Boolean,
override val type: SystemType = SystemType.Join
) : MessageContent()
/**
* 活动审核通知
*
* @property activityId 活动id
* @property activityName 活动名
*
*/
data class ActCheckContent(
val activityId: Long, val activityName: String,
override val createTime: Date,
override val readState: Boolean,
override val type: SystemType = SystemType.ActCheck
) : MessageContent()
/**
* 社团重命名审核通知
*
* @property oldAssociationName 老社团名字
* @property newAssociationName 新社团名字
*
*/
data class RenameContent(
val oldAssociationName: String, val newAssociationName: String,
override val createTime: Date,
override val readState: Boolean,
override val type: SystemType = SystemType.Rename
) : MessageContent()
/**
* 系统通知数据状态管理
*
*/
class SysMessageViewModel : ScrollList<MessageContent>() {
val title = "系统通知"
override val initSize: Int = 10
init {
load()
}
/**
* TODO 加载通知
*
*/
override fun load() {
viewModelScope.launch {
_data.value?.apply {
repeat(initSize) {
when ((0..2).random()) {
0 -> add(
JoinContent(
createTime = randomDateTime(),
readState = Random.nextBoolean(),
studentId = randomNum().toLong(),
studentName = randomChinese(5)
)
)
1 -> add(
ActCheckContent(
activityId = randomNum().toLong(),
activityName = randomChinese(5),
createTime = randomDateTime(),
readState = Random.nextBoolean()
)
)
2 -> add(
RenameContent(
oldAssociationName = randomChinese(3),
newAssociationName = randomChinese(3),
createTime = randomDateTime(),
readState = Random.nextBoolean()
)
)
}
}
}
}
}
/**
* TODO 加载更多通知
*
* @param callback
*/
override fun loadMore(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
}

@ -0,0 +1,81 @@
package com.gyf.csams.message.ui
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R
import com.gyf.csams.message.model.MessageType
import com.gyf.csams.message.model.MessageViewModel
import com.gyf.csams.uikit.*
/**
* 消息界面
*
*/
class MessageActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Body { scaffoldState ->
val model: MessageViewModel = viewModel()
val context = LocalContext.current
MainFrame(background = {
Background(
image = BackgroundImage.ActivityMessage,
alpha = 0.6F
)
}) {
TextTopAppBar(title = model.message)
Spacer(modifier = Modifier.height(5.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.clickable(onClick = {
context.startActivity(
Intent(
context,
SysMessageActivity::class.java
)
)
})
) {
Icon(
painter = painterResource(id = R.drawable.ic_notice),
contentDescription = null,
modifier = Modifier.size(30.dp)
)
Text(text = MessageType.System.desc)
}
}
Spacer(modifier = Modifier.height(5.dp))
Divider(
modifier = Modifier.height(10.dp),
color = MaterialTheme.colors.background
)
}
}
}
}
}

@ -0,0 +1,135 @@
package com.gyf.csams.message.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.message.model.*
import com.gyf.csams.uikit.*
import com.gyf.csams.util.format
/**
* 系统通知
*
*/
class SysMessageActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Body { scaffoldState ->
MainFrame(background = {
Background(
image = BackgroundImage.ActivityMessage,
alpha = 0.6F
)
}) {
TextTopAppBar(modifier = Modifier.weight(0.1F), title = MessageType.System.desc)
Spacer(modifier = Modifier.weight(0.05F))
MessageList(modifier = Modifier.weight(0.8F))
}
}
}
}
@Composable
private fun MessageList(
modifier: Modifier = Modifier,
model: SysMessageViewModel = viewModel()
) {
val listState = rememberLazyListState()
val list by model.data.observeAsState()
Box(modifier = modifier.padding(10.dp)) {
LazyColumn(state = listState) {
list?.forEach {
item {
MessageItem(content = it)
Spacer(modifier = Modifier.height(10.dp))
}
}
}
}
}
@Composable
private fun MessageItem(modifier: Modifier = Modifier, content: MessageContent) {
Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) {
Column(modifier = Modifier.padding(10.dp)) {
Text(text = content.type.desc, style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(5.dp))
Card(
backgroundColor = MaterialTheme.colors.background,
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
when (content) {
is JoinContent -> JoinMessage(content = content)
is ActCheckContent -> ActCheckMessage(content = content)
is RenameContent -> RenameMessage(content = content)
}
}
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Text(text = content.createTime.format())
}
}
}
}
@Composable
private fun JoinMessage(content: JoinContent) {
Text(buildAnnotatedString {
withStyle(
style = MaterialTheme.typography.subtitle1.toSpanStyle()
.copy(color = MaterialTheme.colors.primary)
) {
append(content.studentName)
}
append("申请进入社团")
})
}
@Composable
private fun ActCheckMessage(content: ActCheckContent) {
Text(buildAnnotatedString {
append("\"")
withStyle(style = SpanStyle(color = MaterialTheme.colors.primary)) {
append(content.activityName)
}
append("\"活动审核不通过")
})
}
@Composable
private fun RenameMessage(content: RenameContent) {
Text(buildAnnotatedString {
withStyle(style = SpanStyle(color = MaterialTheme.colors.primary)) {
append(content.oldAssociationName)
}
append("重命名为")
withStyle(style = SpanStyle(color = MaterialTheme.colors.secondary)) {
append(content.newAssociationName)
}
append("审核通过")
})
}
}

@ -24,7 +24,6 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
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
@ -90,7 +89,12 @@ enum class MainMenu(
* @param onClick
*/
@Composable
fun MenuIconButton(_menu: MainMenu, menu: MainMenu, modifier: Modifier=Modifier, onClick: () -> Unit) {
fun MenuIconButton(
_menu: MainMenu,
menu: MainMenu,
modifier: Modifier = Modifier,
onClick: () -> Unit
) {
Row(
modifier = modifier, horizontalArrangement = Arrangement.Center
) {
@ -113,7 +117,7 @@ fun MenuIconButton(_menu: MainMenu, menu: MainMenu, modifier: Modifier=Modifier,
@Composable
fun MainBottomAppBar(menu: MainMenu, nav: NavHostController, modifier: Modifier = Modifier) {
BottomAppBar(backgroundColor = MaterialTheme.colors.background, modifier = modifier) {
Row(Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
MenuIconButton(_menu = menu, menu = MainMenu.Main,
onClick = { nav.navigate(MainMenu.Main.name) })
MenuIconButton(_menu = menu, menu = MainMenu.List,
@ -130,7 +134,7 @@ fun MainBottomAppBar(menu: MainMenu, nav: NavHostController, modifier: Modifier
* 顶部菜单
*
*/
interface TopMenuInterface<T:TopBarMenu>{
interface TopMenuInterface<T : TopBarMenu> {
/**
* 当前菜单
*/
@ -142,59 +146,59 @@ interface TopMenuInterface<T:TopBarMenu>{
*
* @param menu
*/
fun clickMenu(menu:T){
_currentMenu.value=menu
fun clickMenu(menu: T) {
_currentMenu.value = menu
}
}
interface TopBarMenu{
val menuName:String
interface TopBarMenu {
val menuName: String
val name:String
val name: String
}
interface StartMenu<T>{
val startMenu:T
interface StartMenu<T> {
val startMenu: T
}
/**
* 社团菜单
*
*/
enum class AssociationMenu(override val menuName: String):TopBarMenu {
enum class AssociationMenu(override val menuName: String) : TopBarMenu {
Member("社团成员"),
Main("社团主页"),
ActivityList("活动列表");
companion object Menu:StartMenu<AssociationMenu>{
companion object Menu : StartMenu<AssociationMenu> {
override val startMenu: AssociationMenu = Main
}
}
enum class ActivityDetailMenu(override val menuName:String):TopBarMenu{
enum class ActivityDetailMenu(override val menuName: String) : TopBarMenu {
Info("活动信息"),
Photo("相册"),
Member("活动成员"),
BBS("交流区");
companion object Menu:StartMenu<ActivityDetailMenu>{
companion object Menu : StartMenu<ActivityDetailMenu> {
override val startMenu: ActivityDetailMenu = Info
}
}
interface SendInterface{
interface SendInterface {
/**
* 弹窗状态
*/
val _openDialog:MutableLiveData<Boolean>
val openDialog:LiveData<Boolean>
val _openDialog: MutableLiveData<Boolean>
val openDialog: LiveData<Boolean>
/**
* 编辑内容
*/
val newContent:StringForm
val newContent: StringForm
/**
* 打开弹窗
@ -224,9 +228,9 @@ interface SendInterface{
* @param scaffoldModel
*/
@Composable
fun <T:SendInterface> SendComment(model:T, scaffoldModel: ScaffoldModel= viewModel()){
fun <T : SendInterface> SendComment(model: T, scaffoldModel: ScaffoldModel = viewModel()) {
val openDialog by model.openDialog.observeAsState()
if(openDialog==true) {
if (openDialog == true) {
AlertDialog(onDismissRequest = { /*TODO*/ },
buttons = {
Row(
@ -265,22 +269,51 @@ fun <T:SendInterface> SendComment(model:T, scaffoldModel: ScaffoldModel= viewMod
}
/**
* 社团顶部菜单
* 只包含返回按钮的顶部菜单
*
* @param title
*/
@Composable
fun TextTopAppBar(modifier: Modifier = Modifier, title: String) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
val context = LocalContext.current as Activity
Row(modifier = Modifier.weight(1F / 3)) {
IconButton(onClick = {
context.finish()
}) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_left),
contentDescription = null
)
}
}
Row(modifier = Modifier.weight(1F / 3), horizontalArrangement = Arrangement.Center) {
Text(text = title, style = MaterialTheme.typography.h5)
}
Spacer(modifier = Modifier.weight(1F / 3))
}
}
/**
* 顶部菜单
*
*/
@Composable
fun TextTopAppBar(
nav:NavHostController,
currentMenuName:String,
menuNames:Array<out TopBarMenu>,
nav: NavHostController,
currentMenuName: String,
menuNames: Array<out TopBarMenu>,
iconMenu: (() -> Unit)? = null,
@DrawableRes icon:Int = R.drawable.ic_configuration,
dropMenuContent:@Composable () -> Unit = {},
@DrawableRes icon: Int = R.drawable.ic_configuration,
dropMenuContent: @Composable () -> Unit = {},
) {
TopAppBar(backgroundColor = MaterialTheme.colors.secondary) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
val context= LocalContext.current as Activity
IconButton(onClick = {context.finish()}, modifier = Modifier.weight(0.1F)) {
val context = LocalContext.current as Activity
IconButton(onClick = { context.finish() }, modifier = Modifier.weight(0.1F)) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_left),
contentDescription = null
@ -307,18 +340,18 @@ fun TextTopAppBar(
}
}
}
if(iconMenu!=null) {
if (iconMenu != null) {
IconButton(onClick = iconMenu, modifier = Modifier.weight(0.1F)) {
Icon(
painter = painterResource(id = icon),
contentDescription = null
)
}
}else{
} else {
Spacer(modifier = Modifier.weight(0.1F))
}
}
if(iconMenu!=null) dropMenuContent()
if (iconMenu != null) dropMenuContent()
}
}
@ -480,17 +513,17 @@ fun ShowSnackbar(model: ScaffoldModel = viewModel(), scaffoldState: ScaffoldStat
LaunchedEffect(scaffoldState) {
launch {
if (actionLabel != null) {
val result=scaffoldState.snackbarHostState.showSnackbar(
val result = scaffoldState.snackbarHostState.showSnackbar(
message = message, actionLabel = actionLabel,
duration = duration
)
when(result){
SnackbarResult.ActionPerformed->{
when (result) {
SnackbarResult.ActionPerformed -> {
Logger.i("点击操作按钮")
callback()
}
SnackbarResult.Dismissed->{
SnackbarResult.Dismissed -> {
Logger.d("窗口消失")
}
}
@ -535,12 +568,20 @@ enum class BackgroundImage(@DrawableRes val id: Int) {
//活动信息
ActivityInfo(R.drawable.mb_bg_fb_01),
//活动相册
ActivityPhoto(R.drawable.mb_bg_fb_02),
//活动成员
ActivityMember(R.drawable.mb_bg_fb_03),
//交流区
ActivityBBS(R.drawable.mb_bg_fb_04)
ActivityBBS(R.drawable.mb_bg_fb_04),
//系统通知
ActivityMessage(R.drawable.mb_bg_fb_26)
}
/**
@ -631,7 +672,7 @@ fun Poster(modifier: Modifier = Modifier, @DrawableRes id: Int) {
*
*/
@Composable
fun DescCard(modifier: Modifier,content:String) {
fun DescCard(modifier: Modifier, content: String) {
Card(
modifier = modifier,
backgroundColor = Color.Transparent
@ -643,9 +684,11 @@ fun DescCard(modifier: Modifier,content:String) {
contentScale = ContentScale.FillBounds,
modifier = Modifier.fillMaxSize()
)
Column(modifier = Modifier
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 10.dp)) {
.padding(horizontal = 10.dp)
) {
Spacer(modifier = Modifier.weight(0.15F))
Text(
modifier = Modifier.weight(0.65F),
@ -665,10 +708,10 @@ fun AnimationTextPreview() {
AnimationText(text = "6666")
}
@Preview
//@Preview
@Composable
fun MyBottomAppBarPreview() {
val arr:List<Arrangement.Horizontal> = listOf(
val arr: List<Arrangement.Horizontal> = listOf(
Arrangement.Start,
Arrangement.End,
Arrangement.Center,
@ -680,26 +723,33 @@ fun MyBottomAppBarPreview() {
mutableStateOf(3)
}
Column(modifier = Modifier.fillMaxSize()) {
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceEvenly) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
OutlinedButton(onClick = { i += 1 }) {
Text(text = "添加元素")
}
if(i>1) {
if (i > 1) {
OutlinedButton(onClick = { i -= 1 }) {
Text(text = "删除元素")
}
}
}
arr.forEach {
Column(modifier = Modifier
Column(
modifier = Modifier
.weight(1F / arr.size)
.border(width = 1.dp, color = MaterialTheme.colors.background)) {
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
Text(text = "$it",style = MaterialTheme.typography.h5)
.border(width = 1.dp, color = MaterialTheme.colors.background)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(text = "$it", style = MaterialTheme.typography.h5)
}
Row(modifier = Modifier.fillMaxSize(),
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = it,
verticalAlignment = Alignment.CenterVertically) {
verticalAlignment = Alignment.CenterVertically
) {
repeat(i) {
Text(text = "$it", style = MaterialTheme.typography.h3)
}

@ -6,16 +6,16 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.orhanobut.logger.Logger
interface FormLength{
val nameLengthError:String
interface FormLength {
val nameLengthError: String
}
abstract class FormName<T>(val formDesc:String){
protected val _formValue= MutableLiveData<T>()
abstract class FormName<T>(val formDesc: String) {
protected val _formValue = MutableLiveData<T>()
val formValue: LiveData<T> = _formValue
val formPlaceholder="请输入$formDesc"
val formPlaceholder = "请输入$formDesc"
abstract fun onChange(value:T)
abstract fun onChange(value: T)
}
@ -28,55 +28,66 @@ abstract class FormName<T>(val formDesc:String){
*
* @param formDesc
*/
open class StringForm(formDesc: String, val textLength: Int) : FormName<String>(formDesc = formDesc),
open class StringForm(formDesc: String, val textLength: Int) :
FormName<String>(formDesc = formDesc),
FormLength {
constructor(formDesc: String,textLength: Int,value:String) : this(formDesc = formDesc,textLength = textLength) {
_formValue.value=value
constructor(formDesc: String, textLength: Int, value: String) : this(
formDesc = formDesc,
textLength = textLength
) {
_formValue.value = value
}
override val nameLengthError="${formDesc}不能超过最大长度$textLength"
override val nameLengthError = "${formDesc}不能超过最大长度$textLength"
override fun onChange(value: String) {
if(value.length>textLength){
_formValue.value=value.slice(IntRange(0,textLength-1))
}else{
_formValue.value=value
if (value.length > textLength) {
_formValue.value = value.slice(IntRange(0, textLength - 1))
} else {
_formValue.value = value
}
Logger.i("${formDesc}更新值:${_formValue.value}")
}
}
data class SnackBar(val message:String?, val actionLabel:String?=null,val duration: SnackbarDuration=SnackbarDuration.Short, val callback: () -> Unit?)
data class SnackBar(
val message: String?,
val actionLabel: String? = null,
val duration: SnackbarDuration = SnackbarDuration.Short,
val callback: () -> Unit?
)
/**
* snackbar
*
*/
class ScaffoldModel:ViewModel(){
private val _data=MutableLiveData<SnackBar>()
val data:LiveData<SnackBar> = _data
fun update(message:String?=null,actionLabel: String? = null,callback: () -> Unit? = {}){
if(message==null){
_data.value=null
}else {
_data.value = SnackBar(message = message, actionLabel = actionLabel,callback = callback)
class ScaffoldModel : ViewModel() {
private val _data = MutableLiveData<SnackBar>()
val data: LiveData<SnackBar> = _data
fun update(message: String? = null, actionLabel: String? = null, callback: () -> Unit? = {}) {
if (message == null) {
_data.value = null
} else {
_data.value =
SnackBar(message = message, actionLabel = actionLabel, callback = callback)
}
}
}
abstract class ScrollList<T>:ViewModel(){
protected val _data=MutableLiveData<MutableList<T>>(mutableListOf())
val data:LiveData<MutableList<T>> = _data
abstract class ScrollList<T> : ViewModel() {
protected val _data = MutableLiveData<MutableList<T>>(mutableListOf())
val data: LiveData<MutableList<T>> = _data
abstract val initSize:Int
abstract val initSize: Int
//加载列表
abstract fun load()
//加载更多数据
abstract fun loadMore(callback:(message:String) -> Unit)
abstract fun loadMore(callback: (message: String) -> Unit)
}

@ -6,77 +6,84 @@ import com.gyf.csams.association.model.*
import com.gyf.csams.uikit.StringForm
import java.lang.reflect.Type
class OpenQuestionsVoSerializer: JsonSerializer<OpenQuestionsVo> {
class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> {
override fun serialize(
vo: OpenQuestionsVo?,
p1: Type?,
p2: JsonSerializationContext?
): JsonElement {
val gson= Gson()
val gson = Gson()
val c = JsonObject()
c.add("examType", gson.toJsonTree(vo?.examType?.name))
c.add("question",gson.toJsonTree(vo?.question?.formValue?.value))
c.add("question", gson.toJsonTree(vo?.question?.formValue?.value))
return c
}
}
class OpenQuestionsVoDeserializer: JsonDeserializer<OpenQuestionsVo> {
class OpenQuestionsVoDeserializer : JsonDeserializer<OpenQuestionsVo> {
override fun deserialize(
json: JsonElement?,
p1: Type?,
p2: JsonDeserializationContext?
): OpenQuestionsVo {
val root=json?.asJsonObject
val value=root?.get("question")?.asString
val question= StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH)
val root = json?.asJsonObject
val value = root?.get("question")?.asString
val question = StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH)
if (value != null) {
question.onChange(value)
return OpenQuestionsVo(question = question)
}else{
} else {
throw NullPointerException("问题无法解析!!!!")
}
}
}
class ChoiceQuestionVoSerializer: JsonSerializer<ChoiceQuestionVo> {
class ChoiceQuestionVoSerializer : JsonSerializer<ChoiceQuestionVo> {
override fun serialize(
vo: ChoiceQuestionVo?,
p1: Type?,
p2: JsonSerializationContext?
): JsonElement {
val gson= Gson()
val gson = Gson()
val c = JsonObject()
c.add("examType", gson.toJsonTree(vo?.examType?.name))
c.add("question",gson.toJsonTree(vo?.question?.formValue?.value))
c.add("answers",gson.toJsonTree(vo?.answers))
c.add("rightAnswer",gson.toJsonTree(vo?.rightAnswer))
c.add("question", gson.toJsonTree(vo?.question?.formValue?.value))
c.add("answers", gson.toJsonTree(vo?.answers))
c.add("rightAnswer", gson.toJsonTree(vo?.rightAnswer))
return c
}
}
class ChoiceQuestionVoDeserializer: JsonDeserializer<ChoiceQuestionVo> {
class ChoiceQuestionVoDeserializer : JsonDeserializer<ChoiceQuestionVo> {
override fun deserialize(
json: JsonElement?,
p1: Type?,
p2: JsonDeserializationContext?
): ChoiceQuestionVo {
val root=json?.asJsonObject
val value=root?.get("question")?.asString
val question=StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH)
val root = json?.asJsonObject
val value = root?.get("question")?.asString
val question = StringForm(formDesc = "问题", textLength = QUESTION_TEXT_LENGTH)
if (value != null) {
question.onChange(value)
val gson=Gson()
val answers:List<String> = gson.fromJson(root.get("answers"),object : TypeToken<List<String>>() {}.type)
if(answers.size!= ANSWER_SIZE){
val gson = Gson()
val answers: List<String> =
gson.fromJson(root.get("answers"), object : TypeToken<List<String>>() {}.type)
if (answers.size != ANSWER_SIZE) {
throw IllegalArgumentException("选项数量!=$QUESTION_TEXT_LENGTH")
}
val rightAnswer=root.get("rightAnswer").asInt
val rightAnswer = root.get("rightAnswer").asInt
return ChoiceQuestionVo(
question = question,
answers = answers.map { StringForm(formDesc = "选项",textLength = ANSWER_TEXT_LENGTH,value=it) },
answers = answers.map {
StringForm(
formDesc = "选项",
textLength = ANSWER_TEXT_LENGTH,
value = it
)
},
rightAnswer = rightAnswer
)
}else{
} else {
throw NullPointerException("问题无法解析!!!!")
}
}
@ -86,25 +93,29 @@ class ChoiceQuestionVoDeserializer: JsonDeserializer<ChoiceQuestionVo> {
* 题目反序列化
*
*/
class ExamDeserializer:JsonDeserializer<Exam>{
companion object{
val gson: Gson =GsonBuilder()
.registerTypeAdapter(OpenQuestionsVo::class.java,OpenQuestionsVoDeserializer())
.registerTypeAdapter(ChoiceQuestionVo::class.java,ChoiceQuestionVoDeserializer())
class ExamDeserializer : JsonDeserializer<Exam> {
companion object {
val gson: Gson = GsonBuilder()
.registerTypeAdapter(OpenQuestionsVo::class.java, OpenQuestionsVoDeserializer())
.registerTypeAdapter(ChoiceQuestionVo::class.java, ChoiceQuestionVoDeserializer())
.create()
}
override fun deserialize(json: JsonElement?, p1: Type?, p2: JsonDeserializationContext?): Exam {
val root=json?.asJsonObject
val root = json?.asJsonObject
val type=root?.get("examType")?.asString
val type = root?.get("examType")?.asString
return when (gson.fromJson(type, ExamType::class.java)) {
ExamType.CQ -> gson.fromJson(json,
object : TypeToken<ChoiceQuestionVo>() {}.type)
ExamType.OQ -> gson.fromJson(json,
object : TypeToken<OpenQuestionsVo>() {}.type)
null->throw NullPointerException("无法识别题目类型!")
ExamType.CQ -> gson.fromJson(
json,
object : TypeToken<ChoiceQuestionVo>() {}.type
)
ExamType.OQ -> gson.fromJson(
json,
object : TypeToken<OpenQuestionsVo>() {}.type
)
null -> throw NullPointerException("无法识别题目类型!")
}
}

@ -10,30 +10,30 @@ import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.lang.reflect.Type
object HttpClient{
private val httpClient:OkHttpClient=OkHttpClient()
object HttpClient {
private val httpClient: OkHttpClient = OkHttpClient()
private val JSON_CONTENT_TYPE="application/json; charset=UTF-8".toMediaType()
private val JSON_CONTENT_TYPE = "application/json; charset=UTF-8".toMediaType()
private fun buildQueryParams(params:Map<String,String>?):String{
return if(params?.isNotEmpty() == true) {
private fun buildQueryParams(params: Map<String, String>?): String {
return if (params?.isNotEmpty() == true) {
val urlPath = StringBuilder("?")
for(i in params){
for (i in params) {
urlPath.append(i.key).append("=").append(i.value)
}
urlPath.toString()
}else{
} else {
""
}
}
private fun buildFormBody(params: Map<String, String>?):FormBody{
val builder=FormBody.Builder()
if(params?.isNotEmpty()==true){
for(item in params){
builder.add(item.key,item.value)
private fun buildFormBody(params: Map<String, String>?): FormBody {
val builder = FormBody.Builder()
if (params?.isNotEmpty() == true) {
for (item in params) {
builder.add(item.key, item.value)
}
}
return builder.build()
@ -46,11 +46,11 @@ object HttpClient{
* @param callback
* @param params
*/
fun get(url:String, callback: Callback, params: Map<String, String>?=null){
fun get(url: String, callback: Callback, params: Map<String, String>? = null) {
val request = Request.Builder()
.url(url.plus(buildQueryParams(params = params)))
.build()
val call=httpClient.newCall(request)
val call = httpClient.newCall(request)
call.enqueue(callback)
}
@ -62,12 +62,12 @@ object HttpClient{
* @param callback
* @param params
*/
fun post(url:String, callback: Callback, params: Map<String, String>?=null){
fun post(url: String, callback: Callback, params: Map<String, String>? = null) {
val request = Request.Builder()
.url(url)
.post(body = buildFormBody(params))
.build()
val call=httpClient.newCall(request)
val call = httpClient.newCall(request)
call.enqueue(callback)
}
@ -79,13 +79,13 @@ object HttpClient{
* @param callback
* @param jsonBody
*/
fun post(url:String, callback: Callback, jsonBody:String){
fun post(url: String, callback: Callback, jsonBody: String) {
Logger.json(jsonBody)
val request = Request.Builder()
.url(url)
.post(body = jsonBody.toRequestBody(contentType = JSON_CONTENT_TYPE))
.build()
val call=httpClient.newCall(request)
val call = httpClient.newCall(request)
call.enqueue(callback)
}
@ -93,31 +93,33 @@ object HttpClient{
}
data class ApiResponse<T>(val code:Int,val message:String,val body:T?=null)
data class ApiResponse<T>(val code: Int, val message: String, val body: T? = null)
class SimpleCallback<T>(private val action:String,
private val onSuccess:(res:ApiResponse<T>) -> Unit,
private val onFail:(error:String) -> Unit,
private val type: Type):Callback{
class SimpleCallback<T>(
private val action: String,
private val onSuccess: (res: ApiResponse<T>) -> Unit,
private val onFail: (error: String) -> Unit,
private val type: Type
) : Callback {
companion object{
val gson: Gson =GsonBuilder()
.registerTypeAdapter(Exam::class.java,ExamDeserializer())
companion object {
val gson: Gson = GsonBuilder()
.registerTypeAdapter(Exam::class.java, ExamDeserializer())
.create()
}
override fun onFailure(call: Call, e: IOException) {
onFail("${action}失败,请联系管理员")
Logger.e(e,"${action}请求失败,发生IO异常")
Logger.e(e, "${action}请求失败,发生IO异常")
}
override fun onResponse(call: Call, response: Response) {
if (response.code == 200) {
val body=response.body
if (body!=null&&body.contentType()?.subtype == "json") {
val jsonRes=body.string()
val res:ApiResponse<T> = gson.fromJson(jsonRes, type)
val body = response.body
if (body != null && body.contentType()?.subtype == "json") {
val jsonRes = body.string()
val res: ApiResponse<T> = gson.fromJson(jsonRes, type)
Logger.i("${action}请求响应成功:")
Logger.json(jsonRes)
onSuccess(res)
@ -125,7 +127,7 @@ class SimpleCallback<T>(private val action:String,
onFail("${action}失败,请联系管理员")
Logger.e("无法解析${action}请求响应数据:,响应码:${response.code},${response.body}")
}
}else{
} else {
onFail("${action}失败,请联系管理员")
Logger.e("${action}失败,请求响应码:${response.code}")
}

@ -4,8 +4,8 @@ import okhttp3.internal.toHexString
import java.text.SimpleDateFormat
import java.util.*
fun randomNum(length:Int=8):String{
return List(length) {('0' .. '9').random()}.joinToString("")
fun randomNum(length: Int = 8): String {
return List(length) { ('0'..'9').random() }.joinToString("")
}
@ -22,7 +22,7 @@ fun String.decodeUnicode(): String {
return String(unicodes.toCharArray())
}
val CHINESE_UNICODE_AREA=0X4e00..0X9fa5
val CHINESE_UNICODE_AREA = 0X4e00..0X9fa5
/**
* 随机中文
@ -30,26 +30,30 @@ val CHINESE_UNICODE_AREA=0X4e00..0X9fa5
* @param length
* @return
*/
fun randomChinese(length:Int=8):String{
return List(length){ "\\u${CHINESE_UNICODE_AREA.random().toHexString()}".decodeUnicode() }.joinToString("")
fun randomChinese(length: Int = 8): String {
return List(length) {
"\\u${
CHINESE_UNICODE_AREA.random().toHexString()
}".decodeUnicode()
}.joinToString("")
}
const val DATETIME_FORMAT="yyyy-MM-dd HH:mm"
const val DATETIME_FORMAT = "yyyy-MM-dd HH:mm"
const val START_TIME="2021-01-01 00:00"
const val START_TIME = "2021-01-01 00:00"
val FORMAT=SimpleDateFormat(DATETIME_FORMAT,Locale.US)
val FORMAT = SimpleDateFormat(DATETIME_FORMAT, Locale.US)
val startUnix= FORMAT.parse(START_TIME)?.time
val startUnix = FORMAT.parse(START_TIME)?.time
fun randomDateTime():Date{
fun randomDateTime(): Date {
if (startUnix != null) {
return Date("${(startUnix..Date().time).random()}".toLong())
}else{
} else {
throw IllegalArgumentException("生成随机失败,无法获取起始时间")
}
}
fun Date.format():String{
fun Date.format(): String {
return FORMAT.format(this)
}

@ -12,7 +12,11 @@ import kotlinx.serialization.Serializable
*/
@Entity
@Serializable
data class Token(@PrimaryKey val studentId:String,@ColumnInfo val token:String,@ColumnInfo val createTime:Long)
data class Token(
@PrimaryKey val studentId: String,
@ColumnInfo val token: String,
@ColumnInfo val createTime: Long
)
/**
* 令牌传输
@ -21,7 +25,7 @@ data class Token(@PrimaryKey val studentId:String,@ColumnInfo val token:String,@
* @property token
*/
@Serializable
data class TokenResDto(val isValid:Boolean,val token: Token?)
data class TokenResDto(val isValid: Boolean, val token: Token?)
@Dao
interface TokenDao {
@ -40,7 +44,7 @@ class TokenManager private constructor(private var token: Token?) {
@Volatile
private var instance: TokenManager? = null
fun getInstance(token: Token?=null) =
fun getInstance(token: Token? = null) =
instance ?: synchronized(this) {
instance ?: TokenManager(token).also { instance = it }
}

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M641.77,222.1L641.77,768.21c0,43.63 -53.87,64.79 -84.05,33.02l-140.31,-149.42 -12.07,-0.02L405.33,746.67a64,64 0,0 1,-64 64h-64a64,64 0,0 1,-64 -64v-94.89L182.06,651.78a64,64 0,0 1,-64 -64v-192.36a64,64 0,0 1,60.22 -63.89l4.61,-0.11 236.67,3.11 138.18,-145.45c30.17,-31.79 84.05,-10.6 84.05,33.02zM769.45,234.41c92.54,47.08 158.23,139.39 167.96,247.83v56.02c-9.45,105.47 -71.85,195.69 -160.43,243.88L747.46,725.33A245.27,245.27 0,0 0,874.67 510.25a245.33,245.33 0,0 0,-134.7 -219.03l29.48,-56.81zM577.77,260.91l-131.07,137.98 -264.64,-3.48v192.38l95.25,-0.04L277.33,746.67h64v-158.98l103.79,0.13 132.65,141.25L577.77,260.91zM715.54,338.33a191.47,191.47 0,0 1,94.44 165.42,191.47 191.47,0 0,1 -92.42,164.2l-29.65,-56.96a127.49,127.49 0,0 0,58.07 -107.24,127.49 127.49,0 0,0 -60.01,-108.5l29.57,-56.92z" />
</vector>
Loading…
Cancel
Save